1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * Inspire is copyright (C) 2002-2003 ChatSpike-Dev.
7 * <brain@chatspike.net>
8 * <Craig@chatspike.net>
10 * Written by Craig Edwards, Craig McLure, and others.
11 * This program is free but copyrighted software; see
12 * the file COPYING for details.
14 * ---------------------------------------------------
17 /* Now with added unF! ;) */
20 #include "inspircd_io.h"
21 #include "inspircd_util.h"
22 #include "inspircd_config.h"
25 #include <sys/errno.h>
26 #include <sys/ioctl.h>
27 #include <sys/utsname.h>
37 #include "connection.h"
48 char ServerName[MAXBUF];
50 char ServerDesc[MAXBUF];
51 char AdminName[MAXBUF];
52 char AdminEmail[MAXBUF];
53 char AdminNick[MAXBUF];
55 char restartpass[MAXBUF];
59 char PrefixQuit[MAXBUF];
60 char DieValue[MAXBUF];
63 int WHOWAS_STALE = 48; // default WHOWAS Entries last 2 days before they go 'stale'
64 int WHOWAS_MAX = 100; // default 100 people maximum in the WHOWAS list
66 time_t startup_time = time(NULL);
68 template<> struct hash<in_addr>
70 size_t operator()(const struct in_addr &a) const
73 memcpy(&q,&a,sizeof(size_t));
78 template<> struct hash<string>
80 size_t operator()(const string &s) const
83 static struct hash<const char *> strhash;
95 bool operator()(const string& s1, const string& s2) const
97 char a[MAXBUF],b[MAXBUF];
100 return (strcasecmp(a,b) == 0);
105 struct InAddr_HashComp
108 bool operator()(const in_addr &s1, const in_addr &s2) const
113 memcpy(&q,&s1,sizeof(size_t));
114 memcpy(&p,&s2,sizeof(size_t));
122 typedef hash_map<string, userrec*, hash<string>, StrHashComp> user_hash;
123 typedef hash_map<string, chanrec*, hash<string>, StrHashComp> chan_hash;
124 typedef hash_map<in_addr,string*, hash<in_addr>, InAddr_HashComp> address_cache;
125 typedef deque<command_t> command_table;
126 typedef DLLFactory<ModuleFactory> ircd_module;
129 server_list* servers;
131 user_hash clientlist;
134 command_table cmdlist;
138 vector<Module*> modules(255);
139 vector<ircd_module*> factory(255);
142 struct linger linger = { 0 };
143 char bannerBuffer[MAXBUF];
144 int boundPortCount = 0;
148 int has_channel(userrec *u, chanrec *c);
149 int usercount(chanrec *c);
150 int usercount_i(chanrec *c);
151 void update_stats_l(int fd,int data_out);
152 char* Passwd(userrec *user);
153 bool IsDenied(userrec *user);
154 void AddWhoWas(userrec* u);
157 void safedelete(userrec *p)
161 debug("deleting %s %s %s %s",p->nick,p->ident,p->dhost,p->fullname);
162 debug("safedelete(userrec*): pointer is safe to delete");
167 debug("safedelete(userrec*): unsafe pointer operation squished");
171 void safedelete(chanrec *p)
176 debug("safedelete(chanrec*): pointer is safe to delete");
180 debug("safedelete(chanrec*): unsafe pointer operation squished");
185 /* chop a string down to 512 characters and preserve linefeed (irc max
190 if (strlen(str) > 512)
199 string getservername()
204 string getserverdesc()
209 string getnetworkname()
214 string getadminname()
219 string getadminemail()
224 string getadminnick()
229 void debug(char *text, ...)
231 char textbuffer[MAXBUF];
235 struct tm * timeinfo;
238 timeinfo = localtime (&rawtime);
242 f = fopen("ircd.log","a+");
246 va_start (argsPtr, text);
247 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
249 strcpy(b,asctime(timeinfo));
250 b[strlen(b)-1] = ':';
251 fprintf(f,"%s %s\n",b,textbuffer);
256 printf("Can't write log file, bailing!!!");
262 void readfile(file_cache &F, const char* fname)
265 char linebuf[MAXBUF];
267 debug("readfile: loading %s",fname);
269 file = fopen(fname,"r");
274 fgets(linebuf,sizeof(linebuf),file);
275 linebuf[strlen(linebuf)-1]='\0';
276 if (!strcmp(linebuf,""))
282 F.push_back(linebuf);
289 debug("readfile: failed to load file: %s",fname);
291 debug("readfile: loaded %s, %d lines",fname,F.size());
294 void ReadConfig(void)
296 char dbg[MAXBUF],pauseval[MAXBUF],Value[MAXBUF];
299 ConfValue("server","name",0,ServerName);
300 ConfValue("server","description",0,ServerDesc);
301 ConfValue("server","network",0,Network);
302 ConfValue("admin","name",0,AdminName);
303 ConfValue("admin","email",0,AdminEmail);
304 ConfValue("admin","nick",0,AdminNick);
305 ConfValue("files","motd",0,motd);
306 ConfValue("files","rules",0,rules);
307 ConfValue("power","diepass",0,diepass);
308 ConfValue("power","pause",0,pauseval);
309 ConfValue("power","restartpass",0,restartpass);
310 ConfValue("options","prefixquit",0,PrefixQuit);
311 ConfValue("die","value",0,DieValue);
312 ConfValue("options","debug",0,dbg);
314 if (!strcmp(dbg,"on"))
318 DieDelay = atoi(pauseval);
320 readfile(RULES,rules);
321 debug("Reading connect classes");
323 for (int i = 0; i < ConfValueEnum("connect"); i++)
326 ConfValue("connect","allow",i,Value);
327 if (strcmp(Value,""))
329 strcpy(c.host,Value);
332 ConfValue("connect","password",i,Value);
333 strcpy(c.pass,Value);
334 Classes.push_back(c);
335 debug("Read connect class type ALLOW, host=%s password=%s",c.host,c.pass);
339 ConfValue("connect","deny",i,Value);
340 strcpy(c.host,Value);
342 Classes.push_back(c);
343 debug("Read connect class type DENY, host=%s",c.host);
352 debug("Blocking: %d",s);
353 flags = fcntl(s, F_GETFL, 0);
354 fcntl(s, F_SETFL, flags ^ O_NONBLOCK);
357 void NonBlocking(int s)
360 debug("NonBlocking: %d",s);
361 flags = fcntl(s, F_GETFL, 0);
362 fcntl(s, F_SETFL, flags | O_NONBLOCK);
366 int CleanAndResolve (char *resolvedHost, const char *unresolvedHost)
368 struct hostent *hostPtr = NULL;
371 memset (resolvedHost, '\0',MAXBUF);
372 if(unresolvedHost == NULL)
374 if ((inet_aton(unresolvedHost,&addr)) == 0)
376 hostPtr = gethostbyaddr ((char *)&addr.s_addr,sizeof(addr.s_addr),AF_INET);
378 snprintf(resolvedHost,MAXBUF,"%s",hostPtr->h_name);
380 snprintf(resolvedHost,MAXBUF,"%s",unresolvedHost);
384 /* write formatted text to a socket, in same format as printf */
386 void Write(int sock,char *text, ...)
388 char textbuffer[MAXBUF];
392 va_start (argsPtr, text);
393 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
395 sprintf(tb,"%s\r\n",textbuffer);
397 write(sock,tb,strlen(tb));
398 update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
401 /* write a server formatted numeric response to a single socket */
403 void WriteServ(int sock, char* text, ...)
405 char textbuffer[MAXBUF],tb[MAXBUF];
407 va_start (argsPtr, text);
409 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
411 sprintf(tb,":%s %s\r\n",ServerName,textbuffer);
413 write(sock,tb,strlen(tb));
414 update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
417 /* write text from an originating user to originating user */
419 void WriteFrom(int sock, userrec *user,char* text, ...)
421 char textbuffer[MAXBUF],tb[MAXBUF];
423 va_start (argsPtr, text);
425 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
427 sprintf(tb,":%s!%s@%s %s\r\n",user->nick,user->ident,user->dhost,textbuffer);
429 write(sock,tb,strlen(tb));
430 update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
433 /* write text to an destination user from a source user (e.g. user privmsg) */
435 void WriteTo(userrec *source, userrec *dest,char *data, ...)
437 char textbuffer[MAXBUF],tb[MAXBUF];
439 va_start (argsPtr, data);
440 if ((!dest) || (!source))
444 vsnprintf(textbuffer, MAXBUF, data, argsPtr);
447 WriteFrom(dest->fd,source,"%s",textbuffer);
450 /* write formatted text from a source user to all users on a channel
451 * including the sender (NOT for privmsg, notice etc!) */
453 void WriteChannel(chanrec* Ptr, userrec* user, char* text, ...)
455 char textbuffer[MAXBUF];
457 va_start (argsPtr, text);
458 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
460 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
462 if (has_channel(i->second,Ptr))
464 WriteTo(user,i->second,"%s",textbuffer);
469 /* write formatted text from a source user to all users on a channel except
470 * for the sender (for privmsg etc) */
472 void ChanExceptSender(chanrec* Ptr, userrec* user, char* text, ...)
474 char textbuffer[MAXBUF];
476 va_start (argsPtr, text);
477 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
480 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
482 if (has_channel(i->second,Ptr) && (user != i->second))
484 WriteTo(user,i->second,"%s",textbuffer);
489 int c_count(userrec* u)
492 for (int i =0; i != MAXCHANS; i++)
493 if (u->chans[i].channel)
499 /* return 0 or 1 depending if users u and u2 share one or more common channels
500 * (used by QUIT, NICK etc which arent channel specific notices) */
502 int common_channels(userrec *u, userrec *u2)
511 for (i = 0; i != MAXCHANS; i++)
513 for (z = 0; z != MAXCHANS; z++)
515 if ((u->chans[i].channel == u2->chans[z].channel) && (u->chans[i].channel) && (u2->chans[z].channel) && (u->registered == 7) && (u2->registered == 7))
517 if ((c_count(u)) && (c_count(u2)))
527 /* write a formatted string to all users who share at least one common
528 * channel, including the source user e.g. for use in NICK */
530 void WriteCommon(userrec *u, char* text, ...)
532 char textbuffer[MAXBUF];
534 va_start (argsPtr, text);
535 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
538 WriteFrom(u->fd,u,"%s",textbuffer);
540 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
542 if (common_channels(u,i->second) && (i->second != u))
544 WriteFrom(i->second->fd,u,"%s",textbuffer);
549 /* write a formatted string to all users who share at least one common
550 * channel, NOT including the source user e.g. for use in QUIT */
552 void WriteCommonExcept(userrec *u, char* text, ...)
554 char textbuffer[MAXBUF];
556 va_start (argsPtr, text);
557 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
560 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
562 if ((common_channels(u,i->second)) && (u != i->second))
564 WriteFrom(i->second->fd,u,"%s",textbuffer);
569 void WriteOpers(char* text, ...)
571 char textbuffer[MAXBUF];
573 va_start (argsPtr, text);
574 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
577 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
579 if (strchr(i->second->modes,'o'))
581 if (strchr(i->second->modes,'s'))
583 // send server notices to all with +s
584 // (TODO: needs SNOMASKs)
585 WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,textbuffer);
591 void WriteWallOps(userrec *source, char* text, ...)
594 char textbuffer[MAXBUF];
596 va_start (argsPtr, text);
597 vsnprintf(textbuffer, MAXBUF, text, argsPtr);
600 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
602 if (strchr(i->second->modes,'w'))
604 WriteTo(source,i->second,"WALLOPS %s",textbuffer);
609 /* convert a string to lowercase. Note following special circumstances
610 * taken from RFC 1459. Many "official" server branches still hold to this
611 * rule so i will too;
613 * Because of IRC's scandanavian origin, the characters {}| are
614 * considered to be the lower case equivalents of the characters []\,
615 * respectively. This is a critical issue when determining the
616 * equivalence of two nicknames.
619 void strlower(char *n)
625 for (int i = 0; i != strlen(n); i++)
627 n[i] = tolower(n[i]);
637 /* verify that a user's nickname is valid */
639 int isnick(const char* n)
651 if (strlen(n) > NICKMAX-1)
655 for (i = 0; i != strlen(n); i++)
657 if ((n[i] < 33) || (n[i] > 125))
661 /* can't occur ANYWHERE in a nickname! */
662 if (strchr("<>,./?:;@'~#=+()*&%$£ \"!",n[i]))
666 /* can't occur as the first char of a nickname... */
667 if ((strchr("0123456789",n[i])) && (!i))
675 /* Find a user record by nickname and return a pointer to it */
677 userrec* Find(string nick)
679 user_hash::iterator iter = clientlist.find(nick);
681 if (iter == clientlist.end())
682 /* Couldn't find it */
688 void update_stats_l(int fd,int data_out) /* add one line-out to stats L for this fd */
690 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
692 if (i->second->fd == fd)
694 i->second->bytes_out+=data_out;
695 i->second->cmds_out++;
701 /* find a channel record by channel name and return a pointer to it */
703 chanrec* FindChan(const char* chan)
705 chan_hash::iterator iter = chanlist.find(chan);
707 if (iter == chanlist.end())
708 /* Couldn't find it */
715 void purge_empty_chans(void)
717 int go_again = 1, purge = 0;
722 for (chan_hash::iterator i = chanlist.begin(); i != chanlist.end(); i++)
725 if (!usercount(i->second))
727 /* kill the record */
728 if (i != chanlist.end())
730 debug("del_channel: destroyed: %s",i->second->name);
741 debug("completed channel purge, killed %d",purge);
744 /* returns the status character for a given user on a channel, e.g. @ for op,
745 * % for halfop etc. If the user has several modes set, the highest mode
746 * the user has must be returned. */
748 char* cmode(userrec *user, chanrec *chan)
751 for (i = 0; i != MAXCHANS; i++)
753 if ((user->chans[i].channel == chan) && (chan != NULL))
755 if ((user->chans[i].uc_modes & UCMODE_OP) > 0)
759 if ((user->chans[i].uc_modes & UCMODE_HOP) > 0)
763 if ((user->chans[i].uc_modes & UCMODE_VOICE) > 0)
772 char scratch[MAXMODES];
774 char* chanmodes(chanrec *chan)
777 if (chan->noexternal)
785 if (strcmp(chan->key,""))
793 if (chan->inviteonly)
809 if (strcmp(chan->key,""))
812 strcat(scratch,chan->key);
817 sprintf(foo," %d",chan->limit);
820 debug("chanmodes: %s %s",chan->name,scratch);
824 /* returns the status value for a given user on a channel, e.g. STATUS_OP for
825 * op, STATUS_VOICE for voice etc. If the user has several modes set, the
826 * highest mode the user has must be returned. */
828 int cstatus(userrec *user, chanrec *chan)
831 for (i = 0; i != MAXCHANS; i++)
833 if ((user->chans[i].channel == chan) && (chan != NULL))
835 if ((user->chans[i].uc_modes & UCMODE_OP) > 0)
839 if ((user->chans[i].uc_modes & UCMODE_HOP) > 0)
843 if ((user->chans[i].uc_modes & UCMODE_VOICE) > 0)
847 return STATUS_NORMAL;
853 /* compile a userlist of a channel into a string, each nick seperated by
854 * spaces and op, voice etc status shown as @ and + */
856 void userlist(userrec *user,chanrec *c)
858 sprintf(list,"353 %s = %s :", user->nick, c->name);
859 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
861 if (has_channel(i->second,c))
863 if (isnick(i->second->nick))
865 if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
867 /* user is +i, and source not on the channel, does not show
868 * nick in NAMES list */
871 strcat(list,cmode(i->second,c));
872 strcat(list,i->second->nick);
874 if (strlen(list)>(480-NICKMAX))
876 /* list overflowed into
877 * multiple numerics */
878 WriteServ(user->fd,list);
879 sprintf(list,"353 %s = %s :", user->nick, c->name);
884 /* if whats left in the list isnt empty, send it */
885 if (list[strlen(list)-1] != ':')
887 WriteServ(user->fd,list);
891 /* return a count of the users on a specific channel accounting for
892 * invisible users who won't increase the count. e.g. for /LIST */
894 int usercount_i(chanrec *c)
900 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
902 if (has_channel(i->second,c))
904 if (isnick(i->second->nick))
906 if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
908 /* user is +i, and source not on the channel, does not show
909 * nick in NAMES list */
916 debug("usercount_i: %s %d",c->name,count);
921 int usercount(chanrec *c)
927 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
929 if (has_channel(i->second,c))
931 if (isnick(i->second->nick))
937 debug("usercount: %s %d",c->name,count);
942 /* add a channel to a user, creating the record for it if needed and linking
943 * it to the user record */
945 chanrec* add_channel(userrec *user, char* cname, char* key)
951 if ((!cname) || (!user))
955 if (strlen(cname) > CHANMAX-1)
957 cname[CHANMAX-1] = '\0';
960 debug("add_channel: %s %s",user->nick,cname);
962 if ((has_channel(user,FindChan(cname))) && (FindChan(cname)))
964 return NULL; // already on the channel!
967 if (!FindChan(cname))
969 /* create a new one */
970 debug("add_channel: creating: %s",cname);
972 chanlist[cname] = new chanrec();
974 strcpy(chanlist[cname]->name, cname);
975 chanlist[cname]->topiclock = 1;
976 chanlist[cname]->noexternal = 1;
977 chanlist[cname]->created = time(NULL);
978 strcpy(chanlist[cname]->topic, "");
979 strncpy(chanlist[cname]->setby, user->nick,NICKMAX);
980 chanlist[cname]->topicset = 0;
981 Ptr = chanlist[cname];
982 debug("add_channel: created: %s",cname);
983 /* set created to 2 to indicate user
984 * is the first in the channel
985 * and should be given ops */
991 /* channel exists, just fish out a pointer to its struct */
992 Ptr = FindChan(cname);
995 debug("add_channel: joining to: %s",Ptr->name);
996 if (strcmp(Ptr->key,""))
998 debug("add_channel: %s has key %s",Ptr->name,Ptr->key);
1001 debug("add_channel: no key given in JOIN");
1002 WriteServ(user->fd,"475 %s %s :Cannot join channel (Requires key)",user->nick, Ptr->name);
1007 debug("key at %p is %s",key,key);
1008 if (strcasecmp(key,Ptr->key))
1010 debug("add_channel: bad key given in JOIN");
1011 WriteServ(user->fd,"475 %s %s :Cannot join channel (Incorrect key)",user->nick, Ptr->name);
1017 if (Ptr->inviteonly)
1019 if (user->IsInvited(Ptr->name))
1021 /* user was invited to channel */
1022 /* there may be an optional channel NOTICE here */
1026 WriteServ(user->fd,"473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name);
1033 if (usercount(Ptr) == Ptr->limit)
1035 WriteServ(user->fd,"471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name);
1040 /* check user against the channel banlist */
1041 for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++)
1043 if (match(user->GetFullHost(),i->data))
1045 WriteServ(user->fd,"474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name);
1050 user->RemoveInvite(Ptr->name);
1057 for (i =0; i != MAXCHANS; i++)
1059 if (user->chans[i].channel == NULL)
1063 /* first user in is given ops */
1064 user->chans[i].uc_modes = UCMODE_OP;
1068 user->chans[i].uc_modes = 0;
1070 user->chans[i].channel = Ptr;
1071 WriteChannel(Ptr,user,"JOIN :%s",Ptr->name);
1074 WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
1075 WriteServ(user->fd,"333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset);
1078 WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
1079 WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name,chanmodes(Ptr));
1080 WriteServ(user->fd,"329 %s %s %d", user->nick, Ptr->name, Ptr->created);
1081 FOREACH_MOD OnUserJoin(user,Ptr);
1085 debug("add_channel: user channel max exceeded: %s %s",user->nick,cname);
1086 WriteServ(user->fd,"405 %s %s :You are on too many channels",user->nick, cname);
1090 /* remove a channel from a users record, and remove the record from memory
1091 * if the channel has become empty */
1093 chanrec* del_channel(userrec *user, char* cname, char* reason)
1099 if ((!cname) || (!user))
1104 Ptr = FindChan(cname);
1111 FOREACH_MOD OnUserPart(user,Ptr);
1112 debug("del_channel: removing: %s %s",user->nick,Ptr->name);
1114 for (i =0; i != MAXCHANS; i++)
1116 /* zap it from the channel list of the user */
1117 if (user->chans[i].channel == Ptr)
1121 WriteChannel(Ptr,user,"PART %s :%s",Ptr->name, reason);
1125 WriteChannel(Ptr,user,"PART :%s",Ptr->name);
1127 user->chans[i].uc_modes = 0;
1128 user->chans[i].channel = NULL;
1129 debug("del_channel: unlinked: %s %s",user->nick,Ptr->name);
1134 /* if there are no users left on the channel */
1135 if (!usercount(Ptr))
1137 chan_hash::iterator iter = chanlist.find(Ptr->name);
1139 debug("del_channel: destroying channel: %s",Ptr->name);
1141 /* kill the record */
1142 if (iter != chanlist.end())
1144 debug("del_channel: destroyed: %s",Ptr->name);
1145 delete iter->second;
1146 chanlist.erase(iter);
1152 void kick_channel(userrec *src,userrec *user, chanrec *Ptr, char* reason)
1157 if ((!Ptr) || (!user) || (!src))
1162 debug("kick_channel: removing: %s %s %s",user->nick,Ptr->name,src->nick);
1164 if (!has_channel(user,Ptr))
1166 WriteServ(src->fd,"441 %s %s %s :They are not on that channel",src->nick, user->nick, Ptr->name);
1169 if ((cstatus(src,Ptr) < STATUS_HOP) || (cstatus(src,Ptr) < cstatus(user,Ptr)))
1171 if (cstatus(src,Ptr) == STATUS_HOP)
1173 WriteServ(src->fd,"482 %s %s :You must be a channel operator",src->nick, Ptr->name);
1177 WriteServ(src->fd,"482 %s %s :You must be at least a half-operator",src->nick, Ptr->name);
1183 for (i =0; i != MAXCHANS; i++)
1185 /* zap it from the channel list of the user */
1186 if (user->chans[i].channel == Ptr)
1188 WriteChannel(Ptr,src,"KICK %s %s :%s",Ptr->name, user->nick, reason);
1189 user->chans[i].uc_modes = 0;
1190 user->chans[i].channel = NULL;
1191 debug("del_channel: unlinked: %s %s",user->nick,Ptr->name);
1196 /* if there are no users left on the channel */
1197 if (!usercount(Ptr))
1199 chan_hash::iterator iter = chanlist.find(Ptr->name);
1201 debug("del_channel: destroying channel: %s",Ptr->name);
1203 /* kill the record */
1204 if (iter != chanlist.end())
1206 debug("del_channel: destroyed: %s",Ptr->name);
1207 delete iter->second;
1208 chanlist.erase(iter);
1214 /* returns 1 if user u has channel c in their record, 0 if not */
1216 int has_channel(userrec *u, chanrec *c)
1224 for (i =0; i != MAXCHANS; i++)
1226 if (u->chans[i].channel == c)
1234 int give_ops(userrec *user,char *dest,chanrec *chan,int status)
1239 if ((!user) || (!dest) || (!chan))
1243 if (status != STATUS_OP)
1245 WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
1252 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1258 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1263 for (i = 0; i != MAXCHANS; i++)
1265 if ((d->chans[i].channel == chan) && (chan != NULL))
1267 if (d->chans[i].uc_modes & UCMODE_OP)
1269 /* mode already set on user, dont allow multiple */
1272 d->chans[i].uc_modes = d->chans[i].uc_modes | UCMODE_OP;
1273 debug("gave ops: %s %s",d->chans[i].channel->name,d->nick);
1281 int give_hops(userrec *user,char *dest,chanrec *chan,int status)
1286 if ((!user) || (!dest) || (!chan))
1290 if (status != STATUS_OP)
1292 WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
1300 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1305 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1310 for (i = 0; i != MAXCHANS; i++)
1312 if ((d->chans[i].channel == chan) && (chan != NULL))
1314 if (d->chans[i].uc_modes & UCMODE_HOP)
1316 /* mode already set on user, dont allow multiple */
1319 d->chans[i].uc_modes = d->chans[i].uc_modes | UCMODE_HOP;
1320 debug("gave h-ops: %s %s",d->chans[i].channel->name,d->nick);
1328 int give_voice(userrec *user,char *dest,chanrec *chan,int status)
1333 if ((!user) || (!dest) || (!chan))
1337 if (status < STATUS_HOP)
1339 WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, chan->name);
1347 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1352 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1357 for (i = 0; i != MAXCHANS; i++)
1359 if ((d->chans[i].channel == chan) && (chan != NULL))
1361 if (d->chans[i].uc_modes & UCMODE_VOICE)
1363 /* mode already set on user, dont allow multiple */
1366 d->chans[i].uc_modes = d->chans[i].uc_modes | UCMODE_VOICE;
1367 debug("gave voice: %s %s",d->chans[i].channel->name,d->nick);
1375 int take_ops(userrec *user,char *dest,chanrec *chan,int status)
1380 if ((!user) || (!dest) || (!chan))
1384 if (status != STATUS_OP)
1386 WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
1394 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1399 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1404 for (i = 0; i != MAXCHANS; i++)
1406 if ((d->chans[i].channel == chan) && (chan != NULL))
1408 if ((d->chans[i].uc_modes & UCMODE_OP) == 0)
1410 /* mode already set on user, dont allow multiple */
1413 d->chans[i].uc_modes ^= UCMODE_OP;
1414 debug("took ops: %s %s",d->chans[i].channel->name,d->nick);
1422 int take_hops(userrec *user,char *dest,chanrec *chan,int status)
1427 if ((!user) || (!dest) || (!chan))
1431 if (status != STATUS_OP)
1433 WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
1441 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1446 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1451 for (i = 0; i != MAXCHANS; i++)
1453 if ((d->chans[i].channel == chan) && (chan != NULL))
1455 if ((d->chans[i].uc_modes & UCMODE_HOP) == 0)
1457 /* mode already set on user, dont allow multiple */
1460 d->chans[i].uc_modes ^= UCMODE_HOP;
1461 debug("took h-ops: %s %s",d->chans[i].channel->name,d->nick);
1469 int take_voice(userrec *user,char *dest,chanrec *chan,int status)
1474 if ((!user) || (!dest) || (!chan))
1478 if (status < STATUS_HOP)
1480 WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, chan->name);
1488 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1493 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1498 for (i = 0; i != MAXCHANS; i++)
1500 if ((d->chans[i].channel == chan) && (chan != NULL))
1502 if ((d->chans[i].uc_modes & UCMODE_VOICE) == 0)
1504 /* mode already set on user, dont allow multiple */
1507 d->chans[i].uc_modes ^= UCMODE_VOICE;
1508 debug("took voice: %s %s",d->chans[i].channel->name,d->nick);
1516 void TidyBan(char *ban)
1518 char temp[MAXBUF],NICK[MAXBUF],IDENT[MAXBUF],HOST[MAXBUF];
1522 char* pos_of_pling = strchr(temp,'!');
1523 char* pos_of_at = strchr(temp,'@');
1525 pos_of_pling[0] = '\0';
1526 pos_of_at[0] = '\0';
1530 strncpy(NICK,temp,NICKMAX);
1531 strncpy(IDENT,pos_of_pling,IDENTMAX+1);
1532 strncpy(HOST,pos_of_at,160);
1534 sprintf(ban,"%s!%s@%s",NICK,IDENT,HOST);
1537 int add_ban(userrec *user,char *dest,chanrec *chan,int status)
1540 if ((!user) || (!dest) || (!chan))
1542 if (strchr(dest,'!')==0)
1544 if (strchr(dest,'@')==0)
1546 for (int i = 0; i < strlen(dest); i++)
1549 for (int i = 0; i < strlen(dest); i++)
1553 for (int i = 0; i < strlen(dest); i++)
1559 for (int i = 0; i < strlen(dest); i++)
1564 debug("add_ban: %s %s",chan->name,user->nick);
1567 for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
1569 if (!strcasecmp(i->data,dest))
1571 // dont allow a user to set the same ban twice
1576 b.set_time = time(NULL);
1577 strncpy(b.data,dest,MAXBUF);
1578 strncpy(b.set_by,user->nick,NICKMAX);
1579 chan->bans.push_back(b);
1583 int take_ban(userrec *user,char *dest,chanrec *chan,int status)
1585 if ((!user) || (!dest) || (!chan))
1590 debug("del_ban: %s %s",chan->name,user->nick);
1591 for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
1593 if (!strcasecmp(i->data,dest))
1595 chan->bans.erase(i);
1602 void process_modes(char **parameters,userrec* user,chanrec *chan,int status, int pcnt)
1604 char modelist[MAXBUF];
1605 char outlist[MAXBUF];
1606 char outstr[MAXBUF];
1607 char outpars[32][MAXBUF];
1613 bool k_set = false, l_set = false;
1620 debug("process_modes: start");
1622 strcpy(modelist,parameters[1]); /* mode list, e.g. +oo-o */
1623 /* parameters[2] onwards are parameters for
1624 * modes that require them :) */
1625 strcpy(outlist,"+");
1628 debug("process_modes: modelist: %s",modelist);
1630 for (ptr = 0; ptr < strlen(modelist); ptr++)
1635 debug("process_modes: modechar: %c",modelist[ptr]);
1636 switch (modelist[ptr])
1641 if ((outlist[strlen(outlist)-1] == '+') || (outlist[strlen(outlist)-1] == '-'))
1643 outlist[strlen(outlist)-1] = '-';
1647 strcat(outlist,"-");
1657 if ((outlist[strlen(outlist)-1] == '+') || (outlist[strlen(outlist)-1] == '-'))
1659 outlist[strlen(outlist)-1] = '+';
1663 strcat(outlist,"+");
1670 if ((param >= pcnt)) break;
1673 r = give_ops(user,parameters[param++],chan,status);
1677 r = take_ops(user,parameters[param++],chan,status);
1681 strcat(outlist,"o");
1682 strcpy(outpars[pc++],parameters[param-1]);
1687 if ((param >= pcnt)) break;
1690 r = give_hops(user,parameters[param++],chan,status);
1694 r = take_hops(user,parameters[param++],chan,status);
1698 strcat(outlist,"h");
1699 strcpy(outpars[pc++],parameters[param-1]);
1705 if ((param >= pcnt)) break;
1708 r = give_voice(user,parameters[param++],chan,status);
1712 r = take_voice(user,parameters[param++],chan,status);
1716 strcat(outlist,"v");
1717 strcpy(outpars[pc++],parameters[param-1]);
1722 if ((param >= pcnt)) break;
1725 r = add_ban(user,parameters[param++],chan,status);
1729 r = take_ban(user,parameters[param++],chan,status);
1733 strcat(outlist,"b");
1734 strcpy(outpars[pc++],parameters[param-1]);
1739 if ((param >= pcnt))
1747 if (!strcmp(chan->key,""))
1749 strcat(outlist,"k");
1750 strcpy(outpars[pc++],parameters[param++]);
1751 strcpy(chan->key,parameters[param-1]);
1757 /* only allow -k if correct key given */
1758 if (strcmp(chan->key,""))
1760 strcat(outlist,"k");
1761 strcpy(chan->key,"");
1771 strcat(outlist,"l");
1776 if ((param >= pcnt)) break;
1782 bool invalid = false;
1783 for (int i = 0; i < strlen(parameters[param]); i++)
1785 if ((parameters[param][i] < '0') || (parameters[param][i] > '9'))
1790 if (atoi(parameters[param]) < 1)
1798 chan->limit = atoi(parameters[param]);
1801 strcat(outlist,"l");
1802 strcpy(outpars[pc++],parameters[param++]);
1809 if (chan->inviteonly != mdir)
1811 strcat(outlist,"i");
1813 chan->inviteonly = mdir;
1817 if (chan->topiclock != mdir)
1819 strcat(outlist,"t");
1821 chan->topiclock = mdir;
1825 if (chan->noexternal != mdir)
1827 strcat(outlist,"n");
1829 chan->noexternal = mdir;
1833 if (chan->moderated != mdir)
1835 strcat(outlist,"m");
1837 chan->moderated = mdir;
1841 if (chan->secret != mdir)
1843 strcat(outlist,"s");
1844 if (chan->c_private)
1846 chan->c_private = 0;
1849 strcat(outlist,"-p+");
1853 strcat(outlist,"+p-");
1857 chan->secret = mdir;
1861 if (chan->c_private != mdir)
1863 strcat(outlist,"p");
1869 strcat(outlist,"-s+");
1873 strcat(outlist,"+s-");
1877 chan->c_private = mdir;
1884 /* this ensures only the *valid* modes are sent out onto the network */
1885 while ((outlist[strlen(outlist)-1] == '-') || (outlist[strlen(outlist)-1] == '+'))
1887 outlist[strlen(outlist)-1] = '\0';
1889 if (strcmp(outlist,""))
1891 strcpy(outstr,outlist);
1892 for (ptr = 0; ptr < pc; ptr++)
1895 strcat(outstr,outpars[ptr]);
1897 WriteChannel(chan,user,"MODE %s %s",chan->name,outstr);
1901 void handle_mode(char **parameters, int pcnt, userrec *user)
1907 char outpars[MAXBUF];
1909 dest = Find(parameters[0]);
1911 if ((dest) && (pcnt == 1))
1913 WriteServ(user->fd,"221 %s :+%s",user->nick,user->modes);
1916 if ((dest) && (pcnt > 1))
1921 if (strchr(user->modes,'o'))
1932 WriteServ(user->fd,"482 %s :Can't change mode for other users",user->nick);
1936 strcpy(outpars,"+");
1939 if ((parameters[1][0] != '+') && (parameters[1][0] != '-'))
1942 for (i = 0; i < strlen(parameters[1]); i++)
1944 if (parameters[1][i] == '+')
1948 if ((outpars[strlen(outpars)-1] == '+') || (outpars[strlen(outpars)-1] == '-'))
1950 outpars[strlen(outpars)-1] = '+';
1954 strcat(outpars,"+");
1960 if (parameters[1][i] == '-')
1964 if ((outpars[strlen(outpars)-1] == '+') || (outpars[strlen(outpars)-1] == '-'))
1966 outpars[strlen(outpars)-1] = '-';
1970 strcat(outpars,"-");
1978 if (strchr(user->modes,'o'))
1984 if ((parameters[1][i] == 'i') || (parameters[1][i] == 'w') || (parameters[1][i] == 's'))
1993 if (!strchr(dest->modes,parameters[1][i]))
1995 dest->modes[strlen(dest->modes)+1]='\0';
1996 dest->modes[strlen(dest->modes)] = parameters[1][i];
1997 outpars[strlen(outpars)+1]='\0';
1998 outpars[strlen(outpars)] = parameters[1][i];
2007 outpars[strlen(outpars)+1]='\0';
2008 outpars[strlen(outpars)] = parameters[1][i];
2011 for (q = 0; q < strlen(user->modes); q++)
2013 if (user->modes[q] != parameters[1][i])
2015 moo[0] = user->modes[q];
2020 strcpy(user->modes,temp);
2025 if (strlen(outpars))
2031 while (i < strlen (outpars))
2033 b[z++] = outpars[i++];
2035 if (i<strlen(outpars)-1)
2037 if (((outpars[i] == '-') || (outpars[i] == '+')) && ((outpars[i+1] == '-') || (outpars[i+1] == '+')))
2039 // someones playing silly buggers and trying
2040 // to put a +- or -+ into the line...
2044 if (i == strlen(outpars)-1)
2046 if ((outpars[i] == '-') || (outpars[i] == '+'))
2054 if ((b[z] == '-') || (b[z] == '+'))
2057 if ((!strcmp(b,"+")) || (!strcmp(b,"-")))
2060 WriteTo(user, dest, "MODE %s :%s", dest->nick, b);
2065 Ptr = FindChan(parameters[0]);
2070 /* just /modes #channel */
2071 WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name, chanmodes(Ptr));
2072 WriteServ(user->fd,"329 %s %s %d", user->nick, Ptr->name, Ptr->created);
2078 if ((!strcmp(parameters[1],"+b")) || (!strcmp(parameters[1],"b")))
2081 for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++)
2083 WriteServ(user->fd,"367 %s %s %s %s %d",user->nick, Ptr->name, i->data, i->set_by, i->set_time);
2085 WriteServ(user->fd,"368 %s %s :End of channel ban list",user->nick, Ptr->name);
2089 if ((cstatus(user,Ptr) < STATUS_HOP) && (Ptr))
2091 WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, Ptr->name);
2095 process_modes(parameters,user,Ptr,cstatus(user,Ptr),pcnt);
2099 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2103 /* This function pokes and hacks at a parameter list like the following:
2105 * PART #winbot, #darkgalaxy :m00!
2107 * to turn it into a series of individual calls like this:
2109 * PART #winbot :m00!
2110 * PART #darkgalaxy :m00!
2112 * The seperate calls are sent to a callback function provided by the caller
2113 * (the caller will usually call itself recursively). The callback function
2114 * must be a command handler. Calling this function on a line with no list causes
2115 * no action to be taken. You must provide a starting and ending parameter number
2116 * where the range of the list can be found, useful if you have a terminating
2117 * parameter as above which is actually not part of the list, or parameters
2118 * before the actual list as well. This code is used by many functions which
2119 * can function as "one to list" (see the RFC) */
2121 int loop_call(handlerfunc fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins)
2126 char blog[32][MAXBUF];
2127 char blog2[32][MAXBUF];
2128 int i = 0, j = 0, q = 0, total = 0, t = 0, t2 = 0, total2 = 0;
2129 char keystr[MAXBUF];
2132 for (i = 0; i <32; i++)
2135 for (i = 0; i <32; i++)
2136 strcpy(blog2[i],"");
2139 for (i = 0; i <10; i++)
2143 parameters[i] = moo;
2148 if (pcnt > 1) /* we have a key to copy */
2150 strcpy(keystr,parameters[1]);
2154 if (!parameters[start])
2158 if (!strchr(parameters[start],','))
2163 for (i = start; i <= end; i++)
2167 strcat(plist,parameters[i]);
2175 for (i = 0; i < t; i++)
2177 if (plist[i] == ',')
2180 strcpy(blog[j++],param);
2184 strcpy(blog[j++],param);
2187 if ((joins) && (keystr) && (total>0)) // more than one channel and is joining
2192 if ((joins) && (keystr))
2194 if (strchr(keystr,','))
2198 t2 = strlen(keystr);
2199 for (i = 0; i < t2; i++)
2201 if (keystr[i] == ',')
2204 strcpy(blog2[j++],param);
2208 strcpy(blog2[j++],param);
2213 for (j = 0; j < total; j++)
2219 for (q = end; q < pcnt-1; q++)
2221 if (parameters[q+1])
2223 pars[q-end+1] = parameters[q+1];
2226 if ((joins) && (parameters[1]))
2237 /* repeatedly call the function with the hacked parameter list */
2238 if ((joins) && (pcnt > 1))
2242 // pars[1] already set up and containing key from blog2[j]
2247 pars[1] = parameters[1];
2253 fn(pars,pcnt-(end-start),u);
2260 void handle_join(char **parameters, int pcnt, userrec *user)
2265 if (loop_call(handle_join,parameters,pcnt,user,0,0,1))
2267 if (parameters[0][0] == '#')
2269 Ptr = add_channel(user,parameters[0],parameters[1]);
2274 void handle_part(char **parameters, int pcnt, userrec *user)
2280 if (loop_call(handle_part,parameters,pcnt,user,0,pcnt-2,0))
2282 del_channel(user,parameters[0],parameters[1]);
2286 if (loop_call(handle_part,parameters,pcnt,user,0,pcnt-1,0))
2288 del_channel(user,parameters[0],NULL);
2292 void handle_kick(char **parameters, int pcnt, userrec *user)
2294 chanrec* Ptr = FindChan(parameters[0]);
2295 userrec* u = Find(parameters[1]);
2299 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2303 if (!has_channel(u,Ptr))
2305 WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, parameters[0]);
2311 kick_channel(user,u,Ptr,parameters[2]);
2315 kick_channel(user,u,Ptr,user->nick);
2320 void handle_die(char **parameters, int pcnt, userrec *user)
2322 debug("die: %s",user->nick);
2323 if (!strcmp(parameters[0],diepass))
2325 WriteOpers("*** DIE command from %s!%s@%s, terminating...",user->nick,user->ident,user->host);
2331 WriteOpers("*** Failed DIE Command from %s!%s@%s.",user->nick,user->ident,user->host);
2335 void handle_restart(char **parameters, int pcnt, userrec *user)
2337 debug("restart: %s",user->nick);
2338 if (!strcmp(parameters[0],restartpass))
2340 WriteOpers("*** RESTART command from %s!%s@%s, Pretending to restart till this is finished :D",user->nick,user->ident,user->host);
2343 /* Will finish this later when i can be arsed :) */
2347 WriteOpers("*** Failed RESTART Command from %s!%s@%s.",user->nick,user->ident,user->host);
2352 void kill_link(userrec *user,char* reason)
2354 user_hash::iterator iter = clientlist.find(user->nick);
2356 debug("kill_link: %s '%s'",user->nick,reason);
2357 Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
2358 fdatasync(user->fd);
2359 WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason);
2360 FOREACH_MOD OnUserQuit(user);
2361 debug("closing fd %d",user->fd);
2362 /* bugfix, cant close() a nonblocking socket (sux!) */
2363 WriteCommonExcept(user,"QUIT :%s",reason);
2366 NonBlocking(user->fd);
2369 if (iter != clientlist.end())
2371 debug("deleting user hash value %p",iter->second);
2372 delete iter->second;
2373 clientlist.erase(iter);
2376 purge_empty_chans();
2380 void handle_kill(char **parameters, int pcnt, userrec *user)
2382 userrec *u = Find(parameters[0]);
2383 char killreason[MAXBUF];
2385 debug("kill: %s %s",parameters[0],parameters[1]);
2388 WriteOpers("*** Local Kill by %s: %s!%s@%s (%s)",user->nick,u->nick,u->ident,u->host,parameters[1]);
2389 sprintf(killreason,"Killed (%s (%s))",user->nick,parameters[1]);
2390 kill_link(u,killreason);
2394 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2398 void handle_summon(char **parameters, int pcnt, userrec *user)
2400 WriteServ(user->fd,"445 %s :SUMMON has been disabled (depreciated command)",user->nick);
2403 void handle_users(char **parameters, int pcnt, userrec *user)
2405 WriteServ(user->fd,"445 %s :USERS has been disabled (depreciated command)",user->nick);
2409 // looks up a users password for their connection class (<ALLOW>/<DENY> tags)
2411 char* Passwd(userrec *user)
2413 for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2415 if (match(user->host,i->host) && (i->type == CC_ALLOW))
2423 bool IsDenied(userrec *user)
2425 for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2427 if (match(user->host,i->host) && (i->type == CC_DENY))
2436 void handle_pass(char **parameters, int pcnt, userrec *user)
2438 if (!strcasecmp(parameters[0],Passwd(user)))
2440 user->haspassed = true;
2444 void handle_invite(char **parameters, int pcnt, userrec *user)
2446 userrec* u = Find(parameters[0]);
2447 chanrec* c = FindChan(parameters[1]);
2453 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[1]);
2459 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
2468 if (cstatus(user,c) < STATUS_HOP)
2470 WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, c->name);
2474 u->InviteTo(c->name);
2475 WriteFrom(u->fd,user,"INVITE %s :%s",u->nick,c->name);
2476 WriteServ(user->fd,"341 %s %s %s",user->nick,u->nick,c->name);
2480 void handle_topic(char **parameters, int pcnt, userrec *user)
2486 if (strlen(parameters[0]) <= CHANMAX)
2488 Ptr = FindChan(parameters[0]);
2493 WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
2494 WriteServ(user->fd,"333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset);
2498 WriteServ(user->fd,"331 %s %s :No topic is set.", user->nick, Ptr->name);
2503 WriteServ(user->fd,"331 %s %s :No topic is set.", user->nick, Ptr->name);
2509 if (loop_call(handle_topic,parameters,pcnt,user,0,pcnt-2,0))
2511 if (strlen(parameters[0]) <= CHANMAX)
2513 Ptr = FindChan(parameters[0]);
2516 if ((Ptr->topiclock) && (cstatus(user,Ptr)<STATUS_HOP))
2518 WriteServ(user->fd,"482 %s %s :You must be at least a half-operator", user->nick, Ptr->name);
2521 strcpy(Ptr->topic,parameters[1]);
2522 strcpy(Ptr->setby,user->nick);
2523 Ptr->topicset = time(NULL);
2524 WriteChannel(Ptr,user,"TOPIC %s :%s",Ptr->name, Ptr->topic);
2528 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2534 /* sends out an error notice to all connected clients (not to be used
2537 void send_error(char *s)
2539 debug("send_error: %s",s);
2540 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2542 WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,s);
2546 void Error(int status)
2548 signal (SIGALRM, SIG_IGN);
2549 signal (SIGPIPE, SIG_IGN);
2550 signal (SIGTERM, SIG_IGN);
2551 signal (SIGABRT, SIG_IGN);
2552 signal (SIGSEGV, SIG_IGN);
2553 signal (SIGURG, SIG_IGN);
2554 signal (SIGKILL, SIG_IGN);
2555 debug("*** fell down a pothole in the road to perfection ***");
2556 send_error("Error! Segmentation fault! save meeeeeeeeeeeeee *splat!*");
2560 int main (int argc, char *argv[])
2563 debug("*** InspIRCd starting up!");
2566 debug("main: no config");
2567 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
2570 if (InspIRCd() == ERROR)
2572 debug("main: daemon function bailed");
2573 printf("ERROR: could not initialise. Shutting down.\n");
2580 template<typename T> inline string ConvToStr(const T &in)
2583 if (!(tmp << in)) return string();
2587 /* re-allocates a nick in the user_hash after they change nicknames,
2588 * returns a pointer to the new user as it may have moved */
2590 userrec* ReHashNick(char* Old, char* New)
2592 user_hash::iterator newnick;
2593 user_hash::iterator oldnick = clientlist.find(Old);
2595 debug("ReHashNick: %s %s",Old,New);
2597 if (!strcasecmp(Old,New))
2599 debug("old nick is new nick, skipping");
2600 return oldnick->second;
2603 if (oldnick == clientlist.end()) return NULL; /* doesnt exist */
2605 debug("ReHashNick: Found hashed nick %s",Old);
2607 clientlist[New] = new userrec();
2608 clientlist[New] = oldnick->second;
2609 /*delete oldnick->second; */
2610 clientlist.erase(oldnick);
2612 debug("ReHashNick: Nick rehashed as %s",New);
2614 return clientlist[New];
2617 /* adds or updates an entry in the whowas list */
2618 void AddWhoWas(userrec* u)
2620 user_hash::iterator iter = whowas.find(u->nick);
2621 userrec *a = new userrec();
2622 strcpy(a->nick,u->nick);
2623 strcpy(a->ident,u->ident);
2624 strcpy(a->dhost,u->dhost);
2625 strcpy(a->host,u->host);
2626 strcpy(a->fullname,u->fullname);
2627 strcpy(a->server,u->server);
2628 a->signon = u->signon;
2630 /* MAX_WHOWAS: max number of /WHOWAS items
2631 * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and
2632 * can be replaced by a newer one
2635 if (iter == whowas.end())
2637 if (whowas.size() == WHOWAS_MAX)
2639 for (user_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
2641 // 3600 seconds in an hour ;)
2642 if ((i->second->signon)<(time(NULL)-(WHOWAS_STALE*3600)))
2646 debug("added WHOWAS entry, purged an old record");
2653 debug("added fresh WHOWAS entry");
2654 whowas[a->nick] = a;
2659 debug("updated WHOWAS entry");
2660 delete iter->second;
2666 /* add a client connection to the sockets list */
2667 void AddClient(int socket, char* host, int port, bool iscached)
2671 char resolved[MAXBUF];
2674 user_hash::iterator iter;
2676 tempnick = ConvToStr(socket) + "-unknown";
2677 sprintf(tn2,"%d-unknown",socket);
2679 iter = clientlist.find(tempnick);
2681 if (iter != clientlist.end()) return;
2684 * It is OK to access the value here this way since we know
2685 * it exists, we just created it above.
2687 * At NO other time should you access a value in a map or a
2688 * hash_map this way.
2690 clientlist[tempnick] = new userrec();
2692 NonBlocking(socket);
2693 debug("AddClient: %d %s %d",socket,host,port);
2696 clientlist[tempnick]->fd = socket;
2697 strncpy(clientlist[tempnick]->nick, tn2,NICKMAX);
2698 strncpy(clientlist[tempnick]->host, host,160);
2699 strncpy(clientlist[tempnick]->dhost, host,160);
2700 strncpy(clientlist[tempnick]->server, ServerName,256);
2701 clientlist[tempnick]->registered = 0;
2702 clientlist[tempnick]->signon = time(NULL);
2703 clientlist[tempnick]->nping = time(NULL)+240;
2704 clientlist[tempnick]->lastping = 1;
2705 clientlist[tempnick]->port = port;
2709 WriteServ(socket,"NOTICE Auth :Found your hostname (cached)...");
2713 WriteServ(socket,"NOTICE Auth :Looking up your hostname...");
2716 if (clientlist.size() == MAXCLIENTS)
2717 kill_link(clientlist[tempnick],"No more connections allowed in this class");
2720 void handle_names(char **parameters, int pcnt, userrec *user)
2724 if (loop_call(handle_names,parameters,pcnt,user,0,pcnt-1,0))
2726 c = FindChan(parameters[0]);
2729 /*WriteServ(user->fd,"353 %s = %s :%s", user->nick, c->name,*/
2731 WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, c->name);
2735 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2740 void handle_privmsg(char **parameters, int pcnt, userrec *user)
2745 if (loop_call(handle_privmsg,parameters,pcnt,user,0,pcnt-2,0))
2747 if (parameters[0][0] == '#')
2749 chan = FindChan(parameters[0]);
2752 if ((chan->noexternal) && (!has_channel(user,chan)))
2754 WriteServ(user->fd,"404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
2757 if ((chan->moderated) && (cstatus(user,chan)<STATUS_VOICE))
2759 WriteServ(user->fd,"404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
2762 ChanExceptSender(chan, user, "PRIVMSG %s :%s", chan->name, parameters[1]);
2766 /* no such nick/channel */
2767 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2772 dest = Find(parameters[0]);
2775 if (strcmp(dest->awaymsg,""))
2777 /* auto respond with aweh msg */
2778 WriteServ(user->fd,"301 %s %s :%s",user->nick,dest->nick,dest->awaymsg);
2780 WriteTo(user, dest, "PRIVMSG %s :%s", dest->nick, parameters[1]);
2784 /* no such nick/channel */
2785 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2789 void handle_notice(char **parameters, int pcnt, userrec *user)
2794 if (loop_call(handle_notice,parameters,pcnt,user,0,pcnt-2,0))
2796 if (parameters[0][0] == '#')
2798 chan = FindChan(parameters[0]);
2801 if ((chan->noexternal) && (!has_channel(user,chan)))
2803 WriteServ(user->fd,"404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
2806 if ((chan->moderated) && (cstatus(user,chan)<STATUS_VOICE))
2808 WriteServ(user->fd,"404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
2811 WriteChannel(chan, user, "NOTICE %s :%s", chan->name, parameters[1]);
2815 /* no such nick/channel */
2816 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2821 dest = Find(parameters[0]);
2824 WriteTo(user, dest, "NOTICE %s :%s", dest->nick, parameters[1]);
2828 /* no such nick/channel */
2829 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2835 char* chlist(userrec *user)
2840 debug("chlist: %s",user->nick);
2846 for (i = 0; i != MAXCHANS; i++)
2848 if (user->chans[i].channel != NULL)
2850 if (user->chans[i].channel->name)
2852 strcpy(cmp,user->chans[i].channel->name);
2854 if (!strstr(lst,cmp))
2856 if ((!user->chans[i].channel->c_private) && (!user->chans[i].channel->secret))
2858 strcat(lst,cmode(user,user->chans[i].channel));
2859 strcat(lst,user->chans[i].channel->name);
2869 void handle_info(char **parameters, int pcnt, userrec *user)
2871 WriteServ(user->fd,"371 %s :The Inspire IRCd Project Has been brought to you by the following people..",user->nick);
2872 WriteServ(user->fd,"371 %s :Craig Edwards, Craig McLure, and Others..",user->nick);
2873 WriteServ(user->fd,"371 %s :Will finish this later when i can be arsed :p",user->nick);
2874 WriteServ(user->fd,"374 %s :End of /INFO list",user->nick);
2877 void handle_time(char **parameters, int pcnt, userrec *user)
2880 struct tm * timeinfo;
2883 timeinfo = localtime ( &rawtime );
2884 WriteServ(user->fd,"391 %s %s :%s",user->nick,ServerName, asctime (timeinfo) );
2888 void handle_whois(char **parameters, int pcnt, userrec *user)
2893 if (loop_call(handle_whois,parameters,pcnt,user,0,pcnt-1,0))
2895 dest = Find(parameters[0]);
2898 WriteServ(user->fd,"311 %s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname);
2899 if ((user == dest) || (strchr(user->modes,'o')))
2901 WriteServ(user->fd,"378 %s %s :is connecting from *@%s",user->nick, dest->nick, dest->host);
2903 if (strcmp(chlist(dest),""))
2905 WriteServ(user->fd,"319 %s %s :%s",user->nick, dest->nick, chlist(dest));
2907 WriteServ(user->fd,"312 %s %s %s :%s",user->nick, dest->nick, dest->server, ServerDesc);
2908 if (strcmp(dest->awaymsg,""))
2910 WriteServ(user->fd,"301 %s %s :%s",user->nick, dest->nick, dest->awaymsg);
2912 if (strchr(dest->modes,'o'))
2914 WriteServ(user->fd,"313 %s %s :is an IRC operator",user->nick, dest->nick);
2916 //WriteServ(user->fd,"310 %s %s :is available for help.",user->nick, dest->nick);
2917 WriteServ(user->fd,"317 %s %s %d %d :seconds idle, signon time",user->nick, dest->nick, abs((dest->idle_lastmsg)-time(NULL)), dest->signon);
2919 WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, dest->nick);
2923 /* no such nick/channel */
2924 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2928 void handle_quit(char **parameters, int pcnt, userrec *user)
2930 user_hash::iterator iter = clientlist.find(user->nick);
2932 /* theres more to do here, but for now just close the socket */
2935 if (parameters[0][0] == ':')
2939 Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,parameters[0]);
2940 WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,parameters[0]);
2941 WriteCommonExcept(user,"QUIT :%s%s",PrefixQuit,parameters[0]);
2945 Write(user->fd,"ERROR :Closing link (%s@%s) [QUIT]",user->ident,user->host);
2946 WriteOpers("*** Client exiting: %s!%s@%s [Client exited]",user->nick,user->ident,user->host);
2947 WriteCommonExcept(user,"QUIT :Client exited");
2950 FOREACH_MOD OnUserQuit(user);
2952 /* confucious say, he who close nonblocking socket, get nothing! */
2955 NonBlocking(user->fd);
2958 if (iter != clientlist.end())
2960 debug("deleting user hash value");
2961 delete iter->second;
2962 clientlist.erase(iter);
2965 purge_empty_chans();
2968 void handle_who(char **parameters, int pcnt, userrec *user)
2972 /* theres more to do here, but for now just close the socket */
2975 if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*")))
2977 Ptr = user->chans[0].channel;
2978 printf(user->chans[0].channel->name);
2979 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2981 if ((common_channels(user,i->second)) && (isnick(i->second->nick)))
2983 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);
2986 WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
2989 if (parameters[0][0] = '#')
2991 Ptr = FindChan(parameters[0]);
2994 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2996 if ((has_channel(i->second,Ptr)) && (isnick(i->second->nick)))
2998 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);
3001 WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
3005 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
3011 if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*")) && (!strcmp(parameters[1],"o")))
3013 Ptr = user->chans[0].channel;
3014 printf(user->chans[0].channel->name);
3015 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3017 if ((common_channels(user,i->second)) && (isnick(i->second->nick)))
3019 if (strchr(i->second->modes,'o'))
3021 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);
3025 WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
3031 void handle_wallops(char **parameters, int pcnt, userrec *user)
3033 WriteWallOps(user,"%s",parameters[0]);
3036 void handle_list(char **parameters, int pcnt, userrec *user)
3040 WriteServ(user->fd,"321 %s Channel :Users Name",user->nick);
3041 for (chan_hash::const_iterator i = chanlist.begin(); i != chanlist.end(); i++)
3043 if ((!i->second->c_private) && (!i->second->secret))
3045 WriteServ(user->fd,"322 %s %s %d :[+%s] %s",user->nick,i->second->name,usercount_i(i->second),chanmodes(i->second),i->second->topic);
3048 WriteServ(user->fd,"323 %s :End of channel list.",user->nick);
3052 void handle_rehash(char **parameters, int pcnt, userrec *user)
3054 WriteServ(user->fd,"382 %s %s :Rehashing",user->nick,CONFIG_FILE);
3056 WriteOpers("%s is rehashing config file %s",user->nick,CONFIG_FILE);
3062 return clientlist.size();
3065 int usercount_invisible(void)
3069 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3071 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'i'))) c++;
3076 int usercount_opers(void)
3080 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3082 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'o'))) c++;
3087 int usercount_unknown(void)
3091 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3093 if ((i->second->fd) && (i->second->registered != 7))
3101 return chanlist.size();
3104 int servercount(void)
3109 void handle_lusers(char **parameters, int pcnt, userrec *user)
3111 WriteServ(user->fd,"251 %s :There are %d users and %d invisible on %d servers",user->nick,usercnt()-usercount_invisible(),usercount_invisible(),servercount());
3112 WriteServ(user->fd,"252 %s %d :operator(s) online",user->nick,usercount_opers());
3113 WriteServ(user->fd,"253 %s %d :unknown connections",user->nick,usercount_unknown());
3114 WriteServ(user->fd,"254 %s %d :channels formed",user->nick,chancount());
3115 WriteServ(user->fd,"254 %s :I have %d clients and 0 servers",user->nick,usercnt());
3118 void handle_admin(char **parameters, int pcnt, userrec *user)
3120 WriteServ(user->fd,"256 %s :Administrative info for %s",user->nick,ServerName);
3121 WriteServ(user->fd,"257 %s :Name - %s",user->nick,AdminName);
3122 WriteServ(user->fd,"258 %s :Nickname - %s",user->nick,AdminNick);
3123 WriteServ(user->fd,"258 %s :E-Mail - %s",user->nick,AdminEmail);
3126 void ShowMOTD(userrec *user)
3130 WriteServ(user->fd,"422 %s :Message of the day file is missing.",user->nick);
3133 WriteServ(user->fd,"375 %s :- %s message of the day",user->nick,ServerName);
3134 for (int i = 0; i != MOTD.size(); i++)
3136 WriteServ(user->fd,"372 %s :- %s",user->nick,MOTD[i].c_str());
3138 WriteServ(user->fd,"376 %s :End of %s message of the day.",user->nick,ServerName);
3141 void ShowRULES(userrec *user)
3145 WriteServ(user->fd,"NOTICE %s :Rules file is missing.",user->nick);
3148 WriteServ(user->fd,"NOTICE %s :%s rules",user->nick,ServerName);
3149 for (int i = 0; i != RULES.size(); i++)
3151 WriteServ(user->fd,"NOTICE %s :%s",user->nick,RULES[i].c_str());
3153 WriteServ(user->fd,"NOTICE %s :End of %s rules.",user->nick,ServerName);
3156 /* shows the message of the day, and any other on-logon stuff */
3157 void ConnectUser(userrec *user)
3159 user->registered = 7;
3160 user->idle_lastmsg = time(NULL);
3161 debug("ConnectUser: %s",user->nick);
3163 if (strcmp(Passwd(user),"") && (!user->haspassed))
3165 Write(user->fd,"ERROR :Closing link: Invalid password");
3166 fdatasync(user->fd);
3167 kill_link(user,"Invalid password");
3172 Write(user->fd,"ERROR :Closing link: Unauthorized connection");
3173 fdatasync(user->fd);
3174 kill_link(user,"Unauthorised connection");
3177 WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Network);
3178 WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Network,user->nick,user->ident,user->host);
3179 WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,ServerName,VERSION);
3180 WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__);
3181 WriteServ(user->fd,"004 %s :%s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,ServerName,VERSION);
3182 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);
3183 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);
3185 FOREACH_MOD OnUserConnect(user);
3186 WriteOpers("*** Client connecting on port %d: %s!%s@%s",user->port,user->nick,user->ident,user->host);
3189 void handle_version(char **parameters, int pcnt, userrec *user)
3191 WriteServ(user->fd,"351 %s :%s %s :%s",user->nick,VERSION,ServerName,SYSTEM);
3194 void handle_ping(char **parameters, int pcnt, userrec *user)
3196 WriteServ(user->fd,"PONG %s :%s",ServerName,parameters[0]);
3199 void handle_pong(char **parameters, int pcnt, userrec *user)
3201 // set the user as alive so they survive to next ping
3205 void handle_motd(char **parameters, int pcnt, userrec *user)
3210 void handle_rules(char **parameters, int pcnt, userrec *user)
3215 void handle_user(char **parameters, int pcnt, userrec *user)
3217 if (user->registered < 3)
3219 WriteServ(user->fd,"NOTICE Auth :No ident response, ident prefixed with ~");
3220 strcpy(user->ident,"~"); /* we arent checking ident... but these days why bother anyway? */
3221 strncat(user->ident,parameters[0],IDENTMAX);
3222 strncpy(user->fullname,parameters[3],128);
3223 user->registered = (user->registered | 1);
3227 WriteServ(user->fd,"462 %s :You may not reregister",user->nick);
3230 /* parameters 2 and 3 are local and remote hosts, ignored when sent by client connection */
3231 if (user->registered == 3)
3233 /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
3238 void handle_userhost(char **parameters, int pcnt, userrec *user)
3240 char Return[MAXBUF],junk[MAXBUF];
3241 sprintf(Return,"302 %s :",user->nick);
3242 for (int i = 0; i < pcnt; i++)
3244 userrec *u = Find(parameters[i]);
3247 if (strchr(u->modes,'o'))
3249 sprintf(junk,"%s*=+%s@%s ",u->nick,u->ident,u->host);
3250 strcat(Return,junk);
3254 sprintf(junk,"%s=+%s@%s ",u->nick,u->ident,u->host);
3255 strcat(Return,junk);
3259 WriteServ(user->fd,Return);
3263 void handle_ison(char **parameters, int pcnt, userrec *user)
3265 char Return[MAXBUF];
3266 sprintf(Return,"303 %s :",user->nick);
3267 for (int i = 0; i < pcnt; i++)
3269 userrec *u = Find(parameters[i]);
3272 strcat(Return,u->nick);
3276 WriteServ(user->fd,Return);
3280 void handle_away(char **parameters, int pcnt, userrec *user)
3284 strcpy(user->awaymsg,parameters[0]);
3285 WriteServ(user->fd,"306 %s :You have been marked as being away",user->nick);
3289 strcpy(user->awaymsg,"");
3290 WriteServ(user->fd,"305 %s :You are no longer marked as being away",user->nick);
3294 void handle_whowas(char **parameters, int pcnt, userrec* user)
3296 user_hash::iterator i = whowas.find(parameters[0]);
3298 if (i == whowas.end())
3300 WriteServ(user->fd,"406 %s %s :There was no such nickname",user->nick,parameters[0]);
3301 WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]);
3305 time_t rawtime = i->second->signon;
3309 timeinfo = localtime(&rawtime);
3310 strcpy(b,asctime(timeinfo));
3311 b[strlen(b)-1] = '\0';
3313 WriteServ(user->fd,"314 %s %s %s %s * :%s",user->nick,i->second->nick,i->second->ident,i->second->dhost,i->second->fullname);
3314 WriteServ(user->fd,"312 %s %s %s :%s",user->nick,i->second->nick,i->second->server,b);
3315 WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]);
3320 void handle_trace(char **parameters, int pcnt, userrec *user)
3322 for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
3326 if (isnick(i->second->nick))
3328 if (strchr(i->second->modes,'o'))
3330 WriteServ(user->fd,"205 %s :Oper 0 %s",user->nick,i->second->nick);
3334 WriteServ(user->fd,"204 %s :User 0 %s",user->nick,i->second->nick);
3339 WriteServ(user->fd,"203 %s :???? 0 [%s]",user->nick,i->second->host);
3345 void handle_stats(char **parameters, int pcnt, userrec *user)
3351 if (strlen(parameters[0])>1)
3353 /* make the stats query 1 character long */
3354 parameters[0][1] = '\0';
3357 /* stats m (list number of times each command has been used, plus bytecount) */
3358 if (!strcasecmp(parameters[0],"m"))
3360 for (int i = 0; i < cmdlist.size(); i++)
3362 if (cmdlist[i].handler_function)
3364 if (cmdlist[i].use_count)
3366 /* RPL_STATSCOMMANDS */
3367 WriteServ(user->fd,"212 %s %s %d %d",user->nick,cmdlist[i].command,cmdlist[i].use_count,cmdlist[i].total_bytes);
3374 /* stats z (debug and memory info) */
3375 if (!strcasecmp(parameters[0],"z"))
3377 WriteServ(user->fd,"249 %s :Users(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,clientlist.size(),clientlist.size()*sizeof(userrec),clientlist.bucket_count());
3378 WriteServ(user->fd,"249 %s :Channels(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,chanlist.size(),chanlist.size()*sizeof(chanrec),chanlist.bucket_count());
3379 WriteServ(user->fd,"249 %s :Commands(VECTOR) %d (%d bytes)",user->nick,cmdlist.size(),cmdlist.size()*sizeof(command_t));
3380 WriteServ(user->fd,"249 %s :MOTD(VECTOR) %d, RULES(VECTOR) %d",user->nick,MOTD.size(),RULES.size());
3381 WriteServ(user->fd,"249 %s :address_cache(HASH_MAP) %d (%d buckets)",user->nick,IP.size(),IP.bucket_count());
3382 WriteServ(user->fd,"249 %s :Modules(VECTOR) %d (%d)",user->nick,modules.size(),modules.size()*sizeof(Module));
3383 WriteServ(user->fd,"249 %s :ClassFactories(VECTOR) %d (%d)",user->nick,factory.size(),factory.size()*sizeof(ircd_module));
3384 WriteServ(user->fd,"249 %s :Ports(STATIC_ARRAY) %d",user->nick,boundPortCount);
3388 if (!strcasecmp(parameters[0],"o"))
3390 for (int i = 0; i < ConfValueEnum("oper"); i++)
3392 char LoginName[MAXBUF];
3393 char HostName[MAXBUF];
3394 char OperType[MAXBUF];
3395 ConfValue("oper","name",i,LoginName);
3396 ConfValue("oper","host",i,HostName);
3397 ConfValue("oper","type",i,OperType);
3398 WriteServ(user->fd,"243 %s O %s * %s %s 0",user->nick,HostName,LoginName,OperType);
3402 /* stats l (show user I/O stats) */
3403 if (!strcasecmp(parameters[0],"l"))
3405 WriteServ(user->fd,"211 %s :server:port nick bytes_in cmds_in bytes_out cmds_out",user->nick);
3406 for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
3408 if (isnick(i->second->nick))
3410 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);
3414 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);
3420 /* stats u (show server uptime) */
3421 if (!strcasecmp(parameters[0],"u"))
3423 time_t current_time = 0;
3424 current_time = time(NULL);
3425 time_t server_uptime = current_time - startup_time;
3427 stime = gmtime(&server_uptime);
3428 /* i dont know who the hell would have an ircd running for over a year nonstop, but
3429 * Craig suggested this, and it seemed a good idea so in it went */
3430 if (stime->tm_year > 70)
3432 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);
3436 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);
3440 WriteServ(user->fd,"219 %s %s :End of /STATS report",user->nick,parameters[0]);
3441 WriteOpers("*** Notice: Stats '%s' requested by %s (%s@%s)",parameters[0],user->nick,user->ident,user->host);
3445 void handle_connect(char **parameters, int pcnt, userrec *user)
3447 WriteServ(user->fd,"NOTICE %s :*** Connecting to %s port 7000...",user->nick,parameters[0]);
3448 if (!me->BeginLink(parameters[0],7000,"password"))
3450 WriteServ(user->fd,"NOTICE %s :*** Failed to send auth packet to %s!",user->nick,parameters[0]);
3454 void handle_squit(char **parameters, int pcnt, userrec *user)
3458 void handle_oper(char **parameters, int pcnt, userrec *user)
3460 char LoginName[MAXBUF];
3461 char Password[MAXBUF];
3462 char OperType[MAXBUF];
3463 char TypeName[MAXBUF];
3464 char Hostname[MAXBUF];
3467 for (i = 0; i < ConfValueEnum("oper"); i++)
3469 ConfValue("oper","name",i,LoginName);
3470 ConfValue("oper","password",i,Password);
3471 if ((!strcmp(LoginName,parameters[0])) && (!strcmp(Password,parameters[1])))
3473 /* correct oper credentials */
3474 ConfValue("oper","type",i,OperType);
3475 WriteOpers("*** %s (%s@%s) is now an IRC operator of type %s",user->nick,user->ident,user->host,OperType);
3476 WriteServ(user->fd,"381 %s :You are now an IRC operator of type %s",user->nick,OperType);
3477 WriteServ(user->fd,"MODE %s :+o",user->nick);
3478 for (j =0; j < ConfValueEnum("type"); j++)
3480 ConfValue("type","name",j,TypeName);
3481 if (!strcmp(TypeName,OperType))
3483 /* found this oper's opertype */
3484 ConfValue("type","host",j,Hostname);
3485 strncpy(user->dhost,Hostname,256);
3488 if (!strchr(user->modes,'o'))
3490 strcat(user->modes,"o");
3496 WriteServ(user->fd,"491 %s :Invalid oper credentials",user->nick);
3497 WriteOpers("*** WARNING! Failed oper attempt by %s!%s@%s!",user->nick,user->ident,user->host);
3500 void handle_nick(char **parameters, int pcnt, userrec *user)
3504 debug("not enough params for handle_nick");
3509 debug("invalid parameter passed to handle_nick");
3512 if (!strlen(parameters[0]))
3514 debug("zero length new nick passed to handle_nick");
3519 debug("invalid user passed to handle_nick");
3524 debug("invalid old nick passed to handle_nick");
3527 if (!strcasecmp(user->nick,parameters[0]))
3529 debug("old nick is new nick, skipping");
3534 if (strlen(parameters[0]) > 1)
3536 if (parameters[0][0] == ':')
3541 if ((Find(parameters[0])) && (Find(parameters[0]) != user))
3543 WriteServ(user->fd,"433 %s %s :Nickname is already in use.",user->nick,parameters[0]);
3547 if (isnick(parameters[0]) == 0)
3549 WriteServ(user->fd,"432 %s %s :Erroneous Nickname",user->nick,parameters[0]);
3553 if (user->registered == 7)
3555 WriteCommon(user,"NICK %s",parameters[0]);
3558 /* change the nick of the user in the users_hash */
3559 user = ReHashNick(user->nick, parameters[0]);
3560 /* actually change the nick within the record */
3562 if (!user->nick) return;
3564 strncpy(user->nick, parameters[0],NICKMAX);
3566 debug("new nick set: %s",user->nick);
3568 if (user->registered < 3)
3569 user->registered = (user->registered | 2);
3570 if (user->registered == 3)
3572 /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
3575 debug("exit nickchange: %s",user->nick);
3578 int process_parameters(char **command_p,char *parameters)
3583 q = strlen(parameters);
3586 /* no parameters, command_p invalid! */
3589 if (parameters[0] == ':')
3591 command_p[0] = parameters+1;
3596 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
3598 /* only one parameter */
3599 command_p[0] = parameters;
3600 if (parameters[0] == ':')
3602 if (strchr(parameters,' ') != NULL)
3610 command_p[j++] = parameters;
3611 for (i = 0; i <= q; i++)
3613 if (parameters[i] == ' ')
3615 command_p[j++] = parameters+i+1;
3616 parameters[i] = '\0';
3617 if (command_p[j-1][0] == ':')
3619 *command_p[j-1]++; /* remove dodgy ":" */
3621 /* parameter like this marks end of the sequence */
3625 return j; /* returns total number of items in the list */
3628 void process_command(userrec *user, char* cmd)
3632 char *command_p[127];
3633 char p[MAXBUF], temp[MAXBUF];
3634 int i, j, items, cmd_found;
3636 for (int i = 0; i < 127; i++)
3637 command_p[i] = NULL;
3647 if (!strcmp(cmd,""))
3652 if (!strchr(cmd,' '))
3654 /* no parameters, lets skip the formalities and not chop up
3657 command_p[0] = NULL;
3659 for (int i = 0; i <= strlen(cmd); i++)
3661 cmd[i] = toupper(cmd[i]);
3668 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
3669 for (i = 0; i < strlen(temp); i++)
3671 if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
3677 /* split the full string into a command plus parameters */
3681 if (strchr(cmd,' '))
3683 for (i = 0; i <= strlen(cmd); i++)
3685 /* capitalise the command ONLY, leave params intact */
3686 cmd[i] = toupper(cmd[i]);
3687 /* are we nearly there yet?! :P */
3691 parameters = cmd+i+1;
3699 for (i = 0; i <= strlen(cmd); i++)
3701 cmd[i] = toupper(cmd[i]);
3709 for (i = 0; i != cmdlist.size(); i++)
3711 if (strcmp(cmdlist[i].command,""))
3713 if (!strcmp(command, cmdlist[i].command))
3717 if (strcmp(parameters,""))
3719 items = process_parameters(command_p,parameters);
3724 command_p[0] = NULL;
3730 command_p[0] = NULL;
3735 user->idle_lastmsg = time(NULL);
3736 /* activity resets the ping pending timer */
3737 user->nping = time(NULL) + 120;
3738 if ((items) < cmdlist[i].min_params)
3740 debug("process_command: not enough parameters: %s %s",user->nick,command);
3741 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
3744 if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed))
3746 debug("process_command: permission denied: %s %s",user->nick,command);
3747 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
3751 /* if the command isnt USER, PASS, or NICK, and nick is empty,
3753 if ((strcmp(command,"USER")) && (strcmp(command,"NICK")) && (strcmp(command,"PASS")))
3755 if ((!isnick(user->nick)) || (user->registered != 7))
3757 debug("process_command: not registered: %s %s",user->nick,command);
3758 WriteServ(user->fd,"451 %s :You have not registered",command);
3762 if ((user->registered == 7) || (!strcmp(command,"USER")) || (!strcmp(command,"NICK")) || (!strcmp(command,"PASS")))
3764 debug("process_command: handler: %s %s %d",user->nick,command,items);
3765 if (cmdlist[i].handler_function)
3767 /* ikky /stats counters */
3772 user->bytes_in += strlen(temp);
3775 cmdlist[i].use_count++;
3776 cmdlist[i].total_bytes+=strlen(temp);
3779 /* WARNING: nothing may come after the
3780 * command handler call, as the handler
3781 * may free the user structure! */
3783 cmdlist[i].handler_function(command_p,items,user);
3789 debug("process_command: not registered: %s %s",user->nick,command);
3790 WriteServ(user->fd,"451 %s :You have not registered",command);
3798 if ((!cmd_found) && (user))
3800 debug("process_command: not in table: %s %s",user->nick,command);
3801 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
3806 void createcommand(char* cmd, handlerfunc f, char flags, int minparams)
3809 /* create the command and push it onto the table */
3810 strcpy(comm.command,cmd);
3811 comm.handler_function = f;
3812 comm.flags_needed = flags;
3813 comm.min_params = minparams;
3815 comm.total_bytes = 0;
3816 cmdlist.push_back(comm);
3819 void SetupCommandTable(void)
3821 createcommand("USER",handle_user,0,4);
3822 createcommand("NICK",handle_nick,0,1);
3823 createcommand("QUIT",handle_quit,0,1);
3824 createcommand("VERSION",handle_version,0,0);
3825 createcommand("PING",handle_ping,0,1);
3826 createcommand("PONG",handle_pong,0,1);
3827 createcommand("ADMIN",handle_admin,0,0);
3828 createcommand("PRIVMSG",handle_privmsg,0,2);
3829 createcommand("INFO",handle_info,0,0);
3830 createcommand("TIME",handle_time,0,0);
3831 createcommand("WHOIS",handle_whois,0,1);
3832 createcommand("WALLOPS",handle_wallops,'o',1);
3833 createcommand("NOTICE",handle_notice,0,2);
3834 createcommand("JOIN",handle_join,0,1);
3835 createcommand("NAMES",handle_names,0,1);
3836 createcommand("PART",handle_part,0,1);
3837 createcommand("KICK",handle_kick,0,2);
3838 createcommand("MODE",handle_mode,0,1);
3839 createcommand("TOPIC",handle_topic,0,1);
3840 createcommand("WHO",handle_who,0,1);
3841 createcommand("MOTD",handle_motd,0,0);
3842 createcommand("RULES",handle_join,0,0);
3843 createcommand("OPER",handle_oper,0,2);
3844 createcommand("LIST",handle_list,0,0);
3845 createcommand("DIE",handle_die,'o',1);
3846 createcommand("RESTART",handle_restart,'o',1);
3847 createcommand("KILL",handle_kill,'o',2);
3848 createcommand("REHASH",handle_rehash,'o',0);
3849 createcommand("LUSERS",handle_lusers,0,0);
3850 createcommand("STATS",handle_stats,0,1);
3851 createcommand("USERHOST",handle_userhost,0,1);
3852 createcommand("AWAY",handle_away,0,0);
3853 createcommand("ISON",handle_ison,0,0);
3854 createcommand("SUMMON",handle_summon,0,0);
3855 createcommand("USERS",handle_users,0,0);
3856 createcommand("INVITE",handle_invite,0,2);
3857 createcommand("PASS",handle_pass,0,1);
3858 createcommand("TRACE",handle_trace,'o',0);
3859 createcommand("WHOWAS",handle_whowas,0,1);
3860 createcommand("CONNECT",handle_connect,'o',1);
3861 createcommand("SQUIT",handle_squit,'o',1);
3864 void process_buffer(userrec *user)
3872 if (!strcmp(user->inbuf,""))
3876 strncpy(cmd,user->inbuf,MAXBUF);
3877 if (!strcmp(cmd,""))
3881 if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
3883 cmd[strlen(cmd)-1] = '\0';
3885 if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
3887 cmd[strlen(cmd)-1] = '\0';
3889 strcpy(user->inbuf,"");
3890 if (!strcmp(cmd,""))
3894 debug("InspIRCd: processing: %s %s",user->nick,cmd);
3895 process_command(user,cmd);
3900 struct sockaddr_in client, server;
3901 int portCount = 0, ports[MAXSOCKS];
3902 char addrs[MAXBUF][255];
3903 int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE;
3905 int count = 0, scanDetectTrigger = TRUE, showBanner = FALSE;
3906 int selectResult = 0;
3907 char *temp, configToken[MAXBUF], stuff[MAXBUF], Addr[MAXBUF];
3908 char resolvedHost[MAXBUF];
3913 debug("InspIRCd: startup: begin");
3915 if ((geteuid()) && (getuid()) == 0)
3917 printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
3919 debug("InspIRCd: startup: not starting with UID 0!");
3921 SetupCommandTable();
3922 debug("InspIRCd: startup: default command table set up");
3925 if (strcmp(DieValue,""))
3927 printf("WARNING: %s\n\n",DieValue);
3930 debug("InspIRCd: startup: read config");
3932 for (count = 0; count < ConfValueEnum("bind"); count++)
3934 ConfValue("bind","port",count,configToken);
3935 ConfValue("bind","address",count,Addr);
3936 ports[count] = atoi(configToken);
3937 strcpy(addrs[count],Addr);
3938 debug("InspIRCd: startup: read binding %s:%d from config",addrs[count],ports[count]);
3940 portCount = ConfValueEnum("bind");
3941 debug("InspIRCd: startup: read %d total ports",portCount);
3943 debug("InspIRCd: startup: InspIRCd is now running!");
3946 for (count = 0; count < ConfValueEnum("module"); count++)
3948 char modfile[MAXBUF];
3949 ConfValue("module","name",count,configToken);
3950 sprintf(modfile,"%s/%s",MOD_PATH,configToken);
3951 printf("Loading module... \033[1;37m%s\033[0;37m\n",modfile);
3952 debug("InspIRCd: startup: Loading module: %s",modfile);
3954 factory[count] = new ircd_module(modfile);
3955 if (factory[count]->LastError())
3957 debug("Unable to load %s: %s",modfile,factory[count]->LastError());
3958 sprintf("Unable to load %s: %s\nExiting...\n",modfile,factory[count]->LastError());
3961 if (factory[count]->factory)
3963 modules[count] = factory[count]->factory->CreateModule();
3964 /* save the module and the module's classfactory, if
3965 * this isnt done, random crashes can occur :/ */
3969 debug("Unable to load %s",modfile);
3970 sprintf("Unable to load %s\nExiting...\n",modfile);
3974 MODCOUNT = count - 1;
3975 debug("Total loaded modules: %d",MODCOUNT+1);
3977 me = new serverrec(ServerName,100L,false);
3978 servers = new server_list;
3981 me->CreateListener("127.0.0.1",7000);
3983 printf("\nInspIRCd is now running!\n");
3985 startup_time = time(NULL);
3987 if (DaemonSeed() == ERROR)
3989 debug("InspIRCd: startup: can't daemonise");
3990 printf("ERROR: could not go into daemon mode. Shutting down.\n");
3995 /* setup select call */
3996 FD_ZERO(&selectFds);
3997 debug("InspIRCd: startup: zero selects");
3999 for (count = 0; count < portCount; count++)
4001 if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR)
4003 debug("InspIRCd: startup: bad fd %d",openSockfd[boundPortCount]);
4006 if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR)
4008 debug("InspIRCd: startup: failed to bind port %d",ports[count]);
4010 else /* well we at least bound to one socket so we'll continue */
4016 debug("InspIRCd: startup: total bound ports %d",boundPortCount);
4018 /* if we didn't bind to anything then abort */
4019 if (boundPortCount == 0)
4021 debug("InspIRCd: startup: no ports bound, bailing!");
4025 length = sizeof (client);
4026 int flip_flop = 0, udp_port = 0;
4027 char udp_msg[MAXBUF], udp_host[MAXBUF];
4029 /* main loop for multiplexing/resetting */
4032 /* set up select call */
4033 for (count = 0; count < boundPortCount; count++)
4035 FD_SET (openSockfd[count], &selectFds);
4038 /* added timeout! select was waiting forever... wank... :/ */
4049 selectResult = select(MAXSOCKS, &selectFds, NULL, NULL, &tv);
4051 if (me->RecvPacket(udp_msg, udp_host, udp_port))
4053 WriteOpers("UDP Link Packet: '%s' from %s:%d",udp_msg,udp_host,udp_port);
4056 for (user_hash::iterator count2 = clientlist.begin(); count2 != clientlist.end(); count2++)
4060 if (!count2->second) break;
4063 if (count2->second->fd)
4065 if (((time(NULL)) > count2->second->nping) && (isnick(count2->second->nick)) && (count2->second->registered == 7))
4067 if (!count2->second->lastping)
4069 debug("InspIRCd: ping timeout: %s",count2->second->nick);
4070 kill_link(count2->second,"Ping timeout");
4073 Write(count2->second->fd,"PING :%s",ServerName);
4074 debug("InspIRCd: pinging: %s",count2->second->nick);
4075 count2->second->lastping = 0;
4076 count2->second->nping = time(NULL)+120;
4079 result = read(count2->second->fd, data, 1);
4080 // result EAGAIN means nothing read
4081 if (result == EAGAIN)
4087 debug("InspIRCd: Exited: %s",count2->second->nick);
4088 kill_link(count2->second,"Client exited");
4090 else if (result > 0)
4092 strncat(count2->second->inbuf, data, result);
4093 if (strchr(count2->second->inbuf, '\n') || strchr(count2->second->inbuf, '\r'))
4095 /* at least one complete line is waiting to be processed */
4096 if (!count2->second->fd)
4100 process_buffer(count2->second);
4108 /* select is reporting a waiting socket. Poll them all to find out which */
4109 if (selectResult > 0)
4111 char target[MAXBUF], resolved[MAXBUF];
4112 for (count = 0; count < boundPortCount; count++)
4114 if (FD_ISSET (openSockfd[count], &selectFds))
4116 incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
4118 address_cache::iterator iter = IP.find(client.sin_addr);
4119 bool iscached = false;
4120 if (iter == IP.end())
4122 /* ip isn't in cache, add it */
4123 strncpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF);
4124 if(CleanAndResolve(resolved, target) != TRUE)
4126 strncpy(resolved,target,MAXBUF);
4128 /* hostname now in 'target' */
4129 IP[client.sin_addr] = new string(resolved);
4130 /* hostname in cache */
4134 /* found ip (cached) */
4135 strncpy(resolved, iter->second->c_str(), MAXBUF);
4139 if (incomingSockfd < 0)
4141 WriteOpers("*** WARNING: Accept failed on port %d (%s)", ports[count],target);
4142 debug("InspIRCd: accept failed: %d",ports[count]);
4146 AddClient(incomingSockfd, resolved, ports[count], iscached);
4147 debug("InspIRCd: adding client on port %d fd=%d",ports[count],incomingSockfd);
4156 close (incomingSockfd);