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