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