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