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