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