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