]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd.cpp
839ad82c87e5e677c1f30a492bbe52cabb8d8105
[user/henk/code/inspircd.git] / src / inspircd.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2003 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 /* Now with added unF! ;) */
18
19 #include "inspircd.h"
20 #include "inspircd_io.h"
21 #include "inspircd_util.h"
22 #include "inspircd_config.h"
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <sys/errno.h>
26 #include <sys/ioctl.h>
27 #include <sys/utsname.h>
28 #include <cstdio>
29 #include <time.h>
30 #include <string>
31 #include <hash_map.h>
32 #include <map.h>
33 #include <sstream>
34 #include <vector>
35 #include <errno.h>
36 #include <deque>
37 #include "connection.h"
38 #include "users.h"
39 #include "servers.h"
40 #include "ctables.h"
41 #include "globals.h"
42 #include "modules.h"
43 #include "dynamic.h"
44 #include "wildcard.h"
45
46 using namespace std;
47
48 int LogLevel = DEFAULT;
49 char ServerName[MAXBUF];
50 char Network[MAXBUF];
51 char ServerDesc[MAXBUF];
52 char AdminName[MAXBUF];
53 char AdminEmail[MAXBUF];
54 char AdminNick[MAXBUF];
55 char diepass[MAXBUF];
56 char restartpass[MAXBUF];
57 char motd[MAXBUF];
58 char rules[MAXBUF];
59 char list[MAXBUF];
60 char PrefixQuit[MAXBUF];
61 char DieValue[MAXBUF];
62 int debugging =  0;
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
65 int DieDelay  =  5;
66 time_t startup_time = time(NULL);
67
68 extern vector<Module*> modules;
69 extern vector<ircd_module*> factory;
70
71 extern int MODCOUNT;
72
73 template<> struct hash<in_addr>
74 {
75         size_t operator()(const struct in_addr &a) const
76         {
77                 size_t q;
78                 memcpy(&q,&a,sizeof(size_t));
79                 return q;
80         }
81 };
82
83 template<> struct hash<string>
84 {
85         size_t operator()(const string &s) const
86         {
87                 char a[MAXBUF];
88                 static struct hash<const char *> strhash;
89                 strcpy(a,s.c_str());
90                 strlower(a);
91                 return strhash(a);
92         }
93 };
94         
95
96
97 struct StrHashComp
98 {
99
100         bool operator()(const string& s1, const string& s2) const
101         {
102                 char a[MAXBUF],b[MAXBUF];
103                 strcpy(a,s1.c_str());
104                 strcpy(b,s2.c_str());
105                 return (strcasecmp(a,b) == 0);
106         }
107
108 };
109
110 struct InAddr_HashComp
111 {
112
113         bool operator()(const in_addr &s1, const in_addr &s2) const
114         {
115                 size_t q;
116                 size_t p;
117                 
118                 memcpy(&q,&s1,sizeof(size_t));
119                 memcpy(&p,&s2,sizeof(size_t));
120                 
121                 return (q == p);
122         }
123
124 };
125
126
127 typedef hash_map<string, userrec*, hash<string>, StrHashComp> user_hash;
128 typedef hash_map<string, chanrec*, hash<string>, StrHashComp> chan_hash;
129 typedef hash_map<in_addr,string*, hash<in_addr>, InAddr_HashComp> address_cache;
130 typedef deque<command_t> command_table;
131
132 serverrec* me[32];
133 server_list* servers;
134
135 user_hash clientlist;
136 chan_hash chanlist;
137 user_hash whowas;
138 command_table cmdlist;
139 file_cache MOTD;
140 file_cache RULES;
141 address_cache IP;
142
143 ClassVector Classes;
144
145 struct linger linger = { 0 };
146 char bannerBuffer[MAXBUF];
147 int boundPortCount = 0;
148 int portCount = 0, UDPportCount = 0, ports[MAXSOCKS];
149 int defaultRoute = 0;
150
151 /* prototypes */
152
153 int has_channel(userrec *u, chanrec *c);
154 int usercount(chanrec *c);
155 int usercount_i(chanrec *c);
156 void update_stats_l(int fd,int data_out);
157 char* Passwd(userrec *user);
158 bool IsDenied(userrec *user);
159 void AddWhoWas(userrec* u);
160
161
162 void safedelete(userrec *p)
163 {
164         if (p)
165         {
166                 log(DEBUG,"deleting %s %s %s %s",p->nick,p->ident,p->dhost,p->fullname);
167                 log(DEBUG,"safedelete(userrec*): pointer is safe to delete");
168                 delete p;
169         }
170         else
171         {
172                 log(DEBUG,"safedelete(userrec*): unsafe pointer operation squished");
173         }
174 }
175
176 void safedelete(chanrec *p)
177 {
178         if (p)
179         {
180                 delete p;
181                 log(DEBUG,"safedelete(chanrec*): pointer is safe to delete");
182         }
183         else
184         {
185                 log(DEBUG,"safedelete(chanrec*): unsafe pointer operation squished");
186         }
187 }
188
189
190 /* chop a string down to 512 characters and preserve linefeed (irc max
191  * line length) */
192
193 void chop(char* str)
194 {
195
196   string temp = str;
197   FOREACH_MOD OnServerRaw(temp,false);
198   const char* str2 = temp.c_str();
199   sprintf(str,"%s",str2);
200   
201
202   if (strlen(str) > 512)
203   {
204         str[510] = '\r';
205         str[511] = '\n';
206         str[512] = '\0';
207   }
208 }
209
210
211 string getservername()
212 {
213         return ServerName;
214 }
215
216 string getserverdesc()
217 {
218         return ServerDesc;
219 }
220
221 string getnetworkname()
222 {
223         return Network;
224 }
225
226 string getadminname()
227 {
228         return AdminName;
229 }
230
231 string getadminemail()
232 {
233         return AdminEmail;
234 }
235
236 string getadminnick()
237 {
238         return AdminNick;
239 }
240
241 void log(int level,char *text, ...)
242 {
243   char textbuffer[MAXBUF];
244   va_list argsPtr;
245   FILE *f;
246   time_t rawtime;
247   struct tm * timeinfo;
248
249   if (level < LogLevel)
250         return;
251
252   time(&rawtime);
253   timeinfo = localtime (&rawtime);
254
255           f = fopen("ircd.log","a+");
256           if (f)
257           {
258                   char b[MAXBUF];
259                   va_start (argsPtr, text);
260                   vsnprintf(textbuffer, MAXBUF, text, argsPtr);
261                   va_end(argsPtr);
262                   strcpy(b,asctime(timeinfo));
263                   b[strlen(b)-1] = ':';
264                   fprintf(f,"%s %s\n",b,textbuffer);
265                   fclose(f);
266           }
267           else
268           {
269                   printf("Can't write log file, bailing!!!");
270                   Exit(ERROR);
271           }
272 }
273
274 void readfile(file_cache &F, const char* fname)
275 {
276   FILE* file;
277   char linebuf[MAXBUF];
278
279   log(DEBUG,"readfile: loading %s",fname);
280   F.clear();
281   file =  fopen(fname,"r");
282   if (file)
283   {
284         while (!feof(file))
285         {
286                 fgets(linebuf,sizeof(linebuf),file);
287                 linebuf[strlen(linebuf)-1]='\0';
288                 if (!strcmp(linebuf,""))
289                 {
290                         strcpy(linebuf,"  ");
291                 }
292                 if (!feof(file))
293                 {
294                         F.push_back(linebuf);
295                 }
296         }
297         fclose(file);
298   }
299   else
300   {
301           log(DEBUG,"readfile: failed to load file: %s",fname);
302   }
303   log(DEBUG,"readfile: loaded %s, %d lines",fname,F.size());
304 }
305
306 void ReadConfig(void)
307 {
308   char dbg[MAXBUF],pauseval[MAXBUF],Value[MAXBUF];
309   ConnectClass c;
310
311   ConfValue("server","name",0,ServerName);
312   ConfValue("server","description",0,ServerDesc);
313   ConfValue("server","network",0,Network);
314   ConfValue("admin","name",0,AdminName);
315   ConfValue("admin","email",0,AdminEmail);
316   ConfValue("admin","nick",0,AdminNick);
317   ConfValue("files","motd",0,motd);
318   ConfValue("files","rules",0,rules);
319   ConfValue("power","diepass",0,diepass);
320   ConfValue("power","pause",0,pauseval);
321   ConfValue("power","restartpass",0,restartpass);
322   ConfValue("options","prefixquit",0,PrefixQuit);
323   ConfValue("die","value",0,DieValue);
324   ConfValue("options","loglevel",0,dbg);
325   if (!strcmp(dbg,"debug"))
326         LogLevel = DEBUG;
327   if (!strcmp(dbg,"verbose"))
328         LogLevel = VERBOSE;
329   if (!strcmp(dbg,"default"))
330         LogLevel = DEFAULT;
331   if (!strcmp(dbg,"sparse"))
332         LogLevel = SPARSE;
333   if (!strcmp(dbg,"none"))
334         LogLevel = NONE;
335   readfile(RULES,rules);
336   log(DEBUG,"Reading connect classes");
337   Classes.clear();
338   for (int i = 0; i < ConfValueEnum("connect"); i++)
339   {
340         strcpy(Value,"");
341         ConfValue("connect","allow",i,Value);
342         if (strcmp(Value,""))
343         {
344                 strcpy(c.host,Value);
345                 c.type = CC_ALLOW;
346                 strcpy(Value,"");
347                 ConfValue("connect","password",i,Value);
348                 strcpy(c.pass,Value);
349                 Classes.push_back(c);
350                 log(DEBUG,"Read connect class type ALLOW, host=%s password=%s",c.host,c.pass);
351         }
352         else
353         {
354                 ConfValue("connect","deny",i,Value);
355                 strcpy(c.host,Value);
356                 c.type = CC_DENY;
357                 Classes.push_back(c);
358                 log(DEBUG,"Read connect class type DENY, host=%s",c.host);
359         }
360         
361   }
362 }
363
364 void Blocking(int s)
365 {
366   int flags;
367   log(DEBUG,"Blocking: %d",s);
368   flags = fcntl(s, F_GETFL, 0);
369   fcntl(s, F_SETFL, flags ^ O_NONBLOCK);
370 }
371
372 void NonBlocking(int s)
373 {
374   int flags;
375   log(DEBUG,"NonBlocking: %d",s);
376   flags = fcntl(s, F_GETFL, 0);
377   fcntl(s, F_SETFL, flags | O_NONBLOCK);
378 }
379
380
381 int CleanAndResolve (char *resolvedHost, const char *unresolvedHost)
382 {
383   struct hostent *hostPtr = NULL;
384   struct in_addr addr;
385
386   memset (resolvedHost, '\0',MAXBUF);
387   if(unresolvedHost == NULL)
388         return(ERROR);
389   if ((inet_aton(unresolvedHost,&addr)) == 0)
390         return(ERROR);
391   hostPtr = gethostbyaddr ((char *)&addr.s_addr,sizeof(addr.s_addr),AF_INET);
392   if (hostPtr != NULL)
393         snprintf(resolvedHost,MAXBUF,"%s",hostPtr->h_name);
394   else
395         snprintf(resolvedHost,MAXBUF,"%s",unresolvedHost);
396   return (TRUE);
397 }
398
399 /* write formatted text to a socket, in same format as printf */
400
401 void Write(int sock,char *text, ...)
402 {
403   char textbuffer[MAXBUF];
404   va_list argsPtr;
405   char tb[MAXBUF];
406
407   va_start (argsPtr, text);
408   vsnprintf(textbuffer, MAXBUF, text, argsPtr);
409   va_end(argsPtr);
410   sprintf(tb,"%s\r\n",textbuffer);
411   chop(tb);
412   write(sock,tb,strlen(tb));
413   update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
414 }
415
416 /* write a server formatted numeric response to a single socket */
417
418 void WriteServ(int sock, char* text, ...)
419 {
420   char textbuffer[MAXBUF],tb[MAXBUF];
421   va_list argsPtr;
422   va_start (argsPtr, text);
423
424   vsnprintf(textbuffer, MAXBUF, text, argsPtr);
425   va_end(argsPtr);
426   sprintf(tb,":%s %s\r\n",ServerName,textbuffer);
427   chop(tb);
428   write(sock,tb,strlen(tb));
429   update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
430 }
431
432 /* write text from an originating user to originating user */
433
434 void WriteFrom(int sock, userrec *user,char* text, ...)
435 {
436   char textbuffer[MAXBUF],tb[MAXBUF];
437   va_list argsPtr;
438   va_start (argsPtr, text);
439
440   vsnprintf(textbuffer, MAXBUF, text, argsPtr);
441   va_end(argsPtr);
442   sprintf(tb,":%s!%s@%s %s\r\n",user->nick,user->ident,user->dhost,textbuffer);
443   chop(tb);
444   write(sock,tb,strlen(tb));
445   update_stats_l(sock,strlen(tb)); /* add one line-out to stats L for this fd */
446 }
447
448 /* write text to an destination user from a source user (e.g. user privmsg) */
449
450 void WriteTo(userrec *source, userrec *dest,char *data, ...)
451 {
452         char textbuffer[MAXBUF],tb[MAXBUF];
453         va_list argsPtr;
454         va_start (argsPtr, data);
455         if ((!dest) || (!source))
456         {
457                 return;
458         }
459         vsnprintf(textbuffer, MAXBUF, data, argsPtr);
460         va_end(argsPtr);
461         chop(tb);
462         WriteFrom(dest->fd,source,"%s",textbuffer);
463 }
464
465 /* write formatted text from a source user to all users on a channel
466  * including the sender (NOT for privmsg, notice etc!) */
467
468 void WriteChannel(chanrec* Ptr, userrec* user, char* text, ...)
469 {
470         char textbuffer[MAXBUF];
471         va_list argsPtr;
472         va_start (argsPtr, text);
473         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
474         va_end(argsPtr);
475         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
476         {
477                 if (has_channel(i->second,Ptr))
478                 {
479                         WriteTo(user,i->second,"%s",textbuffer);
480                 }
481         }
482 }
483
484 /* write formatted text from a source user to all users on a channel except
485  * for the sender (for privmsg etc) */
486
487 void ChanExceptSender(chanrec* Ptr, userrec* user, char* text, ...)
488 {
489         char textbuffer[MAXBUF];
490         va_list argsPtr;
491         va_start (argsPtr, text);
492         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
493         va_end(argsPtr);
494
495         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
496         {
497                 if (has_channel(i->second,Ptr) && (user != i->second))
498                 {
499                         WriteTo(user,i->second,"%s",textbuffer);
500                 }
501         }
502 }
503
504 int c_count(userrec* u)
505 {
506         int z = 0;
507         for (int i =0; i != MAXCHANS; i++)
508                 if (u->chans[i].channel)
509                         z++;
510         return z;
511
512 }
513
514 /* return 0 or 1 depending if users u and u2 share one or more common channels
515  * (used by QUIT, NICK etc which arent channel specific notices) */
516
517 int common_channels(userrec *u, userrec *u2)
518 {
519         int i = 0;
520         int z = 0;
521
522         if ((!u) || (!u2))
523         {
524                 return 0;
525         }
526         for (i = 0; i != MAXCHANS; i++)
527         {
528                 for (z = 0; z != MAXCHANS; z++)
529                 {
530                         if ((u->chans[i].channel == u2->chans[z].channel) && (u->chans[i].channel) && (u2->chans[z].channel) && (u->registered == 7) && (u2->registered == 7))
531                         {
532                                 if ((c_count(u)) && (c_count(u2)))
533                                 {
534                                         return 1;
535                                 }
536                         }
537                 }
538         }
539         return 0;
540 }
541
542 /* write a formatted string to all users who share at least one common
543  * channel, including the source user e.g. for use in NICK */
544
545 void WriteCommon(userrec *u, char* text, ...)
546 {
547         char textbuffer[MAXBUF];
548         va_list argsPtr;
549         va_start (argsPtr, text);
550         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
551         va_end(argsPtr);
552
553         WriteFrom(u->fd,u,"%s",textbuffer);
554
555         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
556         {
557                 if (common_channels(u,i->second) && (i->second != u))
558                 {
559                         WriteFrom(i->second->fd,u,"%s",textbuffer);
560                 }
561         }
562 }
563
564 /* write a formatted string to all users who share at least one common
565  * channel, NOT including the source user e.g. for use in QUIT */
566
567 void WriteCommonExcept(userrec *u, char* text, ...)
568 {
569         char textbuffer[MAXBUF];
570         va_list argsPtr;
571         va_start (argsPtr, text);
572         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
573         va_end(argsPtr);
574
575         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
576         {
577                 if ((common_channels(u,i->second)) && (u != i->second))
578                 {
579                         WriteFrom(i->second->fd,u,"%s",textbuffer);
580                 }
581         }
582 }
583
584 void WriteOpers(char* text, ...)
585 {
586         char textbuffer[MAXBUF];
587         va_list argsPtr;
588         va_start (argsPtr, text);
589         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
590         va_end(argsPtr);
591
592         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
593         {
594                 if (strchr(i->second->modes,'o'))
595                 {
596                         if (strchr(i->second->modes,'s'))
597                         {
598                                 // send server notices to all with +s
599                                 // (TODO: needs SNOMASKs)
600                                 WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,textbuffer);
601                         }
602                 }
603         }
604 }
605
606 void WriteWallOps(userrec *source, char* text, ...)  
607 {  
608         int i = 0;  
609         char textbuffer[MAXBUF];  
610         va_list argsPtr;  
611         va_start (argsPtr, text);  
612         vsnprintf(textbuffer, MAXBUF, text, argsPtr);  
613         va_end(argsPtr);  
614   
615         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
616         {  
617                 if (strchr(i->second->modes,'w'))
618                 {  
619                         WriteTo(source,i->second,"WALLOPS %s",textbuffer);
620                 }
621         }
622 }  
623
624 /* convert a string to lowercase. Note following special circumstances
625  * taken from RFC 1459. Many "official" server branches still hold to this
626  * rule so i will too;
627  *
628  *  Because of IRC's scandanavian origin, the characters {}| are
629  *  considered to be the lower case equivalents of the characters []\,
630  *  respectively. This is a critical issue when determining the
631  *  equivalence of two nicknames.
632  */
633
634 void strlower(char *n)
635 {
636         if (!n)
637         {
638                 return;
639         }
640         for (int i = 0; i != strlen(n); i++)
641         {
642                 n[i] = tolower(n[i]);
643                 if (n[i] == '[')
644                         n[i] = '{';
645                 if (n[i] == ']')
646                         n[i] = '}';
647                 if (n[i] == '\\')
648                         n[i] = '|';
649         }
650 }
651
652 /* verify that a user's nickname is valid */
653
654 int isnick(const char* n)
655 {
656         int i = 0;
657         char v[MAXBUF];
658         if (!n)
659         {
660                 return 0;
661         }
662         if (!strcmp(n,""))
663         {
664                 return 0;
665         }
666         if (strlen(n) > NICKMAX-1)
667         {
668                 return 0;
669         }
670         for (i = 0; i != strlen(n); i++)
671         {
672                 if ((n[i] < 33) || (n[i] > 125))
673                 {
674                         return 0;
675                 }
676                 /* can't occur ANYWHERE in a nickname! */
677                 if (strchr("<>,./?:;@'~#=+()*&%$£ \"!",n[i]))
678                 {
679                         return 0;
680                 }
681                 /* can't occur as the first char of a nickname... */
682                 if ((strchr("0123456789",n[i])) && (!i))
683                 {
684                         return 0;
685                 }
686         }
687         return 1;
688 }
689
690 /* Find a user record by nickname and return a pointer to it */
691
692 userrec* Find(string nick)
693 {
694         user_hash::iterator iter = clientlist.find(nick);
695
696         if (iter == clientlist.end())
697                 /* Couldn't find it */
698                 return NULL;
699
700         return iter->second;
701 }
702
703 void update_stats_l(int fd,int data_out) /* add one line-out to stats L for this fd */
704 {
705         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
706         {
707                 if (i->second->fd == fd)
708                 {
709                         i->second->bytes_out+=data_out;
710                         i->second->cmds_out++;
711                 }
712         }
713 }
714
715
716 /* find a channel record by channel name and return a pointer to it */
717
718 chanrec* FindChan(const char* chan)
719 {
720         chan_hash::iterator iter = chanlist.find(chan);
721
722         if (iter == chanlist.end())
723                 /* Couldn't find it */
724                 return NULL;
725
726         return iter->second;
727 }
728
729
730 void purge_empty_chans(void)
731 {
732         int go_again = 1, purge = 0;
733         
734         while (go_again)
735         {
736                 go_again = 0;
737                 for (chan_hash::iterator i = chanlist.begin(); i != chanlist.end(); i++)
738                 {
739                         if (i->second) {
740                                 if (!usercount(i->second))
741                                 {
742                                         /* kill the record */
743                                         if (i != chanlist.end())
744                                         {
745                                                 log(DEBUG,"del_channel: destroyed: %s",i->second->name);
746                                                 delete i->second;
747                                                 chanlist.erase(i);
748                                                 go_again = 1;
749                                                 purge++;
750                                                 break;
751                                         }
752                                 }
753                         }
754                 }
755         }
756         log(DEBUG,"completed channel purge, killed %d",purge);
757 }
758
759 /* returns the status character for a given user on a channel, e.g. @ for op,
760  * % for halfop etc. If the user has several modes set, the highest mode
761  * the user has must be returned. */
762
763 char* cmode(userrec *user, chanrec *chan)
764 {
765         int i;
766         for (i = 0; i != MAXCHANS; i++)
767         {
768                 if ((user->chans[i].channel == chan) && (chan != NULL))
769                 {
770                         if ((user->chans[i].uc_modes & UCMODE_OP) > 0)
771                         {
772                                 return "@";
773                         }
774                         if ((user->chans[i].uc_modes & UCMODE_HOP) > 0)
775                         {
776                                 return "%";
777                         }
778                         if ((user->chans[i].uc_modes & UCMODE_VOICE) > 0)
779                         {
780                                 return "+";
781                         }
782                         return "";
783                 }
784         }
785 }
786
787 char scratch[MAXMODES];
788
789 char* chanmodes(chanrec *chan)
790 {
791         strcpy(scratch,"");
792         if (chan->noexternal)
793         {
794                 strcat(scratch,"n");
795         }
796         if (chan->topiclock)
797         {
798                 strcat(scratch,"t");
799         }
800         if (strcmp(chan->key,""))
801         {
802                 strcat(scratch,"k");
803         }
804         if (chan->limit)
805         {
806                 strcat(scratch,"l");
807         }
808         if (chan->inviteonly)
809         {
810                 strcat(scratch,"i");
811         }
812         if (chan->moderated)
813         {
814                 strcat(scratch,"m");
815         }
816         if (chan->secret)
817         {
818                 strcat(scratch,"s");
819         }
820         if (chan->c_private)
821         {
822                 strcat(scratch,"p");
823         }
824         if (strcmp(chan->key,""))
825         {
826                 strcat(scratch," ");
827                 strcat(scratch,chan->key);
828         }
829         if (chan->limit)
830         {
831                 char foo[24];
832                 sprintf(foo," %d",chan->limit);
833                 strcat(scratch,foo);
834         }
835         log(DEBUG,"chanmodes: %s %s",chan->name,scratch);
836         return scratch;
837 }
838
839 /* returns the status value for a given user on a channel, e.g. STATUS_OP for
840  * op, STATUS_VOICE for voice etc. If the user has several modes set, the
841  * highest mode the user has must be returned. */
842
843 int cstatus(userrec *user, chanrec *chan)
844 {
845         int i;
846         for (i = 0; i != MAXCHANS; i++)
847         {
848                 if ((user->chans[i].channel == chan) && (chan != NULL))
849                 {
850                         if ((user->chans[i].uc_modes & UCMODE_OP) > 0)
851                         {
852                                 return STATUS_OP;
853                         }
854                         if ((user->chans[i].uc_modes & UCMODE_HOP) > 0)
855                         {
856                                 return STATUS_HOP;
857                         }
858                         if ((user->chans[i].uc_modes & UCMODE_VOICE) > 0)
859                         {
860                                 return STATUS_VOICE;
861                         }
862                         return STATUS_NORMAL;
863                 }
864         }
865 }
866
867
868 /* compile a userlist of a channel into a string, each nick seperated by
869  * spaces and op, voice etc status shown as @ and + */
870
871 void userlist(userrec *user,chanrec *c)
872 {
873         sprintf(list,"353 %s = %s :", user->nick, c->name);
874         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
875         {
876                 if (has_channel(i->second,c))
877                 {
878                         if (isnick(i->second->nick))
879                         {
880                                 if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
881                                 {
882                                         /* user is +i, and source not on the channel, does not show
883                                          * nick in NAMES list */
884                                         continue;
885                                 }
886                                 strcat(list,cmode(i->second,c));
887                                 strcat(list,i->second->nick);
888                                 strcat(list," ");
889                                 if (strlen(list)>(480-NICKMAX))
890                                 {
891                                         /* list overflowed into
892                                          * multiple numerics */
893                                         WriteServ(user->fd,list);
894                                         sprintf(list,"353 %s = %s :", user->nick, c->name);
895                                 }
896                         }
897                 }
898         }
899         /* if whats left in the list isnt empty, send it */
900         if (list[strlen(list)-1] != ':')
901         {
902                 WriteServ(user->fd,list);
903         }
904 }
905
906 /* return a count of the users on a specific channel accounting for
907  * invisible users who won't increase the count. e.g. for /LIST */
908
909 int usercount_i(chanrec *c)
910 {
911         int i = 0;
912         int count = 0;
913
914         strcpy(list,"");
915         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
916         {
917                 if (has_channel(i->second,c))
918                 {
919                         if (isnick(i->second->nick))
920                         {
921                                 if ((!has_channel(i->second,c)) && (strchr(i->second->modes,'i')))
922                                 {
923                                         /* user is +i, and source not on the channel, does not show
924                                          * nick in NAMES list */
925                                         continue;
926                                 }
927                                 count++;
928                         }
929                 }
930         }
931         log(DEBUG,"usercount_i: %s %d",c->name,count);
932         return count;
933 }
934
935
936 int usercount(chanrec *c)
937 {
938         int i = 0;
939         int count = 0;
940
941         strcpy(list,"");
942         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
943         {
944                 if (has_channel(i->second,c))
945                 {
946                         if (isnick(i->second->nick))
947                         {
948                                 count++;
949                         }
950                 }
951         }
952         log(DEBUG,"usercount: %s %d",c->name,count);
953         return count;
954 }
955
956
957 /* add a channel to a user, creating the record for it if needed and linking
958  * it to the user record */
959
960 chanrec* add_channel(userrec *user, char* cname, char* key)
961 {
962         int i = 0;
963         chanrec* Ptr;
964         int created = 0;
965
966         if ((!cname) || (!user))
967         {
968                 return NULL;
969         }
970         if (strlen(cname) > CHANMAX-1)
971         {
972                 cname[CHANMAX-1] = '\0';
973         }
974
975         log(DEBUG,"add_channel: %s %s",user->nick,cname);
976         
977         if ((has_channel(user,FindChan(cname))) && (FindChan(cname)))
978         {
979                 return NULL; // already on the channel!
980         }
981         
982         if (!FindChan(cname))
983         {
984                 /* create a new one */
985                 log(DEBUG,"add_channel: creating: %s",cname);
986                 {
987                         chanlist[cname] = new chanrec();
988
989                         strcpy(chanlist[cname]->name, cname);
990                         chanlist[cname]->topiclock = 1;
991                         chanlist[cname]->noexternal = 1;
992                         chanlist[cname]->created = time(NULL);
993                         strcpy(chanlist[cname]->topic, "");
994                         strncpy(chanlist[cname]->setby, user->nick,NICKMAX);
995                         chanlist[cname]->topicset = 0;
996                         Ptr = chanlist[cname];
997                         log(DEBUG,"add_channel: created: %s",cname);
998                         /* set created to 2 to indicate user
999                          * is the first in the channel
1000                          * and should be given ops */
1001                         created = 2;
1002                 }
1003         }
1004         else
1005         {
1006                 /* channel exists, just fish out a pointer to its struct */
1007                 Ptr = FindChan(cname);
1008                 if (Ptr)
1009                 {
1010                         log(DEBUG,"add_channel: joining to: %s",Ptr->name);
1011                         if (strcmp(Ptr->key,""))
1012                         {
1013                                 log(DEBUG,"add_channel: %s has key %s",Ptr->name,Ptr->key);
1014                                 if (!key)
1015                                 {
1016                                         log(DEBUG,"add_channel: no key given in JOIN");
1017                                         WriteServ(user->fd,"475 %s %s :Cannot join channel (Requires key)",user->nick, Ptr->name);
1018                                         return NULL;
1019                                 }
1020                                 else
1021                                 {
1022                                         log(DEBUG,"key at %p is %s",key,key);
1023                                         if (strcasecmp(key,Ptr->key))
1024                                         {
1025                                                 log(DEBUG,"add_channel: bad key given in JOIN");
1026                                                 WriteServ(user->fd,"475 %s %s :Cannot join channel (Incorrect key)",user->nick, Ptr->name);
1027                                                 return NULL;
1028                                         }
1029                                 }
1030                         }
1031
1032                         if (Ptr->inviteonly)
1033                         {
1034                                 if (user->IsInvited(Ptr->name))
1035                                 {
1036                                         /* user was invited to channel */
1037                                         /* there may be an optional channel NOTICE here */
1038                                 }
1039                                 else
1040                                 {
1041                                         WriteServ(user->fd,"473 %s %s :Cannot join channel (Invite only)",user->nick, Ptr->name);
1042                                         return NULL;
1043                                 }
1044                         }
1045
1046                         if (Ptr->limit)
1047                         {
1048                                 if (usercount(Ptr) == Ptr->limit)
1049                                 {
1050                                         WriteServ(user->fd,"471 %s %s :Cannot join channel (Channel is full)",user->nick, Ptr->name);
1051                                         return NULL;
1052                                 }
1053                         }
1054                         
1055                         /* check user against the channel banlist */
1056                         for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++)
1057                         {
1058                                 if (match(user->GetFullHost(),i->data))
1059                                 {
1060                                         WriteServ(user->fd,"474 %s %s :Cannot join channel (You're banned)",user->nick, Ptr->name);
1061                                         return NULL;
1062                                 }
1063                         }
1064
1065                         user->RemoveInvite(Ptr->name);
1066                         
1067                 }
1068                 created = 1;
1069         }
1070
1071         
1072         for (i =0; i != MAXCHANS; i++)
1073         {
1074                 if (user->chans[i].channel == NULL)
1075                 {
1076                         if (created == 2) 
1077                         {
1078                                 /* first user in is given ops */
1079                                 user->chans[i].uc_modes = UCMODE_OP;
1080                         }
1081                         else
1082                         {
1083                                 user->chans[i].uc_modes = 0;
1084                         }
1085                         user->chans[i].channel = Ptr;
1086                         WriteChannel(Ptr,user,"JOIN :%s",Ptr->name);
1087                         if (Ptr->topicset)
1088                         {
1089                                 WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
1090                                 WriteServ(user->fd,"333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset);
1091                         }
1092                         userlist(user,Ptr);
1093                         WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, Ptr->name);
1094                         WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name,chanmodes(Ptr));
1095                         WriteServ(user->fd,"329 %s %s %d", user->nick, Ptr->name, Ptr->created);
1096                         FOREACH_MOD OnUserJoin(user,Ptr);
1097                         return Ptr;
1098                 }
1099         }
1100         log(DEBUG,"add_channel: user channel max exceeded: %s %s",user->nick,cname);
1101         WriteServ(user->fd,"405 %s %s :You are on too many channels",user->nick, cname);
1102         return NULL;
1103 }
1104
1105 /* remove a channel from a users record, and remove the record from memory
1106  * if the channel has become empty */
1107
1108 chanrec* del_channel(userrec *user, char* cname, char* reason)
1109 {
1110         int i = 0;
1111         chanrec* Ptr;
1112         int created = 0;
1113
1114         if ((!cname) || (!user))
1115         {
1116                 return NULL;
1117         }
1118
1119         Ptr = FindChan(cname);
1120         
1121         if (!Ptr)
1122         {
1123                 return NULL;
1124         }
1125
1126         FOREACH_MOD OnUserPart(user,Ptr);
1127         log(DEBUG,"del_channel: removing: %s %s",user->nick,Ptr->name);
1128         
1129         for (i =0; i != MAXCHANS; i++)
1130         {
1131                 /* zap it from the channel list of the user */
1132                 if (user->chans[i].channel == Ptr)
1133                 {
1134                         if (reason)
1135                         {
1136                                 WriteChannel(Ptr,user,"PART %s :%s",Ptr->name, reason);
1137                         }
1138                         else
1139                         {
1140                                 WriteChannel(Ptr,user,"PART :%s",Ptr->name);
1141                         }
1142                         user->chans[i].uc_modes = 0;
1143                         user->chans[i].channel = NULL;
1144                         log(DEBUG,"del_channel: unlinked: %s %s",user->nick,Ptr->name);
1145                         break;
1146                 }
1147         }
1148         
1149         /* if there are no users left on the channel */
1150         if (!usercount(Ptr))
1151         {
1152                 chan_hash::iterator iter = chanlist.find(Ptr->name);
1153
1154                 log(DEBUG,"del_channel: destroying channel: %s",Ptr->name);
1155
1156                 /* kill the record */
1157                 if (iter != chanlist.end())
1158                 {
1159                         log(DEBUG,"del_channel: destroyed: %s",Ptr->name);
1160                         delete iter->second;
1161                         chanlist.erase(iter);
1162                 }
1163         }
1164 }
1165
1166
1167 void kick_channel(userrec *src,userrec *user, chanrec *Ptr, char* reason)
1168 {
1169         int i = 0;
1170         int created = 0;
1171
1172         if ((!Ptr) || (!user) || (!src))
1173         {
1174                 return;
1175         }
1176
1177         log(DEBUG,"kick_channel: removing: %s %s %s",user->nick,Ptr->name,src->nick);
1178
1179         if (!has_channel(user,Ptr))
1180         {
1181                 WriteServ(src->fd,"441 %s %s %s :They are not on that channel",src->nick, user->nick, Ptr->name);
1182                 return;
1183         }
1184         if ((cstatus(src,Ptr) < STATUS_HOP) || (cstatus(src,Ptr) < cstatus(user,Ptr)))
1185         {
1186                 if (cstatus(src,Ptr) == STATUS_HOP)
1187                 {
1188                         WriteServ(src->fd,"482 %s %s :You must be a channel operator",src->nick, Ptr->name);
1189                 }
1190                 else
1191                 {
1192                         WriteServ(src->fd,"482 %s %s :You must be at least a half-operator",src->nick, Ptr->name);
1193                 }
1194                 
1195                 return;
1196         }
1197         
1198         for (i =0; i != MAXCHANS; i++)
1199         {
1200                 /* zap it from the channel list of the user */
1201                 if (user->chans[i].channel == Ptr)
1202                 {
1203                         WriteChannel(Ptr,src,"KICK %s %s :%s",Ptr->name, user->nick, reason);
1204                         user->chans[i].uc_modes = 0;
1205                         user->chans[i].channel = NULL;
1206                         log(DEBUG,"del_channel: unlinked: %s %s",user->nick,Ptr->name);
1207                         break;
1208                 }
1209         }
1210         
1211         /* if there are no users left on the channel */
1212         if (!usercount(Ptr))
1213         {
1214                 chan_hash::iterator iter = chanlist.find(Ptr->name);
1215
1216                 log(DEBUG,"del_channel: destroying channel: %s",Ptr->name);
1217
1218                 /* kill the record */
1219                 if (iter != chanlist.end())
1220                 {
1221                         log(DEBUG,"del_channel: destroyed: %s",Ptr->name);
1222                         delete iter->second;
1223                         chanlist.erase(iter);
1224                 }
1225         }
1226 }
1227
1228
1229 /* returns 1 if user u has channel c in their record, 0 if not */
1230
1231 int has_channel(userrec *u, chanrec *c)
1232 {
1233         int i = 0;
1234
1235         if (!u)
1236         {
1237                 return 0;
1238         }
1239         for (i =0; i != MAXCHANS; i++)
1240         {
1241                 if (u->chans[i].channel == c)
1242                 {
1243                         return 1;
1244                 }
1245         }
1246         return 0;
1247 }
1248
1249 int give_ops(userrec *user,char *dest,chanrec *chan,int status)
1250 {
1251         userrec *d;
1252         int i;
1253         
1254         if ((!user) || (!dest) || (!chan))
1255         {
1256                 return 0;
1257         }
1258         if (status != STATUS_OP)
1259         {
1260                 WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
1261                 return 0;
1262         }
1263         else
1264         {
1265                 if (!isnick(dest))
1266                 {
1267                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1268                         return 0;
1269                 }
1270                 d = Find(dest);
1271                 if (!d)
1272                 {
1273                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1274                         return 0;
1275                 }
1276                 else
1277                 {
1278                         for (i = 0; i != MAXCHANS; i++)
1279                         {
1280                                 if ((d->chans[i].channel == chan) && (chan != NULL))
1281                                 {
1282                                         if (d->chans[i].uc_modes & UCMODE_OP)
1283                                         {
1284                                                 /* mode already set on user, dont allow multiple */
1285                                                 return 0;
1286                                         }
1287                                         d->chans[i].uc_modes = d->chans[i].uc_modes | UCMODE_OP;
1288                                         log(DEBUG,"gave ops: %s %s",d->chans[i].channel->name,d->nick);
1289                                 }
1290                         }
1291                 }
1292         }
1293         return 1;
1294 }
1295
1296 int give_hops(userrec *user,char *dest,chanrec *chan,int status)
1297 {
1298         userrec *d;
1299         int i;
1300         
1301         if ((!user) || (!dest) || (!chan))
1302         {
1303                 return 0;
1304         }
1305         if (status != STATUS_OP)
1306         {
1307                 WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
1308                 return 0;
1309         }
1310         else
1311         {
1312                 d = Find(dest);
1313                 if (!isnick(dest))
1314                 {
1315                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1316                         return 0;
1317                 }
1318                 if (!d)
1319                 {
1320                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1321                         return 0;
1322                 }
1323                 else
1324                 {
1325                         for (i = 0; i != MAXCHANS; i++)
1326                         {
1327                                 if ((d->chans[i].channel == chan) && (chan != NULL))
1328                                 {
1329                                         if (d->chans[i].uc_modes & UCMODE_HOP)
1330                                         {
1331                                                 /* mode already set on user, dont allow multiple */
1332                                                 return 0;
1333                                         }
1334                                         d->chans[i].uc_modes = d->chans[i].uc_modes | UCMODE_HOP;
1335                                         log(DEBUG,"gave h-ops: %s %s",d->chans[i].channel->name,d->nick);
1336                                 }
1337                         }
1338                 }
1339         }
1340         return 1;
1341 }
1342
1343 int give_voice(userrec *user,char *dest,chanrec *chan,int status)
1344 {
1345         userrec *d;
1346         int i;
1347         
1348         if ((!user) || (!dest) || (!chan))
1349         {
1350                 return 0;
1351         }
1352         if (status < STATUS_HOP)
1353         {
1354                 WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, chan->name);
1355                 return 0;
1356         }
1357         else
1358         {
1359                 d = Find(dest);
1360                 if (!isnick(dest))
1361                 {
1362                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1363                         return 0;
1364                 }
1365                 if (!d)
1366                 {
1367                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1368                         return 0;
1369                 }
1370                 else
1371                 {
1372                         for (i = 0; i != MAXCHANS; i++)
1373                         {
1374                                 if ((d->chans[i].channel == chan) && (chan != NULL))
1375                                 {
1376                                         if (d->chans[i].uc_modes & UCMODE_VOICE)
1377                                         {
1378                                                 /* mode already set on user, dont allow multiple */
1379                                                 return 0;
1380                                         }
1381                                         d->chans[i].uc_modes = d->chans[i].uc_modes | UCMODE_VOICE;
1382                                         log(DEBUG,"gave voice: %s %s",d->chans[i].channel->name,d->nick);
1383                                 }
1384                         }
1385                 }
1386         }
1387         return 1;
1388 }
1389
1390 int take_ops(userrec *user,char *dest,chanrec *chan,int status)
1391 {
1392         userrec *d;
1393         int i;
1394         
1395         if ((!user) || (!dest) || (!chan))
1396         {
1397                 return 0;
1398         }
1399         if (status != STATUS_OP)
1400         {
1401                 WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
1402                 return 0;
1403         }
1404         else
1405         {
1406                 d = Find(dest);
1407                 if (!isnick(dest))
1408                 {
1409                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1410                         return 0;
1411                 }
1412                 if (!d)
1413                 {
1414                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1415                         return 0;
1416                 }
1417                 else
1418                 {
1419                         for (i = 0; i != MAXCHANS; i++)
1420                         {
1421                                 if ((d->chans[i].channel == chan) && (chan != NULL))
1422                                 {
1423                                         if ((d->chans[i].uc_modes & UCMODE_OP) == 0)
1424                                         {
1425                                                 /* mode already set on user, dont allow multiple */
1426                                                 return 0;
1427                                         }
1428                                         d->chans[i].uc_modes ^= UCMODE_OP;
1429                                         log(DEBUG,"took ops: %s %s",d->chans[i].channel->name,d->nick);
1430                                 }
1431                         }
1432                 }
1433         }
1434         return 1;
1435 }
1436
1437 int take_hops(userrec *user,char *dest,chanrec *chan,int status)
1438 {
1439         userrec *d;
1440         int i;
1441         
1442         if ((!user) || (!dest) || (!chan))
1443         {
1444                 return 0;
1445         }
1446         if (status != STATUS_OP)
1447         {
1448                 WriteServ(user->fd,"482 %s %s :You're not a channel operator",user->nick, chan->name);
1449                 return 0;
1450         }
1451         else
1452         {
1453                 d = Find(dest);
1454                 if (!isnick(dest))
1455                 {
1456                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1457                         return 0;
1458                 }
1459                 if (!d)
1460                 {
1461                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1462                         return 0;
1463                 }
1464                 else
1465                 {
1466                         for (i = 0; i != MAXCHANS; i++)
1467                         {
1468                                 if ((d->chans[i].channel == chan) && (chan != NULL))
1469                                 {
1470                                         if ((d->chans[i].uc_modes & UCMODE_HOP) == 0)
1471                                         {
1472                                                 /* mode already set on user, dont allow multiple */
1473                                                 return 0;
1474                                         }
1475                                         d->chans[i].uc_modes ^= UCMODE_HOP;
1476                                         log(DEBUG,"took h-ops: %s %s",d->chans[i].channel->name,d->nick);
1477                                 }
1478                         }
1479                 }
1480         }
1481         return 1;
1482 }
1483
1484 int take_voice(userrec *user,char *dest,chanrec *chan,int status)
1485 {
1486         userrec *d;
1487         int i;
1488         
1489         if ((!user) || (!dest) || (!chan))
1490         {
1491                 return 0;
1492         }
1493         if (status < STATUS_HOP)
1494         {
1495                 WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, chan->name);
1496                 return 0;
1497         }
1498         else
1499         {
1500                 d = Find(dest);
1501                 if (!isnick(dest))
1502                 {
1503                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1504                         return 0;
1505                 }
1506                 if (!d)
1507                 {
1508                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, dest);
1509                         return 0;
1510                 }
1511                 else
1512                 {
1513                         for (i = 0; i != MAXCHANS; i++)
1514                         {
1515                                 if ((d->chans[i].channel == chan) && (chan != NULL))
1516                                 {
1517                                         if ((d->chans[i].uc_modes & UCMODE_VOICE) == 0)
1518                                         {
1519                                                 /* mode already set on user, dont allow multiple */
1520                                                 return 0;
1521                                         }
1522                                         d->chans[i].uc_modes ^= UCMODE_VOICE;
1523                                         log(DEBUG,"took voice: %s %s",d->chans[i].channel->name,d->nick);
1524                                 }
1525                         }
1526                 }
1527         }
1528         return 1;
1529 }
1530
1531 void TidyBan(char *ban)
1532 {
1533         char temp[MAXBUF],NICK[MAXBUF],IDENT[MAXBUF],HOST[MAXBUF];
1534
1535         strcpy(temp,ban);
1536
1537         char* pos_of_pling = strchr(temp,'!');
1538         char* pos_of_at = strchr(temp,'@');
1539
1540         pos_of_pling[0] = '\0';
1541         pos_of_at[0] = '\0';
1542         pos_of_pling++;
1543         pos_of_at++;
1544
1545         strncpy(NICK,temp,NICKMAX);
1546         strncpy(IDENT,pos_of_pling,IDENTMAX+1);
1547         strncpy(HOST,pos_of_at,160);
1548
1549         sprintf(ban,"%s!%s@%s",NICK,IDENT,HOST);
1550 }
1551
1552 int add_ban(userrec *user,char *dest,chanrec *chan,int status)
1553 {
1554         BanItem b;
1555         if ((!user) || (!dest) || (!chan))
1556                 return 0;
1557         if (strchr(dest,'!')==0)
1558                 return 0;
1559         if (strchr(dest,'@')==0)
1560                 return 0;
1561         for (int i = 0; i < strlen(dest); i++)
1562                 if (dest[i] < 32)
1563                         return 0;
1564         for (int i = 0; i < strlen(dest); i++)
1565                 if (dest[i] > 126)
1566                         return 0;
1567         int c = 0;
1568         for (int i = 0; i < strlen(dest); i++)
1569                 if (dest[i] == '!')
1570                         c++;
1571         if (c>1)
1572                 return 0;
1573         c = 0;
1574         for (int i = 0; i < strlen(dest); i++)
1575                 if (dest[i] == '@')
1576                         c++;
1577         if (c>1)
1578                 return 0;
1579         log(DEBUG,"add_ban: %s %s",chan->name,user->nick);
1580
1581         TidyBan(dest);
1582         for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
1583         {
1584                 if (!strcasecmp(i->data,dest))
1585                 {
1586                         // dont allow a user to set the same ban twice
1587                         return 0;
1588                 }
1589         }
1590
1591         b.set_time = time(NULL);
1592         strncpy(b.data,dest,MAXBUF);
1593         strncpy(b.set_by,user->nick,NICKMAX);
1594         chan->bans.push_back(b);
1595         return 1;
1596 }
1597
1598 int take_ban(userrec *user,char *dest,chanrec *chan,int status)
1599 {
1600         if ((!user) || (!dest) || (!chan))
1601         {
1602                 return 0;
1603         }
1604
1605         log(DEBUG,"del_ban: %s %s",chan->name,user->nick);
1606         for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++)
1607         {
1608                 if (!strcasecmp(i->data,dest))
1609                 {
1610                         chan->bans.erase(i);
1611                         return 1;
1612                 }
1613         }
1614         return 0;
1615 }
1616
1617 void process_modes(char **parameters,userrec* user,chanrec *chan,int status, int pcnt)
1618 {
1619         char modelist[MAXBUF];
1620         char outlist[MAXBUF];
1621         char outstr[MAXBUF];
1622         char outpars[32][MAXBUF];
1623         int param = 2;
1624         int pc = 0;
1625         int ptr = 0;
1626         int mdir = 1;
1627         int r = 0;
1628         bool k_set = false, l_set = false;
1629
1630         if (pcnt < 2)
1631         {
1632                 return;
1633         }
1634
1635         log(DEBUG,"process_modes: start");
1636
1637         strcpy(modelist,parameters[1]); /* mode list, e.g. +oo-o */
1638                                         /* parameters[2] onwards are parameters for
1639                                          * modes that require them :) */
1640         strcpy(outlist,"+");
1641         mdir = 1;
1642
1643         log(DEBUG,"process_modes: modelist: %s",modelist);
1644
1645         for (ptr = 0; ptr < strlen(modelist); ptr++)
1646         {
1647                 r = 0;
1648
1649                 {
1650                         log(DEBUG,"process_modes: modechar: %c",modelist[ptr]);
1651                         switch (modelist[ptr])
1652                         {
1653                                 case '-':
1654                                         if (mdir != 0)
1655                                         {
1656                                                 if ((outlist[strlen(outlist)-1] == '+') || (outlist[strlen(outlist)-1] == '-'))
1657                                                 {
1658                                                         outlist[strlen(outlist)-1] = '-';
1659                                                 }
1660                                                 else
1661                                                 {
1662                                                         strcat(outlist,"-");
1663                                                 }
1664                                         }
1665                                         mdir = 0;
1666                                         
1667                                 break;                  
1668
1669                                 case '+':
1670                                         if (mdir != 1)
1671                                         {
1672                                                 if ((outlist[strlen(outlist)-1] == '+') || (outlist[strlen(outlist)-1] == '-'))
1673                                                 {
1674                                                         outlist[strlen(outlist)-1] = '+';
1675                                                 }
1676                                                 else
1677                                                 {
1678                                                         strcat(outlist,"+");
1679                                                 }
1680                                         }
1681                                         mdir = 1;
1682                                 break;
1683
1684                                 case 'o':
1685                                         if ((param >= pcnt)) break;
1686                                         if (mdir == 1)
1687                                         {
1688                                                 r = give_ops(user,parameters[param++],chan,status);
1689                                         }
1690                                         else
1691                                         {
1692                                                 r = take_ops(user,parameters[param++],chan,status);
1693                                         }
1694                                         if (r)
1695                                         {
1696                                                 strcat(outlist,"o");
1697                                                 strcpy(outpars[pc++],parameters[param-1]);
1698                                         }
1699                                 break;
1700                         
1701                                 case 'h':
1702                                         if ((param >= pcnt)) break;
1703                                         if (mdir == 1)
1704                                         {
1705                                                 r = give_hops(user,parameters[param++],chan,status);
1706                                         }
1707                                         else
1708                                         {
1709                                                 r = take_hops(user,parameters[param++],chan,status);
1710                                         }
1711                                         if (r)
1712                                         {
1713                                                 strcat(outlist,"h");
1714                                                 strcpy(outpars[pc++],parameters[param-1]);
1715                                         }
1716                                 break;
1717                         
1718                                 
1719                                 case 'v':
1720                                         if ((param >= pcnt)) break;
1721                                         if (mdir == 1)
1722                                         {
1723                                                 r = give_voice(user,parameters[param++],chan,status);
1724                                         }
1725                                         else
1726                                         {
1727                                                 r = take_voice(user,parameters[param++],chan,status);
1728                                         }
1729                                         if (r)
1730                                         {
1731                                                 strcat(outlist,"v");
1732                                                 strcpy(outpars[pc++],parameters[param-1]);
1733                                         }
1734                                 break;
1735                                 
1736                                 case 'b':
1737                                         if ((param >= pcnt)) break;
1738                                         if (mdir == 1)
1739                                         {
1740                                                 r = add_ban(user,parameters[param++],chan,status);
1741                                         }
1742                                         else
1743                                         {
1744                                                 r = take_ban(user,parameters[param++],chan,status);
1745                                         }
1746                                         if (r)
1747                                         {
1748                                                 strcat(outlist,"b");
1749                                                 strcpy(outpars[pc++],parameters[param-1]);
1750                                         }
1751                                 break;
1752
1753                                 case 'k':
1754                                         if ((param >= pcnt))
1755                                                 break;
1756
1757                                         if (mdir == 1)
1758                                         {
1759                                                 if (k_set)
1760                                                         break;
1761                                                 
1762                                                 if (!strcmp(chan->key,""))
1763                                                 {
1764                                                         strcat(outlist,"k");
1765                                                         strcpy(outpars[pc++],parameters[param++]);
1766                                                         strcpy(chan->key,parameters[param-1]);
1767                                                         k_set = true;
1768                                                 }
1769                                         }
1770                                         else
1771                                         {
1772                                                 /* only allow -k if correct key given */
1773                                                 if (strcmp(chan->key,""))
1774                                                 {
1775                                                         strcat(outlist,"k");
1776                                                         strcpy(chan->key,"");
1777                                                 }
1778                                         }
1779                                 break;
1780                                 
1781                                 case 'l':
1782                                         if (mdir == 0)
1783                                         {
1784                                                 if (chan->limit)
1785                                                 {
1786                                                         strcat(outlist,"l");
1787                                                         chan->limit = 0;
1788                                                 }
1789                                         }
1790                                         
1791                                         if ((param >= pcnt)) break;
1792                                         if (mdir == 1)
1793                                         {
1794                                                 if (l_set)
1795                                                         break;
1796                                                 
1797                                                 bool invalid = false;
1798                                                 for (int i = 0; i < strlen(parameters[param]); i++)
1799                                                 {
1800                                                         if ((parameters[param][i] < '0') || (parameters[param][i] > '9'))
1801                                                         {
1802                                                                 invalid = true;
1803                                                         }
1804                                                 }
1805                                                 if (atoi(parameters[param]) < 1)
1806                                                 {
1807                                                         invalid = true;
1808                                                 }
1809
1810                                                 if (invalid)
1811                                                         break;
1812                                                 
1813                                                 chan->limit = atoi(parameters[param]);
1814                                                 if (chan->limit)
1815                                                 {
1816                                                         strcat(outlist,"l");
1817                                                         strcpy(outpars[pc++],parameters[param++]);
1818                                                         l_set = true;
1819                                                 }
1820                                         }
1821                                 break;
1822                                 
1823                                 case 'i':
1824                                         if (chan->inviteonly != mdir)
1825                                         {
1826                                                 strcat(outlist,"i");
1827                                         }
1828                                         chan->inviteonly = mdir;
1829                                 break;
1830                                 
1831                                 case 't':
1832                                         if (chan->topiclock != mdir)
1833                                         {
1834                                                 strcat(outlist,"t");
1835                                         }
1836                                         chan->topiclock = mdir;
1837                                 break;
1838                                 
1839                                 case 'n':
1840                                         if (chan->noexternal != mdir)
1841                                         {
1842                                                 strcat(outlist,"n");
1843                                         }
1844                                         chan->noexternal = mdir;
1845                                 break;
1846                                 
1847                                 case 'm':
1848                                         if (chan->moderated != mdir)
1849                                         {
1850                                                 strcat(outlist,"m");
1851                                         }
1852                                         chan->moderated = mdir;
1853                                 break;
1854                                 
1855                                 case 's':
1856                                         if (chan->secret != mdir)
1857                                         {
1858                                                 strcat(outlist,"s");
1859                                                 if (chan->c_private)
1860                                                 {
1861                                                         chan->c_private = 0;
1862                                                         if (mdir)
1863                                                         {
1864                                                                 strcat(outlist,"-p+");
1865                                                         }
1866                                                         else
1867                                                         {
1868                                                                 strcat(outlist,"+p-");
1869                                                         }
1870                                                 }
1871                                         }
1872                                         chan->secret = mdir;
1873                                 break;
1874                                 
1875                                 case 'p':
1876                                         if (chan->c_private != mdir)
1877                                         {
1878                                                 strcat(outlist,"p");
1879                                                 if (chan->secret)
1880                                                 {
1881                                                         chan->secret = 0;
1882                                                         if (mdir)
1883                                                         {
1884                                                                 strcat(outlist,"-s+");
1885                                                         }
1886                                                         else
1887                                                         {
1888                                                                 strcat(outlist,"+s-");
1889                                                         }
1890                                                 }
1891                                         }
1892                                         chan->c_private = mdir;
1893                                 break;
1894                                 
1895                         }
1896                 }
1897         }
1898
1899         /* this ensures only the *valid* modes are sent out onto the network */
1900         while ((outlist[strlen(outlist)-1] == '-') || (outlist[strlen(outlist)-1] == '+'))
1901         {
1902                 outlist[strlen(outlist)-1] = '\0';
1903         }
1904         if (strcmp(outlist,""))
1905         {
1906                 strcpy(outstr,outlist);
1907                 for (ptr = 0; ptr < pc; ptr++)
1908                 {
1909                         strcat(outstr," ");
1910                         strcat(outstr,outpars[ptr]);
1911                 }
1912                 WriteChannel(chan,user,"MODE %s %s",chan->name,outstr);
1913         }
1914 }
1915
1916 void handle_mode(char **parameters, int pcnt, userrec *user)
1917 {
1918         chanrec* Ptr;
1919         userrec* dest;
1920         int can_change,i;
1921         int direction = 1;
1922         char outpars[MAXBUF];
1923
1924         dest = Find(parameters[0]);
1925
1926         if ((dest) && (pcnt == 1))
1927         {
1928                 WriteServ(user->fd,"221 %s :+%s",user->nick,user->modes);
1929                 return;
1930         }
1931         if ((dest) && (pcnt > 1))
1932         {
1933                 can_change = 0;
1934                 if (user != dest)
1935                 {
1936                         if (strchr(user->modes,'o'))
1937                         {
1938                                 can_change = 1;
1939                         }
1940                 }
1941                 else
1942                 {
1943                         can_change = 1;
1944                 }
1945                 if (!can_change)
1946                 {
1947                         WriteServ(user->fd,"482 %s :Can't change mode for other users",user->nick);
1948                         return;
1949                 }
1950                 
1951                 strcpy(outpars,"+");
1952                 direction = 1;
1953
1954                 if ((parameters[1][0] != '+') && (parameters[1][0] != '-'))
1955                         return;
1956
1957                 for (i = 0; i < strlen(parameters[1]); i++)
1958                 {
1959                         if (parameters[1][i] == '+')
1960                         {
1961                                 if (direction != 1)
1962                                 {
1963                                         if ((outpars[strlen(outpars)-1] == '+') || (outpars[strlen(outpars)-1] == '-'))
1964                                         {
1965                                                 outpars[strlen(outpars)-1] = '+';
1966                                         }
1967                                         else
1968                                         {
1969                                                 strcat(outpars,"+");
1970                                         }
1971                                 }
1972                                 direction = 1;
1973                         }
1974                         else
1975                         if (parameters[1][i] == '-')
1976                         {
1977                                 if (direction != 0)
1978                                 {
1979                                         if ((outpars[strlen(outpars)-1] == '+') || (outpars[strlen(outpars)-1] == '-'))
1980                                         {
1981                                                 outpars[strlen(outpars)-1] = '-';
1982                                         }
1983                                         else
1984                                         {
1985                                                 strcat(outpars,"-");
1986                                         }
1987                                 }
1988                                 direction = 0;
1989                         }
1990                         else
1991                         {
1992                                 can_change = 0;
1993                                 if (strchr(user->modes,'o'))
1994                                 {
1995                                         can_change = 1;
1996                                 }
1997                                 else
1998                                 {
1999                                         if ((parameters[1][i] == 'i') || (parameters[1][i] == 'w') || (parameters[1][i] == 's'))
2000                                         {
2001                                                 can_change = 1;
2002                                         }
2003                                 }
2004                                 if (can_change)
2005                                 {
2006                                         if (direction == 1)
2007                                         {
2008                                                 if (!strchr(dest->modes,parameters[1][i]))
2009                                                 {
2010                                                         dest->modes[strlen(dest->modes)+1]='\0';
2011                                                         dest->modes[strlen(dest->modes)] = parameters[1][i];
2012                                                         outpars[strlen(outpars)+1]='\0';
2013                                                         outpars[strlen(outpars)] = parameters[1][i];
2014                                                 }
2015                                         }
2016                                         else
2017                                         {
2018                                                 int q = 0;
2019                                                 char temp[MAXBUF];
2020                                                 char moo[MAXBUF];
2021
2022                                                 outpars[strlen(outpars)+1]='\0';
2023                                                 outpars[strlen(outpars)] = parameters[1][i];
2024                                                 
2025                                                 strcpy(temp,"");
2026                                                 for (q = 0; q < strlen(user->modes); q++)
2027                                                 {
2028                                                         if (user->modes[q] != parameters[1][i])
2029                                                         {
2030                                                                 moo[0] = user->modes[q];
2031                                                                 moo[1] = '\0';
2032                                                                 strcat(temp,moo);
2033                                                         }
2034                                                 }
2035                                                 strcpy(user->modes,temp);
2036                                         }
2037                                 }
2038                         }
2039                 }
2040                 if (strlen(outpars))
2041                 {
2042                         char b[MAXBUF];
2043                         strcpy(b,"");
2044                         int z = 0;
2045                         int i = 0;
2046                         while (i < strlen (outpars))
2047                         {
2048                                 b[z++] = outpars[i++];
2049                                 b[z] = '\0';
2050                                 if (i<strlen(outpars)-1)
2051                                 {
2052                                         if (((outpars[i] == '-') || (outpars[i] == '+')) && ((outpars[i+1] == '-') || (outpars[i+1] == '+')))
2053                                         {
2054                                                 // someones playing silly buggers and trying
2055                                                 // to put a +- or -+ into the line...
2056                                                 i++;
2057                                         }
2058                                 }
2059                                 if (i == strlen(outpars)-1)
2060                                 {
2061                                         if ((outpars[i] == '-') || (outpars[i] == '+'))
2062                                         {
2063                                                 i++;
2064                                         }
2065                                 }
2066                         }
2067
2068                         z = strlen(b)-1;
2069                         if ((b[z] == '-') || (b[z] == '+'))
2070                                 b[z] == '\0';
2071
2072                         if ((!strcmp(b,"+")) || (!strcmp(b,"-")))
2073                                 return;
2074
2075                         WriteTo(user, dest, "MODE %s :%s", dest->nick, b);
2076                 }
2077                 return;
2078         }
2079         
2080         Ptr = FindChan(parameters[0]);
2081         if (Ptr)
2082         {
2083                 if (pcnt == 1)
2084                 {
2085                         /* just /modes #channel */
2086                         WriteServ(user->fd,"324 %s %s +%s",user->nick, Ptr->name, chanmodes(Ptr));
2087                         WriteServ(user->fd,"329 %s %s %d", user->nick, Ptr->name, Ptr->created);
2088                         return;
2089                 }
2090                 else
2091                 if (pcnt == 2)
2092                 {
2093                         if ((!strcmp(parameters[1],"+b")) || (!strcmp(parameters[1],"b")))
2094                         {
2095
2096                                 for (BanList::iterator i = Ptr->bans.begin(); i != Ptr->bans.end(); i++)
2097                                 {
2098                                         WriteServ(user->fd,"367 %s %s %s %s %d",user->nick, Ptr->name, i->data, i->set_by, i->set_time);
2099                                 }
2100                                 WriteServ(user->fd,"368 %s %s :End of channel ban list",user->nick, Ptr->name);
2101                         }
2102                 }
2103
2104                 if ((cstatus(user,Ptr) < STATUS_HOP) && (Ptr))
2105                 {
2106                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, Ptr->name);
2107                         return;
2108                 }
2109
2110                 process_modes(parameters,user,Ptr,cstatus(user,Ptr),pcnt);
2111         }
2112         else
2113         {
2114                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2115         }
2116 }
2117
2118 /* This function pokes and hacks at a parameter list like the following:
2119  *
2120  * PART #winbot, #darkgalaxy :m00!
2121  *
2122  * to turn it into a series of individual calls like this:
2123  *
2124  * PART #winbot :m00!
2125  * PART #darkgalaxy :m00!
2126  *
2127  * The seperate calls are sent to a callback function provided by the caller
2128  * (the caller will usually call itself recursively). The callback function
2129  * must be a command handler. Calling this function on a line with no list causes
2130  * no action to be taken. You must provide a starting and ending parameter number
2131  * where the range of the list can be found, useful if you have a terminating
2132  * parameter as above which is actually not part of the list, or parameters
2133  * before the actual list as well. This code is used by many functions which
2134  * can function as "one to list" (see the RFC) */
2135
2136 int loop_call(handlerfunc fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins)
2137 {
2138         char plist[MAXBUF];
2139         char *param;
2140         char *pars[32];
2141         char blog[32][MAXBUF];
2142         char blog2[32][MAXBUF];
2143         int i = 0, j = 0, q = 0, total = 0, t = 0, t2 = 0, total2 = 0;
2144         char keystr[MAXBUF];
2145         char moo[MAXBUF];
2146
2147         for (i = 0; i <32; i++)
2148                 strcpy(blog[i],"");
2149
2150         for (i = 0; i <32; i++)
2151                 strcpy(blog2[i],"");
2152
2153         strcpy(moo,"");
2154         for (i = 0; i <10; i++)
2155         {
2156                 if (!parameters[i])
2157                 {
2158                         parameters[i] = moo;
2159                 }
2160         }
2161         if (joins)
2162         {
2163                 if (pcnt > 1) /* we have a key to copy */
2164                 {
2165                         strcpy(keystr,parameters[1]);
2166                 }
2167         }
2168
2169         if (!parameters[start])
2170         {
2171                 return 0;
2172         }
2173         if (!strchr(parameters[start],','))
2174         {
2175                 return 0;
2176         }
2177         strcpy(plist,"");
2178         for (i = start; i <= end; i++)
2179         {
2180                 if (parameters[i])
2181                 {
2182                         strcat(plist,parameters[i]);
2183                 }
2184         }
2185         
2186         j = 0;
2187         param = plist;
2188
2189         t = strlen(plist);
2190         for (i = 0; i < t; i++)
2191         {
2192                 if (plist[i] == ',')
2193                 {
2194                         plist[i] = '\0';
2195                         strcpy(blog[j++],param);
2196                         param = plist+i+1;
2197                 }
2198         }
2199         strcpy(blog[j++],param);
2200         total = j;
2201
2202         if ((joins) && (keystr) && (total>0)) // more than one channel and is joining
2203         {
2204                 strcat(keystr,",");
2205         }
2206         
2207         if ((joins) && (keystr))
2208         {
2209                 if (strchr(keystr,','))
2210                 {
2211                         j = 0;
2212                         param = keystr;
2213                         t2 = strlen(keystr);
2214                         for (i = 0; i < t2; i++)
2215                         {
2216                                 if (keystr[i] == ',')
2217                                 {
2218                                         keystr[i] = '\0';
2219                                         strcpy(blog2[j++],param);
2220                                         param = keystr+i+1;
2221                                 }
2222                         }
2223                         strcpy(blog2[j++],param);
2224                         total2 = j;
2225                 }
2226         }
2227
2228         for (j = 0; j < total; j++)
2229         {
2230                 if (blog[j])
2231                 {
2232                         pars[0] = blog[j];
2233                 }
2234                 for (q = end; q < pcnt-1; q++)
2235                 {
2236                         if (parameters[q+1])
2237                         {
2238                                 pars[q-end+1] = parameters[q+1];
2239                         }
2240                 }
2241                 if ((joins) && (parameters[1]))
2242                 {
2243                         if (pcnt > 1)
2244                         {
2245                                 pars[1] = blog2[j];
2246                         }
2247                         else
2248                         {
2249                                 pars[1] = NULL;
2250                         }
2251                 }
2252                 /* repeatedly call the function with the hacked parameter list */
2253                 if ((joins) && (pcnt > 1))
2254                 {
2255                         if (pars[1])
2256                         {
2257                                 // pars[1] already set up and containing key from blog2[j]
2258                                 fn(pars,2,u);
2259                         }
2260                         else
2261                         {
2262                                 pars[1] = parameters[1];
2263                                 fn(pars,2,u);
2264                         }
2265                 }
2266                 else
2267                 {
2268                         fn(pars,pcnt-(end-start),u);
2269                 }
2270         }
2271
2272         return 1;
2273 }
2274
2275 void handle_join(char **parameters, int pcnt, userrec *user)
2276 {
2277         chanrec* Ptr;
2278         int i = 0;
2279         
2280         if (loop_call(handle_join,parameters,pcnt,user,0,0,1))
2281                 return;
2282         if (parameters[0][0] == '#')
2283         {
2284                 Ptr = add_channel(user,parameters[0],parameters[1]);
2285         }
2286 }
2287
2288
2289 void handle_part(char **parameters, int pcnt, userrec *user)
2290 {
2291         chanrec* Ptr;
2292
2293         if (pcnt > 1)
2294         {
2295                 if (loop_call(handle_part,parameters,pcnt,user,0,pcnt-2,0))
2296                         return;
2297                 del_channel(user,parameters[0],parameters[1]);
2298         }
2299         else
2300         {
2301                 if (loop_call(handle_part,parameters,pcnt,user,0,pcnt-1,0))
2302                         return;
2303                 del_channel(user,parameters[0],NULL);
2304         }
2305 }
2306
2307 void handle_kick(char **parameters, int pcnt, userrec *user)
2308 {
2309         chanrec* Ptr = FindChan(parameters[0]);
2310         userrec* u   = Find(parameters[1]);
2311
2312         if ((!u) || (!Ptr))
2313         {
2314                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2315                 return;
2316         }
2317         
2318         if (!has_channel(u,Ptr))
2319         {
2320                 WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, parameters[0]);
2321                 return;
2322         }
2323         
2324         if (pcnt > 2)
2325         {
2326                 kick_channel(user,u,Ptr,parameters[2]);
2327         }
2328         else
2329         {
2330                 kick_channel(user,u,Ptr,user->nick);
2331         }
2332 }
2333
2334
2335 void handle_die(char **parameters, int pcnt, userrec *user)
2336 {
2337         log(DEBUG,"die: %s",user->nick);
2338         if (!strcmp(parameters[0],diepass))
2339         {
2340                 WriteOpers("*** DIE command from %s!%s@%s, terminating...",user->nick,user->ident,user->host);
2341                 sleep(DieDelay);
2342                 Exit(ERROR);
2343         }
2344         else
2345         {
2346                 WriteOpers("*** Failed DIE Command from %s!%s@%s.",user->nick,user->ident,user->host);
2347         }
2348 }
2349
2350 void handle_restart(char **parameters, int pcnt, userrec *user)
2351 {
2352         log(DEBUG,"restart: %s",user->nick);
2353         if (!strcmp(parameters[0],restartpass))
2354         {
2355                 WriteOpers("*** RESTART command from %s!%s@%s, Pretending to restart till this is finished :D",user->nick,user->ident,user->host);
2356                 sleep(DieDelay);
2357                 Exit(ERROR);
2358                 /* Will finish this later when i can be arsed :) */
2359         }
2360         else
2361         {
2362                 WriteOpers("*** Failed RESTART Command from %s!%s@%s.",user->nick,user->ident,user->host);
2363         }
2364 }
2365
2366
2367 void kill_link(userrec *user,char* reason)
2368 {
2369         user_hash::iterator iter = clientlist.find(user->nick);
2370
2371         log(DEBUG,"kill_link: %s '%s'",user->nick,reason);
2372         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason);
2373         fdatasync(user->fd);
2374         WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason);
2375         FOREACH_MOD OnUserQuit(user);
2376         log(DEBUG,"closing fd %d",user->fd);
2377         /* bugfix, cant close() a nonblocking socket (sux!) */
2378         WriteCommonExcept(user,"QUIT :%s",reason);
2379         Blocking(user->fd);
2380         close(user->fd);
2381         NonBlocking(user->fd);
2382         AddWhoWas(user);
2383
2384         if (iter != clientlist.end())
2385         {
2386                 log(DEBUG,"deleting user hash value %p",iter->second);
2387                 delete iter->second;
2388                 clientlist.erase(iter);
2389         }
2390         
2391         purge_empty_chans();
2392 }
2393
2394
2395 void handle_kill(char **parameters, int pcnt, userrec *user)
2396 {
2397         userrec *u = Find(parameters[0]);
2398         char killreason[MAXBUF];
2399         
2400         log(DEBUG,"kill: %s %s",parameters[0],parameters[1]);
2401         if (u)
2402         {
2403                 WriteOpers("*** Local Kill by %s: %s!%s@%s (%s)",user->nick,u->nick,u->ident,u->host,parameters[1]);
2404                 sprintf(killreason,"Killed (%s (%s))",user->nick,parameters[1]);
2405                 kill_link(u,killreason);
2406         }
2407         else
2408         {
2409                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2410         }
2411 }
2412
2413 void handle_summon(char **parameters, int pcnt, userrec *user)
2414 {
2415         WriteServ(user->fd,"445 %s :SUMMON has been disabled (depreciated command)",user->nick);
2416 }
2417
2418 void handle_users(char **parameters, int pcnt, userrec *user)
2419 {
2420         WriteServ(user->fd,"445 %s :USERS has been disabled (depreciated command)",user->nick);
2421 }
2422
2423
2424 // looks up a users password for their connection class (<ALLOW>/<DENY> tags)
2425
2426 char* Passwd(userrec *user)
2427 {
2428         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2429         {
2430                 if (match(user->host,i->host) && (i->type == CC_ALLOW))
2431                 {
2432                         return i->pass;
2433                 }
2434         }
2435         return "";
2436 }
2437
2438 bool IsDenied(userrec *user)
2439 {
2440         for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
2441         {
2442                 if (match(user->host,i->host) && (i->type == CC_DENY))
2443                 {
2444                         return true;
2445                 }
2446         }
2447         return false;
2448 }
2449
2450
2451 void handle_pass(char **parameters, int pcnt, userrec *user)
2452 {
2453         if (!strcasecmp(parameters[0],Passwd(user)))
2454         {
2455                 user->haspassed = true;
2456         }
2457 }
2458
2459 void handle_invite(char **parameters, int pcnt, userrec *user)
2460 {
2461         userrec* u = Find(parameters[0]);
2462         chanrec* c = FindChan(parameters[1]);
2463
2464         if ((!c) || (!u))
2465         {
2466                 if (!c)
2467                 {
2468                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[1]);
2469                 }
2470                 else
2471                 {
2472                         if (c->inviteonly)
2473                         {
2474                                 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
2475                         }
2476                 }
2477
2478                 return;
2479         }
2480
2481         if (c->inviteonly)
2482         {
2483                 if (cstatus(user,c) < STATUS_HOP)
2484                 {
2485                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator",user->nick, c->name);
2486                         return;
2487                 }
2488
2489                 u->InviteTo(c->name);
2490                 WriteFrom(u->fd,user,"INVITE %s :%s",u->nick,c->name);
2491                 WriteServ(user->fd,"341 %s %s %s",user->nick,u->nick,c->name);
2492         }
2493 }
2494
2495 void handle_topic(char **parameters, int pcnt, userrec *user)
2496 {
2497         chanrec* Ptr;
2498
2499         if (pcnt == 1)
2500         {
2501                 if (strlen(parameters[0]) <= CHANMAX)
2502                 {
2503                         Ptr = FindChan(parameters[0]);
2504                         if (Ptr)
2505                         {
2506                                 if (Ptr->topicset)
2507                                 {
2508                                         WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
2509                                         WriteServ(user->fd,"333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset);
2510                                 }
2511                                 else
2512                                 {
2513                                         WriteServ(user->fd,"331 %s %s :No topic is set.", user->nick, Ptr->name);
2514                                 }
2515                         }
2516                         else
2517                         {
2518                                 WriteServ(user->fd,"331 %s %s :No topic is set.", user->nick, Ptr->name);
2519                         }
2520                 }
2521         }
2522         else if (pcnt>1)
2523         {
2524                 if (loop_call(handle_topic,parameters,pcnt,user,0,pcnt-2,0))
2525                         return;
2526                 if (strlen(parameters[0]) <= CHANMAX)
2527                 {
2528                         Ptr = FindChan(parameters[0]);
2529                         if (Ptr)
2530                         {
2531                                 if ((Ptr->topiclock) && (cstatus(user,Ptr)<STATUS_HOP))
2532                                 {
2533                                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator", user->nick, Ptr->name);
2534                                         return;
2535                                 }
2536                                 strcpy(Ptr->topic,parameters[1]);
2537                                 strcpy(Ptr->setby,user->nick);
2538                                 Ptr->topicset = time(NULL);
2539                                 WriteChannel(Ptr,user,"TOPIC %s :%s",Ptr->name, Ptr->topic);
2540                         }
2541                         else
2542                         {
2543                                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2544                         }
2545                 }
2546         }
2547 }
2548
2549 /* sends out an error notice to all connected clients (not to be used
2550  * lightly!) */
2551
2552 void send_error(char *s)
2553 {
2554         log(DEBUG,"send_error: %s",s);
2555         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2556         {
2557                 WriteServ(i->second->fd,"NOTICE %s :%s",i->second->nick,s);
2558         }
2559 }
2560
2561 void Error(int status)
2562 {
2563   signal (SIGALRM, SIG_IGN);
2564   signal (SIGPIPE, SIG_IGN);
2565   signal (SIGTERM, SIG_IGN);
2566   signal (SIGABRT, SIG_IGN);
2567   signal (SIGSEGV, SIG_IGN);
2568   signal (SIGURG, SIG_IGN);
2569   signal (SIGKILL, SIG_IGN);
2570   log(DEBUG,"*** fell down a pothole in the road to perfection ***");
2571   send_error("Error! Segmentation fault! save meeeeeeeeeeeeee *splat!*");
2572   exit(status);
2573 }
2574
2575 int main (int argc, char *argv[])
2576 {
2577         Start();
2578         log(DEBUG,"*** InspIRCd starting up!");
2579         if (!CheckConfig())
2580         {
2581                 log(DEBUG,"main: no config");
2582                 printf("ERROR: Your config file is missing, this IRCd will self destruct in 10 seconds!\n");
2583                 Exit(ERROR);
2584         }
2585         if (InspIRCd() == ERROR)
2586         {
2587                 log(DEBUG,"main: daemon function bailed");
2588                 printf("ERROR: could not initialise. Shutting down.\n");
2589                 Exit(ERROR);
2590         }
2591         Exit(TRUE);
2592         return 0;
2593 }
2594
2595 template<typename T> inline string ConvToStr(const T &in)
2596 {
2597         stringstream tmp;
2598         if (!(tmp << in)) return string();
2599         return tmp.str();
2600 }
2601
2602 /* re-allocates a nick in the user_hash after they change nicknames,
2603  * returns a pointer to the new user as it may have moved */
2604
2605 userrec* ReHashNick(char* Old, char* New)
2606 {
2607         user_hash::iterator newnick;
2608         user_hash::iterator oldnick = clientlist.find(Old);
2609
2610         log(DEBUG,"ReHashNick: %s %s",Old,New);
2611         
2612         if (!strcasecmp(Old,New))
2613         {
2614                 log(DEBUG,"old nick is new nick, skipping");
2615                 return oldnick->second;
2616         }
2617         
2618         if (oldnick == clientlist.end()) return NULL; /* doesnt exist */
2619
2620         log(DEBUG,"ReHashNick: Found hashed nick %s",Old);
2621
2622         clientlist[New] = new userrec();
2623         clientlist[New] = oldnick->second;
2624         /*delete oldnick->second; */
2625         clientlist.erase(oldnick);
2626
2627         log(DEBUG,"ReHashNick: Nick rehashed as %s",New);
2628         
2629         return clientlist[New];
2630 }
2631
2632 /* adds or updates an entry in the whowas list */
2633 void AddWhoWas(userrec* u)
2634 {
2635         user_hash::iterator iter = whowas.find(u->nick);
2636         userrec *a = new userrec();
2637         strcpy(a->nick,u->nick);
2638         strcpy(a->ident,u->ident);
2639         strcpy(a->dhost,u->dhost);
2640         strcpy(a->host,u->host);
2641         strcpy(a->fullname,u->fullname);
2642         strcpy(a->server,u->server);
2643         a->signon = u->signon;
2644
2645         /* MAX_WHOWAS:   max number of /WHOWAS items
2646          * WHOWAS_STALE: number of hours before a WHOWAS item is marked as stale and
2647          *               can be replaced by a newer one
2648          */
2649         
2650         if (iter == whowas.end())
2651         {
2652                 if (whowas.size() == WHOWAS_MAX)
2653                 {
2654                         for (user_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
2655                         {
2656                                 // 3600 seconds in an hour ;)
2657                                 if ((i->second->signon)<(time(NULL)-(WHOWAS_STALE*3600)))
2658                                 {
2659                                         delete i->second;
2660                                         i->second = a;
2661                                         log(DEBUG,"added WHOWAS entry, purged an old record");
2662                                         return;
2663                                 }
2664                         }
2665                 }
2666                 else
2667                 {
2668                         log(DEBUG,"added fresh WHOWAS entry");
2669                         whowas[a->nick] = a;
2670                 }
2671         }
2672         else
2673         {
2674                 log(DEBUG,"updated WHOWAS entry");
2675                 delete iter->second;
2676                 iter->second = a;
2677         }
2678 }
2679
2680
2681 /* add a client connection to the sockets list */
2682 void AddClient(int socket, char* host, int port, bool iscached)
2683 {
2684         int i;
2685         int blocking = 1;
2686         char resolved[MAXBUF];
2687         string tempnick;
2688         char tn2[MAXBUF];
2689         user_hash::iterator iter;
2690
2691         tempnick = ConvToStr(socket) + "-unknown";
2692         sprintf(tn2,"%d-unknown",socket);
2693
2694         iter = clientlist.find(tempnick);
2695
2696         if (iter != clientlist.end()) return;
2697
2698         /*
2699          * It is OK to access the value here this way since we know
2700          * it exists, we just created it above.
2701          *
2702          * At NO other time should you access a value in a map or a
2703          * hash_map this way.
2704          */
2705         clientlist[tempnick] = new userrec();
2706
2707         NonBlocking(socket);
2708         log(DEBUG,"AddClient: %d %s %d",socket,host,port);
2709
2710
2711         clientlist[tempnick]->fd = socket;
2712         strncpy(clientlist[tempnick]->nick, tn2,NICKMAX);
2713         strncpy(clientlist[tempnick]->host, host,160);
2714         strncpy(clientlist[tempnick]->dhost, host,160);
2715         strncpy(clientlist[tempnick]->server, ServerName,256);
2716         clientlist[tempnick]->registered = 0;
2717         clientlist[tempnick]->signon = time(NULL);
2718         clientlist[tempnick]->nping = time(NULL)+240;
2719         clientlist[tempnick]->lastping = 1;
2720         clientlist[tempnick]->port = port;
2721
2722         if (iscached)
2723         {
2724                 WriteServ(socket,"NOTICE Auth :Found your hostname (cached)...");
2725         }
2726         else
2727         {
2728                 WriteServ(socket,"NOTICE Auth :Looking up your hostname...");
2729         }
2730
2731         if (clientlist.size() == MAXCLIENTS)
2732                 kill_link(clientlist[tempnick],"No more connections allowed in this class");
2733 }
2734
2735 void handle_names(char **parameters, int pcnt, userrec *user)
2736 {
2737         chanrec* c;
2738
2739         if (loop_call(handle_names,parameters,pcnt,user,0,pcnt-1,0))
2740                 return;
2741         c = FindChan(parameters[0]);
2742         if (c)
2743         {
2744                 /*WriteServ(user->fd,"353 %s = %s :%s", user->nick, c->name,*/
2745                 userlist(user,c);
2746                 WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, c->name);
2747         }
2748         else
2749         {
2750                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2751         }
2752 }
2753
2754
2755 void handle_privmsg(char **parameters, int pcnt, userrec *user)
2756 {
2757         userrec *dest;
2758         chanrec *chan;
2759         
2760         if (loop_call(handle_privmsg,parameters,pcnt,user,0,pcnt-2,0))
2761                 return;
2762         if (parameters[0][0] == '#')
2763         {
2764                 chan = FindChan(parameters[0]);
2765                 if (chan)
2766                 {
2767                         if ((chan->noexternal) && (!has_channel(user,chan)))
2768                         {
2769                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
2770                                 return;
2771                         }
2772                         if ((chan->moderated) && (cstatus(user,chan)<STATUS_VOICE))
2773                         {
2774                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
2775                                 return;
2776                         }
2777                         ChanExceptSender(chan, user, "PRIVMSG %s :%s", chan->name, parameters[1]);
2778                 }
2779                 else
2780                 {
2781                         /* no such nick/channel */
2782                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2783                 }
2784                 return;
2785         }
2786         
2787         dest = Find(parameters[0]);
2788         if (dest)
2789         {
2790                 if (strcmp(dest->awaymsg,""))
2791                 {
2792                         /* auto respond with aweh msg */
2793                         WriteServ(user->fd,"301 %s %s :%s",user->nick,dest->nick,dest->awaymsg);
2794                 }
2795                 WriteTo(user, dest, "PRIVMSG %s :%s", dest->nick, parameters[1]);
2796         }
2797         else
2798         {
2799                 /* no such nick/channel */
2800                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2801         }
2802 }
2803
2804 void handle_notice(char **parameters, int pcnt, userrec *user)
2805 {
2806         userrec *dest;
2807         chanrec *chan;
2808
2809         if (loop_call(handle_notice,parameters,pcnt,user,0,pcnt-2,0))
2810                 return;
2811         if (parameters[0][0] == '#')
2812         {
2813                 chan = FindChan(parameters[0]);
2814                 if (chan)
2815                 {
2816                         if ((chan->noexternal) && (!has_channel(user,chan)))
2817                         {
2818                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
2819                                 return;
2820                         }
2821                         if ((chan->moderated) && (cstatus(user,chan)<STATUS_VOICE))
2822                         {
2823                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
2824                                 return;
2825                         }
2826                         WriteChannel(chan, user, "NOTICE %s :%s", chan->name, parameters[1]);
2827                 }
2828                 else
2829                 {
2830                         /* no such nick/channel */
2831                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2832                 }
2833                 return;
2834         }
2835         
2836         dest = Find(parameters[0]);
2837         if (dest)
2838         {
2839                 WriteTo(user, dest, "NOTICE %s :%s", dest->nick, parameters[1]);
2840         }
2841         else
2842         {
2843                 /* no such nick/channel */
2844                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2845         }
2846 }
2847
2848 char lst[MAXBUF];
2849
2850 char* chlist(userrec *user)
2851 {
2852         int i = 0;
2853         char cmp[MAXBUF];
2854
2855         log(DEBUG,"chlist: %s",user->nick);
2856         strcpy(lst,"");
2857         if (!user)
2858         {
2859                 return lst;
2860         }
2861         for (i = 0; i != MAXCHANS; i++)
2862         {
2863                 if (user->chans[i].channel != NULL)
2864                 {
2865                         if (user->chans[i].channel->name)
2866                         {
2867                                 strcpy(cmp,user->chans[i].channel->name);
2868                                 strcat(cmp," ");
2869                                 if (!strstr(lst,cmp))
2870                                 {
2871                                         if ((!user->chans[i].channel->c_private) && (!user->chans[i].channel->secret))
2872                                         {
2873                                                 strcat(lst,cmode(user,user->chans[i].channel));
2874                                                 strcat(lst,user->chans[i].channel->name);
2875                                                 strcat(lst," ");
2876                                         }
2877                                 }
2878                         }
2879                 }
2880         }
2881         return lst;
2882 }
2883
2884 void handle_info(char **parameters, int pcnt, userrec *user)
2885 {
2886         WriteServ(user->fd,"371 %s :The Inspire IRCd Project Has been brought to you by the following people..",user->nick);
2887         WriteServ(user->fd,"371 %s :Craig Edwards, Craig McLure, and Others..",user->nick);
2888         WriteServ(user->fd,"371 %s :Will finish this later when i can be arsed :p",user->nick);
2889         WriteServ(user->fd,"374 %s :End of /INFO list",user->nick);
2890 }
2891
2892 void handle_time(char **parameters, int pcnt, userrec *user)
2893 {
2894         time_t rawtime;
2895         struct tm * timeinfo;
2896
2897         time ( &rawtime );
2898         timeinfo = localtime ( &rawtime );
2899         WriteServ(user->fd,"391 %s %s :%s",user->nick,ServerName, asctime (timeinfo) );
2900   
2901 }
2902
2903 void handle_whois(char **parameters, int pcnt, userrec *user)
2904 {
2905         userrec *dest;
2906         char *t;
2907
2908         if (loop_call(handle_whois,parameters,pcnt,user,0,pcnt-1,0))
2909                 return;
2910         dest = Find(parameters[0]);
2911         if (dest)
2912         {
2913                 WriteServ(user->fd,"311 %s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname);
2914                 if ((user == dest) || (strchr(user->modes,'o')))
2915                 {
2916                         WriteServ(user->fd,"378 %s %s :is connecting from *@%s",user->nick, dest->nick, dest->host);
2917                 }
2918                 if (strcmp(chlist(dest),""))
2919                 {
2920                         WriteServ(user->fd,"319 %s %s :%s",user->nick, dest->nick, chlist(dest));
2921                 }
2922                 WriteServ(user->fd,"312 %s %s %s :%s",user->nick, dest->nick, dest->server, ServerDesc);
2923                 if (strcmp(dest->awaymsg,""))
2924                 {
2925                         WriteServ(user->fd,"301 %s %s :%s",user->nick, dest->nick, dest->awaymsg);
2926                 }
2927                 if (strchr(dest->modes,'o'))
2928                 {
2929                         WriteServ(user->fd,"313 %s %s :is an IRC operator",user->nick, dest->nick);
2930                 }
2931                 //WriteServ(user->fd,"310 %s %s :is available for help.",user->nick, dest->nick);
2932                 WriteServ(user->fd,"317 %s %s %d %d :seconds idle, signon time",user->nick, dest->nick, abs((dest->idle_lastmsg)-time(NULL)), dest->signon);
2933                 
2934                 WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, dest->nick);
2935         }
2936         else
2937         {
2938                 /* no such nick/channel */
2939                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
2940         }
2941 }
2942
2943 void handle_quit(char **parameters, int pcnt, userrec *user)
2944 {
2945         user_hash::iterator iter = clientlist.find(user->nick);
2946
2947         /* theres more to do here, but for now just close the socket */
2948         if (pcnt == 1)
2949         {
2950                 if (parameters[0][0] == ':')
2951                 {
2952                         *parameters[0]++;
2953                 }
2954                 Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,parameters[0]);
2955                 WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,parameters[0]);
2956                 WriteCommonExcept(user,"QUIT :%s%s",PrefixQuit,parameters[0]);
2957         }
2958         else
2959         {
2960                 Write(user->fd,"ERROR :Closing link (%s@%s) [QUIT]",user->ident,user->host);
2961                 WriteOpers("*** Client exiting: %s!%s@%s [Client exited]",user->nick,user->ident,user->host);
2962                 WriteCommonExcept(user,"QUIT :Client exited");
2963         }
2964
2965         FOREACH_MOD OnUserQuit(user);
2966
2967         /* confucious say, he who close nonblocking socket, get nothing! */
2968         Blocking(user->fd);
2969         close(user->fd);
2970         NonBlocking(user->fd);
2971         AddWhoWas(user);
2972
2973         if (iter != clientlist.end())
2974         {
2975                 log(DEBUG,"deleting user hash value");
2976                 delete iter->second;
2977                 clientlist.erase(iter);
2978         }
2979         
2980         purge_empty_chans();
2981 }
2982
2983 void handle_who(char **parameters, int pcnt, userrec *user)
2984 {
2985         chanrec* Ptr;
2986         
2987         /* theres more to do here, but for now just close the socket */
2988         if (pcnt == 1)
2989         {
2990                 if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*")))
2991                 {
2992                         Ptr = user->chans[0].channel;
2993                         printf(user->chans[0].channel->name);
2994                         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
2995                         {
2996                                 if ((common_channels(user,i->second)) && (isnick(i->second->nick)))
2997                                 {
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);
2999                                 }
3000                         }
3001                         WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
3002                         return;
3003                 }
3004                 if (parameters[0][0] = '#')
3005                 {
3006                         Ptr = FindChan(parameters[0]);
3007                         if (Ptr)
3008                         {
3009                                 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3010                                 {
3011                                         if ((has_channel(i->second,Ptr)) && (isnick(i->second->nick)))
3012                                         {
3013                                                 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);
3014                                         }
3015                                 }
3016                                 WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
3017                         }
3018                         else
3019                         {
3020                                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
3021                         }
3022                 }
3023         }
3024         if (pcnt == 2)
3025         {
3026                 if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*")) && (!strcmp(parameters[1],"o")))
3027                 {
3028                         Ptr = user->chans[0].channel;
3029                         printf(user->chans[0].channel->name);
3030                         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3031                         {
3032                                 if ((common_channels(user,i->second)) && (isnick(i->second->nick)))
3033                                 {
3034                                         if (strchr(i->second->modes,'o'))
3035                                         {
3036                                                 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);
3037                                         }
3038                                 }
3039                         }
3040                         WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
3041                         return;
3042                 }
3043         }
3044 }
3045
3046 void handle_wallops(char **parameters, int pcnt, userrec *user)
3047 {
3048         WriteWallOps(user,"%s",parameters[0]);
3049 }
3050
3051 void handle_list(char **parameters, int pcnt, userrec *user)
3052 {
3053         chanrec* Ptr;
3054         
3055         WriteServ(user->fd,"321 %s Channel :Users Name",user->nick);
3056         for (chan_hash::const_iterator i = chanlist.begin(); i != chanlist.end(); i++)
3057         {
3058                 if ((!i->second->c_private) && (!i->second->secret))
3059                 {
3060                         WriteServ(user->fd,"322 %s %s %d :[+%s] %s",user->nick,i->second->name,usercount_i(i->second),chanmodes(i->second),i->second->topic);
3061                 }
3062         }
3063         WriteServ(user->fd,"323 %s :End of channel list.",user->nick);
3064 }
3065
3066
3067 void handle_rehash(char **parameters, int pcnt, userrec *user)
3068 {
3069         WriteServ(user->fd,"382 %s %s :Rehashing",user->nick,CONFIG_FILE);
3070         ReadConfig();
3071         FOREACH_MOD OnRehash();
3072         WriteOpers("%s is rehashing config file %s",user->nick,CONFIG_FILE);
3073 }
3074
3075
3076 int usercnt(void)
3077 {
3078         return clientlist.size();
3079 }
3080
3081 int usercount_invisible(void)
3082 {
3083         int c = 0;
3084
3085         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3086         {
3087                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'i'))) c++;
3088         }
3089         return c;
3090 }
3091
3092 int usercount_opers(void)
3093 {
3094         int c = 0;
3095
3096         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3097         {
3098                 if ((i->second->fd) && (isnick(i->second->nick)) && (strchr(i->second->modes,'o'))) c++;
3099         }
3100         return c;
3101 }
3102
3103 int usercount_unknown(void)
3104 {
3105         int c = 0;
3106
3107         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
3108         {
3109                 if ((i->second->fd) && (i->second->registered != 7))
3110                         c++;
3111         }
3112         return c;
3113 }
3114
3115 int chancount(void)
3116 {
3117         return chanlist.size();
3118 }
3119
3120 int servercount(void)
3121 {
3122         return 1;
3123 }
3124
3125 void handle_lusers(char **parameters, int pcnt, userrec *user)
3126 {
3127         WriteServ(user->fd,"251 %s :There are %d users and %d invisible on %d servers",user->nick,usercnt()-usercount_invisible(),usercount_invisible(),servercount());
3128         WriteServ(user->fd,"252 %s %d :operator(s) online",user->nick,usercount_opers());
3129         WriteServ(user->fd,"253 %s %d :unknown connections",user->nick,usercount_unknown());
3130         WriteServ(user->fd,"254 %s %d :channels formed",user->nick,chancount());
3131         WriteServ(user->fd,"254 %s :I have %d clients and 0 servers",user->nick,usercnt());
3132 }
3133
3134 void handle_admin(char **parameters, int pcnt, userrec *user)
3135 {
3136         WriteServ(user->fd,"256 %s :Administrative info for %s",user->nick,ServerName);
3137         WriteServ(user->fd,"257 %s :Name     - %s",user->nick,AdminName);
3138         WriteServ(user->fd,"258 %s :Nickname - %s",user->nick,AdminNick);
3139         WriteServ(user->fd,"258 %s :E-Mail   - %s",user->nick,AdminEmail);
3140 }
3141
3142 void ShowMOTD(userrec *user)
3143 {
3144         if (!MOTD.size())
3145         {
3146                 WriteServ(user->fd,"422 %s :Message of the day file is missing.",user->nick);
3147                 return;
3148         }
3149         WriteServ(user->fd,"375 %s :- %s message of the day",user->nick,ServerName);
3150         for (int i = 0; i != MOTD.size(); i++)
3151         {
3152                                 WriteServ(user->fd,"372 %s :- %s",user->nick,MOTD[i].c_str());
3153         }
3154         WriteServ(user->fd,"376 %s :End of %s message of the day.",user->nick,ServerName);
3155 }
3156
3157 void ShowRULES(userrec *user)
3158 {
3159         if (!RULES.size())
3160         {
3161                 WriteServ(user->fd,"NOTICE %s :Rules file is missing.",user->nick);
3162                 return;
3163         }
3164         WriteServ(user->fd,"NOTICE %s :%s rules",user->nick,ServerName);
3165         for (int i = 0; i != RULES.size(); i++)
3166         {
3167                                 WriteServ(user->fd,"NOTICE %s :%s",user->nick,RULES[i].c_str());
3168         }
3169         WriteServ(user->fd,"NOTICE %s :End of %s rules.",user->nick,ServerName);
3170 }
3171
3172 /* shows the message of the day, and any other on-logon stuff */
3173 void ConnectUser(userrec *user)
3174 {
3175         user->registered = 7;
3176         user->idle_lastmsg = time(NULL);
3177         log(DEBUG,"ConnectUser: %s",user->nick);
3178
3179         if (strcmp(Passwd(user),"") && (!user->haspassed))
3180         {
3181                 Write(user->fd,"ERROR :Closing link: Invalid password");
3182                 fdatasync(user->fd);
3183                 kill_link(user,"Invalid password");
3184                 return;
3185         }
3186         if (IsDenied(user))
3187         {
3188                 Write(user->fd,"ERROR :Closing link: Unauthorized connection");
3189                 fdatasync(user->fd);
3190                 kill_link(user,"Unauthorised connection");
3191         }
3192
3193         WriteServ(user->fd,"NOTICE Auth :Welcome to \002%s\002!",Network);
3194         WriteServ(user->fd,"001 %s :Welcome to the %s IRC Network %s!%s@%s",user->nick,Network,user->nick,user->ident,user->host);
3195         WriteServ(user->fd,"002 %s :Your host is %s, running version %s",user->nick,ServerName,VERSION);
3196         WriteServ(user->fd,"003 %s :This server was created %s %s",user->nick,__TIME__,__DATE__);
3197         WriteServ(user->fd,"004 %s :%s %s iowghraAsORVSxNCWqBzvdHtGI lvhopsmntikrRcaqOALQbSeKVfHGCuzN",user->nick,ServerName,VERSION);
3198         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);
3199         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);
3200         ShowMOTD(user);
3201         FOREACH_MOD OnUserConnect(user);
3202         WriteOpers("*** Client connecting on port %d: %s!%s@%s",user->port,user->nick,user->ident,user->host);
3203 }
3204
3205 void handle_version(char **parameters, int pcnt, userrec *user)
3206 {
3207         WriteServ(user->fd,"351 %s :%s %s :%s",user->nick,VERSION,ServerName,SYSTEM);
3208 }
3209
3210 void handle_ping(char **parameters, int pcnt, userrec *user)
3211 {
3212         WriteServ(user->fd,"PONG %s :%s",ServerName,parameters[0]);
3213 }
3214
3215 void handle_pong(char **parameters, int pcnt, userrec *user)
3216 {
3217         // set the user as alive so they survive to next ping
3218         user->lastping = 1;
3219 }
3220
3221 void handle_motd(char **parameters, int pcnt, userrec *user)
3222 {
3223         ShowMOTD(user);
3224 }
3225
3226 void handle_rules(char **parameters, int pcnt, userrec *user)
3227 {
3228         ShowRULES(user);
3229 }
3230
3231 void handle_user(char **parameters, int pcnt, userrec *user)
3232 {
3233         if (user->registered < 3)
3234         {
3235                 WriteServ(user->fd,"NOTICE Auth :No ident response, ident prefixed with ~");
3236                 strcpy(user->ident,"~"); /* we arent checking ident... but these days why bother anyway? */
3237                 strncat(user->ident,parameters[0],IDENTMAX);
3238                 strncpy(user->fullname,parameters[3],128);
3239                 user->registered = (user->registered | 1);
3240         }
3241         else
3242         {
3243                 WriteServ(user->fd,"462 %s :You may not reregister",user->nick);
3244                 return;
3245         }
3246         /* parameters 2 and 3 are local and remote hosts, ignored when sent by client connection */
3247         if (user->registered == 3)
3248         {
3249                 /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
3250                 ConnectUser(user);
3251         }
3252 }
3253
3254 void handle_userhost(char **parameters, int pcnt, userrec *user)
3255 {
3256         char Return[MAXBUF],junk[MAXBUF];
3257         sprintf(Return,"302 %s :",user->nick);
3258         for (int i = 0; i < pcnt; i++)
3259         {
3260                 userrec *u = Find(parameters[i]);
3261                 if (u)
3262                 {
3263                         if (strchr(u->modes,'o'))
3264                         {
3265                                 sprintf(junk,"%s*=+%s@%s ",u->nick,u->ident,u->host);
3266                                 strcat(Return,junk);
3267                         }
3268                         else
3269                         {
3270                                 sprintf(junk,"%s=+%s@%s ",u->nick,u->ident,u->host);
3271                                 strcat(Return,junk);
3272                         }
3273                 }
3274         }
3275         WriteServ(user->fd,Return);
3276 }
3277
3278
3279 void handle_ison(char **parameters, int pcnt, userrec *user)
3280 {
3281         char Return[MAXBUF];
3282         sprintf(Return,"303 %s :",user->nick);
3283         for (int i = 0; i < pcnt; i++)
3284         {
3285                 userrec *u = Find(parameters[i]);
3286                 if (u)
3287                 {
3288                         strcat(Return,u->nick);
3289                         strcat(Return," ");
3290                 }
3291         }
3292         WriteServ(user->fd,Return);
3293 }
3294
3295
3296 void handle_away(char **parameters, int pcnt, userrec *user)
3297 {
3298         if (pcnt)
3299         {
3300                 strcpy(user->awaymsg,parameters[0]);
3301                 WriteServ(user->fd,"306 %s :You have been marked as being away",user->nick);
3302         }
3303         else
3304         {
3305                 strcpy(user->awaymsg,"");
3306                 WriteServ(user->fd,"305 %s :You are no longer marked as being away",user->nick);
3307         }
3308 }
3309
3310 void handle_whowas(char **parameters, int pcnt, userrec* user)
3311 {
3312         user_hash::iterator i = whowas.find(parameters[0]);
3313
3314         if (i == whowas.end())
3315         {
3316                 WriteServ(user->fd,"406 %s %s :There was no such nickname",user->nick,parameters[0]);
3317                 WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]);
3318         }
3319         else
3320         {
3321                 time_t rawtime = i->second->signon;
3322                 tm *timeinfo;
3323                 char b[MAXBUF];
3324                 
3325                 timeinfo = localtime(&rawtime);
3326                 strcpy(b,asctime(timeinfo));
3327                 b[strlen(b)-1] = '\0';
3328                 
3329                 WriteServ(user->fd,"314 %s %s %s %s * :%s",user->nick,i->second->nick,i->second->ident,i->second->dhost,i->second->fullname);
3330                 WriteServ(user->fd,"312 %s %s %s :%s",user->nick,i->second->nick,i->second->server,b);
3331                 WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]);
3332         }
3333
3334 }
3335
3336 void handle_trace(char **parameters, int pcnt, userrec *user)
3337 {
3338         for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
3339         {
3340                 if (i->second)
3341                 {
3342                         if (isnick(i->second->nick))
3343                         {
3344                                 if (strchr(i->second->modes,'o'))
3345                                 {
3346                                         WriteServ(user->fd,"205 %s :Oper 0 %s",user->nick,i->second->nick);
3347                                 }
3348                                 else
3349                                 {
3350                                         WriteServ(user->fd,"204 %s :User 0 %s",user->nick,i->second->nick);
3351                                 }
3352                         }
3353                         else
3354                         {
3355                                 WriteServ(user->fd,"203 %s :???? 0 [%s]",user->nick,i->second->host);
3356                         }
3357                 }
3358         }
3359 }
3360
3361 void handle_stats(char **parameters, int pcnt, userrec *user)
3362 {
3363         if (pcnt != 1)
3364         {
3365                 return;
3366         }
3367         if (strlen(parameters[0])>1)
3368         {
3369                 /* make the stats query 1 character long */
3370                 parameters[0][1] = '\0';
3371         }
3372
3373         /* stats m (list number of times each command has been used, plus bytecount) */
3374         if (!strcasecmp(parameters[0],"m"))
3375         {
3376                 for (int i = 0; i < cmdlist.size(); i++)
3377                 {
3378                         if (cmdlist[i].handler_function)
3379                         {
3380                                 if (cmdlist[i].use_count)
3381                                 {
3382                                         /* RPL_STATSCOMMANDS */
3383                                         WriteServ(user->fd,"212 %s %s %d %d",user->nick,cmdlist[i].command,cmdlist[i].use_count,cmdlist[i].total_bytes);
3384                                 }
3385                         }
3386                 }
3387                         
3388         }
3389
3390         /* stats z (debug and memory info) */
3391         if (!strcasecmp(parameters[0],"z"))
3392         {
3393                 WriteServ(user->fd,"249 %s :Users(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,clientlist.size(),clientlist.size()*sizeof(userrec),clientlist.bucket_count());
3394                 WriteServ(user->fd,"249 %s :Channels(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,chanlist.size(),chanlist.size()*sizeof(chanrec),chanlist.bucket_count());
3395                 WriteServ(user->fd,"249 %s :Commands(VECTOR) %d (%d bytes)",user->nick,cmdlist.size(),cmdlist.size()*sizeof(command_t));
3396                 WriteServ(user->fd,"249 %s :MOTD(VECTOR) %d, RULES(VECTOR) %d",user->nick,MOTD.size(),RULES.size());
3397                 WriteServ(user->fd,"249 %s :address_cache(HASH_MAP) %d (%d buckets)",user->nick,IP.size(),IP.bucket_count());
3398                 WriteServ(user->fd,"249 %s :Modules(VECTOR) %d (%d)",user->nick,modules.size(),modules.size()*sizeof(Module));
3399                 WriteServ(user->fd,"249 %s :ClassFactories(VECTOR) %d (%d)",user->nick,factory.size(),factory.size()*sizeof(ircd_module));
3400                 WriteServ(user->fd,"249 %s :Ports(STATIC_ARRAY) %d",user->nick,boundPortCount);
3401         }
3402         
3403         /* stats o */
3404         if (!strcasecmp(parameters[0],"o"))
3405         {
3406                 for (int i = 0; i < ConfValueEnum("oper"); i++)
3407                 {
3408                         char LoginName[MAXBUF];
3409                         char HostName[MAXBUF];
3410                         char OperType[MAXBUF];
3411                         ConfValue("oper","name",i,LoginName);
3412                         ConfValue("oper","host",i,HostName);
3413                         ConfValue("oper","type",i,OperType);
3414                         WriteServ(user->fd,"243 %s O %s * %s %s 0",user->nick,HostName,LoginName,OperType);
3415                 }
3416         }
3417         
3418         /* stats l (show user I/O stats) */
3419         if (!strcasecmp(parameters[0],"l"))
3420         {
3421                 WriteServ(user->fd,"211 %s :server:port nick bytes_in cmds_in bytes_out cmds_out",user->nick);
3422                 for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
3423                 {
3424                         if (isnick(i->second->nick))
3425                         {
3426                                 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);
3427                         }
3428                         else
3429                         {
3430                                 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);
3431                         }
3432                         
3433                 }
3434         }
3435         
3436         /* stats u (show server uptime) */
3437         if (!strcasecmp(parameters[0],"u"))
3438         {
3439                 time_t current_time = 0;
3440                 current_time = time(NULL);
3441                 time_t server_uptime = current_time - startup_time;
3442                 struct tm* stime;
3443                 stime = gmtime(&server_uptime);
3444                 /* i dont know who the hell would have an ircd running for over a year nonstop, but
3445                  * Craig suggested this, and it seemed a good idea so in it went */
3446                 if (stime->tm_year > 70)
3447                 {
3448                         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);
3449                 }
3450                 else
3451                 {
3452                         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);
3453                 }
3454         }
3455
3456         WriteServ(user->fd,"219 %s %s :End of /STATS report",user->nick,parameters[0]);
3457         WriteOpers("*** Notice: Stats '%s' requested by %s (%s@%s)",parameters[0],user->nick,user->ident,user->host);
3458         
3459 }
3460
3461 void handle_connect(char **parameters, int pcnt, userrec *user)
3462 {
3463         WriteServ(user->fd,"NOTICE %s :*** Connecting to %s port %s...",user->nick,parameters[0],parameters[1]);
3464         if (!me[defaultRoute]->BeginLink(parameters[0],atoi(parameters[1]),"password"))
3465         {
3466                 WriteServ(user->fd,"NOTICE %s :*** Failed to send auth packet to %s!",user->nick,parameters[0]);
3467         }
3468 }
3469
3470 void handle_squit(char **parameters, int pcnt, userrec *user)
3471 {
3472 }
3473
3474 void handle_oper(char **parameters, int pcnt, userrec *user)
3475 {
3476         char LoginName[MAXBUF];
3477         char Password[MAXBUF];
3478         char OperType[MAXBUF];
3479         char TypeName[MAXBUF];
3480         char Hostname[MAXBUF];
3481         int i,j;
3482
3483         for (i = 0; i < ConfValueEnum("oper"); i++)
3484         {
3485                 ConfValue("oper","name",i,LoginName);
3486                 ConfValue("oper","password",i,Password);
3487                 if ((!strcmp(LoginName,parameters[0])) && (!strcmp(Password,parameters[1])))
3488                 {
3489                         /* correct oper credentials */
3490                         ConfValue("oper","type",i,OperType);
3491                         WriteOpers("*** %s (%s@%s) is now an IRC operator of type %s",user->nick,user->ident,user->host,OperType);
3492                         WriteServ(user->fd,"381 %s :You are now an IRC operator of type %s",user->nick,OperType);
3493                         WriteServ(user->fd,"MODE %s :+o",user->nick);
3494                         for (j =0; j < ConfValueEnum("type"); j++)
3495                         {
3496                                 ConfValue("type","name",j,TypeName);
3497                                 if (!strcmp(TypeName,OperType))
3498                                 {
3499                                         /* found this oper's opertype */
3500                                         ConfValue("type","host",j,Hostname);
3501                                         strncpy(user->dhost,Hostname,256);
3502                                 }
3503                         }
3504                         if (!strchr(user->modes,'o'))
3505                         {
3506                                 strcat(user->modes,"o");
3507                         }
3508                         return;
3509                 }
3510         }
3511         /* no such oper */
3512         WriteServ(user->fd,"491 %s :Invalid oper credentials",user->nick);
3513         WriteOpers("*** WARNING! Failed oper attempt by %s!%s@%s!",user->nick,user->ident,user->host);
3514 }
3515                                 
3516 void handle_nick(char **parameters, int pcnt, userrec *user)
3517 {
3518         if (pcnt < 1) 
3519         {
3520                 log(DEBUG,"not enough params for handle_nick");
3521                 return;
3522         }
3523         if (!parameters[0])
3524         {
3525                 log(DEBUG,"invalid parameter passed to handle_nick");
3526                 return;
3527         }
3528         if (!strlen(parameters[0]))
3529         {
3530                 log(DEBUG,"zero length new nick passed to handle_nick");
3531                 return;
3532         }
3533         if (!user)
3534         {
3535                 log(DEBUG,"invalid user passed to handle_nick");
3536                 return;
3537         }
3538         if (!user->nick)
3539         {
3540                 log(DEBUG,"invalid old nick passed to handle_nick");
3541                 return;
3542         }
3543         if (!strcasecmp(user->nick,parameters[0]))
3544         {
3545                 log(DEBUG,"old nick is new nick, skipping");
3546                 return;
3547         }
3548         else
3549         {
3550                 if (strlen(parameters[0]) > 1)
3551                 {
3552                         if (parameters[0][0] == ':')
3553                         {
3554                                 *parameters[0]++;
3555                         }
3556                 }
3557                 if ((Find(parameters[0])) && (Find(parameters[0]) != user))
3558                 {
3559                         WriteServ(user->fd,"433 %s %s :Nickname is already in use.",user->nick,parameters[0]);
3560                         return;
3561                 }
3562         }
3563         if (isnick(parameters[0]) == 0)
3564         {
3565                 WriteServ(user->fd,"432 %s %s :Erroneous Nickname",user->nick,parameters[0]);
3566                 return;
3567         }
3568
3569         if (user->registered == 7)
3570         {
3571                 WriteCommon(user,"NICK %s",parameters[0]);
3572         }
3573         
3574         /* change the nick of the user in the users_hash */
3575         user = ReHashNick(user->nick, parameters[0]);
3576         /* actually change the nick within the record */
3577         if (!user) return;
3578         if (!user->nick) return;
3579
3580         strncpy(user->nick, parameters[0],NICKMAX);
3581
3582         log(DEBUG,"new nick set: %s",user->nick);
3583         
3584         if (user->registered < 3)
3585                 user->registered = (user->registered | 2);
3586         if (user->registered == 3)
3587         {
3588                 /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
3589                 ConnectUser(user);
3590         }
3591         log(DEBUG,"exit nickchange: %s",user->nick);
3592 }
3593
3594 int process_parameters(char **command_p,char *parameters)
3595 {
3596         int i = 0;
3597         int j = 0;
3598         int q = 0;
3599         q = strlen(parameters);
3600         if (!q)
3601         {
3602                 /* no parameters, command_p invalid! */
3603                 return 0;
3604         }
3605         if (parameters[0] == ':')
3606         {
3607                 command_p[0] = parameters+1;
3608                 return 1;
3609         }
3610         if (q)
3611         {
3612                 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
3613                 {
3614                         /* only one parameter */
3615                         command_p[0] = parameters;
3616                         if (parameters[0] == ':')
3617                         {
3618                                 if (strchr(parameters,' ') != NULL)
3619                                 {
3620                                         command_p[0]++;
3621                                 }
3622                         }
3623                         return 1;
3624                 }
3625         }
3626         command_p[j++] = parameters;
3627         for (i = 0; i <= q; i++)
3628         {
3629                 if (parameters[i] == ' ')
3630                 {
3631                         command_p[j++] = parameters+i+1;
3632                         parameters[i] = '\0';
3633                         if (command_p[j-1][0] == ':')
3634                         {
3635                                 *command_p[j-1]++; /* remove dodgy ":" */
3636                                 break;
3637                                 /* parameter like this marks end of the sequence */
3638                         }
3639                 }
3640         }
3641         return j; /* returns total number of items in the list */
3642 }
3643
3644 void process_command(userrec *user, char* cmd)
3645 {
3646         char *parameters;
3647         char *command;
3648         char *command_p[127];
3649         char p[MAXBUF], temp[MAXBUF];
3650         int i, j, items, cmd_found;
3651
3652         for (int i = 0; i < 127; i++)
3653                 command_p[i] = NULL;
3654
3655         if (!user)
3656         {
3657                 return;
3658         }
3659         if (!cmd)
3660         {
3661                 return;
3662         }
3663         if (!strcmp(cmd,""))
3664         {
3665                 return;
3666         }
3667         strcpy(temp,cmd);
3668         if (!strchr(cmd,' '))
3669         {
3670                 /* no parameters, lets skip the formalities and not chop up
3671                  * the string */
3672                 items = 0;
3673                 command_p[0] = NULL;
3674                 parameters = NULL;
3675                 for (int i = 0; i <= strlen(cmd); i++)
3676                 {
3677                         cmd[i] = toupper(cmd[i]);
3678                 }
3679         }
3680         else
3681         {
3682                 strcpy(cmd,"");
3683                 j = 0;
3684                 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
3685                 for (i = 0; i < strlen(temp); i++)
3686                 {
3687                         if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
3688                         {
3689                                 cmd[j++] = temp[i];
3690                                 cmd[j] = 0;
3691                         }
3692                 }
3693                 /* split the full string into a command plus parameters */
3694                 parameters = p;
3695                 strcpy(p," ");
3696                 command = cmd;
3697                 if (strchr(cmd,' '))
3698                 {
3699                         for (i = 0; i <= strlen(cmd); i++)
3700                         {
3701                                 /* capitalise the command ONLY, leave params intact */
3702                                 cmd[i] = toupper(cmd[i]);
3703                                 /* are we nearly there yet?! :P */
3704                                 if (cmd[i] == ' ')
3705                                 {
3706                                         command = cmd;
3707                                         parameters = cmd+i+1;
3708                                         cmd[i] = '\0';
3709                                         break;
3710                                 }
3711                         }
3712                 }
3713                 else
3714                 {
3715                         for (i = 0; i <= strlen(cmd); i++)
3716                         {
3717                                 cmd[i] = toupper(cmd[i]);
3718                         }
3719                 }
3720
3721         }
3722         
3723         cmd_found = 0;
3724
3725         for (i = 0; i != cmdlist.size(); i++)
3726         {
3727                 if (strcmp(cmdlist[i].command,""))
3728                 {
3729                         if (!strcmp(command, cmdlist[i].command))
3730                         {
3731                                 if (parameters)
3732                                 {
3733                                         if (strcmp(parameters,""))
3734                                         {
3735                                                 items = process_parameters(command_p,parameters);
3736                                         }
3737                                         else
3738                                         {
3739                                                 items = 0;
3740                                                 command_p[0] = NULL;
3741                                         }
3742                                 }
3743                                 else
3744                                 {
3745                                         items = 0;
3746                                         command_p[0] = NULL;
3747                                 }
3748                                 
3749                                 if (user)
3750                                 {
3751                                         user->idle_lastmsg = time(NULL);
3752                                         /* activity resets the ping pending timer */
3753                                         user->nping = time(NULL) + 120;
3754                                         if ((items) < cmdlist[i].min_params)
3755                                         {
3756                                                 log(DEBUG,"process_command: not enough parameters: %s %s",user->nick,command);
3757                                                 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
3758                                                 return;
3759                                         }
3760                                         if ((!strchr(user->modes,cmdlist[i].flags_needed)) && (cmdlist[i].flags_needed))
3761                                         {
3762                                                 log(DEBUG,"process_command: permission denied: %s %s",user->nick,command);
3763                                                 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
3764                                                 cmd_found = 1;
3765                                                 return;
3766                                         }
3767                 /* if the command isnt USER, PASS, or NICK, and nick is empty,
3768                  * deny command! */
3769                                         if ((strcmp(command,"USER")) && (strcmp(command,"NICK")) && (strcmp(command,"PASS")))
3770                                         {
3771                                                 if ((!isnick(user->nick)) || (user->registered != 7))
3772                                                 {
3773                                                         log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
3774                                                         WriteServ(user->fd,"451 %s :You have not registered",command);
3775                                                         return;
3776                                                 }
3777                                         }
3778                                         if ((user->registered == 7) || (!strcmp(command,"USER")) || (!strcmp(command,"NICK")) || (!strcmp(command,"PASS")))
3779                                         {
3780                                                 log(DEBUG,"process_command: handler: %s %s %d",user->nick,command,items);
3781                                                 if (cmdlist[i].handler_function)
3782                                                 {
3783                                                         /* ikky /stats counters */
3784                                                         if (temp)
3785                                                         {
3786                                                                 if (user)
3787                                                                 {
3788                                                                         user->bytes_in += strlen(temp);
3789                                                                         user->cmds_in++;
3790                                                                 }
3791                                                                 cmdlist[i].use_count++;
3792                                                                 cmdlist[i].total_bytes+=strlen(temp);
3793                                                         }
3794
3795                                                         /* WARNING: nothing may come after the
3796                                                          * command handler call, as the handler
3797                                                          * may free the user structure! */
3798
3799                                                         cmdlist[i].handler_function(command_p,items,user);
3800                                                 }
3801                                                 return;
3802                                         }
3803                                         else
3804                                         {
3805                                                 log(DEBUG,"process_command: not registered: %s %s",user->nick,command);
3806                                                 WriteServ(user->fd,"451 %s :You have not registered",command);
3807                                                 return;
3808                                         }
3809                                 }
3810                                 cmd_found = 1;
3811                         }
3812                 }
3813         }
3814         if ((!cmd_found) && (user))
3815         {
3816                 log(DEBUG,"process_command: not in table: %s %s",user->nick,command);
3817                 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
3818         }
3819 }
3820
3821
3822 void createcommand(char* cmd, handlerfunc f, char flags, int minparams)
3823 {
3824         command_t comm;
3825         /* create the command and push it onto the table */     
3826         strcpy(comm.command,cmd);
3827         comm.handler_function = f;
3828         comm.flags_needed = flags;
3829         comm.min_params = minparams;
3830         comm.use_count = 0;
3831         comm.total_bytes = 0;
3832         cmdlist.push_back(comm);
3833 }
3834
3835 void SetupCommandTable(void)
3836 {
3837   createcommand("USER",handle_user,0,4);
3838   createcommand("NICK",handle_nick,0,1);
3839   createcommand("QUIT",handle_quit,0,1);
3840   createcommand("VERSION",handle_version,0,0);
3841   createcommand("PING",handle_ping,0,1);
3842   createcommand("PONG",handle_pong,0,1);
3843   createcommand("ADMIN",handle_admin,0,0);
3844   createcommand("PRIVMSG",handle_privmsg,0,2);
3845   createcommand("INFO",handle_info,0,0);
3846   createcommand("TIME",handle_time,0,0);
3847   createcommand("WHOIS",handle_whois,0,1);
3848   createcommand("WALLOPS",handle_wallops,'o',1);
3849   createcommand("NOTICE",handle_notice,0,2);
3850   createcommand("JOIN",handle_join,0,1);
3851   createcommand("NAMES",handle_names,0,1);
3852   createcommand("PART",handle_part,0,1);
3853   createcommand("KICK",handle_kick,0,2);
3854   createcommand("MODE",handle_mode,0,1);
3855   createcommand("TOPIC",handle_topic,0,1);
3856   createcommand("WHO",handle_who,0,1);
3857   createcommand("MOTD",handle_motd,0,0);
3858   createcommand("RULES",handle_join,0,0);
3859   createcommand("OPER",handle_oper,0,2);
3860   createcommand("LIST",handle_list,0,0);
3861   createcommand("DIE",handle_die,'o',1);
3862   createcommand("RESTART",handle_restart,'o',1);
3863   createcommand("KILL",handle_kill,'o',2);
3864   createcommand("REHASH",handle_rehash,'o',0);
3865   createcommand("LUSERS",handle_lusers,0,0);
3866   createcommand("STATS",handle_stats,0,1);
3867   createcommand("USERHOST",handle_userhost,0,1);
3868   createcommand("AWAY",handle_away,0,0);
3869   createcommand("ISON",handle_ison,0,0);
3870   createcommand("SUMMON",handle_summon,0,0);
3871   createcommand("USERS",handle_users,0,0);
3872   createcommand("INVITE",handle_invite,0,2);
3873   createcommand("PASS",handle_pass,0,1);
3874   createcommand("TRACE",handle_trace,'o',0);
3875   createcommand("WHOWAS",handle_whowas,0,1);
3876   createcommand("CONNECT",handle_connect,'o',2);
3877   createcommand("SQUIT",handle_squit,'o',1);
3878 }
3879
3880 void process_buffer(userrec *user)
3881 {
3882         char cmd[MAXBUF];
3883         int i;
3884         if (!user->inbuf)
3885         {
3886                 return;
3887         }
3888         if (!strcmp(user->inbuf,""))
3889         {
3890                 return;
3891         }
3892         strncpy(cmd,user->inbuf,MAXBUF);
3893         if (!strcmp(cmd,""))
3894         {
3895                 return;
3896         }
3897         if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
3898         {
3899                 cmd[strlen(cmd)-1] = '\0';
3900         }
3901         if ((cmd[strlen(cmd)-1] == 13) || (cmd[strlen(cmd)-1] == 10))
3902         {
3903                 cmd[strlen(cmd)-1] = '\0';
3904         }
3905         strcpy(user->inbuf,"");
3906         if (!strcmp(cmd,""))
3907         {
3908                 return;
3909         }
3910         log(DEBUG,"InspIRCd: processing: %s %s",user->nick,cmd);
3911         process_command(user,cmd);
3912 }
3913
3914 void handle_link_packet(char* udp_msg, char* udp_host, int udp_port, serverrec *serv)
3915 {
3916 }
3917
3918 int InspIRCd(void)
3919 {
3920   struct sockaddr_in client, server;
3921   char addrs[MAXBUF][255];
3922   int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE;
3923   socklen_t length;
3924   int count = 0, scanDetectTrigger = TRUE, showBanner = FALSE;
3925   int selectResult = 0;
3926   char *temp, configToken[MAXBUF], stuff[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
3927   char resolvedHost[MAXBUF];
3928   fd_set selectFds;
3929   struct timeval tv;
3930
3931   log(DEBUG,"InspIRCd: startup: begin");
3932   log(DEBUG,"$Id$");
3933   if (geteuid() == 0)
3934   {
3935         printf("WARNING!!! You are running an irc server as ROOT!!! DO NOT DO THIS!!!\n\n");
3936         Exit(ERROR);
3937         log(DEBUG,"InspIRCd: startup: not starting with UID 0!");
3938   }
3939   SetupCommandTable();
3940   log(DEBUG,"InspIRCd: startup: default command table set up");
3941
3942   ReadConfig();
3943   if (strcmp(DieValue,"")) 
3944   { 
3945         printf("WARNING: %s\n\n",DieValue);
3946         exit(0); 
3947   }  
3948   log(DEBUG,"InspIRCd: startup: read config");
3949   
3950   int count2 = 0, count3 = 0;
3951   for (count = 0; count < ConfValueEnum("bind"); count++)
3952   {
3953         ConfValue("bind","port",count,configToken);
3954         ConfValue("bind","address",count,Addr);
3955         ConfValue("bind","type",count,Type);
3956         if (!strcmp(Type,"clients"))
3957         {
3958                 ports[count2] = atoi(configToken);
3959                 strcpy(addrs[count2],Addr);
3960                 count2++;
3961         }
3962         else
3963         {
3964                 char Default[MAXBUF];
3965                 strcpy(Default,"no");
3966                 ConfValue("bind","default",count,Default);
3967                 if (strchr(Default,'y'))
3968                 {
3969                                 defaultRoute = count3;
3970                                 log(DEBUG,"InspIRCd: startup: binding '%s:%s' is default server route",Addr,configToken);
3971                 }
3972                 me[count3] = new serverrec(ServerName,100L,false);
3973                 me[count3]->CreateListener(Addr,atoi(configToken));
3974                 count3++;
3975         }
3976         log(DEBUG,"InspIRCd: startup: read binding %s:%s [%s] from config",Addr,configToken, Type);
3977   }
3978   portCount = count2;
3979   UDPportCount = count3;
3980   
3981   log(DEBUG,"InspIRCd: startup: read %d total client ports and %d total server ports",portCount,UDPportCount);
3982
3983   log(DEBUG,"InspIRCd: startup: InspIRCd is now running!");
3984
3985   printf("\n");
3986   for (count = 0; count < ConfValueEnum("module"); count++)
3987   {
3988         char modfile[MAXBUF];
3989         ConfValue("module","name",count,configToken);
3990         sprintf(modfile,"%s/%s",MOD_PATH,configToken);
3991         printf("Loading module... \033[1;37m%s\033[0;37m\n",modfile);
3992         log(DEBUG,"InspIRCd: startup: Loading module: %s",modfile);
3993         
3994         factory[count] = new ircd_module(modfile);
3995         if (factory[count]->LastError())
3996         {
3997                 log(DEBUG,"Unable to load %s: %s",modfile,factory[count]->LastError());
3998                 sprintf("Unable to load %s: %s\nExiting...\n",modfile,factory[count]->LastError());
3999                 Exit(ERROR);
4000         }
4001         if (factory[count]->factory)
4002         {
4003                 modules[count] = factory[count]->factory->CreateModule();
4004                 /* save the module and the module's classfactory, if
4005                  * this isnt done, random crashes can occur :/ */
4006         }
4007         else
4008         {
4009                 log(DEBUG,"Unable to load %s",modfile);
4010                 sprintf("Unable to load %s\nExiting...\n",modfile);
4011                 Exit(ERROR);
4012         }
4013   }
4014   MODCOUNT = count - 1;
4015   log(DEBUG,"Total loaded modules: %d",MODCOUNT+1);
4016
4017   servers = new server_list;
4018   servers->clear();
4019
4020   printf("\nInspIRCd is now running!\n");
4021
4022   startup_time = time(NULL);
4023   
4024   if (DaemonSeed() == ERROR)
4025   {
4026      log(DEBUG,"InspIRCd: startup: can't daemonise");
4027      printf("ERROR: could not go into daemon mode. Shutting down.\n");
4028      Exit(ERROR);
4029   }
4030   
4031   
4032   /* setup select call */
4033   FD_ZERO(&selectFds);
4034   log(DEBUG,"InspIRCd: startup: zero selects");
4035
4036   for (count = 0; count < portCount; count++)
4037   {
4038           if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR)
4039       {
4040                 log(DEBUG,"InspIRCd: startup: bad fd %d",openSockfd[boundPortCount]);
4041                 return(ERROR);
4042       }
4043       if (BindSocket(openSockfd[boundPortCount],client,server,ports[count],addrs[count]) == ERROR)
4044       {
4045                 log(DEBUG,"InspIRCd: startup: failed to bind port %d",ports[count]);
4046       }
4047       else                      /* well we at least bound to one socket so we'll continue */
4048       {
4049                 boundPortCount++;
4050       }
4051   }
4052
4053   log(DEBUG,"InspIRCd: startup: total bound ports %d",boundPortCount);
4054   
4055   /* if we didn't bind to anything then abort */
4056   if (boundPortCount == 0)
4057   {
4058      log(DEBUG,"InspIRCd: startup: no ports bound, bailing!");
4059      return (ERROR);
4060   }
4061
4062   length = sizeof (client);
4063   int flip_flop = 0, udp_port = 0;
4064   char udp_msg[MAXBUF], udp_host[MAXBUF];
4065   
4066   /* main loop for multiplexing/resetting */
4067   for (;;)
4068   {
4069       /* set up select call */
4070       for (count = 0; count < boundPortCount; count++)
4071       {
4072                 FD_SET (openSockfd[count], &selectFds);
4073       }
4074         
4075       /* added timeout! select was waiting forever... wank... :/ */
4076       tv.tv_usec = 0;
4077
4078       flip_flop++;
4079       if (flip_flop > 20)
4080       {
4081               tv.tv_usec = 1;
4082               flip_flop = 0;
4083       }
4084       
4085       tv.tv_sec = 0;
4086       selectResult = select(MAXSOCKS, &selectFds, NULL, NULL, &tv);
4087
4088       for (int x = 0; x != UDPportCount; x++)
4089       {
4090                 if (me[x]->RecvPacket(udp_msg, udp_host, udp_port))
4091                 {
4092                     FOREACH_MOD OnPacketReceive(udp_msg);
4093                         WriteOpers("UDP Link Packet: '%s' from %s:%d:%d [route%d]",udp_msg,udp_host,udp_port,me[x]->port,x);
4094                         // Packets must go back via the route they arrived on :)
4095                         handle_link_packet(udp_msg, udp_host, udp_port, me[x]);
4096             }
4097       }
4098
4099         for (user_hash::iterator count2 = clientlist.begin(); count2 != clientlist.end(); count2++)
4100         {
4101                 char data[MAXBUF];
4102
4103                 if (!count2->second) break;
4104                 
4105                 if (count2->second)
4106                 if (count2->second->fd)
4107                 {
4108                         if (((time(NULL)) > count2->second->nping) && (isnick(count2->second->nick)) && (count2->second->registered == 7))
4109                         {
4110                                 if (!count2->second->lastping) 
4111                                 {
4112                                         log(DEBUG,"InspIRCd: ping timeout: %s",count2->second->nick);
4113                                         kill_link(count2->second,"Ping timeout");
4114                                         break;
4115                                 }
4116                                 Write(count2->second->fd,"PING :%s",ServerName);
4117                                 log(DEBUG,"InspIRCd: pinging: %s",count2->second->nick);
4118                                 count2->second->lastping = 0;
4119                                 count2->second->nping = time(NULL)+120;
4120                         }
4121                         
4122                         result = read(count2->second->fd, data, 1);
4123                         // result EAGAIN means nothing read
4124                         if (result == EAGAIN)
4125                         {
4126                         }
4127                         else
4128                         if (result == 0)
4129                         {
4130                                 log(DEBUG,"InspIRCd: Exited: %s",count2->second->nick);
4131                                 kill_link(count2->second,"Client exited");
4132                         }
4133                         else if (result > 0)
4134                         {
4135                                 strncat(count2->second->inbuf, data, result);
4136                                 if (strchr(count2->second->inbuf, '\n') || strchr(count2->second->inbuf, '\r'))
4137                                 {
4138                                         /* at least one complete line is waiting to be processed */
4139                                         if (!count2->second->fd)
4140                                                 break;
4141                                         else
4142                                         {
4143                                                 process_buffer(count2->second);
4144                                                 break;
4145                                         }
4146                                 }
4147                         }
4148                 }
4149         }
4150
4151       /* select is reporting a waiting socket. Poll them all to find out which */
4152       if (selectResult > 0)
4153       {
4154         char target[MAXBUF], resolved[MAXBUF];
4155         for (count = 0; count < boundPortCount; count++)                
4156         {
4157             if (FD_ISSET (openSockfd[count], &selectFds))
4158             {
4159               incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length);
4160               
4161               address_cache::iterator iter = IP.find(client.sin_addr);
4162               bool iscached = false;
4163               if (iter == IP.end())
4164               {
4165                         /* ip isn't in cache, add it */
4166                         strncpy (target, (char *) inet_ntoa (client.sin_addr), MAXBUF);
4167                         if(CleanAndResolve(resolved, target) != TRUE)
4168                         {
4169                                 strncpy(resolved,target,MAXBUF);
4170                         }
4171                         /* hostname now in 'target' */
4172                         IP[client.sin_addr] = new string(resolved);
4173               /* hostname in cache */
4174               }
4175               else
4176               {
4177               /* found ip (cached) */
4178               strncpy(resolved, iter->second->c_str(), MAXBUF);
4179               iscached = true;
4180            }
4181
4182               if (incomingSockfd < 0)
4183               {
4184                         WriteOpers("*** WARNING: Accept failed on port %d (%s)", ports[count],target);
4185                         log(DEBUG,"InspIRCd: accept failed: %d",ports[count]);
4186                         break;
4187               }
4188
4189               AddClient(incomingSockfd, resolved, ports[count], iscached);
4190               log(DEBUG,"InspIRCd: adding client on port %d fd=%d",ports[count],incomingSockfd);
4191               break;
4192             }
4193
4194            }
4195       }
4196   }
4197
4198   /* not reached */
4199   close (incomingSockfd);
4200 }
4201
4202