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