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