From f81a2e12b09634cacd4ccf9da584c835ee71bf24 Mon Sep 17 00:00:00 2001 From: brain Date: Sun, 26 Jan 2003 23:53:03 +0000 Subject: Modified documentation for base classes Added base classes git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@151 e03df62e-2008-0410-955e-edbf42e46eb7 --- include/base.h | 27 + include/base.h~ | 27 + include/channels.h | 15 +- include/channels.h~ | 203 +++ include/connection.h | 44 + include/connection.h~ | 44 + include/ctables.h | 13 +- include/ctables.h~ | 59 + include/modules.h | 23 +- include/modules.h~ | 339 ++++ include/servers.h | 19 +- include/servers.h~ | 54 + include/users.h | 92 +- include/users.h~ | 172 ++ src/Makefile | 20 + src/Makefile.FreeBSD | 2 +- src/Makefile.Linux | 2 +- src/inspircd | Bin 0 -> 311459 bytes src/inspircd.cpp | 17 + src/inspircd.cpp~ | 4317 +++++++++++++++++++++++++++++++++++++++++++++++++ src/servers.cpp | 23 +- src/servers.cpp~ | 63 + 22 files changed, 5456 insertions(+), 119 deletions(-) create mode 100644 include/base.h create mode 100644 include/base.h~ create mode 100644 include/channels.h~ create mode 100644 include/connection.h create mode 100644 include/connection.h~ create mode 100644 include/ctables.h~ create mode 100644 include/modules.h~ create mode 100644 include/servers.h~ create mode 100644 include/users.h~ create mode 100644 src/Makefile create mode 100644 src/inspircd create mode 100644 src/inspircd.cpp~ create mode 100644 src/servers.cpp~ diff --git a/include/base.h b/include/base.h new file mode 100644 index 000000000..d5102bd7c --- /dev/null +++ b/include/base.h @@ -0,0 +1,27 @@ +/* + +$Log$ +Revision 1.1 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + + +*/ + +#include "inspircd_config.h" +#include + +#ifndef __BASE_H__ +#define __BASE_H__ + +class classbase +{ + public: + time_t age; + + classbase() { age = time(NULL); } + ~classbase() { } +}; + +#endif + diff --git a/include/base.h~ b/include/base.h~ new file mode 100644 index 000000000..8a1503c44 --- /dev/null +++ b/include/base.h~ @@ -0,0 +1,27 @@ +/* + +$Log$ +Revision 1.1 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + + +*/ + +#include "inspircd_config.h" +#include + +#ifndef __BASE_H__ +#define __BASE_H__ + +class classbase +{ + public: + time_t age; + private: + classbase() { age = time(NULL); } + ~classbase() { } +}; + +#endif + diff --git a/include/channels.h b/include/channels.h index c9cdd7b31..8d27f098d 100644 --- a/include/channels.h +++ b/include/channels.h @@ -1,8 +1,12 @@ /* $Log$ -Revision 1.1 2003/01/23 19:45:58 brain -Initial revision +Revision 1.2 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + +Revision 1.1.1.1 2003/01/23 19:45:58 brain +InspIRCd second source tree Revision 1.7 2003/01/22 00:44:26 brain Added documentation comments @@ -20,6 +24,7 @@ Changed user and channel structs to classes (finally) */ #include "inspircd_config.h" +#include "base.h" #include #include @@ -29,7 +34,7 @@ Changed user and channel structs to classes (finally) /** Holds an entry for a ban list, exemption list, or invite list. * This class contains a single element in a channel list, such as a banlist. */ -class HostItem +class HostItem : public classbase { public: time_t set_time; @@ -82,7 +87,7 @@ typedef vector InviteList; * This class represents a channel, and contains its name, modes, time created, topic, topic set time, * etc, and an instance of the BanList type. */ -class chanrec +class chanrec : public classbase { public: /** The channels name. @@ -178,7 +183,7 @@ class chanrec * a userrec and chanrec class. The uc_modes member holds a bitmask of which privilages the user * has on the channel, such as op, voice, etc. */ -class ucrec +class ucrec : public classbase { public: /** Contains a bitmask of the UCMODE_OP ... UCMODE_FOUNDER values. diff --git a/include/channels.h~ b/include/channels.h~ new file mode 100644 index 000000000..dc1ed38a2 --- /dev/null +++ b/include/channels.h~ @@ -0,0 +1,203 @@ +/* + +$Log$ +Revision 1.1 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + +Revision 1.1.1.1 2003/01/23 19:45:58 brain +InspIRCd second source tree + +Revision 1.7 2003/01/22 00:44:26 brain +Added documentation comments + +Revision 1.6 2003/01/21 21:11:17 brain +Added documentation + +Revision 1.5 2003/01/16 20:11:55 brain +fixed some ugly pointer bugs (thanks dblack and a|KK|y!) + +Revision 1.4 2003/01/15 22:47:44 brain +Changed user and channel structs to classes (finally) + + +*/ + +#include "inspircd_config.h" +#include +#include + +#ifndef __CHANNELS_H__ +#define __CHANNELS_H__ + +/** Holds an entry for a ban list, exemption list, or invite list. + * This class contains a single element in a channel list, such as a banlist. + */ +class HostItem +{ + public: + time_t set_time; + char set_by[NICKMAX]; + char data[MAXBUF]; + + HostItem() { /* stub */ } + virtual ~HostItem() { /* stub */ } +}; + +// banlist is inherited from HostList mainly for readability +// reasons only + +/** A subclass of HostItem designed to hold channel bans (+b) + */ +class BanItem : public HostItem +{ +}; + +// same with this... + +/** A subclass of HostItem designed to hold channel exempts (+e) + */ +class ExemptItem : public HostItem +{ +}; + +// and this... + +/** A subclass of HostItem designed to hold channel invites (+I) + */ +class InviteItem : public HostItem +{ +}; + + +/** Holds a complete ban list + */ +typedef vector BanList; + +/** Holds a complete exempt list + */ +typedef vector ExemptList; + +/** Holds a complete invite list + */ +typedef vector InviteList; + +/** Holds all relevent information for a channel. + * This class represents a channel, and contains its name, modes, time created, topic, topic set time, + * etc, and an instance of the BanList type. + */ +class chanrec +{ + public: + /** The channels name. + */ + char name[CHANMAX]; /* channel name */ + /** Custom modes for the channel. + * Plugins may use this field in any way they see fit. + */ + char custom_modes[MAXMODES]; /* modes handled by modules */ + /** Channel topic. + * If this is an empty string, no channel topic is set. + */ + char topic[MAXBUF]; + /** Creation time. + */ + time_t created; + /** Time topic was set. + * If no topic was ever set, this will be equal to chanrec::created + */ + time_t topicset; + /** The last user to set the topic. + * If this member is an empty string, no topic was ever set. + */ + char setby[NICKMAX]; + + /** Contains the channel user limit. + * If this value is zero, there is no limit in place. + */ + long limit; + + /** Contains the channel key. + * If this value is an empty string, there is no channel key in place. + */ + char key[32]; + + /** Nonzero if the mode +t is set. + */ + short int topiclock; + + /** Nonzero if the mode +n is set. + */ + short int noexternal; + + /** Nonzero if the mode +i is set. + */ + short int inviteonly; + + /** Nonzero if the mode +m is set. + */ + short int moderated; + + /** Nonzero if the mode +s is set. + * This value cannot be set at the same time as chanrec::c_private + */ + short int secret; + + /** Nonzero if the mode +p is set. + * This value cannot be set at the same time as chanrec::secret + */ + short int c_private; + + /** The list of all bans set on the channel. + */ + BanList bans; + + /** Creates a channel record and initialises it with default values + */ + chanrec() + { + strcpy(name,""); + strcpy(custom_modes,""); + strcpy(topic,""); + strcpy(setby,""); + strcpy(key,""); + created = topicset = limit = 0; + topiclock = noexternal = inviteonly = moderated = secret = c_private = false; + } + + virtual ~chanrec() { /* stub */ } +}; + +/* used to hold a channel and a users modes on that channel, e.g. +v, +h, +o + * needs to come AFTER struct chanrec */ + +#define UCMODE_OP 1 +#define UCMODE_VOICE 2 +#define UCMODE_HOP 4 +#define UCMODE_PROTECT 8 +#define UCMODE_FOUNDER 16 + +/** Holds a user's modes on a channel + * This class associates a users privilages with a channel by creating a pointer link between + * a userrec and chanrec class. The uc_modes member holds a bitmask of which privilages the user + * has on the channel, such as op, voice, etc. + */ +class ucrec +{ + public: + /** Contains a bitmask of the UCMODE_OP ... UCMODE_FOUNDER values. + * If this value is zero, the user has no privilages upon the channel. + */ + long uc_modes; + + /** Points to the channel record where the given modes apply. + * If the record is not in use, this value will be NULL. + */ + chanrec *channel; + + ucrec() { /* stub */ } + virtual ~ucrec() { /* stub */ } +}; + +#endif + diff --git a/include/connection.h b/include/connection.h new file mode 100644 index 000000000..d74d6d20f --- /dev/null +++ b/include/connection.h @@ -0,0 +1,44 @@ +/* + +$Log$ +Revision 1.1 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + +Revision 1.1 2003/01/26 20:15:00 brain +Added server classes for linking + + +*/ + +#include "inspircd_config.h" +#include "base.h" +#include +#include + +#ifndef __CONNECTION_H__ +#define __CONNECTION_H__ + +class connection : public classbase +{ + public: + int fd; // file descriptor + char host[256]; // hostname + long ip; // ipv4 address + char inbuf[MAXBUF]; // recvQ + long bytes_in; + long bytes_out; + long cmds_in; + long cmds_out; + bool haspassed; + int port; + int registered; + time_t lastping; + time_t signon; + time_t idle_lastmsg; + time_t nping; +}; + + +#endif + diff --git a/include/connection.h~ b/include/connection.h~ new file mode 100644 index 000000000..d74d6d20f --- /dev/null +++ b/include/connection.h~ @@ -0,0 +1,44 @@ +/* + +$Log$ +Revision 1.1 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + +Revision 1.1 2003/01/26 20:15:00 brain +Added server classes for linking + + +*/ + +#include "inspircd_config.h" +#include "base.h" +#include +#include + +#ifndef __CONNECTION_H__ +#define __CONNECTION_H__ + +class connection : public classbase +{ + public: + int fd; // file descriptor + char host[256]; // hostname + long ip; // ipv4 address + char inbuf[MAXBUF]; // recvQ + long bytes_in; + long bytes_out; + long cmds_in; + long cmds_out; + bool haspassed; + int port; + int registered; + time_t lastping; + time_t signon; + time_t idle_lastmsg; + time_t nping; +}; + + +#endif + diff --git a/include/ctables.h b/include/ctables.h index 78eb150d9..33b4d564c 100644 --- a/include/ctables.h +++ b/include/ctables.h @@ -14,8 +14,12 @@ * --------------------------------------------------- $Log$ - Revision 1.1 2003/01/23 19:45:58 brain - Initial revision + Revision 1.2 2003/01/26 23:52:59 brain + Modified documentation for base classes + Added base classes + + Revision 1.1.1.1 2003/01/23 19:45:58 brain + InspIRCd second source tree Revision 1.3 2003/01/15 22:47:44 brain Changed user and channel structs to classes (finally) @@ -32,6 +36,7 @@ */ #include "inspircd_config.h" #include "inspircd.h" +#include "base.h" #ifndef __CTABLES_H__ #define __CTABLES_H__ @@ -40,7 +45,9 @@ typedef void (handlerfunc) (char**, int, userrec*); /* a structure that defines a command */ -struct command_t { +class command_t : public classbase +{ + public: char command[MAXBUF]; /* command name */ handlerfunc *handler_function; /* handler function as in typedef */ char flags_needed; /* user flags needed to execute the command or 0 */ diff --git a/include/ctables.h~ b/include/ctables.h~ new file mode 100644 index 000000000..489cfafe9 --- /dev/null +++ b/include/ctables.h~ @@ -0,0 +1,59 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * Inspire is copyright (C) 2002-2003 ChatSpike-Dev. + * E-mail: + * + * + * + * Written by Craig Edwards, Craig McLure, and others. + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + + $Log$ + Revision 1.1 2003/01/26 23:52:59 brain + Modified documentation for base classes + Added base classes + + Revision 1.1.1.1 2003/01/23 19:45:58 brain + InspIRCd second source tree + + Revision 1.3 2003/01/15 22:47:44 brain + Changed user and channel structs to classes (finally) + + Revision 1.2 2003/01/09 21:09:50 brain + added '/stats M' command + + Revision 1.1 2003/01/07 01:02:14 brain + + definitions for command table types + + + * --------------------------------------------------- + */ +#include "inspircd_config.h" +#include "inspircd.h" +#include "base.h" + +#ifndef __CTABLES_H__ +#define __CTABLES_H__ + +typedef void (handlerfunc) (char**, int, userrec*); + +/* a structure that defines a command */ + +class command_t : public classbase +{ + char command[MAXBUF]; /* command name */ + handlerfunc *handler_function; /* handler function as in typedef */ + char flags_needed; /* user flags needed to execute the command or 0 */ + int min_params; /* minimum number of parameters command takes */ + long use_count; /* used by /stats m */ + long total_bytes; /* used by /stats m */ +}; + +#endif + diff --git a/include/modules.h b/include/modules.h index 971f3ec26..5a7ec9fbf 100644 --- a/include/modules.h +++ b/include/modules.h @@ -1,8 +1,12 @@ /* $Log$ -Revision 1.1 2003/01/23 19:45:58 brain -Initial revision +Revision 1.2 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + +Revision 1.1.1.1 2003/01/23 19:45:58 brain +InspIRCd second source tree Revision 1.12 2003/01/22 20:59:10 brain Added FileReader class documentation @@ -36,6 +40,7 @@ Added methods to Server class #define __PLUGIN_H #include "dynamic.h" +#include "base.h" #include #include @@ -57,7 +62,7 @@ typedef deque file_cache; * The four members (set by the constructor only) indicate details as to the version number * of a module. A class of type Version is returned by the GetVersion method of the Module class. */ -class Version +class Version : public classbase { public: const int Major, Minor, Revision, Build; @@ -70,7 +75,7 @@ class Version * and has three read-only values, Name, Email and Nick that contain the specified values for the * server where the module is running. */ -class Admin +class Admin : public classbase { public: const string Name, Email, Nick; @@ -82,7 +87,7 @@ class Admin * its methods will be called when irc server events occur. class inherited from module must be * instantiated by the ModuleFactory class (see relevent section) for the plugin to be initialised. */ -class Module +class Module : public classbase { public: /** Default constructor @@ -124,7 +129,7 @@ class Module * output to users and other servers. All modules should instantiate at least one copy of this class, * and use its member functions to perform their tasks. */ -class Server +class Server : public classbase { public: /** Default constructor. @@ -229,7 +234,7 @@ class Server * Constructing the class using one parameter allows you to specify a path to your own configuration * file, otherwise, inspircd.conf is read. */ -class ConfigReader +class ConfigReader : public classbase { protected: /** The filename of the configuration file, as set by the constructor. @@ -274,7 +279,7 @@ class ConfigReader * Either use the constructor type with one parameter to load a file into memory * at construction, or use the LoadFile method to load a file. */ -class FileReader +class FileReader : public classbase { file_cache fc; public: @@ -319,7 +324,7 @@ class FileReader * In most cases, the simple class shown in the example module m_foobar.so will suffice for most * modules. */ -class ModuleFactory +class ModuleFactory : public classbase { public: ModuleFactory() { } diff --git a/include/modules.h~ b/include/modules.h~ new file mode 100644 index 000000000..ec5df7da6 --- /dev/null +++ b/include/modules.h~ @@ -0,0 +1,339 @@ +/* + +$Log$ +Revision 1.1 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + +Revision 1.1.1.1 2003/01/23 19:45:58 brain +InspIRCd second source tree + +Revision 1.12 2003/01/22 20:59:10 brain +Added FileReader class documentation + +Revision 1.11 2003/01/22 20:49:16 brain +Added FileReader file-caching class +Changed m_randquote to use FileReader class + +Revision 1.10 2003/01/22 00:57:27 brain +Changes to documentation + +Revision 1.9 2003/01/22 00:44:26 brain +Added documentation comments + +Revision 1.8 2003/01/21 20:31:24 brain +Modified to add documentation +Added ConfigReader class for modules + +Revision 1.7 2003/01/15 22:47:44 brain +Changed user and channel structs to classes (finally) + +Revision 1.6 2003/01/13 22:30:50 brain +Added Admin class (holds /admin info for modules) +Added methods to Server class + + +*/ + + +#ifndef __PLUGIN_H +#define __PLUGIN_H + +#include "dynamic.h" +#include "base.h" +#include +#include + +/** Low level definition of a FileReader classes file cache area + */ +typedef deque file_cache; + + +// This #define allows us to call a method in all +// loaded modules in a readable simple way, e.g.: +// 'FOREACH_MOD OnConnect(user);' + +#define FOREACH_MOD for (int i = 0; i <= MODCOUNT; i++) modules[i]-> + +// class Version holds the version information of a Module, returned +// by Module::GetVersion (thanks RD) + +/** Holds a module's Version information + * The four members (set by the constructor only) indicate details as to the version number + * of a module. A class of type Version is returned by the GetVersion method of the Module class. + */ +class Version +{ + public: + const int Major, Minor, Revision, Build; + Version(int major, int minor, int revision, int build); +}; + + +/** Holds /ADMIN data + * This class contains the admin details of the local server. It is constructed by class Server, + * and has three read-only values, Name, Email and Nick that contain the specified values for the + * server where the module is running. + */ +class Admin +{ + public: + const string Name, Email, Nick; + Admin(string name,string email,string nick); +}; + +/** Base class for all InspIRCd modules + * This class is the base class for InspIRCd modules. All modules must inherit from this class, + * its methods will be called when irc server events occur. class inherited from module must be + * instantiated by the ModuleFactory class (see relevent section) for the plugin to be initialised. + */ +class Module +{ + public: + /** Default constructor + * creates a module class + */ + Module(); + /** Default destructor + * destroys a module class + */ + virtual ~Module(); + /** Returns the version number of a Module. + * The method should return a Version object with its version information assigned via + * Version::Version + */ + virtual Version GetVersion(); + /** Called when a user connects. + * The details of the connecting user are available to you in the parameter userrec *user + */ + virtual void OnUserConnect(userrec* user); + /** Called when a user quits. + * The details of the exiting user are available to you in the parameter userrec *user + */ + virtual void OnUserQuit(userrec* user); + /** Called when a user joins a channel. + * The details of the joining user are available to you in the parameter userrec *user, + * and the details of the channel they have joined is available in the variable chanrec *channel + */ + virtual void OnUserJoin(userrec* user, chanrec* channel); + /** Called when a user parts a channel. + * The details of the leaving user are available to you in the parameter userrec *user, + * and the details of the channel they have left is available in the variable chanrec *channel + */ + virtual void OnUserPart(userrec* user, chanrec* channel); +}; + + +/** Allows server output and query functions + * This class contains methods which allow a module to query the state of the irc server, and produce + * output to users and other servers. All modules should instantiate at least one copy of this class, + * and use its member functions to perform their tasks. + */ +class Server +{ + public: + /** Default constructor. + * Creates a Server object. + */ + Server(); + /** Default destructor. + * Destroys a Server object. + */ + virtual ~Server(); + + /** Sends text to all opers. + * This method sends a server notice to all opers with the usermode +s. + */ + virtual void SendOpers(string s); + /** Sends a debug string. + * This method writes a line of text to the debug log. If debugging is disabled + * in the configuration, this command has no effect. + */ + virtual void Debug(string s); + /** Sends a line of text down a TCP/IP socket. + * This method writes a line of text to an established socket, cutting it to 510 characters + * plus a carriage return and linefeed if required. + */ + virtual void Send(int Socket, string s); + /** Sends text from the server to a socket. + * This method writes a line of text to an established socket, with the servername prepended + * as used by numerics (see RFC 1459) + */ + virtual void SendServ(int Socket, string s); + /** Sends text from a user to a socket. + * This method writes a line of text to an established socket, with the given user's nick/ident + * /host combination prepended, as used in PRIVSG etc commands (see RFC 1459) + */ + virtual void SendFrom(int Socket, userrec* User, string s); + /** Sends text from a user to another user. + * This method writes a line of text to a user, with a user's nick/ident + * /host combination prepended, as used in PRIVMSG etc commands (see RFC 1459) + */ + virtual void SendTo(userrec* Source, userrec* Dest, string s); + /** Sends text from a user to a channel (mulicast). + * This method writes a line of text to a channel, with the given user's nick/ident + * /host combination prepended, as used in PRIVMSG etc commands (see RFC 1459). If the + * IncludeSender flag is set, then the text is also sent back to the user from which + * it originated, as seen in MODE (see RFC 1459). + */ + virtual void SendChannel(userrec* User, chanrec* Channel, string s,bool IncludeSender); + /** Returns true if two users share a common channel. + * This method is used internally by the NICK and QUIT commands, and the Server::SendCommon + * method. + */ + virtual bool CommonChannels(userrec* u1, userrec* u2); + /** Sends text from a user to one or more channels (mulicast). + * This method writes a line of text to all users which share a common channel with a given + * user, with the user's nick/ident/host combination prepended, as used in PRIVMSG etc + * commands (see RFC 1459). If the IncludeSender flag is set, then the text is also sent + * back to the user from which it originated, as seen in NICK (see RFC 1459). Otherwise, it + * is only sent to the other recipients, as seen in QUIT. + */ + virtual void SendCommon(userrec* User, string text,bool IncludeSender); + /** Sends a WALLOPS message. + * This method writes a WALLOPS message to all users with the +w flag, originating from the + * specified user. + */ + virtual void SendWallops(userrec* User, string text); + + /** Returns true if a nick is valid. + * Nicks for unregistered connections will return false. + */ + virtual bool IsNick(string nick); + /** Attempts to look up a nick and return a pointer to it. + * This function will return NULL if the nick does not exist. + */ + virtual userrec* FindNick(string nick); + /** Attempts to look up a channel and return a pointer to it. + * This function will return NULL if the channel does not exist. + */ + virtual chanrec* FindChannel(string channel); + /** Attempts to look up a user's privilages on a channel. + * This function will return a string containing either @, %, +, or an empty string, + * representing the user's privilages upon the channel you specify. + */ + virtual string ChanMode(userrec* User, chanrec* Chan); + /** Returns the server name of the server where the module is loaded. + */ + virtual string GetServerName(); + /** Returns the network name, global to all linked servers. + */ + virtual string GetNetworkName(); + /** Returns the information of the server as returned by the /ADMIN command. + * See the Admin class for further information of the return value. The members + * Admin::Nick, Admin::Email and Admin::Name contain the information for the + * server where the module is loaded. + */ + virtual Admin GetAdmin(); + +}; + +/** Allows reading of values from configuration files + * This class allows a module to read from either the main configuration file (inspircd.conf) or from + * a module-specified configuration file. It may either be instantiated with one parameter or none. + * Constructing the class using one parameter allows you to specify a path to your own configuration + * file, otherwise, inspircd.conf is read. + */ +class ConfigReader +{ + protected: + /** The filename of the configuration file, as set by the constructor. + */ + string fname; + public: + /** Default constructor. + * This constructor initialises the ConfigReader class to read the inspircd.conf file + * as specified when running ./configure. + */ + ConfigReader(); // default constructor reads ircd.conf + /** Overloaded constructor. + * This constructor initialises the ConfigReader class to read a user-specified config file + */ + ConfigReader(string filename); // read a module-specific config + /** Default destructor. + * This method destroys the ConfigReader class. + */ + ~ConfigReader(); + /** Retrieves a value from the config file. + * This method retrieves a value from the config file. Where multiple copies of the tag + * exist in the config file, index indicates which of the values to retrieve. + */ + string ReadValue(string tag, string name, int index); + /** Counts the number of times a given tag appears in the config file. + * This method counts the number of times a tag appears in a config file, for use where + * there are several tags of the same kind, e.g. with opers and connect types. It can be + * used with the index value of ConfigReader::ReadValue to loop through all copies of a + * multiple instance tag. + */ + int Enumerate(string tag); + /** Returns true if a config file is valid. + * This method is unimplemented and will always return true. + */ + bool Verify(); +}; + + + +/** Caches a text file into memory and can be used to retrieve lines from it. + * This class contains methods for read-only manipulation of a text file in memory. + * Either use the constructor type with one parameter to load a file into memory + * at construction, or use the LoadFile method to load a file. + */ +class FileReader +{ + file_cache fc; + public: + /** Default constructor. + * This method does not load any file into memory, you must use the LoadFile method + * after constructing the class this way. + */ + FileReader(); + /** Secondary constructor. + * This method initialises the class with a file loaded into it ready for GetLine and + * and other methods to be called. If the file could not be loaded, FileReader::FileSize + * returns 0. + */ + FileReader(string filename); + /** Default destructor. + * This deletes the memory allocated to the file. + */ + ~FileReader(); + /** Used to load a file. + * This method loads a file into the class ready for GetLine and + * and other methods to be called. If the file could not be loaded, FileReader::FileSize + * returns 0. + */ + void LoadFile(string filename); + /** Retrieve one line from the file. + * This method retrieves one line from the text file. If an empty non-NULL string is returned, + * the index was out of bounds, or the line had no data on it. + */ + string GetLine(int x); + /** Returns the size of the file in lines. + * This method returns the number of lines in the read file. If it is 0, no lines have been + * read into memory, either because the file is empty or it does not exist, or cannot be + * opened due to permission problems. + */ + int FileSize(); +}; + + +/** Instantiates classes inherited from Module + * This class creates a class inherited from type Module, using new. This is to allow for modules + * to create many different variants of Module, dependent on architecture, configuration, etc. + * In most cases, the simple class shown in the example module m_foobar.so will suffice for most + * modules. + */ +class ModuleFactory +{ + public: + ModuleFactory() { } + virtual ~ModuleFactory() { } + /** Creates a new module. + * Your inherited class of ModuleFactory must return a pointer to your Module class + * using this method. + */ + virtual Module * CreateModule() = 0; +}; + +#endif diff --git a/include/servers.h b/include/servers.h index e62f4d3fa..b6e10f91d 100644 --- a/include/servers.h +++ b/include/servers.h @@ -1,6 +1,10 @@ /* $Log$ +Revision 1.2 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + Revision 1.1 2003/01/26 20:15:00 brain Added server classes for linking @@ -8,7 +12,9 @@ Added server classes for linking */ #include "inspircd_config.h" +#include "connection.h" #include +#include #ifndef __SERVERS_H__ #define __SERVERS_H__ @@ -16,12 +22,10 @@ Added server classes for linking #define LINK_ACTIVE 1 #define LINK_INACTIVE 0 -typedef vector server_list; - -class serverrec +class serverrec : public connection { private: - server_list leaf; // list of child servers (leaves) + map leaf; // list of child servers (leaves) public: char name[MAXBUF]; // server name int pingtime; // last ping response (ms) @@ -37,11 +41,14 @@ class serverrec serverrec(); serverrec(char* n, int link_t, long ver, bool jupe); - ~serverrec() + ~serverrec(); void AddLeaf(serverrec *child); - void DelLeaf(char* n); + void DelLeaf(string n); }; + +typedef map server_list; + #endif diff --git a/include/servers.h~ b/include/servers.h~ new file mode 100644 index 000000000..d8d7d6add --- /dev/null +++ b/include/servers.h~ @@ -0,0 +1,54 @@ +/* + +$Log$ +Revision 1.1 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + +Revision 1.1 2003/01/26 20:15:00 brain +Added server classes for linking + + +*/ + +#include "inspircd_config.h" +#include "connection.h" +#include +#include + +#ifndef __SERVERS_H__ +#define __SERVERS_H__ + +#define LINK_ACTIVE 1 +#define LINK_INACTIVE 0 + +class serverrec : public connection +{ + private: + map leaf; // list of child servers (leaves) + public: + char name[MAXBUF]; // server name + int pingtime; // last ping response (ms) + int linktype; // link type, LINK_ACTIVE or LINK_INACTIVE + time_t lastping; // time the link was last pinged + long usercount_i; // invisible users on server + long usercount; // non-invisible users on server + long opercount; // opers on server + time_t connected_at; // time server was connected into the network + time_t hops_away; // number of hops away (for quick access) + long version; // ircd version + bool jupiter; // is a JUPE server (faked to enforce a server ban) + + serverrec(); + serverrec(char* n, int link_t, long ver, bool jupe); + ~serverrec(); + void AddLeaf(serverrec *child); + void DelLeaf(string n); +}; + + + +typedef map server_list; + +#endif + diff --git a/include/users.h b/include/users.h index dddda8dd6..b1b21ae0b 100644 --- a/include/users.h +++ b/include/users.h @@ -1,8 +1,12 @@ /* $Log$ -Revision 1.1 2003/01/23 19:45:58 brain -Initial revision +Revision 1.2 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + +Revision 1.1.1.1 2003/01/23 19:45:58 brain +InspIRCd second source tree Revision 1.9 2003/01/22 00:44:26 brain Added documentation comments @@ -31,6 +35,7 @@ added /ISON command (for mIRC etc basic notify) #include "inspircd_config.h" #include "channels.h" +#include "connection.h" #include @@ -80,7 +85,7 @@ typedef vector ClassVector; * user's nickname and hostname. Use the Find method of the server class to locate a specific user * by nickname. */ -class userrec +class userrec : public connection { private: @@ -95,18 +100,10 @@ class userrec char nick[NICKMAX]; - /** The users ip address in network order. - */ - unsigned long ip; - /** The users ident reply. */ char ident[64]; - /** The users hostname, or ip address in string form. - */ - char host[256]; - /** The host displayed to non-opers (used for cloaking etc). * This usually matches the value of userrec::host. */ @@ -116,55 +113,12 @@ class userrec */ char fullname[128]; - /** The users file descriptor. - * If this is zero, the socket has been closed and the core has not yet - * realised and removed the record from memory. - */ - int fd; - /** The user's mode string. * This may contain any of the following RFC characters: o, w, s, i * Your module may define other mode characters as it sees fit. */ char modes[32]; - /** The users input buffer. - * Used by the C recv() function. - */ - char inbuf[MAXBUF]; - - /** The last time the user was pinged by the core. - * When this value is more than 120 seconds difference from 'time(NULL)', a ping is sent - * to the client. If the user has an outstanding PING request the next time this - * event occurs after 4 total minutes, they are disconnected. - */ - time_t lastping; - - /** The users signon time. - */ - time_t signon; - - /** The time the user last sent a message. - * See also userrec::lastping and userrec::signon - */ - time_t idle_lastmsg; - - /** True if the user replied to their last ping. - * If this is true, the user can be sent another ping at the specified time, otherwise - * they will be discnnected. See also userrec::lastping - */ - time_t nping; - - /** Bit 1 is set if the user sent a NICK command, bit 2 is set if the user sent a USER command. - * If both bits are set then the connection is awaiting MOTD. Sending of MOTD sets bit 3, and - * makes the value of userrec::registered == 7, showing a fully established client session. - */ - int registered; - - /** A list of the channels the user is currently on. - * If any of these values are NULL, the record is not in use and may be associated with - * a channel by the JOIN command. see RFC 1459. - */ ucrec chans[MAXCHANS]; /** The server the user is connected to. @@ -176,41 +130,11 @@ class userrec */ char awaymsg[512]; - /** The port that the user connected to. - */ - int port; - - /** Stores the number of incoming bytes from the connection. - * Used by /STATS - */ - long bytes_in; - - /** Stores the number of outgoing bytes to the connection. - * Used by /STATS - */ - long bytes_out; - - /** Stores the number of incoming commands from the connection. - * Used by /STATS - */ - long cmds_in; - - /** Stores the number of outgoing commands to the connection. - * Used by /STATS - */ - long cmds_out; - /** Stores the result of the last GetFullHost or GetRealHost call. * You may use this to increase the speed of use of this class. */ char result[256]; - /** True if a correct password has been given using PASS command. - * If the user is a member of a connection class that does not require a password, - * the value stored here is of no use. - */ - bool haspassed; - userrec(); virtual ~userrec() { } diff --git a/include/users.h~ b/include/users.h~ new file mode 100644 index 000000000..4f00b0d17 --- /dev/null +++ b/include/users.h~ @@ -0,0 +1,172 @@ +/* + +$Log$ +Revision 1.1 2003/01/26 23:52:59 brain +Modified documentation for base classes +Added base classes + +Revision 1.1.1.1 2003/01/23 19:45:58 brain +InspIRCd second source tree + +Revision 1.9 2003/01/22 00:44:26 brain +Added documentation comments + +Revision 1.8 2003/01/21 21:11:17 brain +Added documentation + +Revision 1.7 2003/01/17 13:21:38 brain +Added CONNECT ALLOW and CONNECT DENY config tags +Added PASS command + +Revision 1.6 2003/01/17 10:37:55 brain +Added /INVITE command and relevent structures + +Revision 1.5 2003/01/16 20:11:56 brain +fixed some ugly pointer bugs (thanks dblack and a|KK|y!) + +Revision 1.4 2003/01/15 22:47:44 brain +Changed user and channel structs to classes (finally) + +Revision 1.3 2003/01/14 21:14:30 brain +added /ISON command (for mIRC etc basic notify) + + +*/ + +#include "inspircd_config.h" +#include "channels.h" +#include "connection.h" + +#include + +#ifndef __USERS_H__ +#define __USERS_H__ + +#define STATUS_OP 4 +#define STATUS_HOP 2 +#define STATUS_VOICE 1 +#define STATUS_NORMAL 0 + +#define CC_ALLOW 0 +#define CC_DENY 1 + +/** Holds a channel name to which a user has been invited. + */ +class Invited +{ + public: + char channel[CHANMAX]; +}; + + +/** Holds information relevent to <connect allow> and <connect deny> tags in the config file. + */ +class ConnectClass +{ + public: + int type; + char host[MAXBUF]; + char pass[MAXBUF]; +}; + +/** Holds a complete list of all channels to which a user has been invited and has not yet joined. + */ +typedef vector InvitedList; + + + +/** Holds a complete list of all allow and deny tags from the configuration file (connection classes) + */ +typedef vector ClassVector; + +/** Holds all information about a user + * This class stores all information about a user connected to the irc server. Everything about a + * connection is stored here primarily, from the user's socket ID (file descriptor) through to the + * user's nickname and hostname. Use the Find method of the server class to locate a specific user + * by nickname. + */ +class userrec : public connection +{ + private: + + /** A list of channels the user has a pending invite to. + */ + InvitedList invites; + public: + + /** The users nickname. + * An invalid nickname indicates an unregistered connection prior to the NICK command. + */ + + char nick[NICKMAX]; + + /** The users ident reply. + */ + char ident[64]; + + /** The host displayed to non-opers (used for cloaking etc). + * This usually matches the value of userrec::host. + */ + char dhost[256]; + + /** The users full name. + */ + char fullname[128]; + + /** The user's mode string. + * This may contain any of the following RFC characters: o, w, s, i + * Your module may define other mode characters as it sees fit. + */ + char modes[32]; + + ucrec chans[MAXCHANS]; + + /** The server the user is connected to. + */ + char server[256]; + + /** The user's away message. + * If this string is empty, the user is not marked as away. + */ + char awaymsg[512]; + + /** Stores the result of the last GetFullHost or GetRealHost call. + * You may use this to increase the speed of use of this class. + */ + char result[256]; + + userrec(); + + virtual ~userrec() { } + + /** Returns the full displayed host of the user + * This member function returns the hostname of the user as seen by other users + * on the server, in nick!ident&at;host form. + */ + virtual char* GetFullHost(); + + /** Returns the full real host of the user + * This member function returns the hostname of the user as seen by other users + * on the server, in nick!ident&at;host form. If any form of hostname cloaking is in operation, + * e.g. through a module, then this method will ignore it and return the true hostname. + */ + virtual char* GetFullRealHost(); + + /** Returns true if a user is invited to a channel. + */ + virtual bool IsInvited(char* channel); + + /** Adds a channel to a users invite list (invites them to a channel) + */ + virtual void InviteTo(char* channel); + + /** Removes a channel from a users invite list. + * This member function is called on successfully joining an invite only channel + * to which the user has previously been invited, to clear the invitation. + */ + virtual void RemoveInvite(char* channel); + +}; + + +#endif diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 000000000..86f890ca3 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,20 @@ +# Insp Makefile :p +# +# +# + +CC = im a cheezeball + +SRC_1 = inspircd.cpp inspircd_util.cpp inspircd_io.cpp +SRC_2 = dynamic.cpp users.cpp modules.cpp wildcard.cpp servers.cpp + +SRC = $(SRC_1) $(SRC_2) +OBJS = $(SRC:.cpp=.o) + +CXXFLAGS = -I$../../include ${FLAGS} + +all: inspircd + +inspircd: $(OBJS) + $(CXX) -rdynamic $^ -o $@ $(LDLIBS) + diff --git a/src/Makefile.FreeBSD b/src/Makefile.FreeBSD index 60eaaf2f3..755f3c7ff 100644 --- a/src/Makefile.FreeBSD +++ b/src/Makefile.FreeBSD @@ -8,7 +8,7 @@ CC = im a cheezeball SRC_1 = inspircd.cpp inspircd_util.cpp inspircd_io.cpp -SRC_2 = dynamic.cpp users.cpp modules.cpp wildcard.cpp +SRC_2 = dynamic.cpp users.cpp modules.cpp wildcard.cpp servers.cpp SRC = $(SRC_1) $(SRC_2) OBJS = $(SRC:.cpp=.o) diff --git a/src/Makefile.Linux b/src/Makefile.Linux index 60eaaf2f3..755f3c7ff 100644 --- a/src/Makefile.Linux +++ b/src/Makefile.Linux @@ -8,7 +8,7 @@ CC = im a cheezeball SRC_1 = inspircd.cpp inspircd_util.cpp inspircd_io.cpp -SRC_2 = dynamic.cpp users.cpp modules.cpp wildcard.cpp +SRC_2 = dynamic.cpp users.cpp modules.cpp wildcard.cpp servers.cpp SRC = $(SRC_1) $(SRC_2) OBJS = $(SRC:.cpp=.o) diff --git a/src/inspircd b/src/inspircd new file mode 100644 index 000000000..97a752894 Binary files /dev/null and b/src/inspircd differ diff --git a/src/inspircd.cpp b/src/inspircd.cpp index a5bbdcfa0..9bc78878e 100644 --- a/src/inspircd.cpp +++ b/src/inspircd.cpp @@ -14,6 +14,10 @@ * --------------------------------------------------- $Log$ + Revision 1.5 2003/01/26 23:53:03 brain + Modified documentation for base classes + Added base classes + Revision 1.4 2003/01/26 20:15:03 brain Added server classes for linking @@ -208,10 +212,12 @@ #include #include #include +#include #include #include #include #include +#include "connection.h" #include "users.h" #include "servers.h" #include "ctables.h" @@ -3612,6 +3618,14 @@ void handle_stats(char **parameters, int pcnt, userrec *user) } +void handle_connect(char **parameters, int pcnt, userrec *user) +{ +} + +void handle_squit(char **parameters, int pcnt, userrec *user) +{ +} + void handle_oper(char **parameters, int pcnt, userrec *user) { char LoginName[MAXBUF]; @@ -4014,6 +4028,8 @@ void SetupCommandTable(void) createcommand("PASS",handle_pass,0,1); createcommand("TRACE",handle_trace,'o',0); createcommand("WHOWAS",handle_whowas,0,1); + createcommand("CONNECT",handle_connect,'o',1); + createcommand("SQUIT",handle_squit,'o',1); } void process_buffer(userrec *user) @@ -4285,6 +4301,7 @@ int InspIRCd(void) debug("InspIRCd: accept failed: %d",ports[count]); break; } + AddClient(incomingSockfd, resolved, ports[count], iscached); debug("InspIRCd: adding client on port %d fd=%d",ports[count],incomingSockfd); break; diff --git a/src/inspircd.cpp~ b/src/inspircd.cpp~ new file mode 100644 index 000000000..6d899f216 --- /dev/null +++ b/src/inspircd.cpp~ @@ -0,0 +1,4317 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * Inspire is copyright (C) 2002-2003 ChatSpike-Dev. + * E-mail: + * + * + * + * Written by Craig Edwards, Craig McLure, and others. + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + + $Log$ + Revision 1.1 2003/01/26 23:53:03 brain + Modified documentation for base classes + Added base classes + + Revision 1.4 2003/01/26 20:15:03 brain + Added server classes for linking + + Revision 1.3 2003/01/25 20:17:53 brain + Fixed WHOWAS memory leak + + Revision 1.2 2003/01/25 20:00:45 brain + Added /WHOWAS + + Revision 1.1.1.1 2003/01/23 19:45:58 brain + InspIRCd second source tree + + Revision 1.56 2003/01/22 20:49:16 brain + Added FileReader file-caching class + Changed m_randquote to use FileReader class + + Revision 1.55 2003/01/21 16:56:19 brain + Fixed a few minor bugs + + Revision 1.54 2003/01/21 02:07:10 brain + optimisations galore! + + Revision 1.53 2003/01/21 00:18:40 brain + fixed random crash on kill_link (AGAIN) - was /stats + improved speed 10x (because i can...) + + Revision 1.52 2003/01/19 20:12:24 brain + Fixed ident max length to 10 + + Revision 1.51 2003/01/18 22:02:11 brain + fixed multiple /MODE +l bugs (thanks to akky and BOFH bugging meh!) + + Revision 1.50 2003/01/18 01:19:14 brain + Added code to tidy up bans (e.g. max nick length) - i blame mIRC! + + Revision 1.49 2003/01/17 22:03:57 brain + Fixed dodgy mode glitches (the ones Craig loves to play with, awww) + + Revision 1.48 2003/01/17 21:20:43 brain + Implemented usermode +s + + Revision 1.47 2003/01/17 21:13:40 brain + Added channel modes, +k, +l, +i, +m etc + Added user and channel modes +i, +p, +s + + Revision 1.46 2003/01/17 18:44:27 brain + Implemented channel mode +m + + Revision 1.45 2003/01/17 18:26:42 brain + added /TRACE command + + Revision 1.44 2003/01/17 15:21:03 brain + Fixed: /LUSERS cant count :P + + Revision 1.43 2003/01/17 13:21:38 brain + Added CONNECT ALLOW and CONNECT DENY config tags + Added PASS command + + Revision 1.42 2003/01/17 10:37:55 brain + Added /INVITE command and relevent structures + + Revision 1.41 2003/01/16 20:11:55 brain + fixed some ugly pointer bugs (thanks dblack and a|KK|y!) + + Revision 1.40 2003/01/16 08:31:44 brain + Fixed parameter error in QUIT code (was showing junk chars on BSD) + + Revision 1.39 2003/01/15 22:47:44 brain + Changed user and channel structs to classes (finally) + + Revision 1.38 2003/01/15 20:56:58 brain + Added wildcard support + Added channel bans + + Revision 1.37 2003/01/15 16:08:42 brain + Attempted to fix closed client sessions not being detected + + Revision 1.36 2003/01/15 09:36:13 brain + added pause= value to /die and /restart in config + + Revision 1.35 2003/01/14 22:08:31 brain + attemted to fix weird crash on /kill + + Revision 1.34 2003/01/14 21:44:25 brain + Added /USERS stub + Added /SUMMON stub + Changed optimisation to -O3 (much faster!) + + Revision 1.33 2003/01/14 21:14:30 brain + added /ISON command (for mIRC etc basic notify) + + Revision 1.32 2003/01/14 20:55:02 brain + Fixed more param crunching bugs + Added /AWAY + + Revision 1.31 2003/01/14 00:46:02 brain + Added m_cloaking.so module, provides host masking + + Revision 1.30 2003/01/13 22:30:50 brain + Added Admin class (holds /admin info for modules) + Added methods to Server class + + Revision 1.29 2003/01/13 00:43:29 brain + Added Server class + Added more code to example module demonstrating use of Server class + + Revision 1.28 2003/01/12 17:40:44 brain + ./configure improved by Craig (better prompts, dir creation) + '/stats z' added detail + + Revision 1.27 2003/01/12 16:49:53 brain + Added '/stats z' + + Revision 1.26 2003/01/12 15:01:18 brain + Added hostname/ip caching to speed up connects + + Revision 1.25 2003/01/11 21:39:57 brain + Made ircd cache message of the day in a vector (faster!) + Added support for multiple lines of /NAMES on large channels + + Revision 1.24 2003/01/11 19:00:10 brain + Added /USERHOST command + + Revision 1.23 2003/01/11 17:57:28 brain + Added '/STATS O' + Added more module error checking + + Revision 1.22 2003/01/11 00:48:44 brain + removed random debug output + + Revision 1.21 2003/01/11 00:06:46 brain + Fixed random crash on nickchange + Fine tuned ability to handle >300 users + + Revision 1.20 2003/01/09 22:24:59 brain + added '/stats L' (connect-info) + + Revision 1.19 2003/01/09 21:38:51 brain + '/stats u' support added (server uptime) + + Revision 1.18 2003/01/09 21:09:50 brain + added '/stats M' command + + Revision 1.17 2003/01/08 22:11:38 brain + + Added extra dynamic module support, new methods to Module class + + Revision 1.16 2003/01/08 17:48:48 brain + + fixed "user lingering" problem in kill_link + + Revision 1.15 2003/01/07 23:17:51 brain + + Fixed wallops and command parameter counting bugs + + Revision 1.14 2003/01/07 20:47:34 brain + + Fixes random crash on nickchange (must keep classfactory pointers!) + + Revision 1.13 2003/01/07 19:57:56 brain + + Dynamix module support, preliminary release + + Revision 1.12 2003/01/07 01:01:30 brain + + Changed command table to a vector of command_t types + + Revision 1.11 2003/01/06 23:43:30 brain + + extra debug output + + Revision 1.10 2003/01/06 23:38:29 brain + + just playing with header tags + + + * --------------------------------------------------- + */ + +/* Now with added unF! ;) */ + +#include "inspircd.h" +#include "inspircd_io.h" +#include "inspircd_util.h" +#include "inspircd_config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "connection.h" +#include "users.h" +#include "servers.h" +#include "ctables.h" +#include "globals.h" +#include "modules.h" +#include "dynamic.h" +#include "wildcard.h" + +using namespace std; + +char ServerName[MAXBUF]; +char Network[MAXBUF]; +char ServerDesc[MAXBUF]; +char AdminName[MAXBUF]; +char AdminEmail[MAXBUF]; +char AdminNick[MAXBUF]; +char diepass[MAXBUF]; +char restartpass[MAXBUF]; +char motd[MAXBUF]; +char rules[MAXBUF]; +char list[MAXBUF]; +char PrefixQuit[MAXBUF]; +char DieValue[MAXBUF]; +int debugging = 0; +int MODCOUNT = -1; +int WHOWAS_STALE = 48; // default WHOWAS Entries last 2 days before they go 'stale' +int WHOWAS_MAX = 100; // default 100 people maximum in the WHOWAS list +int DieDelay = 5; +time_t startup_time = time(NULL); + +template<> struct hash +{ + size_t operator()(const struct in_addr &a) const + { + size_t q; + memcpy(&q,&a,sizeof(size_t)); + return q; + } +}; + +template<> struct hash +{ + size_t operator()(const string &s) const + { + char a[MAXBUF]; + static struct hash strhash; + strcpy(a,s.c_str()); + strlower(a); + return strhash(a); + } +}; + + + +struct StrHashComp +{ + + bool operator()(const string& s1, const string& s2) const + { + char a[MAXBUF],b[MAXBUF]; + strcpy(a,s1.c_str()); + strcpy(b,s2.c_str()); + return (strcasecmp(a,b) == 0); + } + +}; + +struct InAddr_HashComp +{ + + bool operator()(const in_addr &s1, const in_addr &s2) const + { + size_t q; + size_t p; + + memcpy(&q,&s1,sizeof(size_t)); + memcpy(&p,&s2,sizeof(size_t)); + + return (q == p); + } + +}; + + +typedef hash_map, StrHashComp> user_hash; +typedef hash_map, StrHashComp> chan_hash; +typedef hash_map, InAddr_HashComp> address_cache; +typedef deque command_table; +typedef DLLFactory ircd_module; + +user_hash clientlist; +chan_hash chanlist; +user_hash whowas; +server_list servers; +command_table cmdlist; +file_cache MOTD; +file_cache RULES; +address_cache IP; +vector modules(255); +vector factory(255); +ClassVector Classes; + +struct linger linger = { 0 }; +char bannerBuffer[MAXBUF]; +int boundPortCount = 0; + +/* prototypes */ + +int has_channel(userrec *u, chanrec *c); +int usercount(chanrec *c); +int usercount_i(chanrec *c); +void update_stats_l(int fd,int data_out); +char* Passwd(userrec *user); +bool IsDenied(userrec *user); +void AddWhoWas(userrec* u); + + +void safedelete(userrec *p) +{ + if (p) + { + debug("deleting %s %s %s %s",p->nick,p->ident,p->dhost,p->fullname); + debug("safedelete(userrec*): pointer is safe to delete"); + delete p; + } + else + { + debug("safedelete(userrec*): unsafe pointer operation squished"); + } +} + +void safedelete(chanrec *p) +{ + if (p) + { + delete p; + debug("safedelete(chanrec*): pointer is safe to delete"); + } + else + { + debug("safedelete(chanrec*): unsafe pointer operation squished"); + } +} + + +/* chop a string down to 512 characters and preserve linefeed (irc max + * line length) */ + +void chop(char* str) +{ + if (strlen(str) > 512) + { + str[510] = '\r'; + str[511] = '\n'; + str[512] = '\0'; + } +} + + +string getservername() +{ + return ServerName; +} + +string getnetworkname() +{ + return Network; +} + +string getadminname() +{ + return AdminName; +} + +string getadminemail() +{ + return AdminEmail; +} + +string getadminnick() +{ + return AdminNick; +} + +void debug(char *text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + FILE *f; + time_t rawtime; + struct tm * timeinfo; + + time(&rawtime); + timeinfo = localtime (&rawtime); + + if (debugging) + { + f = fopen("ircd.log","a+"); + if (f) + { + char b[MAXBUF]; + va_start (argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + strcpy(b,asctime(timeinfo)); + b[strlen(b)-1] = ':'; + fprintf(f,"%s %s\n",b,textbuffer); + fclose(f); + } + else + { + printf("Can't write log file, bailing!!!"); + Exit(ERROR); + } + } +} + +void readfile(file_cache &F, const char* fname) +{ + FILE* file; + char linebuf[MAXBUF]; + + debug("readfile: loading %s",fname); + F.clear(); + file = fopen(fname,"r"); + if (file) + { + while (!feof(file)) + { + fgets(linebuf,sizeof(linebuf),file); + linebuf[strlen(linebuf)-1]='\0'; + if (!strcmp(linebuf,"")) + { + strcpy(linebuf," "); + } + if (!feof(file)) + { + F.push_back(linebuf); + } + } + fclose(file); + } + else + { + debug("readfile: failed to load file: %s",fname); + } + debug("readfile: loaded %s, %d lines",fname,F.size()); +} + +void ReadConfig(void) +{ + char dbg[MAXBUF],pauseval[MAXBUF],Value[MAXBUF]; + ConnectClass c; + + ConfValue("server","name",0,ServerName); + ConfValue("server","description",0,ServerDesc); + ConfValue("server","network",0,Network); + ConfValue("admin","name",0,AdminName); + ConfValue("admin","email",0,AdminEmail); + ConfValue("admin","nick",0,AdminNick); + ConfValue("files","motd",0,motd); + ConfValue("files","rules",0,rules); + ConfValue("power","diepass",0,diepass); + ConfValue("power","pause",0,pauseval); + ConfValue("power","restartpass",0,restartpass); + ConfValue("options","prefixquit",0,PrefixQuit); + ConfValue("die","value",0,DieValue); + ConfValue("options","debug",0,dbg); + debugging = 0; + if (!strcmp(dbg,"on")) + { + debugging = 1; + } + DieDelay = atoi(pauseval); + readfile(MOTD,motd); + readfile(RULES,rules); + debug("Reading connect classes"); + Classes.clear(); + for (int i = 0; i < ConfValueEnum("connect"); i++) + { + strcpy(Value,""); + ConfValue("connect","allow",i,Value); + if (strcmp(Value,"")) + { + strcpy(c.host,Value); + c.type = CC_ALLOW; + strcpy(Value,""); + ConfValue("connect","password",i,Value); + strcpy(c.pass,Value); + Classes.push_back(c); + debug("Read connect class type ALLOW, host=%s password=%s",c.host,c.pass); + } + else + { + ConfValue("connect","deny",i,Value); + strcpy(c.host,Value); + c.type = CC_DENY; + Classes.push_back(c); + debug("Read connect class type DENY, host=%s",c.host); + } + + } +} + +void Blocking(int s) +{ + int flags; + debug("Blocking: %d",s); + flags = fcntl(s, F_GETFL, 0); + fcntl(s, F_SETFL, flags ^ O_NONBLOCK); +} + +void NonBlocking(int s) +{ + int flags; + debug("NonBlocking: %d",s); + flags = fcntl(s, F_GETFL, 0); + fcntl(s, F_SETFL, flags | O_NONBLOCK); +} + + +int CleanAndResolve (char *resolvedHost, const char *unresolvedHost) +{ + struct hostent *hostPtr = NULL; + struct in_addr addr; + + memset (resolvedHost, '\0',MAXBUF); + if(unresolvedHost == NULL) + return(ERROR); + if ((inet_aton(unresolvedHost,&addr)) == 0) + return(ERROR); + hostPtr = gethostbyaddr ((char *)&addr.s_addr,sizeof(addr.s_addr),AF_INET); + if (hostPtr != NULL) + snprintf(resolvedHost,MAXBUF,"%s",hostPtr->h_name); + else + snprintf(resolvedHost,MAXBUF,"%s",unresolvedHost); + return (TRUE); +} + +/* write formatted text to a socket, in same format as printf */ + +void Write(int sock,char *text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + char tb[MAXBUF]; + + va_start (argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + sprintf(tb,"%s\r\n",textbuffer); + chop(tb); + write(sock,tb,strlen(tb)); + update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */ +} + +/* write a server formatted numeric response to a single socket */ + +void WriteServ(int sock, char* text, ...) +{ + char textbuffer[MAXBUF],tb[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, text); + + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + sprintf(tb,":%s %s\r\n",ServerName,textbuffer); + chop(tb); + write(sock,tb,strlen(tb)); + update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */ +} + +/* write text from an originating user to originating user */ + +void WriteFrom(int sock, userrec *user,char* text, ...) +{ + char textbuffer[MAXBUF],tb[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, text); + + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + sprintf(tb,":%s!%s@%s %s\r\n",user->nick,user->ident,user->dhost,textbuffer); + chop(tb); + write(sock,tb,strlen(tb)); + update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */ +} + +/* write text to an destination user from a source user (e.g. user privmsg) */ + +void WriteTo(userrec *source, userrec *dest,char *data, ...) +{ + char textbuffer[MAXBUF],tb[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, data); + if ((!dest) || (!source)) + { + return; + } + vsnprintf(textbuffer, MAXBUF, data, argsPtr); + va_end(argsPtr); + chop(tb); + WriteFrom(dest->fd,source,"%s",textbuffer); +} + +/* write formatted text from a source user to all users on a channel + * including the sender (NOT for privmsg, notice etc!) */ + +void WriteChannel(chanrec* Ptr, userrec* user, char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (has_channel(i->second,Ptr)) + { + WriteTo(user,i->second,"%s",textbuffer); + } + } +} + +/* write formatted text from a source user to all users on a channel except + * for the sender (for privmsg etc) */ + +void ChanExceptSender(chanrec* Ptr, userrec* user, char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (has_channel(i->second,Ptr) && (user != i->second)) + { + WriteTo(user,i->second,"%s",textbuffer); + } + } +} + +int c_count(userrec* u) +{ + int z = 0; + for (int i =0; i != MAXCHANS; i++) + if (u->chans[i].channel) + z++; + return z; + +} + +/* return 0 or 1 depending if users u and u2 share one or more common channels + * (used by QUIT, NICK etc which arent channel specific notices) */ + +int common_channels(userrec *u, userrec *u2) +{ + int i = 0; + int z = 0; + + if ((!u) || (!u2)) + { + return 0; + } + for (i = 0; i != MAXCHANS; i++) + { + for (z = 0; z != MAXCHANS; z++) + { + if ((u->chans[i].channel == u2->chans[z].channel) && (u->chans[i].channel) && (u2->chans[z].channel) && (u->registered == 7) && (u2->registered == 7)) + { + if ((c_count(u)) && (c_count(u2))) + { + return 1; + } + } + } + } + return 0; +} + +/* write a formatted string to all users who share at least one common + * channel, including the source user e.g. for use in NICK */ + +void WriteCommon(userrec *u, char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + WriteFrom(u->fd,u,"%s",textbuffer); + + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (common_channels(u,i->second) && (i->second != u)) + { + WriteFrom(i->second->fd,u,"%s",textbuffer); + } + } +} + +/* write a formatted string to all users who share at least one common + * channel, NOT including the source user e.g. for use in QUIT */ + +void WriteCommonExcept(userrec *u, char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if ((common_channels(u,i->second)) && (u != i->second)) + { + WriteFrom(i->second->fd,u,"%s",textbuffer); + } + } +} + +void WriteOpers(char* text, ...) +{ + char textbuffer[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (strchr(i->second->modes,'o')) + { + if (strchr(i->second->modes,'s')) + { + // send server notices to all with +s + // (TODO: needs SNOMASKs) + WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,textbuffer); + } + } + } +} + +void WriteWallOps(userrec *source, char* text, ...) +{ + int i = 0; + char textbuffer[MAXBUF]; + va_list argsPtr; + va_start (argsPtr, text); + vsnprintf(textbuffer, MAXBUF, text, argsPtr); + va_end(argsPtr); + + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (strchr(i->second->modes,'w')) + { + WriteTo(source,i->second,"WALLOPS %s",textbuffer); + } + } +} + +/* convert a string to lowercase. Note following special circumstances + * taken from RFC 1459. Many "official" server branches still hold to this + * rule so i will too; + * + * Because of IRC's scandanavian origin, the characters {}| are + * considered to be the lower case equivalents of the characters []\, + * respectively. This is a critical issue when determining the + * equivalence of two nicknames. + */ + +void strlower(char *n) +{ + if (!n) + { + return; + } + for (int i = 0; i != strlen(n); i++) + { + n[i] = tolower(n[i]); + if (n[i] == '[') + n[i] = '{'; + if (n[i] == ']') + n[i] = '}'; + if (n[i] == '\\') + n[i] = '|'; + } +} + +/* verify that a user's nickname is valid */ + +int isnick(const char* n) +{ + int i = 0; + char v[MAXBUF]; + if (!n) + { + return 0; + } + if (!strcmp(n,"")) + { + return 0; + } + if (strlen(n) > NICKMAX-1) + { + return 0; + } + for (i = 0; i != strlen(n); i++) + { + if ((n[i] < 33) || (n[i] > 125)) + { + return 0; + } + /* can't occur ANYWHERE in a nickname! */ + if (strchr("<>,./?:;@'~#=+()*&%$£ \"!",n[i])) + { + return 0; + } + /* can't occur as the first char of a nickname... */ + if ((strchr("0123456789",n[i])) && (!i)) + { + return 0; + } + } + return 1; +} + +/* Find a user record by nickname and return a pointer to it */ + +userrec* Find(string nick) +{ + user_hash::iterator iter = clientlist.find(nick); + + if (iter == clientlist.end()) + /* Couldn't find it */ + return NULL; + + return iter->second; +} + +void update_stats_l(int fd,int data_out) /* add one line-out to stats L for this fd */ +{ + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (i->second->fd == fd) + { + i->second->bytes_out+=data_out; + i->second->cmds_out++; + } + } +} + + +/* find a channel record by channel name and return a pointer to it */ + +chanrec* FindChan(const char* chan) +{ + chan_hash::iterator iter = chanlist.find(chan); + + if (iter == chanlist.end()) + /* Couldn't find it */ + return NULL; + + return iter->second; +} + + +void purge_empty_chans(void) +{ + int go_again = 1, purge = 0; + + while (go_again) + { + go_again = 0; + for (chan_hash::iterator i = chanlist.begin(); i != chanlist.end(); i++) + { + if (i->second) { + if (!usercount(i->second)) + { + /* kill the record */ + if (i != chanlist.end()) + { + debug("del_channel: destroyed: %s",i->second->name); + delete i->second; + chanlist.erase(i); + go_again = 1; + purge++; + break; + } + } + } + } + } + debug("completed channel purge, killed %d",purge); +} + +/* returns the status character for a given user on a channel, e.g. @ for op, + * % for halfop etc. If the user has several modes set, the highest mode + * the user has must be returned. */ + +char* cmode(userrec *user, chanrec *chan) +{ + int i; + for (i = 0; i != MAXCHANS; i++) + { + if ((user->chans[i].channel == chan) && (chan != NULL)) + { + if ((user->chans[i].uc_modes & UCMODE_OP) > 0) + { + return "@"; + } + if ((user->chans[i].uc_modes & UCMODE_HOP) > 0) + { + return "%"; + } + if ((user->chans[i].uc_modes & UCMODE_VOICE) > 0) + { + return "+"; + } + return ""; + } + } +} + +char scratch[MAXMODES]; + +char* chanmodes(chanrec *chan) +{ + strcpy(scratch,""); + if (chan->noexternal) + { + strcat(scratch,"n"); + } + if (chan->topiclock) + { + strcat(scratch,"t"); + } + if (strcmp(chan->key,"")) + { + strcat(scratch,"k"); + } + if (chan->limit) + { + strcat(scratch,"l"); + } + if (chan->inviteonly) + { + strcat(scratch,"i"); + } + if (chan->moderated) + { + strcat(scratch,"m"); + } + if (chan->secret) + { + strcat(scratch,"s"); + } + if (chan->c_private) + { + strcat(scratch,"p"); + } + if (strcmp(chan->key,"")) + { + strcat(scratch," "); + strcat(scratch,chan->key); + } + if (chan->limit) + { + char foo[24]; + sprintf(foo," %d",chan->limit); + strcat(scratch,foo); + } + debug("chanmodes: %s %s",chan->name,scratch); + return scratch; +} + +/* returns the status value for a given user on a channel, e.g. STATUS_OP for + * op, STATUS_VOICE for voice etc. If the user has several modes set, the + * highest mode the user has must be returned. */ + +int cstatus(userrec *user, chanrec *chan) +{ + int i; + for (i = 0; i != MAXCHANS; i++) + { + if ((user->chans[i].channel == chan) && (chan != NULL)) + { + if ((user->chans[i].uc_modes & UCMODE_OP) > 0) + { + return STATUS_OP; + } + if ((user->chans[i].uc_modes & UCMODE_HOP) > 0) + { + return STATUS_HOP; + } + if ((user->chans[i].uc_modes & UCMODE_VOICE) > 0) + { + return STATUS_VOICE; + } + return STATUS_NORMAL; + } + } +} + + +/* compile a userlist of a channel into a string, each nick seperated by + * spaces and op, voice etc status shown as @ and + */ + +void userlist(userrec *user,chanrec *c) +{ + sprintf(list,"353 %s = %s :", user->nick, c->name); + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (has_channel(i->second,c)) + { + if (isnick(i->second->nick)) + { + if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i'))) + { + /* user is +i, and source not on the channel, does not show + * nick in NAMES list */ + continue; + } + strcat(list,cmode(i->second,c)); + strcat(list,i->second->nick); + strcat(list," "); + if (strlen(list)>(480-NICKMAX)) + { + /* list overflowed into + * multiple numerics */ + WriteServ(user->fd,list); + sprintf(list,"353 %s = %s :", user->nick, c->name); + } + } + } + } + /* if whats left in the list isnt empty, send it */ + if (list[strlen(list)-1] != ':') + { + WriteServ(user->fd,list); + } +} + +/* return a count of the users on a specific channel accounting for + * invisible users who won't increase the count. e.g. for /LIST */ + +int usercount_i(chanrec *c) +{ + int i = 0; + int count = 0; + + strcpy(list,""); + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (has_channel(i->second,c)) + { + if (isnick(i->second->nick)) + { + if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i'))) + { + /* user is +i, and source not on the channel, does not show + * nick in NAMES list */ + continue; + } + count++; + } + } + } + debug("usercount_i: %s %d",c->name,count); + return count; +} + + +int usercount(chanrec *c) +{ + int i = 0; + int count = 0; + + strcpy(list,""); + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (has_channel(i->second,c)) + { + if (isnick(i->second->nick)) + { + count++; + } + } + } + debug("usercount: %s %d",c->name,count); + return count; +} + + +/* add a channel to a user, creating the record for it if needed and linking + * it to the user record */ + +chanrec* add_channel(userrec *user, char* cname, char* key) +{ + int i = 0; + chanrec* Ptr; + int created = 0; + + if ((!cname) || (!user)) + { + return NULL; + } + if (strlen(cname) > CHANMAX-1) + { + cname[CHANMAX-1] = '\0'; + } + + debug("add_channel: %s %s",user->nick,cname); + + if ((has_channel(user,FindChan(cname))) && (FindChan(cname))) + { + return NULL; // already on the channel! + } + + if (!FindChan(cname)) + { + /* create a new one */ + debug("add_channel: creating: %s",cname); + { + chanlist[cname] = new chanrec(); + + strcpy(chanlist[cname]->name, cname); + chanlist[cname]->topiclock = 1; + chanlist[cname]->noexternal = 1; + chanlist[cname]->created = time(NULL); + strcpy(chanlist[cname]->topic, ""); + strncpy(chanlist[cname]->setby, user->nick,NICKMAX); + chanlist[cname]->topicset = 0; + Ptr = chanlist[cname]; + debug("add_channel: created: %s",cname); + /* set created to 2 to indicate user + * is the first in the channel + * and should be given ops */ + created = 2; + } + } + else + { + /* channel exists, just fish out a pointer to its struct */ + Ptr = FindChan(cname); + if (Ptr) + { + debug("add_channel: joining to: %s",Ptr->name); + if (strcmp(Ptr->key,"")) + { + debug("add_channel: %s has key %s",Ptr->name,Ptr->key); + if (!key) + { + debug("add_channel: no key given in JOIN"); + WriteServ(user->fd,"475 %s %s :Cannot join channel (Requires key)",user->nick, Ptr->name); + return NULL; + } + else + { + debug("key at %p is %s",key,key); + if (strcasecmp(key,Ptr->key)) + { + debug("add_channel: bad key given in JOIN"); + WriteServ(user->fd,"475 %s %s :Cannot join channel (Incorrect key)",user->nick, Ptr->name); + return NULL; + } + } + } + + if (Ptr->inviteonly) + { + if (user->IsInvited(Ptr->name)) + { + /* user was invited to channel */ + /* there may be an optional channel NOTICE here */ + } + else + { + WriteServ(user->fd,"473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name); + return NULL; + } + } + + if (Ptr->limit) + { + if (usercount(Ptr) == Ptr->limit) + { + WriteServ(user->fd,"471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name); + return NULL; + } + } + + /* check user against the channel banlist */ + for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++) + { + if (match(user->GetFullHost(),i->data)) + { + WriteServ(user->fd,"474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name); + return NULL; + } + } + + user->RemoveInvite(Ptr->name); + + } + created = 1; + } + + + for (i =0; i != MAXCHANS; i++) + { + if (user->chans[i].channel == NULL) + { + if (created == 2) + { + /* first user in is given ops */ + user->chans[i].uc_modes = UCMODE_OP; + } + else + { + user->chans[i].uc_modes = 0; + } + user->chans[i].channel = Ptr; + WriteChannel(Ptr,user,"JOIN :%s",Ptr->name); + if (Ptr->topicset) + { + WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic); + WriteServ(user->fd,"333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset); + } + userlist(user,Ptr); + WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, Ptr->name); + WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name,chanmodes(Ptr)); + WriteServ(user->fd,"329 %s %s %d", user->nick, Ptr->name, Ptr->created); + FOREACH_MOD OnUserJoin(user,Ptr); + return Ptr; + } + } + debug("add_channel: user channel max exceeded: %s %s",user->nick,cname); + WriteServ(user->fd,"405 %s %s :You are on too many channels",user->nick, cname); + return NULL; +} + +/* remove a channel from a users record, and remove the record from memory + * if the channel has become empty */ + +chanrec* del_channel(userrec *user, char* cname, char* reason) +{ + int i = 0; + chanrec* Ptr; + int created = 0; + + if ((!cname) || (!user)) + { + return NULL; + } + + Ptr = FindChan(cname); + + if (!Ptr) + { + return NULL; + } + + FOREACH_MOD OnUserPart(user,Ptr); + debug("del_channel: removing: %s %s",user->nick,Ptr->name); + + for (i =0; i != MAXCHANS; i++) + { + /* zap it from the channel list of the user */ + if (user->chans[i].channel == Ptr) + { + if (reason) + { + WriteChannel(Ptr,user,"PART %s :%s",Ptr->name, reason); + } + else + { + WriteChannel(Ptr,user,"PART :%s",Ptr->name); + } + user->chans[i].uc_modes = 0; + user->chans[i].channel = NULL; + debug("del_channel: unlinked: %s %s",user->nick,Ptr->name); + break; + } + } + + /* if there are no users left on the channel */ + if (!usercount(Ptr)) + { + chan_hash::iterator iter = chanlist.find(Ptr->name); + + debug("del_channel: destroying channel: %s",Ptr->name); + + /* kill the record */ + if (iter != chanlist.end()) + { + debug("del_channel: destroyed: %s",Ptr->name); + delete iter->second; + chanlist.erase(iter); + } + } +} + + +void kick_channel(userrec *src,userrec *user, chanrec *Ptr, char* reason) +{ + int i = 0; + int created = 0; + + if ((!Ptr) || (!user) || (!src)) + { + return; + } + + debug("kick_channel: removing: %s %s %s",user->nick,Ptr->name,src->nick); + + if (!has_channel(user,Ptr)) + { + WriteServ(src->fd,"441 %s %s %s :They are not on that channel",src->nick, user->nick, Ptr->name); + return; + } + if ((cstatus(src,Ptr) < STATUS_HOP) || (cstatus(src,Ptr) < cstatus(user,Ptr))) + { + if (cstatus(src,Ptr) == STATUS_HOP) + { + WriteServ(src->fd,"482 %s %s :You must be a channel operator",src->nick, Ptr->name); + } + else + { + WriteServ(src->fd,"482 %s %s :You must be at least a half-operator",src->nick, Ptr->name); + } + + return; + } + + for (i =0; i != MAXCHANS; i++) + { + /* zap it from the channel list of the user */ + if (user->chans[i].channel == Ptr) + { + WriteChannel(Ptr,src,"KICK %s %s :%s",Ptr->name, user->nick, reason); + user->chans[i].uc_modes = 0; + user->chans[i].channel = NULL; + debug("del_channel: unlinked: %s %s",user->nick,Ptr->name); + break; + } + } + + /* if there are no users left on the channel */ + if (!usercount(Ptr)) + { + chan_hash::iterator iter = chanlist.find(Ptr->name); + + debug("del_channel: destroying channel: %s",Ptr->name); + + /* kill the record */ + if (iter != chanlist.end()) + { + debug("del_channel: destroyed: %s",Ptr->name); + delete iter->second; + chanlist.erase(iter); + } + } +} + + +/* returns 1 if user u has channel c in their record, 0 if not */ + +int has_channel(userrec *u, chanrec *c) +{ + int i = 0; + + if (!u) + { + return 0; + } + for (i =0; i != MAXCHANS; i++) + { + if (u->chans[i].channel == c) + { + return 1; + } + } + return 0; +} + +int give_ops(userrec *user,char *dest,chanrec *chan,int status) +{ + userrec *d; + int i; + + if ((!user) || (!dest) || (!chan)) + { + return 0; + } + if (status != STATUS_OP) + { + WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name); + return 0; + } + else + { + if (!isnick(dest)) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + d = Find(dest); + if (!d) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + else + { + for (i = 0; i != MAXCHANS; i++) + { + if ((d->chans[i].channel == chan) && (chan != NULL)) + { + if (d->chans[i].uc_modes & UCMODE_OP) + { + /* mode already set on user, dont allow multiple */ + return 0; + } + d->chans[i].uc_modes = d->chans[i].uc_modes | UCMODE_OP; + debug("gave ops: %s %s",d->chans[i].channel->name,d->nick); + } + } + } + } + return 1; +} + +int give_hops(userrec *user,char *dest,chanrec *chan,int status) +{ + userrec *d; + int i; + + if ((!user) || (!dest) || (!chan)) + { + return 0; + } + if (status != STATUS_OP) + { + WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name); + return 0; + } + else + { + d = Find(dest); + if (!isnick(dest)) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + if (!d) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + else + { + for (i = 0; i != MAXCHANS; i++) + { + if ((d->chans[i].channel == chan) && (chan != NULL)) + { + if (d->chans[i].uc_modes & UCMODE_HOP) + { + /* mode already set on user, dont allow multiple */ + return 0; + } + d->chans[i].uc_modes = d->chans[i].uc_modes | UCMODE_HOP; + debug("gave h-ops: %s %s",d->chans[i].channel->name,d->nick); + } + } + } + } + return 1; +} + +int give_voice(userrec *user,char *dest,chanrec *chan,int status) +{ + userrec *d; + int i; + + if ((!user) || (!dest) || (!chan)) + { + return 0; + } + if (status < STATUS_HOP) + { + WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, chan->name); + return 0; + } + else + { + d = Find(dest); + if (!isnick(dest)) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + if (!d) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + else + { + for (i = 0; i != MAXCHANS; i++) + { + if ((d->chans[i].channel == chan) && (chan != NULL)) + { + if (d->chans[i].uc_modes & UCMODE_VOICE) + { + /* mode already set on user, dont allow multiple */ + return 0; + } + d->chans[i].uc_modes = d->chans[i].uc_modes | UCMODE_VOICE; + debug("gave voice: %s %s",d->chans[i].channel->name,d->nick); + } + } + } + } + return 1; +} + +int take_ops(userrec *user,char *dest,chanrec *chan,int status) +{ + userrec *d; + int i; + + if ((!user) || (!dest) || (!chan)) + { + return 0; + } + if (status != STATUS_OP) + { + WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name); + return 0; + } + else + { + d = Find(dest); + if (!isnick(dest)) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + if (!d) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + else + { + for (i = 0; i != MAXCHANS; i++) + { + if ((d->chans[i].channel == chan) && (chan != NULL)) + { + if ((d->chans[i].uc_modes & UCMODE_OP) == 0) + { + /* mode already set on user, dont allow multiple */ + return 0; + } + d->chans[i].uc_modes ^= UCMODE_OP; + debug("took ops: %s %s",d->chans[i].channel->name,d->nick); + } + } + } + } + return 1; +} + +int take_hops(userrec *user,char *dest,chanrec *chan,int status) +{ + userrec *d; + int i; + + if ((!user) || (!dest) || (!chan)) + { + return 0; + } + if (status != STATUS_OP) + { + WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name); + return 0; + } + else + { + d = Find(dest); + if (!isnick(dest)) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + if (!d) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + else + { + for (i = 0; i != MAXCHANS; i++) + { + if ((d->chans[i].channel == chan) && (chan != NULL)) + { + if ((d->chans[i].uc_modes & UCMODE_HOP) == 0) + { + /* mode already set on user, dont allow multiple */ + return 0; + } + d->chans[i].uc_modes ^= UCMODE_HOP; + debug("took h-ops: %s %s",d->chans[i].channel->name,d->nick); + } + } + } + } + return 1; +} + +int take_voice(userrec *user,char *dest,chanrec *chan,int status) +{ + userrec *d; + int i; + + if ((!user) || (!dest) || (!chan)) + { + return 0; + } + if (status < STATUS_HOP) + { + WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, chan->name); + return 0; + } + else + { + d = Find(dest); + if (!isnick(dest)) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + if (!d) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest); + return 0; + } + else + { + for (i = 0; i != MAXCHANS; i++) + { + if ((d->chans[i].channel == chan) && (chan != NULL)) + { + if ((d->chans[i].uc_modes & UCMODE_VOICE) == 0) + { + /* mode already set on user, dont allow multiple */ + return 0; + } + d->chans[i].uc_modes ^= UCMODE_VOICE; + debug("took voice: %s %s",d->chans[i].channel->name,d->nick); + } + } + } + } + return 1; +} + +void TidyBan(char *ban) +{ + char temp[MAXBUF],NICK[MAXBUF],IDENT[MAXBUF],HOST[MAXBUF]; + + strcpy(temp,ban); + + char* pos_of_pling = strchr(temp,'!'); + char* pos_of_at = strchr(temp,'@'); + + pos_of_pling[0] = '\0'; + pos_of_at[0] = '\0'; + pos_of_pling++; + pos_of_at++; + + strncpy(NICK,temp,NICKMAX); + strncpy(IDENT,pos_of_pling,IDENTMAX+1); + strncpy(HOST,pos_of_at,160); + + sprintf(ban,"%s!%s@%s",NICK,IDENT,HOST); +} + +int add_ban(userrec *user,char *dest,chanrec *chan,int status) +{ + BanItem b; + if ((!user) || (!dest) || (!chan)) + return 0; + if (strchr(dest,'!')==0) + return 0; + if (strchr(dest,'@')==0) + return 0; + for (int i = 0; i < strlen(dest); i++) + if (dest[i] < 32) + return 0; + for (int i = 0; i < strlen(dest); i++) + if (dest[i] > 126) + return 0; + int c = 0; + for (int i = 0; i < strlen(dest); i++) + if (dest[i] == '!') + c++; + if (c>1) + return 0; + c = 0; + for (int i = 0; i < strlen(dest); i++) + if (dest[i] == '@') + c++; + if (c>1) + return 0; + debug("add_ban: %s %s",chan->name,user->nick); + + TidyBan(dest); + for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++) + { + if (!strcasecmp(i->data,dest)) + { + // dont allow a user to set the same ban twice + return 0; + } + } + + b.set_time = time(NULL); + strncpy(b.data,dest,MAXBUF); + strncpy(b.set_by,user->nick,NICKMAX); + chan->bans.push_back(b); + return 1; +} + +int take_ban(userrec *user,char *dest,chanrec *chan,int status) +{ + if ((!user) || (!dest) || (!chan)) + { + return 0; + } + + debug("del_ban: %s %s",chan->name,user->nick); + for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++) + { + if (!strcasecmp(i->data,dest)) + { + chan->bans.erase(i); + return 1; + } + } + return 0; +} + +void process_modes(char **parameters,userrec* user,chanrec *chan,int status, int pcnt) +{ + char modelist[MAXBUF]; + char outlist[MAXBUF]; + char outstr[MAXBUF]; + char outpars[32][MAXBUF]; + int param = 2; + int pc = 0; + int ptr = 0; + int mdir = 1; + int r = 0; + bool k_set = false, l_set = false; + + if (pcnt < 2) + { + return; + } + + debug("process_modes: start"); + + strcpy(modelist,parameters[1]); /* mode list, e.g. +oo-o */ + /* parameters[2] onwards are parameters for + * modes that require them :) */ + strcpy(outlist,"+"); + mdir = 1; + + debug("process_modes: modelist: %s",modelist); + + for (ptr = 0; ptr < strlen(modelist); ptr++) + { + r = 0; + + { + debug("process_modes: modechar: %c",modelist[ptr]); + switch (modelist[ptr]) + { + case '-': + if (mdir != 0) + { + if ((outlist[strlen(outlist)-1] == '+') || (outlist[strlen(outlist)-1] == '-')) + { + outlist[strlen(outlist)-1] = '-'; + } + else + { + strcat(outlist,"-"); + } + } + mdir = 0; + + break; + + case '+': + if (mdir != 1) + { + if ((outlist[strlen(outlist)-1] == '+') || (outlist[strlen(outlist)-1] == '-')) + { + outlist[strlen(outlist)-1] = '+'; + } + else + { + strcat(outlist,"+"); + } + } + mdir = 1; + break; + + case 'o': + if ((param >= pcnt)) break; + if (mdir == 1) + { + r = give_ops(user,parameters[param++],chan,status); + } + else + { + r = take_ops(user,parameters[param++],chan,status); + } + if (r) + { + strcat(outlist,"o"); + strcpy(outpars[pc++],parameters[param-1]); + } + break; + + case 'h': + if ((param >= pcnt)) break; + if (mdir == 1) + { + r = give_hops(user,parameters[param++],chan,status); + } + else + { + r = take_hops(user,parameters[param++],chan,status); + } + if (r) + { + strcat(outlist,"h"); + strcpy(outpars[pc++],parameters[param-1]); + } + break; + + + case 'v': + if ((param >= pcnt)) break; + if (mdir == 1) + { + r = give_voice(user,parameters[param++],chan,status); + } + else + { + r = take_voice(user,parameters[param++],chan,status); + } + if (r) + { + strcat(outlist,"v"); + strcpy(outpars[pc++],parameters[param-1]); + } + break; + + case 'b': + if ((param >= pcnt)) break; + if (mdir == 1) + { + r = add_ban(user,parameters[param++],chan,status); + } + else + { + r = take_ban(user,parameters[param++],chan,status); + } + if (r) + { + strcat(outlist,"b"); + strcpy(outpars[pc++],parameters[param-1]); + } + break; + + case 'k': + if ((param >= pcnt)) + break; + + if (mdir == 1) + { + if (k_set) + break; + + if (!strcmp(chan->key,"")) + { + strcat(outlist,"k"); + strcpy(outpars[pc++],parameters[param++]); + strcpy(chan->key,parameters[param-1]); + k_set = true; + } + } + else + { + /* only allow -k if correct key given */ + if (strcmp(chan->key,"")) + { + strcat(outlist,"k"); + strcpy(chan->key,""); + } + } + break; + + case 'l': + if (mdir == 0) + { + if (chan->limit) + { + strcat(outlist,"l"); + chan->limit = 0; + } + } + + if ((param >= pcnt)) break; + if (mdir == 1) + { + if (l_set) + break; + + bool invalid = false; + for (int i = 0; i < strlen(parameters[param]); i++) + { + if ((parameters[param][i] < '0') || (parameters[param][i] > '9')) + { + invalid = true; + } + } + if (atoi(parameters[param]) < 1) + { + invalid = true; + } + + if (invalid) + break; + + chan->limit = atoi(parameters[param]); + if (chan->limit) + { + strcat(outlist,"l"); + strcpy(outpars[pc++],parameters[param++]); + l_set = true; + } + } + break; + + case 'i': + if (chan->inviteonly != mdir) + { + strcat(outlist,"i"); + } + chan->inviteonly = mdir; + break; + + case 't': + if (chan->topiclock != mdir) + { + strcat(outlist,"t"); + } + chan->topiclock = mdir; + break; + + case 'n': + if (chan->noexternal != mdir) + { + strcat(outlist,"n"); + } + chan->noexternal = mdir; + break; + + case 'm': + if (chan->moderated != mdir) + { + strcat(outlist,"m"); + } + chan->moderated = mdir; + break; + + case 's': + if (chan->secret != mdir) + { + strcat(outlist,"s"); + if (chan->c_private) + { + chan->c_private = 0; + if (mdir) + { + strcat(outlist,"-p+"); + } + else + { + strcat(outlist,"+p-"); + } + } + } + chan->secret = mdir; + break; + + case 'p': + if (chan->c_private != mdir) + { + strcat(outlist,"p"); + if (chan->secret) + { + chan->secret = 0; + if (mdir) + { + strcat(outlist,"-s+"); + } + else + { + strcat(outlist,"+s-"); + } + } + } + chan->c_private = mdir; + break; + + } + } + } + + /* this ensures only the *valid* modes are sent out onto the network */ + while ((outlist[strlen(outlist)-1] == '-') || (outlist[strlen(outlist)-1] == '+')) + { + outlist[strlen(outlist)-1] = '\0'; + } + if (strcmp(outlist,"")) + { + strcpy(outstr,outlist); + for (ptr = 0; ptr < pc; ptr++) + { + strcat(outstr," "); + strcat(outstr,outpars[ptr]); + } + WriteChannel(chan,user,"MODE %s %s",chan->name,outstr); + } +} + +void handle_mode(char **parameters, int pcnt, userrec *user) +{ + chanrec* Ptr; + userrec* dest; + int can_change,i; + int direction = 1; + char outpars[MAXBUF]; + + dest = Find(parameters[0]); + + if ((dest) && (pcnt == 1)) + { + WriteServ(user->fd,"221 %s :+%s",user->nick,user->modes); + return; + } + if ((dest) && (pcnt > 1)) + { + can_change = 0; + if (user != dest) + { + if (strchr(user->modes,'o')) + { + can_change = 1; + } + } + else + { + can_change = 1; + } + if (!can_change) + { + WriteServ(user->fd,"482 %s :Can't change mode for other users",user->nick); + return; + } + + strcpy(outpars,"+"); + direction = 1; + + if ((parameters[1][0] != '+') && (parameters[1][0] != '-')) + return; + + for (i = 0; i < strlen(parameters[1]); i++) + { + if (parameters[1][i] == '+') + { + if (direction != 1) + { + if ((outpars[strlen(outpars)-1] == '+') || (outpars[strlen(outpars)-1] == '-')) + { + outpars[strlen(outpars)-1] = '+'; + } + else + { + strcat(outpars,"+"); + } + } + direction = 1; + } + else + if (parameters[1][i] == '-') + { + if (direction != 0) + { + if ((outpars[strlen(outpars)-1] == '+') || (outpars[strlen(outpars)-1] == '-')) + { + outpars[strlen(outpars)-1] = '-'; + } + else + { + strcat(outpars,"-"); + } + } + direction = 0; + } + else + { + can_change = 0; + if (strchr(user->modes,'o')) + { + can_change = 1; + } + else + { + if ((parameters[1][i] == 'i') || (parameters[1][i] == 'w') || (parameters[1][i] == 's')) + { + can_change = 1; + } + } + if (can_change) + { + if (direction == 1) + { + if (!strchr(dest->modes,parameters[1][i])) + { + dest->modes[strlen(dest->modes)+1]='\0'; + dest->modes[strlen(dest->modes)] = parameters[1][i]; + outpars[strlen(outpars)+1]='\0'; + outpars[strlen(outpars)] = parameters[1][i]; + } + } + else + { + int q = 0; + char temp[MAXBUF]; + char moo[MAXBUF]; + + outpars[strlen(outpars)+1]='\0'; + outpars[strlen(outpars)] = parameters[1][i]; + + strcpy(temp,""); + for (q = 0; q < strlen(user->modes); q++) + { + if (user->modes[q] != parameters[1][i]) + { + moo[0] = user->modes[q]; + moo[1] = '\0'; + strcat(temp,moo); + } + } + strcpy(user->modes,temp); + } + } + } + } + if (strlen(outpars)) + { + char b[MAXBUF]; + strcpy(b,""); + int z = 0; + int i = 0; + while (i < strlen (outpars)) + { + b[z++] = outpars[i++]; + b[z] = '\0'; + if (inick, b); + } + return; + } + + Ptr = FindChan(parameters[0]); + if (Ptr) + { + if (pcnt == 1) + { + /* just /modes #channel */ + WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name, chanmodes(Ptr)); + WriteServ(user->fd,"329 %s %s %d", user->nick, Ptr->name, Ptr->created); + return; + } + else + if (pcnt == 2) + { + if ((!strcmp(parameters[1],"+b")) || (!strcmp(parameters[1],"b"))) + { + + for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++) + { + WriteServ(user->fd,"367 %s %s %s %s %d",user->nick, Ptr->name, i->data, i->set_by, i->set_time); + } + WriteServ(user->fd,"368 %s %s :End of channel ban list",user->nick, Ptr->name); + } + } + + if ((cstatus(user,Ptr) < STATUS_HOP) && (Ptr)) + { + WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, Ptr->name); + return; + } + + process_modes(parameters,user,Ptr,cstatus(user,Ptr),pcnt); + } + else + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + } +} + +/* This function pokes and hacks at a parameter list like the following: + * + * PART #winbot, #darkgalaxy :m00! + * + * to turn it into a series of individual calls like this: + * + * PART #winbot :m00! + * PART #darkgalaxy :m00! + * + * The seperate calls are sent to a callback function provided by the caller + * (the caller will usually call itself recursively). The callback function + * must be a command handler. Calling this function on a line with no list causes + * no action to be taken. You must provide a starting and ending parameter number + * where the range of the list can be found, useful if you have a terminating + * parameter as above which is actually not part of the list, or parameters + * before the actual list as well. This code is used by many functions which + * can function as "one to list" (see the RFC) */ + +int loop_call(handlerfunc fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins) +{ + char plist[MAXBUF]; + char *param; + char *pars[32]; + char blog[32][MAXBUF]; + char blog2[32][MAXBUF]; + int i = 0, j = 0, q = 0, total = 0, t = 0, t2 = 0, total2 = 0; + char keystr[MAXBUF]; + char moo[MAXBUF]; + + for (i = 0; i <32; i++) + strcpy(blog[i],""); + + for (i = 0; i <32; i++) + strcpy(blog2[i],""); + + strcpy(moo,""); + for (i = 0; i <10; i++) + { + if (!parameters[i]) + { + parameters[i] = moo; + } + } + if (joins) + { + if (pcnt > 1) /* we have a key to copy */ + { + strcpy(keystr,parameters[1]); + } + } + + if (!parameters[start]) + { + return 0; + } + if (!strchr(parameters[start],',')) + { + return 0; + } + strcpy(plist,""); + for (i = start; i <= end; i++) + { + if (parameters[i]) + { + strcat(plist,parameters[i]); + } + } + + j = 0; + param = plist; + + t = strlen(plist); + for (i = 0; i < t; i++) + { + if (plist[i] == ',') + { + plist[i] = '\0'; + strcpy(blog[j++],param); + param = plist+i+1; + } + } + strcpy(blog[j++],param); + total = j; + + if ((joins) && (keystr) && (total>0)) // more than one channel and is joining + { + strcat(keystr,","); + } + + if ((joins) && (keystr)) + { + if (strchr(keystr,',')) + { + j = 0; + param = keystr; + t2 = strlen(keystr); + for (i = 0; i < t2; i++) + { + if (keystr[i] == ',') + { + keystr[i] = '\0'; + strcpy(blog2[j++],param); + param = keystr+i+1; + } + } + strcpy(blog2[j++],param); + total2 = j; + } + } + + for (j = 0; j < total; j++) + { + if (blog[j]) + { + pars[0] = blog[j]; + } + for (q = end; q < pcnt-1; q++) + { + if (parameters[q+1]) + { + pars[q-end+1] = parameters[q+1]; + } + } + if ((joins) && (parameters[1])) + { + if (pcnt > 1) + { + pars[1] = blog2[j]; + } + else + { + pars[1] = NULL; + } + } + /* repeatedly call the function with the hacked parameter list */ + if ((joins) && (pcnt > 1)) + { + if (pars[1]) + { + // pars[1] already set up and containing key from blog2[j] + fn(pars,2,u); + } + else + { + pars[1] = parameters[1]; + fn(pars,2,u); + } + } + else + { + fn(pars,pcnt-(end-start),u); + } + } + + return 1; +} + +void handle_join(char **parameters, int pcnt, userrec *user) +{ + chanrec* Ptr; + int i = 0; + + if (loop_call(handle_join,parameters,pcnt,user,0,0,1)) + return; + if (parameters[0][0] == '#') + { + Ptr = add_channel(user,parameters[0],parameters[1]); + } +} + + +void handle_part(char **parameters, int pcnt, userrec *user) +{ + chanrec* Ptr; + + if (pcnt > 1) + { + if (loop_call(handle_part,parameters,pcnt,user,0,pcnt-2,0)) + return; + del_channel(user,parameters[0],parameters[1]); + } + else + { + if (loop_call(handle_part,parameters,pcnt,user,0,pcnt-1,0)) + return; + del_channel(user,parameters[0],NULL); + } +} + +void handle_kick(char **parameters, int pcnt, userrec *user) +{ + chanrec* Ptr = FindChan(parameters[0]); + userrec* u = Find(parameters[1]); + + if ((!u) || (!Ptr)) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + return; + } + + if (!has_channel(u,Ptr)) + { + WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, parameters[0]); + return; + } + + if (pcnt > 2) + { + kick_channel(user,u,Ptr,parameters[2]); + } + else + { + kick_channel(user,u,Ptr,user->nick); + } +} + + +void handle_die(char **parameters, int pcnt, userrec *user) +{ + debug("die: %s",user->nick); + if (!strcmp(parameters[0],diepass)) + { + WriteOpers("*** DIE command from %s!%s@%s, terminating...",user->nick,user->ident,user->host); + sleep(DieDelay); + Exit(ERROR); + } + else + { + WriteOpers("*** Failed DIE Command from %s!%s@%s.",user->nick,user->ident,user->host); + } +} + +void handle_restart(char **parameters, int pcnt, userrec *user) +{ + debug("restart: %s",user->nick); + if (!strcmp(parameters[0],restartpass)) + { + WriteOpers("*** RESTART command from %s!%s@%s, Pretending to restart till this is finished :D",user->nick,user->ident,user->host); + sleep(DieDelay); + Exit(ERROR); + /* Will finish this later when i can be arsed :) */ + } + else + { + WriteOpers("*** Failed RESTART Command from %s!%s@%s.",user->nick,user->ident,user->host); + } +} + + +void kill_link(userrec *user,char* reason) +{ + user_hash::iterator iter = clientlist.find(user->nick); + + debug("kill_link: %s '%s'",user->nick,reason); + Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason); + fdatasync(user->fd); + WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason); + FOREACH_MOD OnUserQuit(user); + debug("closing fd %d",user->fd); + /* bugfix, cant close() a nonblocking socket (sux!) */ + WriteCommonExcept(user,"QUIT :%s",reason); + Blocking(user->fd); + close(user->fd); + NonBlocking(user->fd); + AddWhoWas(user); + + if (iter != clientlist.end()) + { + debug("deleting user hash value %p",iter->second); + delete iter->second; + clientlist.erase(iter); + } + + purge_empty_chans(); +} + + +void handle_kill(char **parameters, int pcnt, userrec *user) +{ + userrec *u = Find(parameters[0]); + char killreason[MAXBUF]; + + debug("kill: %s %s",parameters[0],parameters[1]); + if (u) + { + WriteOpers("*** Local Kill by %s: %s!%s@%s (%s)",user->nick,u->nick,u->ident,u->host,parameters[1]); + sprintf(killreason,"Killed (%s (%s))",user->nick,parameters[1]); + kill_link(u,killreason); + } + else + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + } +} + +void handle_summon(char **parameters, int pcnt, userrec *user) +{ + WriteServ(user->fd,"445 %s :SUMMON has been disabled (depreciated command)",user->nick); +} + +void handle_users(char **parameters, int pcnt, userrec *user) +{ + WriteServ(user->fd,"445 %s :USERS has been disabled (depreciated command)",user->nick); +} + + +// looks up a users password for their connection class (/ tags) + +char* Passwd(userrec *user) +{ + for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++) + { + if (match(user->host,i->host) && (i->type == CC_ALLOW)) + { + return i->pass; + } + } + return ""; +} + +bool IsDenied(userrec *user) +{ + for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++) + { + if (match(user->host,i->host) && (i->type == CC_DENY)) + { + return true; + } + } + return false; +} + + +void handle_pass(char **parameters, int pcnt, userrec *user) +{ + if (!strcasecmp(parameters[0],Passwd(user))) + { + user->haspassed = true; + } +} + +void handle_invite(char **parameters, int pcnt, userrec *user) +{ + userrec* u = Find(parameters[0]); + chanrec* c = FindChan(parameters[1]); + + if ((!c) || (!u)) + { + if (!c) + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[1]); + } + else + { + if (c->inviteonly) + { + WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]); + } + } + + return; + } + + if (c->inviteonly) + { + if (cstatus(user,c) < STATUS_HOP) + { + WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, c->name); + return; + } + + u->InviteTo(c->name); + WriteFrom(u->fd,user,"INVITE %s :%s",u->nick,c->name); + WriteServ(user->fd,"341 %s %s %s",user->nick,u->nick,c->name); + } +} + +void handle_topic(char **parameters, int pcnt, userrec *user) +{ + chanrec* Ptr; + + if (pcnt == 1) + { + if (strlen(parameters[0]) <= CHANMAX) + { + Ptr = FindChan(parameters[0]); + if (Ptr) + { + if (Ptr->topicset) + { + WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic); + WriteServ(user->fd,"333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset); + } + else + { + WriteServ(user->fd,"331 %s %s :No topic is set.", user->nick, Ptr->name); + } + } + else + { + WriteServ(user->fd,"331 %s %s :No topic is set.", user->nick, Ptr->name); + } + } + } + else if (pcnt>1) + { + if (loop_call(handle_topic,parameters,pcnt,user,0,pcnt-2,0)) + return; + if (strlen(parameters[0]) <= CHANMAX) + { + Ptr = FindChan(parameters[0]); + if (Ptr) + { + if ((Ptr->topiclock) && (cstatus(user,Ptr)fd,"482 %s %s :You must be at least a half-operator", user->nick, Ptr->name); + return; + } + strcpy(Ptr->topic,parameters[1]); + strcpy(Ptr->setby,user->nick); + Ptr->topicset = time(NULL); + WriteChannel(Ptr,user,"TOPIC %s :%s",Ptr->name, Ptr->topic); + } + else + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + } + } + } +} + +/* sends out an error notice to all connected clients (not to be used + * lightly!) */ + +void send_error(char *s) +{ + debug("send_error: %s",s); + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,s); + } +} + +void Error(int status) +{ + signal (SIGALRM, SIG_IGN); + signal (SIGPIPE, SIG_IGN); + signal (SIGTERM, SIG_IGN); + signal (SIGABRT, SIG_IGN); + signal (SIGSEGV, SIG_IGN); + signal (SIGURG, SIG_IGN); + signal (SIGKILL, SIG_IGN); + debug("*** fell down a pothole in the road to perfection ***"); + send_error("Error! Segmentation fault! save meeeeeeeeeeeeee *splat!*"); + exit(status); +} + +int main (int argc, char *argv[]) +{ + Start(); + debug("*** InspIRCd starting up!"); + if (!CheckConfig()) + { + debug("main: no config"); + printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n"); + Exit(ERROR); + } + if (InspIRCd() == ERROR) + { + debug("main: daemon function bailed"); + printf("ERROR: could not initialise. Shutting down.\n"); + Exit(ERROR); + } + Exit(TRUE); + return 0; +} + +template inline string ConvToStr(const T &in) +{ + stringstream tmp; + if (!(tmp << in)) return string(); + return tmp.str(); +} + +/* re-allocates a nick in the user_hash after they change nicknames, + * returns a pointer to the new user as it may have moved */ + +userrec* ReHashNick(char* Old, char* New) +{ + user_hash::iterator newnick; + user_hash::iterator oldnick = clientlist.find(Old); + + debug("ReHashNick: %s %s",Old,New); + + if (!strcasecmp(Old,New)) + { + debug("old nick is new nick, skipping"); + return oldnick->second; + } + + if (oldnick == clientlist.end()) return NULL; /* doesnt exist */ + + debug("ReHashNick: Found hashed nick %s",Old); + + clientlist[New] = new userrec(); + clientlist[New] = oldnick->second; + /*delete oldnick->second; */ + clientlist.erase(oldnick); + + debug("ReHashNick: Nick rehashed as %s",New); + + return clientlist[New]; +} + +/* adds or updates an entry in the whowas list */ +void AddWhoWas(userrec* u) +{ + user_hash::iterator iter = whowas.find(u->nick); + userrec *a = new userrec(); + strcpy(a->nick,u->nick); + strcpy(a->ident,u->ident); + strcpy(a->dhost,u->dhost); + strcpy(a->host,u->host); + strcpy(a->fullname,u->fullname); + strcpy(a->server,u->server); + a->signon = u->signon; + + /* MAX_WHOWAS: max number of /WHOWAS items + * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and + * can be replaced by a newer one + */ + + if (iter == whowas.end()) + { + if (whowas.size() == WHOWAS_MAX) + { + for (user_hash::iterator i = whowas.begin(); i != whowas.end(); i++) + { + // 3600 seconds in an hour ;) + if ((i->second->signon)<(time(NULL)-(WHOWAS_STALE*3600))) + { + delete i->second; + i->second = a; + debug("added WHOWAS entry, purged an old record"); + return; + } + } + } + else + { + debug("added fresh WHOWAS entry"); + whowas[a->nick] = a; + } + } + else + { + debug("updated WHOWAS entry"); + delete iter->second; + iter->second = a; + } +} + + +/* add a client connection to the sockets list */ +void AddClient(int socket, char* host, int port, bool iscached) +{ + int i; + int blocking = 1; + char resolved[MAXBUF]; + string tempnick; + char tn2[MAXBUF]; + user_hash::iterator iter; + + tempnick = ConvToStr(socket) + "-unknown"; + sprintf(tn2,"%d-unknown",socket); + + iter = clientlist.find(tempnick); + + if (iter != clientlist.end()) return; + + /* + * It is OK to access the value here this way since we know + * it exists, we just created it above. + * + * At NO other time should you access a value in a map or a + * hash_map this way. + */ + clientlist[tempnick] = new userrec(); + + NonBlocking(socket); + debug("AddClient: %d %s %d",socket,host,port); + + + clientlist[tempnick]->fd = socket; + strncpy(clientlist[tempnick]->nick, tn2,NICKMAX); + strncpy(clientlist[tempnick]->host, host,160); + strncpy(clientlist[tempnick]->dhost, host,160); + strncpy(clientlist[tempnick]->server, ServerName,256); + clientlist[tempnick]->registered = 0; + clientlist[tempnick]->signon = time(NULL); + clientlist[tempnick]->nping = time(NULL)+240; + clientlist[tempnick]->lastping = 1; + clientlist[tempnick]->port = port; + + if (iscached) + { + WriteServ(socket,"NOTICE Auth :Found your hostname (cached)..."); + } + else + { + WriteServ(socket,"NOTICE Auth :Looking up your hostname..."); + } + + if (clientlist.size() == MAXCLIENTS) + kill_link(clientlist[tempnick],"No more connections allowed in this class"); +} + +void handle_names(char **parameters, int pcnt, userrec *user) +{ + chanrec* c; + + if (loop_call(handle_names,parameters,pcnt,user,0,pcnt-1,0)) + return; + c = FindChan(parameters[0]); + if (c) + { + /*WriteServ(user->fd,"353 %s = %s :%s", user->nick, c->name,*/ + userlist(user,c); + WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, c->name); + } + else + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + } +} + + +void handle_privmsg(char **parameters, int pcnt, userrec *user) +{ + userrec *dest; + chanrec *chan; + + if (loop_call(handle_privmsg,parameters,pcnt,user,0,pcnt-2,0)) + return; + if (parameters[0][0] == '#') + { + chan = FindChan(parameters[0]); + if (chan) + { + if ((chan->noexternal) && (!has_channel(user,chan))) + { + WriteServ(user->fd,"404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name); + return; + } + if ((chan->moderated) && (cstatus(user,chan)fd,"404 %s %s :Cannot send to channel (+m)", user->nick, chan->name); + return; + } + ChanExceptSender(chan, user, "PRIVMSG %s :%s", chan->name, parameters[1]); + } + else + { + /* no such nick/channel */ + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + } + return; + } + + dest = Find(parameters[0]); + if (dest) + { + if (strcmp(dest->awaymsg,"")) + { + /* auto respond with aweh msg */ + WriteServ(user->fd,"301 %s %s :%s",user->nick,dest->nick,dest->awaymsg); + } + WriteTo(user, dest, "PRIVMSG %s :%s", dest->nick, parameters[1]); + } + else + { + /* no such nick/channel */ + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + } +} + +void handle_notice(char **parameters, int pcnt, userrec *user) +{ + userrec *dest; + chanrec *chan; + + if (loop_call(handle_notice,parameters,pcnt,user,0,pcnt-2,0)) + return; + if (parameters[0][0] == '#') + { + chan = FindChan(parameters[0]); + if (chan) + { + if ((chan->noexternal) && (!has_channel(user,chan))) + { + WriteServ(user->fd,"404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name); + return; + } + if ((chan->moderated) && (cstatus(user,chan)fd,"404 %s %s :Cannot send to channel (+m)", user->nick, chan->name); + return; + } + WriteChannel(chan, user, "NOTICE %s :%s", chan->name, parameters[1]); + } + else + { + /* no such nick/channel */ + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + } + return; + } + + dest = Find(parameters[0]); + if (dest) + { + WriteTo(user, dest, "NOTICE %s :%s", dest->nick, parameters[1]); + } + else + { + /* no such nick/channel */ + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + } +} + +char lst[MAXBUF]; + +char* chlist(userrec *user) +{ + int i = 0; + char cmp[MAXBUF]; + + debug("chlist: %s",user->nick); + strcpy(lst,""); + if (!user) + { + return lst; + } + for (i = 0; i != MAXCHANS; i++) + { + if (user->chans[i].channel != NULL) + { + if (user->chans[i].channel->name) + { + strcpy(cmp,user->chans[i].channel->name); + strcat(cmp," "); + if (!strstr(lst,cmp)) + { + if ((!user->chans[i].channel->c_private) && (!user->chans[i].channel->secret)) + { + strcat(lst,cmode(user,user->chans[i].channel)); + strcat(lst,user->chans[i].channel->name); + strcat(lst," "); + } + } + } + } + } + return lst; +} + +void handle_info(char **parameters, int pcnt, userrec *user) +{ + WriteServ(user->fd,"371 %s :The Inspire IRCd Project Has been brought to you by the following people..",user->nick); + WriteServ(user->fd,"371 %s :Craig Edwards, Craig McLure, and Others..",user->nick); + WriteServ(user->fd,"371 %s :Will finish this later when i can be arsed :p",user->nick); + WriteServ(user->fd,"374 %s :End of /INFO list",user->nick); +} + +void handle_time(char **parameters, int pcnt, userrec *user) +{ + time_t rawtime; + struct tm * timeinfo; + + time ( &rawtime ); + timeinfo = localtime ( &rawtime ); + WriteServ(user->fd,"391 %s %s :%s",user->nick,ServerName, asctime (timeinfo) ); + +} + +void handle_whois(char **parameters, int pcnt, userrec *user) +{ + userrec *dest; + char *t; + + if (loop_call(handle_whois,parameters,pcnt,user,0,pcnt-1,0)) + return; + dest = Find(parameters[0]); + if (dest) + { + WriteServ(user->fd,"311 %s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname); + if ((user == dest) || (strchr(user->modes,'o'))) + { + WriteServ(user->fd,"378 %s %s :is connecting from *@%s",user->nick, dest->nick, dest->host); + } + if (strcmp(chlist(dest),"")) + { + WriteServ(user->fd,"319 %s %s :%s",user->nick, dest->nick, chlist(dest)); + } + WriteServ(user->fd,"312 %s %s %s :%s",user->nick, dest->nick, dest->server, ServerDesc); + if (strcmp(dest->awaymsg,"")) + { + WriteServ(user->fd,"301 %s %s :%s",user->nick, dest->nick, dest->awaymsg); + } + if (strchr(dest->modes,'o')) + { + WriteServ(user->fd,"313 %s %s :is an IRC operator",user->nick, dest->nick); + } + //WriteServ(user->fd,"310 %s %s :is available for help.",user->nick, dest->nick); + WriteServ(user->fd,"317 %s %s %d %d :seconds idle, signon time",user->nick, dest->nick, abs((dest->idle_lastmsg)-time(NULL)), dest->signon); + + WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, dest->nick); + } + else + { + /* no such nick/channel */ + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + } +} + +void handle_quit(char **parameters, int pcnt, userrec *user) +{ + user_hash::iterator iter = clientlist.find(user->nick); + + /* theres more to do here, but for now just close the socket */ + if (pcnt == 1) + { + if (parameters[0][0] == ':') + { + *parameters[0]++; + } + Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,parameters[0]); + WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,parameters[0]); + WriteCommonExcept(user,"QUIT :%s%s",PrefixQuit,parameters[0]); + } + else + { + Write(user->fd,"ERROR :Closing link (%s@%s) [QUIT]",user->ident,user->host); + WriteOpers("*** Client exiting: %s!%s@%s [Client exited]",user->nick,user->ident,user->host); + WriteCommonExcept(user,"QUIT :Client exited"); + } + + FOREACH_MOD OnUserQuit(user); + + /* confucious say, he who close nonblocking socket, get nothing! */ + Blocking(user->fd); + close(user->fd); + NonBlocking(user->fd); + AddWhoWas(user); + + if (iter != clientlist.end()) + { + debug("deleting user hash value"); + delete iter->second; + clientlist.erase(iter); + } + + purge_empty_chans(); +} + +void handle_who(char **parameters, int pcnt, userrec *user) +{ + chanrec* Ptr; + + /* theres more to do here, but for now just close the socket */ + if (pcnt == 1) + { + if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*"))) + { + Ptr = user->chans[0].channel; + printf(user->chans[0].channel->name); + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if ((common_channels(user,i->second)) && (isnick(i->second->nick))) + { + WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, Ptr->name, i->second->ident, i->second->dhost, ServerName, i->second->nick, i->second->fullname); + } + } + WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name); + return; + } + if (parameters[0][0] = '#') + { + Ptr = FindChan(parameters[0]); + if (Ptr) + { + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if ((has_channel(i->second,Ptr)) && (isnick(i->second->nick))) + { + WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, Ptr->name, i->second->ident, i->second->dhost, ServerName, i->second->nick, i->second->fullname); + } + } + WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name); + } + else + { + WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]); + } + } + } + if (pcnt == 2) + { + if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*")) && (!strcmp(parameters[1],"o"))) + { + Ptr = user->chans[0].channel; + printf(user->chans[0].channel->name); + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if ((common_channels(user,i->second)) && (isnick(i->second->nick))) + { + if (strchr(i->second->modes,'o')) + { + WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, Ptr->name, i->second->ident, i->second->dhost, ServerName, i->second->nick, i->second->fullname); + } + } + } + WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name); + return; + } + } +} + +void handle_wallops(char **parameters, int pcnt, userrec *user) +{ + WriteWallOps(user,"%s",parameters[0]); +} + +void handle_list(char **parameters, int pcnt, userrec *user) +{ + chanrec* Ptr; + + WriteServ(user->fd,"321 %s Channel :Users Name",user->nick); + for (chan_hash::const_iterator i = chanlist.begin(); i != chanlist.end(); i++) + { + if ((!i->second->c_private) && (!i->second->secret)) + { + WriteServ(user->fd,"322 %s %s %d :[+%s] %s",user->nick,i->second->name,usercount_i(i->second),chanmodes(i->second),i->second->topic); + } + } + WriteServ(user->fd,"323 %s :End of channel list.",user->nick); +} + + +void handle_rehash(char **parameters, int pcnt, userrec *user) +{ + WriteServ(user->fd,"382 %s %s :Rehashing",user->nick,CONFIG_FILE); + ReadConfig(); + WriteOpers("%s is rehashing config file %s",user->nick,CONFIG_FILE); +} + + +int usercnt(void) +{ + return clientlist.size(); +} + +int usercount_invisible(void) +{ + int c = 0; + + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'i'))) c++; + } + return c; +} + +int usercount_opers(void) +{ + int c = 0; + + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'o'))) c++; + } + return c; +} + +int usercount_unknown(void) +{ + int c = 0; + + for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if ((i->second->fd) && (i->second->registered != 7)) + c++; + } + return c; +} + +int chancount(void) +{ + return chanlist.size(); +} + +int servercount(void) +{ + return 1; +} + +void handle_lusers(char **parameters, int pcnt, userrec *user) +{ + WriteServ(user->fd,"251 %s :There are %d users and %d invisible on %d servers",user->nick,usercnt()-usercount_invisible(),usercount_invisible(),servercount()); + WriteServ(user->fd,"252 %s %d :operator(s) online",user->nick,usercount_opers()); + WriteServ(user->fd,"253 %s %d :unknown connections",user->nick,usercount_unknown()); + WriteServ(user->fd,"254 %s %d :channels formed",user->nick,chancount()); + WriteServ(user->fd,"254 %s :I have %d clients and 0 servers",user->nick,usercnt()); +} + +void handle_admin(char **parameters, int pcnt, userrec *user) +{ + WriteServ(user->fd,"256 %s :Administrative info for %s",user->nick,ServerName); + WriteServ(user->fd,"257 %s :Name - %s",user->nick,AdminName); + WriteServ(user->fd,"258 %s :Nickname - %s",user->nick,AdminNick); + WriteServ(user->fd,"258 %s :E-Mail - %s",user->nick,AdminEmail); +} + +void ShowMOTD(userrec *user) +{ + if (!MOTD.size()) + { + WriteServ(user->fd,"422 %s :Message of the day file is missing.",user->nick); + return; + } + WriteServ(user->fd,"375 %s :- %s message of the day",user->nick,ServerName); + for (int i = 0; i != MOTD.size(); i++) + { + WriteServ(user->fd,"372 %s :- %s",user->nick,MOTD[i].c_str()); + } + WriteServ(user->fd,"376 %s :End of %s message of the day.",user->nick,ServerName); +} + +void ShowRULES(userrec *user) +{ + if (!RULES.size()) + { + WriteServ(user->fd,"NOTICE %s :Rules file is missing.",user->nick); + return; + } + WriteServ(user->fd,"NOTICE %s :%s rules",user->nick,ServerName); + for (int i = 0; i != RULES.size(); i++) + { + WriteServ(user->fd,"NOTICE %s :%s",user->nick,RULES[i].c_str()); + } + WriteServ(user->fd,"NOTICE %s :End of %s rules.",user->nick,ServerName); +} + +/* shows the message of the day, and any other on-logon stuff */ +void ConnectUser(userrec *user) +{ + user->registered = 7; + user->idle_lastmsg = time(NULL); + debug("ConnectUser: %s",user->nick); + + if (strcmp(Passwd(user),"") && (!user->haspassed)) + { + Write(user->fd,"ERROR :Closing link: Invalid password"); + fdatasync(user->fd); + kill_link(user,"Invalid password"); + return; + } + if (IsDenied(user)) + { + Write(user->fd,"ERROR :Closing link: Unauthorized connection"); + fdatasync(user->fd); + kill_link(user,"Unauthorised connection"); + } + + WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Network); + WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Network,user->nick,user->ident,user->host); + WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,ServerName,VERSION); + WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__); + WriteServ(user->fd,"004 %s :%s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,ServerName,VERSION); + WriteServ(user->fd,"005 %s :MAP KNOCK SAFELIST HCN MAXCHANNELS=20 MAXBANS=60 NICKLEN=30 TOPICLEN=307 KICKLEN=307 MAXTARGETS=20 AWAYLEN=307 :are supported by this server",user->nick); + WriteServ(user->fd,"005 %s :WALLCHOPS WATCH=128 SILENCE=5 MODES=13 CHANTYPES=# PREFIX=(ohv)@%c+ CHANMODES=ohvbeqa,kfL,l,psmntirRcOAQKVHGCuzN NETWORK=%s :are supported by this server",user->nick,'%',Network); + ShowMOTD(user); + FOREACH_MOD OnUserConnect(user); + WriteOpers("*** Client connecting on port %d: %s!%s@%s",user->port,user->nick,user->ident,user->host); +} + +void handle_version(char **parameters, int pcnt, userrec *user) +{ + WriteServ(user->fd,"351 %s :%s %s :%s",user->nick,VERSION,ServerName,SYSTEM); +} + +void handle_ping(char **parameters, int pcnt, userrec *user) +{ + WriteServ(user->fd,"PONG %s :%s",ServerName,parameters[0]); +} + +void handle_pong(char **parameters, int pcnt, userrec *user) +{ + // set the user as alive so they survive to next ping + user->lastping = 1; +} + +void handle_motd(char **parameters, int pcnt, userrec *user) +{ + ShowMOTD(user); +} + +void handle_rules(char **parameters, int pcnt, userrec *user) +{ + ShowRULES(user); +} + +void handle_user(char **parameters, int pcnt, userrec *user) +{ + if (user->registered < 3) + { + WriteServ(user->fd,"NOTICE Auth :No ident response, ident prefixed with ~"); + strcpy(user->ident,"~"); /* we arent checking ident... but these days why bother anyway? */ + strncat(user->ident,parameters[0],IDENTMAX); + strncpy(user->fullname,parameters[3],128); + user->registered = (user->registered | 1); + } + else + { + WriteServ(user->fd,"462 %s :You may not reregister",user->nick); + return; + } + /* parameters 2 and 3 are local and remote hosts, ignored when sent by client connection */ + if (user->registered == 3) + { + /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */ + ConnectUser(user); + } +} + +void handle_userhost(char **parameters, int pcnt, userrec *user) +{ + char Return[MAXBUF],junk[MAXBUF]; + sprintf(Return,"302 %s :",user->nick); + for (int i = 0; i < pcnt; i++) + { + userrec *u = Find(parameters[i]); + if (u) + { + if (strchr(u->modes,'o')) + { + sprintf(junk,"%s*=+%s@%s ",u->nick,u->ident,u->host); + strcat(Return,junk); + } + else + { + sprintf(junk,"%s=+%s@%s ",u->nick,u->ident,u->host); + strcat(Return,junk); + } + } + } + WriteServ(user->fd,Return); +} + + +void handle_ison(char **parameters, int pcnt, userrec *user) +{ + char Return[MAXBUF]; + sprintf(Return,"303 %s :",user->nick); + for (int i = 0; i < pcnt; i++) + { + userrec *u = Find(parameters[i]); + if (u) + { + strcat(Return,u->nick); + strcat(Return," "); + } + } + WriteServ(user->fd,Return); +} + + +void handle_away(char **parameters, int pcnt, userrec *user) +{ + if (pcnt) + { + strcpy(user->awaymsg,parameters[0]); + WriteServ(user->fd,"306 %s :You have been marked as being away",user->nick); + } + else + { + strcpy(user->awaymsg,""); + WriteServ(user->fd,"305 %s :You are no longer marked as being away",user->nick); + } +} + +void handle_whowas(char **parameters, int pcnt, userrec* user) +{ + user_hash::iterator i = whowas.find(parameters[0]); + + if (i == whowas.end()) + { + WriteServ(user->fd,"406 %s %s :There was no such nickname",user->nick,parameters[0]); + WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]); + } + else + { + time_t rawtime = i->second->signon; + tm *timeinfo; + char b[MAXBUF]; + + timeinfo = localtime(&rawtime); + strcpy(b,asctime(timeinfo)); + b[strlen(b)-1] = '\0'; + + WriteServ(user->fd,"314 %s %s %s %s * :%s",user->nick,i->second->nick,i->second->ident,i->second->dhost,i->second->fullname); + WriteServ(user->fd,"312 %s %s %s :%s",user->nick,i->second->nick,i->second->server,b); + WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]); + } + +} + +void handle_trace(char **parameters, int pcnt, userrec *user) +{ + for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (i->second) + { + if (isnick(i->second->nick)) + { + if (strchr(i->second->modes,'o')) + { + WriteServ(user->fd,"205 %s :Oper 0 %s",user->nick,i->second->nick); + } + else + { + WriteServ(user->fd,"204 %s :User 0 %s",user->nick,i->second->nick); + } + } + else + { + WriteServ(user->fd,"203 %s :???? 0 [%s]",user->nick,i->second->host); + } + } + } +} + +void handle_stats(char **parameters, int pcnt, userrec *user) +{ + if (pcnt != 1) + { + return; + } + if (strlen(parameters[0])>1) + { + /* make the stats query 1 character long */ + parameters[0][1] = '\0'; + } + + /* stats m (list number of times each command has been used, plus bytecount) */ + if (!strcasecmp(parameters[0],"m")) + { + for (int i = 0; i < cmdlist.size(); i++) + { + if (cmdlist[i].handler_function) + { + if (cmdlist[i].use_count) + { + /* RPL_STATSCOMMANDS */ + WriteServ(user->fd,"212 %s %s %d %d",user->nick,cmdlist[i].command,cmdlist[i].use_count,cmdlist[i].total_bytes); + } + } + } + + } + + /* stats z (debug and memory info) */ + if (!strcasecmp(parameters[0],"z")) + { + WriteServ(user->fd,"249 %s :Users(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,clientlist.size(),clientlist.size()*sizeof(userrec),clientlist.bucket_count()); + WriteServ(user->fd,"249 %s :Channels(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,chanlist.size(),chanlist.size()*sizeof(chanrec),chanlist.bucket_count()); + WriteServ(user->fd,"249 %s :Commands(VECTOR) %d (%d bytes)",user->nick,cmdlist.size(),cmdlist.size()*sizeof(command_t)); + WriteServ(user->fd,"249 %s :MOTD(VECTOR) %d, RULES(VECTOR) %d",user->nick,MOTD.size(),RULES.size()); + WriteServ(user->fd,"249 %s :address_cache(HASH_MAP) %d (%d buckets)",user->nick,IP.size(),IP.bucket_count()); + WriteServ(user->fd,"249 %s :Modules(VECTOR) %d (%d)",user->nick,modules.size(),modules.size()*sizeof(Module)); + WriteServ(user->fd,"249 %s :ClassFactories(VECTOR) %d (%d)",user->nick,factory.size(),factory.size()*sizeof(ircd_module)); + WriteServ(user->fd,"249 %s :Ports(STATIC_ARRAY) %d",user->nick,boundPortCount); + } + + /* stats o */ + if (!strcasecmp(parameters[0],"o")) + { + for (int i = 0; i < ConfValueEnum("oper"); i++) + { + char LoginName[MAXBUF]; + char HostName[MAXBUF]; + char OperType[MAXBUF]; + ConfValue("oper","name",i,LoginName); + ConfValue("oper","host",i,HostName); + ConfValue("oper","type",i,OperType); + WriteServ(user->fd,"243 %s O %s * %s %s 0",user->nick,HostName,LoginName,OperType); + } + } + + /* stats l (show user I/O stats) */ + if (!strcasecmp(parameters[0],"l")) + { + WriteServ(user->fd,"211 %s :server:port nick bytes_in cmds_in bytes_out cmds_out",user->nick); + for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++) + { + if (isnick(i->second->nick)) + { + WriteServ(user->fd,"211 %s :%s:%d %s %d %d %d %d",user->nick,ServerName,i->second->port,i->second->nick,i->second->bytes_in,i->second->cmds_in,i->second->bytes_out,i->second->cmds_out); + } + else + { + WriteServ(user->fd,"211 %s :%s:%d (unknown@%d) %d %d %d %d",user->nick,ServerName,i->second->port,i->second->fd,i->second->bytes_in,i->second->cmds_in,i->second->bytes_out,i->second->cmds_out); + } + + } + } + + /* stats u (show server uptime) */ + if (!strcasecmp(parameters[0],"u")) + { + time_t current_time = 0; + current_time = time(NULL); + time_t server_uptime = current_time - startup_time; + struct tm* stime; + stime = gmtime(&server_uptime); + /* i dont know who the hell would have an ircd running for over a year nonstop, but + * Craig suggested this, and it seemed a good idea so in it went */ + if (stime->tm_year > 70) + { + WriteServ(user->fd,"242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d",user->nick,(stime->tm_year-70),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec); + } + else + { + WriteServ(user->fd,"242 %s :Server up %d days, %.2d:%.2d:%.2d",user->nick,stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec); + } + } + + WriteServ(user->fd,"219 %s %s :End of /STATS report",user->nick,parameters[0]); + WriteOpers("*** Notice: Stats '%s' requested by %s (%s@%s)",parameters[0],user->nick,user->ident,user->host); + +} + +void handle_connect(char **parameters, int pcnt, userrec *user) +{ +} + +void handle_squit(char **parameters, int pcnt, userrec *user) +{ +} + +void handle_oper(char **parameters, int pcnt, userrec *user) +{ + char LoginName[MAXBUF]; + char Password[MAXBUF]; + char OperType[MAXBUF]; + char TypeName[MAXBUF]; + char Hostname[MAXBUF]; + int i,j; + + for (i = 0; i < ConfValueEnum("oper"); i++) + { + ConfValue("oper","name",i,LoginName); + ConfValue("oper","password",i,Password); + if ((!strcmp(LoginName,parameters[0])) && (!strcmp(Password,parameters[1]))) + { + /* correct oper credentials */ + ConfValue("oper","type",i,OperType); + WriteOpers("*** %s (%s@%s) is now an IRC operator of type %s",user->nick,user->ident,user->host,OperType); + WriteServ(user->fd,"381 %s :You are now an IRC operator of type %s",user->nick,OperType); + WriteServ(user->fd,"MODE %s :+o",user->nick); + for (j =0; j < ConfValueEnum("type"); j++) + { + ConfValue("type","name",j,TypeName); + if (!strcmp(TypeName,OperType)) + { + /* found this oper's opertype */ + ConfValue("type","host",j,Hostname); + strncpy(user->dhost,Hostname,256); + } + } + if (!strchr(user->modes,'o')) + { + strcat(user->modes,"o"); + } + return; + } + } + /* no such oper */ + WriteServ(user->fd,"491 %s :Invalid oper credentials",user->nick); + WriteOpers("*** WARNING! Failed oper attempt by %s!%s@%s!",user->nick,user->ident,user->host); +} + +void handle_nick(char **parameters, int pcnt, userrec *user) +{ + if (pcnt < 1) + { + debug("not enough params for handle_nick"); + return; + } + if (!parameters[0]) + { + debug("invalid parameter passed to handle_nick"); + return; + } + if (!strlen(parameters[0])) + { + debug("zero length new nick passed to handle_nick"); + return; + } + if (!user) + { + debug("invalid user passed to handle_nick"); + return; + } + if (!user->nick) + { + debug("invalid old nick passed to handle_nick"); + return; + } + if (!strcasecmp(user->nick,parameters[0])) + { + debug("old nick is new nick, skipping"); + return; + } + else + { + if (strlen(parameters[0]) > 1) + { + if (parameters[0][0] == ':') + { + *parameters[0]++; + } + } + if ((Find(parameters[0])) && (Find(parameters[0]) != user)) + { + WriteServ(user->fd,"433 %s %s :Nickname is already in use.",user->nick,parameters[0]); + return; + } + } + if (isnick(parameters[0]) == 0) + { + WriteServ(user->fd,"432 %s %s :Erroneous Nickname",user->nick,parameters[0]); + return; + } + + if (user->registered == 7) + { + WriteCommon(user,"NICK %s",parameters[0]); + } + + /* change the nick of the user in the users_hash */ + user = ReHashNick(user->nick, parameters[0]); + /* actually change the nick within the record */ + if (!user) return; + if (!user->nick) return; + + strncpy(user->nick, parameters[0],NICKMAX); + + debug("new nick set: %s",user->nick); + + if (user->registered < 3) + user->registered = (user->registered | 2); + if (user->registered == 3) + { + /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */ + ConnectUser(user); + } + debug("exit nickchange: %s",user->nick); +} + +int process_parameters(char **command_p,char *parameters) +{ + int i = 0; + int j = 0; + int q = 0; + q = strlen(parameters); + if (!q) + { + /* no parameters, command_p invalid! */ + return 0; + } + if (parameters[0] == ':') + { + command_p[0] = parameters+1; + return 1; + } + if (q) + { + if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':')) + { + /* only one parameter */ + command_p[0] = parameters; + if (parameters[0] == ':') + { + if (strchr(parameters,' ') != NULL) + { + command_p[0]++; + } + } + return 1; + } + } + command_p[j++] = parameters; + for (i = 0; i <= q; i++) + { + if (parameters[i] == ' ') + { + command_p[j++] = parameters+i+1; + parameters[i] = '\0'; + if (command_p[j-1][0] == ':') + { + *command_p[j-1]++; /* remove dodgy ":" */ + break; + /* parameter like this marks end of the sequence */ + } + } + } + return j; /* returns total number of items in the list */ +} + +void process_command(userrec *user, char* cmd) +{ + char *parameters; + char *command; + char *command_p[127]; + char p[MAXBUF], temp[MAXBUF]; + int i, j, items, cmd_found; + + for (int i = 0; i < 127; i++) + command_p[i] = NULL; + + if (!user) + { + return; + } + if (!cmd) + { + return; + } + if (!strcmp(cmd,"")) + { + return; + } + strcpy(temp,cmd); + if (!strchr(cmd,' ')) + { + /* no parameters, lets skip the formalities and not chop up + * the string */ + items = 0; + command_p[0] = NULL; + parameters = NULL; + for (int i = 0; i <= strlen(cmd); i++) + { + cmd[i] = toupper(cmd[i]); + } + } + else + { + strcpy(cmd,""); + j = 0; + /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */ + for (i = 0; i < strlen(temp); i++) + { + if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7)) + { + cmd[j++] = temp[i]; + cmd[j] = 0; + } + } + /* split the full string into a command plus parameters */ + parameters = p; + strcpy(p," "); + command = cmd; + if (strchr(cmd,' ')) + { + for (i = 0; i <= strlen(cmd); i++) + { + /* capitalise the command ONLY, leave params intact */ + cmd[i] = toupper(cmd[i]); + /* are we nearly there yet?! :P */ + if (cmd[i] == ' ') + { + command = cmd; + parameters = cmd+i+1; + cmd[i] = '\0'; + break; + } + } + } + else + { + for (i = 0; i <= strlen(cmd); i++) + { + cmd[i] = toupper(cmd[i]); + } + } + + } + + cmd_found = 0; + + for (i = 0; i != cmdlist.size(); i++) + { + if (strcmp(cmdlist[i].command,"")) + { + if (!strcmp(command, cmdlist[i].command)) + { + if (parameters) + { + if (strcmp(parameters,"")) + { + items = process_parameters(command_p,parameters); + } + else + { + items = 0; + command_p[0] = NULL; + } + } + else + { + items = 0; + command_p[0] = NULL; + } + + if (user) + { + user->idle_lastmsg = time(NULL); + /* activity resets the ping pending timer */ + user->nping = time(NULL) + 120; + if ((items) < cmdlist[i].min_params) + { + debug("process_command: not enough parameters: %s %s",user->nick,command); + WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command); + return; + } + if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed)) + { + debug("process_command: permission denied: %s %s",user->nick,command); + WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick); + cmd_found = 1; + return; + } + /* if the command isnt USER, PASS, or NICK, and nick is empty, + * deny command! */ + if ((strcmp(command,"USER")) && (strcmp(command,"NICK")) && (strcmp(command,"PASS"))) + { + if ((!isnick(user->nick)) || (user->registered != 7)) + { + debug("process_command: not registered: %s %s",user->nick,command); + WriteServ(user->fd,"451 %s :You have not registered",command); + return; + } + } + if ((user->registered == 7) || (!strcmp(command,"USER")) || (!strcmp(command,"NICK")) || (!strcmp(command,"PASS"))) + { + debug("process_command: handler: %s %s %d",user->nick,command,items); + if (cmdlist[i].handler_function) + { + /* ikky /stats counters */ + if (temp) + { + if (user) + { + user->bytes_in += strlen(temp); + user->cmds_in++; + } + cmdlist[i].use_count++; + cmdlist[i].total_bytes+=strlen(temp); + } + + /* WARNING: nothing may come after the + * command handler call, as the handler + * may free the user structure! */ + + cmdlist[i].handler_function(command_p,items,user); + } + return; + } + else + { + debug("process_command: not registered: %s %s",user->nick,command); + WriteServ(user->fd,"451 %s :You have not registered",command); + return; + } + } + cmd_found = 1; + } + } + } + if ((!cmd_found) && (user)) + { + debug("process_command: not in table: %s %s",user->nick,command); + WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command); + } +} + + +void createcommand(char* cmd, handlerfunc f, char flags, int minparams) +{ + command_t comm; + /* create the command and push it onto the table */ + strcpy(comm.command,cmd); + comm.handler_function = f; + comm.flags_needed = flags; + comm.min_params = minparams; + comm.use_count = 0; + comm.total_bytes = 0; + cmdlist.push_back(comm); +} + +void SetupCommandTable(void) +{ + createcommand("USER",handle_user,0,4); + createcommand("NICK",handle_nick,0,1); + createcommand("QUIT",handle_quit,0,1); + createcommand("VERSION",handle_version,0,0); + createcommand("PING",handle_ping,0,1); + createcommand("PONG",handle_pong,0,1); + createcommand("ADMIN",handle_admin,0,0); + createcommand("PRIVMSG",handle_privmsg,0,2); + createcommand("INFO",handle_info,0,0); + createcommand("TIME",handle_time,0,0); + createcommand("WHOIS",handle_whois,0,1); + createcommand("WALLOPS",handle_wallops,'o',1); + createcommand("NOTICE",handle_notice,0,2); + createcommand("JOIN",handle_join,0,1); + createcommand("NAMES",handle_names,0,1); + createcommand("PART",handle_part,0,1); + createcommand("KICK",handle_kick,0,2); + createcommand("MODE",handle_mode,0,1); + createcommand("TOPIC",handle_topic,0,1); + createcommand("WHO",handle_who,0,1); + createcommand("MOTD",handle_motd,0,0); + createcommand("RULES",handle_join,0,0); + createcommand("OPER",handle_oper,0,2); + createcommand("LIST",handle_list,0,0); + createcommand("DIE",handle_die,'o',1); + createcommand("RESTART",handle_restart,'o',1); + createcommand("KILL",handle_kill,'o',2); + createcommand("REHASH",handle_rehash,'o',0); + createcommand("LUSERS",handle_lusers,0,0); + createcommand("STATS",handle_stats,0,1); + createcommand("USERHOST",handle_userhost,0,1); + createcommand("AWAY",handle_away,0,0); + createcommand("ISON",handle_ison,0,0); + createcommand("SUMMON",handle_summon,0,0); + createcommand("USERS",handle_users,0,0); + createcommand("INVITE",handle_invite,0,2); + createcommand("PASS",handle_pass,0,1); + createcommand("TRACE",handle_trace,'o',0); + createcommand("WHOWAS",handle_whowas,0,1); + createcommand("CONNECT",handle_connect,'o',1); + createcommand("SQUIT",handle_squit,'o',1); +} + +void process_buffer(userrec *user) +{ + char cmd[MAXBUF]; + int i; + if (!user->inbuf) + { + return; + } + if (!strcmp(user->inbuf,"")) + { + return; + } + strncpy(cmd,user->inbuf,MAXBUF); + if (!strcmp(cmd,"")) + { + return; + } + if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10)) + { + cmd[strlen(cmd)-1] = '\0'; + } + if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10)) + { + cmd[strlen(cmd)-1] = '\0'; + } + strcpy(user->inbuf,""); + if (!strcmp(cmd,"")) + { + return; + } + debug("InspIRCd: processing: %s %s",user->nick,cmd); + process_command(user,cmd); +} + +int InspIRCd(void) +{ + struct sockaddr_in client, server; + int portCount = 0, ports[MAXSOCKS]; + char addrs[MAXBUF][255]; + int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE; + socklen_t length; + int count = 0, scanDetectTrigger = TRUE, showBanner = FALSE; + int selectResult = 0; + char *temp, configToken[MAXBUF], stuff[MAXBUF], Addr[MAXBUF]; + char resolvedHost[MAXBUF]; + fd_set selectFds; + struct timeval tv; + int count2; + + debug("InspIRCd: startup: begin"); + debug("$Id$"); + if ((geteuid()) && (getuid()) == 0) + { + printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n"); + Exit(ERROR); + debug("InspIRCd: startup: not starting with UID 0!"); + } + SetupCommandTable(); + debug("InspIRCd: startup: default command table set up"); + + ReadConfig(); + if (strcmp(DieValue,"")) + { + printf("WARNING: %s\n\n",DieValue); + exit(0); + } + debug("InspIRCd: startup: read config"); + + for (count = 0; count < ConfValueEnum("bind"); count++) + { + ConfValue("bind","port",count,configToken); + ConfValue("bind","address",count,Addr); + ports[count] = atoi(configToken); + strcpy(addrs[count],Addr); + debug("InspIRCd: startup: read binding %s:%d from config",addrs[count],ports[count]); + } + portCount = ConfValueEnum("bind"); + debug("InspIRCd: startup: read %d total ports",portCount); + + debug("InspIRCd: startup: InspIRCd is now running!"); + + printf("\n"); + for (count = 0; count < ConfValueEnum("module"); count++) + { + char modfile[MAXBUF]; + ConfValue("module","name",count,configToken); + sprintf(modfile,"%s/%s",MOD_PATH,configToken); + printf("Loading module... \033[1;37m%s\033[0;37m\n",modfile); + debug("InspIRCd: startup: Loading module: %s",modfile); + + factory[count] = new ircd_module(modfile); + if (factory[count]->LastError()) + { + debug("Unable to load %s: %s",modfile,factory[count]->LastError()); + sprintf("Unable to load %s: %s\nExiting...\n",modfile,factory[count]->LastError()); + Exit(ERROR); + } + if (factory[count]->factory) + { + modules[count] = factory[count]->factory->CreateModule(); + /* save the module and the module's classfactory, if + * this isnt done, random crashes can occur :/ */ + } + else + { + debug("Unable to load %s",modfile); + sprintf("Unable to load %s\nExiting...\n",modfile); + Exit(ERROR); + } + } + MODCOUNT = count - 1; + debug("Total loaded modules: %d",MODCOUNT+1); + + printf("\nInspIRCd is now running!\n"); + + startup_time = time(NULL); + + if (DaemonSeed() == ERROR) + { + debug("InspIRCd: startup: can't daemonise"); + printf("ERROR: could not go into daemon mode. Shutting down.\n"); + Exit(ERROR); + } + + + /* setup select call */ + FD_ZERO(&selectFds); + debug("InspIRCd: startup: zero selects"); + + for (count = 0; count < portCount; count++) + { + if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR) + { + debug("InspIRCd: startup: bad fd %d",openSockfd[boundPortCount]); + return(ERROR); + } + if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR) + { + debug("InspIRCd: startup: failed to bind port %d",ports[count]); + } + else /* well we at least bound to one socket so we'll continue */ + { + boundPortCount++; + } + } + + debug("InspIRCd: startup: total bound ports %d",boundPortCount); + + /* if we didn't bind to anything then abort */ + if (boundPortCount == 0) + { + debug("InspIRCd: startup: no ports bound, bailing!"); + return (ERROR); + } + + length = sizeof (client); + int flip_flop = 0; + + /* main loop for multiplexing/resetting */ + for (;;) + { + /* set up select call */ + for (count = 0; count < boundPortCount; count++) + { + FD_SET (openSockfd[count], &selectFds); + } + + /* added timeout! select was waiting forever... wank... :/ */ + tv.tv_usec = 0; + + flip_flop++; + if (flip_flop > 20) + { + tv.tv_usec = 1; + flip_flop = 0; + } + + tv.tv_sec = 0; + selectResult = select(MAXSOCKS, &selectFds, NULL, NULL, &tv); + + for (user_hash::iterator count2 = clientlist.begin(); count2 != clientlist.end(); count2++) + { + char data[MAXBUF]; + + if (!count2->second) break; + + if (count2->second) + if (count2->second->fd) + { + if (((time(NULL)) > count2->second->nping) && (isnick(count2->second->nick)) && (count2->second->registered == 7)) + { + if (!count2->second->lastping) + { + debug("InspIRCd: ping timeout: %s",count2->second->nick); + kill_link(count2->second,"Ping timeout"); + break; + } + Write(count2->second->fd,"PING :%s",ServerName); + debug("InspIRCd: pinging: %s",count2->second->nick); + count2->second->lastping = 0; + count2->second->nping = time(NULL)+120; + } + + result = read(count2->second->fd, data, 1); + // result EAGAIN means nothing read + if (result == EAGAIN) + { + } + else + if (result == 0) + { + debug("InspIRCd: Exited: %s",count2->second->nick); + kill_link(count2->second,"Client exited"); + } + else if (result > 0) + { + strncat(count2->second->inbuf, data, result); + if (strchr(count2->second->inbuf, '\n') || strchr(count2->second->inbuf, '\r')) + { + /* at least one complete line is waiting to be processed */ + if (!count2->second->fd) + break; + else + { + process_buffer(count2->second); + break; + } + } + } + } + } + + /* select is reporting a waiting socket. Poll them all to find out which */ + if (selectResult > 0) + { + char target[MAXBUF], resolved[MAXBUF]; + for (count = 0; count < boundPortCount; count++) + { + if (FD_ISSET (openSockfd[count], &selectFds)) + { + incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length); + + address_cache::iterator iter = IP.find(client.sin_addr); + bool iscached = false; + if (iter == IP.end()) + { + /* ip isn't in cache, add it */ + strncpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF); + if(CleanAndResolve(resolved, target) != TRUE) + { + strncpy(resolved,target,MAXBUF); + } + /* hostname now in 'target' */ + IP[client.sin_addr] = new string(resolved); + /* hostname in cache */ + } + else + { + /* found ip (cached) */ + strncpy(resolved, iter->second->c_str(), MAXBUF); + iscached = true; + } + + if (incomingSockfd < 0) + { + WriteOpers("*** WARNING: Accept failed on port %d (%s)", ports[count],target); + debug("InspIRCd: accept failed: %d",ports[count]); + break; + } + + AddClient(incomingSockfd, resolved, ports[count], iscached); + debug("InspIRCd: adding client on port %d fd=%d",ports[count],incomingSockfd); + break; + } + + } + } + } + + /* not reached */ + close (incomingSockfd); +} + diff --git a/src/servers.cpp b/src/servers.cpp index 44bd2e0c3..c334b5c91 100644 --- a/src/servers.cpp +++ b/src/servers.cpp @@ -1,6 +1,10 @@ /* $Log$ +Revision 1.2 2003/01/26 23:53:03 brain +Modified documentation for base classes +Added base classes + Revision 1.1 2003/01/26 20:15:03 brain Added server classes for linking @@ -11,6 +15,7 @@ Added server classes for linking #include "servers.h" #include "inspircd.h" #include +#include serverrec::serverrec() { @@ -23,6 +28,7 @@ serverrec::serverrec() hops_away = 1; connected_at = time(NULL); jupiter = false; + fd = 0; } @@ -41,22 +47,19 @@ serverrec::serverrec(char* n, int link_t, long ver, bool jupe) hops_away = 1; connected_at = time(NULL); jupiter = jupe; + fd = 0; } void serverrec::AddLeaf(serverrec *child) { - leaf.push_back(child); + leaf[child->name] = child; } -void serverrec::DelLeaf(char* n) +void serverrec::DelLeaf(string n) { - for (server_list::iterator i = leaf.begin(); i != leaf.end(); i++) - { - if (strcasecmp(n,i->name)) - { - leaf.erase(i); - return; - } - } + server_list::iterator i = leaf.find(n); + + if (i != leaf.end()) + leaf.erase(i); } diff --git a/src/servers.cpp~ b/src/servers.cpp~ new file mode 100644 index 000000000..4055a60e1 --- /dev/null +++ b/src/servers.cpp~ @@ -0,0 +1,63 @@ +/* + +$Log$ +Revision 1.1 2003/01/26 23:53:03 brain +Modified documentation for base classes +Added base classes + +Revision 1.1 2003/01/26 20:15:03 brain +Added server classes for linking + + +*/ + +#include "inspircd_config.h" +#include "servers.h" +#include "inspircd.h" +#include +#include + +serverrec::serverrec() +{ + leaf.clear(); + strcpy(name,""); + pingtime = 0; + linktype = LINK_ACTIVE; + lastping = time(NULL); + usercount_i = usercount = opercount = version = 0; + hops_away = 1; + connected_at = time(NULL); + jupiter = false; +} + + +serverrec::~serverrec() +{ +} + +serverrec::serverrec(char* n, int link_t, long ver, bool jupe) +{ + leaf.clear(); + strcpy(name,n); + linktype = link_t; + lastping = time(NULL); + usercount_i = usercount = opercount = 0; + version = ver; + hops_away = 1; + connected_at = time(NULL); + jupiter = jupe; +} + +void serverrec::AddLeaf(serverrec *child) +{ + leaf[child->name] = child; +} + +void serverrec::DelLeaf(string n) +{ + server_list::iterator i = leaf.find(n); + + if (i != leaf.end()) + leaf.erase(i); +} + -- cgit v1.2.3