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