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