]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/commands.cpp
Updated header comments
[user/henk/code/inspircd.git] / src / commands.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2004 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 #include "inspircd.h"
18 #include "inspircd_io.h"
19 #include "inspircd_util.h"
20 #include "inspircd_config.h"
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <sys/errno.h>
24 #include <sys/ioctl.h>
25 #include <sys/utsname.h>
26 #include <cstdio>
27 #include <time.h>
28 #include <string>
29 #ifdef GCC3
30 #include <ext/hash_map>
31 #else
32 #include <hash_map>
33 #endif
34 #include <map>
35 #include <sstream>
36 #include <vector>
37 #include <errno.h>
38 #include <deque>
39 #include <errno.h>
40 #include <unistd.h>
41 #include <sched.h>
42 #include "connection.h"
43 #include "users.h"
44 #include "servers.h"
45 #include "ctables.h"
46 #include "globals.h"
47 #include "modules.h"
48 #include "dynamic.h"
49 #include "wildcard.h"
50 #include "message.h"
51 #include "mode.h"
52 #include "xline.h"
53
54 #ifdef GCC3
55 #define nspace __gnu_cxx
56 #else
57 #define nspace std
58 #endif
59
60 using namespace std;
61
62 extern int MODCOUNT;
63 extern vector<Module*> modules;
64 extern vector<ircd_module*> factory;
65
66 extern int LogLevel;
67 extern char ServerName[MAXBUF];
68 extern char Network[MAXBUF];
69 extern char ServerDesc[MAXBUF];
70 extern char AdminName[MAXBUF];
71 extern char AdminEmail[MAXBUF];
72 extern char AdminNick[MAXBUF];
73 extern char diepass[MAXBUF];
74 extern char restartpass[MAXBUF];
75 extern char motd[MAXBUF];
76 extern char rules[MAXBUF];
77 extern char list[MAXBUF];
78 extern char PrefixQuit[MAXBUF];
79 extern char DieValue[MAXBUF];
80
81 extern int debugging;
82 extern int WHOWAS_STALE;
83 extern int WHOWAS_MAX;
84 extern int DieDelay;
85 extern time_t startup_time;
86 extern int NetBufferSize;
87 extern int MaxWhoResults;
88 extern time_t nb_start;
89
90 extern bool nofork;
91
92 extern std::vector<int> fd_reap;
93 extern std::vector<std::string> module_names;
94
95 extern char MyExecutable[1024];
96 extern int boundPortCount;
97 extern int portCount;
98 extern int UDPportCount;
99 extern int ports[MAXSOCKS];
100 extern int defaultRoute;
101
102 extern std::vector<long> auth_cookies;
103 extern std::stringstream config_f;
104
105 extern serverrec* me[32];
106
107 extern FILE *log_file;
108
109 extern ClassVector Classes;
110
111 const long duration_m = 60;
112 const long duration_h = duration_m * 60;
113 const long duration_d = duration_h * 24;
114 const long duration_w = duration_d * 7;
115 const long duration_y = duration_w * 52;
116
117 namespace nspace
118 {
119         template<> struct nspace::hash<in_addr>
120         {
121                 size_t operator()(const struct in_addr &a) const
122                 {
123                         size_t q;
124                         memcpy(&q,&a,sizeof(size_t));
125                         return q;
126                 }
127         };
128
129         template<> struct nspace::hash<string>
130         {
131                 size_t operator()(const string &s) const
132                 {
133                         char a[MAXBUF];
134                         static struct hash<const char *> strhash;
135                         strcpy(a,s.c_str());
136                         strlower(a);
137                         return strhash(a);
138                 }
139         };
140 }       
141
142
143 struct StrHashComp
144 {
145
146         bool operator()(const string& s1, const string& s2) const
147         {
148                 char a[MAXBUF],b[MAXBUF];
149                 strcpy(a,s1.c_str());
150                 strcpy(b,s2.c_str());
151                 return (strcasecmp(a,b) == 0);
152         }
153
154 };
155
156 struct InAddr_HashComp
157 {
158
159         bool operator()(const in_addr &s1, const in_addr &s2) const
160         {
161                 size_t q;
162                 size_t p;
163                 
164                 memcpy(&q,&s1,sizeof(size_t));
165                 memcpy(&p,&s2,sizeof(size_t));
166                 
167                 return (q == p);
168         }
169
170 };
171
172
173 typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, StrHashComp> user_hash;
174 typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, StrHashComp> chan_hash;
175 typedef nspace::hash_map<in_addr,string*, nspace::hash<in_addr>, InAddr_HashComp> address_cache;
176 typedef std::deque<command_t> command_table;
177
178
179 extern user_hash clientlist;
180 extern chan_hash chanlist;
181 extern user_hash whowas;
182 extern command_table cmdlist;
183 extern file_cache MOTD;
184 extern file_cache RULES;
185 extern address_cache IP;
186
187
188 void handle_join(char **parameters, int pcnt, userrec *user)
189 {
190         chanrec* Ptr;
191         int i = 0;
192         
193         if (loop_call(handle_join,parameters,pcnt,user,0,0,1))
194                 return;
195         if (parameters[0][0] == '#')
196         {
197                 Ptr = add_channel(user,parameters[0],parameters[1],false);
198         }
199 }
200
201
202 void handle_part(char **parameters, int pcnt, userrec *user)
203 {
204         chanrec* Ptr;
205
206         if (pcnt > 1)
207         {
208                 if (loop_call(handle_part,parameters,pcnt,user,0,pcnt-2,0))
209                         return;
210                 del_channel(user,parameters[0],parameters[1],false);
211         }
212         else
213         {
214                 if (loop_call(handle_part,parameters,pcnt,user,0,pcnt-1,0))
215                         return;
216                 del_channel(user,parameters[0],NULL,false);
217         }
218 }
219
220 void handle_kick(char **parameters, int pcnt, userrec *user)
221 {
222         chanrec* Ptr = FindChan(parameters[0]);
223         userrec* u   = Find(parameters[1]);
224
225         if ((!u) || (!Ptr))
226         {
227                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
228                 return;
229         }
230         
231         if (!has_channel(u,Ptr))
232         {
233                 WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, parameters[0]);
234                 return;
235         }
236
237         char reason[MAXBUF];
238         
239         if (pcnt > 2)
240         {
241                 strncpy(reason,parameters[2],MAXBUF);
242                 if (strlen(reason)>MAXKICK)
243                 {
244                         reason[MAXKICK-1] = '\0';
245                 }
246
247                 kick_channel(user,u,Ptr,reason);
248         }
249         else
250         {
251                 strcpy(reason,user->nick);
252                 kick_channel(user,u,Ptr,reason);
253         }
254         
255         // this must be propogated so that channel membership is kept in step network-wide
256         
257         char buffer[MAXBUF];
258         snprintf(buffer,MAXBUF,"k %s %s %s :%s",user->nick,u->nick,Ptr->name,reason);
259         NetSendToAll(buffer);
260 }
261
262
263 void handle_die(char **parameters, int pcnt, userrec *user)
264 {
265         log(DEBUG,"die: %s",user->nick);
266         if (!strcmp(parameters[0],diepass))
267         {
268                 WriteOpers("*** DIE command from %s!%s@%s, terminating...",user->nick,user->ident,user->host);
269                 sleep(DieDelay);
270                 Exit(ERROR);
271         }
272         else
273         {
274                 WriteOpers("*** Failed DIE Command from %s!%s@%s.",user->nick,user->ident,user->host);
275         }
276 }
277
278 void handle_restart(char **parameters, int pcnt, userrec *user)
279 {
280         char restart[1024];
281         char *argv[32];
282         log(DEFAULT,"Restart: %s",user->nick);
283         if (!strcmp(parameters[0],restartpass))
284         {
285                 WriteOpers("*** RESTART command from %s!%s@%s, restarting server.",user->nick,user->ident,user->host);
286
287                 argv[0] = MyExecutable;
288                 argv[1] = "-wait";
289                 if (nofork)
290                 {
291                         argv[2] = "-nofork";
292                 }
293                 else
294                 {
295                         argv[2] = NULL;
296                 }
297                 argv[3] = NULL;
298                 
299                 // close ALL file descriptors
300                 send_error("Server restarting.");
301                 sleep(1);
302                 for (int i = 0; i < 65536; i++)
303                 {
304                         int on = 0;
305                         struct linger linger = { 0 };
306                         setsockopt(i, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
307                         linger.l_onoff = 1;
308                         linger.l_linger = 0;
309                         setsockopt(i, SOL_SOCKET, SO_LINGER, (const char*)&linger,sizeof(linger));
310                         Blocking(i);
311                         close(i);
312                 }
313                 sleep(5);
314                 
315                 execv(MyExecutable,argv);
316
317                 exit(0);
318         }
319         else
320         {
321                 WriteOpers("*** Failed RESTART Command from %s!%s@%s.",user->nick,user->ident,user->host);
322         }
323 }
324
325 void handle_kill(char **parameters, int pcnt, userrec *user)
326 {
327         userrec *u = Find(parameters[0]);
328         char killreason[MAXBUF];
329         
330         log(DEBUG,"kill: %s %s",parameters[0],parameters[1]);
331         if (u)
332         {
333                 if (strcmp(ServerName,u->server))
334                 {
335                         // remote kill
336                         WriteOpers("*** Remote kill by %s: %s!%s@%s (%s)",user->nick,u->nick,u->ident,u->host,parameters[1]);
337                         sprintf(killreason,"[%s] Killed (%s (%s))",ServerName,user->nick,parameters[1]);
338                         WriteCommonExcept(u,"QUIT :%s",killreason);
339                         // K token must go to ALL servers!!!
340                         char buffer[MAXBUF];
341                         snprintf(buffer,MAXBUF,"K %s %s :%s",user->nick,u->nick,killreason);
342                         NetSendToAll(buffer);
343                         
344                         user_hash::iterator iter = clientlist.find(u->nick);
345                         if (iter != clientlist.end())
346                         {
347                                 log(DEBUG,"deleting user hash value %d",iter->second);
348                                 if ((iter->second) && (user->registered == 7)) {
349                                         delete iter->second;
350                                         }
351                         clientlist.erase(iter);
352                         }
353                         purge_empty_chans();
354                 }
355                 else
356                 {
357                         // local kill
358                         WriteTo(user, u, "KILL %s :%s!%s!%s (%s)", u->nick, ServerName,user->dhost,user->nick,parameters[1]);
359                         WriteOpers("*** Local Kill by %s: %s!%s@%s (%s)",user->nick,u->nick,u->ident,u->host,parameters[1]);
360                         sprintf(killreason,"Killed (%s (%s))",user->nick,parameters[1]);
361                         kill_link(u,killreason);
362                 }
363         }
364         else
365         {
366                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
367         }
368 }
369
370 void handle_summon(char **parameters, int pcnt, userrec *user)
371 {
372         WriteServ(user->fd,"445 %s :SUMMON has been disabled (depreciated command)",user->nick);
373 }
374
375 void handle_users(char **parameters, int pcnt, userrec *user)
376 {
377         WriteServ(user->fd,"445 %s :USERS has been disabled (depreciated command)",user->nick);
378 }
379
380 void handle_pass(char **parameters, int pcnt, userrec *user)
381 {
382         // Check to make sure they havnt registered -- Fix by FCS
383         if (user->registered == 7)
384         {
385                 WriteServ(user->fd,"462 %s :You may not reregister",user->nick);
386                 return;
387         }
388         if (!strcasecmp(parameters[0],Passwd(user)))
389         {
390                 user->haspassed = true;
391         }
392 }
393
394 void handle_invite(char **parameters, int pcnt, userrec *user)
395 {
396         userrec* u = Find(parameters[0]);
397         chanrec* c = FindChan(parameters[1]);
398
399         if ((!c) || (!u))
400         {
401                 if (!c)
402                 {
403                         WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[1]);
404                 }
405                 else
406                 {
407                         if (c->inviteonly)
408                         {
409                                 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]);
410                         }
411                 }
412
413                 return;
414         }
415
416         if (c->inviteonly)
417         {
418                 if (cstatus(user,c) < STATUS_HOP)
419                 {
420                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, c->name);
421                         return;
422                 }
423         }
424         if (has_channel(u,c))
425         {
426                 WriteServ(user->fd,"443 %s %s %s :Is already on channel %s",user->nick,u->nick,c->name,c->name);
427                 return;
428         }
429         if (!has_channel(user,c))
430         {
431                 WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, c->name);
432                 return;
433         }
434         u->InviteTo(c->name);
435         WriteFrom(u->fd,user,"INVITE %s :%s",u->nick,c->name);
436         WriteServ(user->fd,"341 %s %s %s",user->nick,u->nick,c->name);
437         
438         // i token must go to ALL servers!!!
439         char buffer[MAXBUF];
440         snprintf(buffer,MAXBUF,"i %s %s %s",u->nick,user->nick,c->name);
441         NetSendToAll(buffer);
442 }
443
444 void handle_topic(char **parameters, int pcnt, userrec *user)
445 {
446         chanrec* Ptr;
447
448         if (pcnt == 1)
449         {
450                 if (strlen(parameters[0]) <= CHANMAX)
451                 {
452                         Ptr = FindChan(parameters[0]);
453                         if (Ptr)
454                         {
455                                 if (Ptr->topicset)
456                                 {
457                                         WriteServ(user->fd,"332 %s %s :%s", user->nick, Ptr->name, Ptr->topic);
458                                         WriteServ(user->fd,"333 %s %s %s %d", user->nick, Ptr->name, Ptr->setby, Ptr->topicset);
459                                 }
460                                 else
461                                 {
462                                         WriteServ(user->fd,"331 %s %s :No topic is set.", user->nick, Ptr->name);
463                                 }
464                         }
465                         else
466                         {
467                                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
468                         }
469                 }
470                 return;
471         }
472         else if (pcnt>1)
473         {
474                 if (strlen(parameters[0]) <= CHANMAX)
475                 {
476                         Ptr = FindChan(parameters[0]);
477                         if (Ptr)
478                         {
479                                 if ((Ptr->topiclock) && (cstatus(user,Ptr)<STATUS_HOP))
480                                 {
481                                         WriteServ(user->fd,"482 %s %s :You must be at least a half-operator to change modes on this channel", user->nick, Ptr->name);
482                                         return;
483                                 }
484                                 
485                                 char topic[MAXBUF];
486                                 strncpy(topic,parameters[1],MAXBUF);
487                                 if (strlen(topic)>MAXTOPIC)
488                                 {
489                                         topic[MAXTOPIC-1] = '\0';
490                                 }
491                                         
492                                 strcpy(Ptr->topic,topic);
493                                 strcpy(Ptr->setby,user->nick);
494                                 Ptr->topicset = time(NULL);
495                                 WriteChannel(Ptr,user,"TOPIC %s :%s",Ptr->name, Ptr->topic);
496
497                                 // t token must go to ALL servers!!!
498                                 char buffer[MAXBUF];
499                                 snprintf(buffer,MAXBUF,"t %s %s :%s",user->nick,Ptr->name,topic);
500                                 NetSendToAll(buffer);
501                         }
502                         else
503                         {
504                                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
505                         }
506                 }
507         }
508 }
509
510 void handle_names(char **parameters, int pcnt, userrec *user)
511 {
512         chanrec* c;
513
514         if (loop_call(handle_names,parameters,pcnt,user,0,pcnt-1,0))
515                 return;
516         c = FindChan(parameters[0]);
517         if (c)
518         {
519                 /*WriteServ(user->fd,"353 %s = %s :%s", user->nick, c->name,*/
520                 userlist(user,c);
521                 WriteServ(user->fd,"366 %s %s :End of /NAMES list.", user->nick, c->name);
522         }
523         else
524         {
525                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
526         }
527 }
528
529 void handle_privmsg(char **parameters, int pcnt, userrec *user)
530 {
531         userrec *dest;
532         chanrec *chan;
533
534         user->idle_lastmsg = time(NULL);
535         
536         if (loop_call(handle_privmsg,parameters,pcnt,user,0,pcnt-2,0))
537                 return;
538         if (parameters[0][0] == '#')
539         {
540                 chan = FindChan(parameters[0]);
541                 if (chan)
542                 {
543                         if ((chan->noexternal) && (!has_channel(user,chan)))
544                         {
545                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
546                                 return;
547                         }
548                         if ((chan->moderated) && (cstatus(user,chan)<STATUS_VOICE))
549                         {
550                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
551                                 return;
552                         }
553                         
554                         int MOD_RESULT = 0;
555
556                         FOREACH_RESULT(OnUserPreMessage(user,chan,TYPE_CHANNEL,std::string(parameters[1])));
557                         if (MOD_RESULT) {
558                                 return;
559                         }
560                         
561                         ChanExceptSender(chan, user, "PRIVMSG %s :%s", chan->name, parameters[1]);
562                         
563                         // if any users of this channel are on remote servers, broadcast the packet
564                         char buffer[MAXBUF];
565                         snprintf(buffer,MAXBUF,"P %s %s :%s",user->nick,chan->name,parameters[1]);
566                         NetSendToCommon(user,buffer);
567                 }
568                 else
569                 {
570                         /* no such nick/channel */
571                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
572                 }
573                 return;
574         }
575         
576         dest = Find(parameters[0]);
577         if (dest)
578         {
579                 if (strcmp(dest->awaymsg,""))
580                 {
581                         /* auto respond with aweh msg */
582                         WriteServ(user->fd,"301 %s %s :%s",user->nick,dest->nick,dest->awaymsg);
583                 }
584
585                 int MOD_RESULT = 0;
586                 
587                 FOREACH_RESULT(OnUserPreMessage(user,dest,TYPE_USER,std::string(parameters[1])));
588                 if (MOD_RESULT) {
589                         return;
590                 }
591
592
593
594                 if (!strcmp(dest->server,user->server))
595                 {
596                         // direct write, same server
597                         WriteTo(user, dest, "PRIVMSG %s :%s", dest->nick, parameters[1]);
598                 }
599                 else
600                 {
601                         char buffer[MAXBUF];
602                         snprintf(buffer,MAXBUF,"P %s %s :%s",user->nick,dest->nick,parameters[1]);
603                         NetSendToOne(dest->server,buffer);
604                 }
605         }
606         else
607         {
608                 /* no such nick/channel */
609                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
610         }
611 }
612
613 void handle_notice(char **parameters, int pcnt, userrec *user)
614 {
615         userrec *dest;
616         chanrec *chan;
617
618         user->idle_lastmsg = time(NULL);
619         
620         if (loop_call(handle_notice,parameters,pcnt,user,0,pcnt-2,0))
621                 return;
622         if (parameters[0][0] == '#')
623         {
624                 chan = FindChan(parameters[0]);
625                 if (chan)
626                 {
627                         if ((chan->noexternal) && (!has_channel(user,chan)))
628                         {
629                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (no external messages)", user->nick, chan->name);
630                                 return;
631                         }
632                         if ((chan->moderated) && (cstatus(user,chan)<STATUS_VOICE))
633                         {
634                                 WriteServ(user->fd,"404 %s %s :Cannot send to channel (+m)", user->nick, chan->name);
635                                 return;
636                         }
637
638                         int MOD_RESULT = 0;
639                 
640                         FOREACH_RESULT(OnUserPreNotice(user,chan,TYPE_CHANNEL,std::string(parameters[1])));
641                         if (MOD_RESULT) {
642                                 return;
643                         }
644
645                         ChanExceptSender(chan, user, "NOTICE %s :%s", chan->name, parameters[1]);
646
647                         // if any users of this channel are on remote servers, broadcast the packet
648                         char buffer[MAXBUF];
649                         snprintf(buffer,MAXBUF,"V %s %s :%s",user->nick,chan->name,parameters[1]);
650                         NetSendToCommon(user,buffer);
651                 }
652                 else
653                 {
654                         /* no such nick/channel */
655                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
656                 }
657                 return;
658         }
659         
660         dest = Find(parameters[0]);
661         if (dest)
662         {
663                 int MOD_RESULT = 0;
664                 
665                 FOREACH_RESULT(OnUserPreNotice(user,dest,TYPE_USER,std::string(parameters[1])));
666                 if (MOD_RESULT) {
667                         return;
668                 }
669
670                 if (!strcmp(dest->server,user->server))
671                 {
672                         // direct write, same server
673                         WriteTo(user, dest, "NOTICE %s :%s", dest->nick, parameters[1]);
674                 }
675                 else
676                 {
677                         char buffer[MAXBUF];
678                         snprintf(buffer,MAXBUF,"V %s %s :%s",user->nick,dest->nick,parameters[1]);
679                         NetSendToOne(dest->server,buffer);
680                 }
681         }
682         else
683         {
684                 /* no such nick/channel */
685                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
686         }
687 }
688
689 void handle_server(char **parameters, int pcnt, userrec *user)
690 {
691         WriteServ(user->fd,"666 %s :You cannot identify as a server, you are a USER. IRC Operators informed.",user->nick);
692         WriteOpers("*** WARNING: %s attempted to issue a SERVER command and is registered as a user!",user->nick);
693 }
694
695 void handle_info(char **parameters, int pcnt, userrec *user)
696 {
697         WriteServ(user->fd,"371 %s :The Inspire IRCd Project Has been brought to you by the following people..",user->nick);
698         WriteServ(user->fd,"371 %s :Craig Edwards, Craig McLure, and Others..",user->nick);
699         WriteServ(user->fd,"371 %s :Will finish this later when i can be arsed :p",user->nick);
700         FOREACH_MOD OnInfo(user);
701         WriteServ(user->fd,"374 %s :End of /INFO list",user->nick);
702 }
703
704 void handle_time(char **parameters, int pcnt, userrec *user)
705 {
706         time_t rawtime;
707         struct tm * timeinfo;
708
709         time ( &rawtime );
710         timeinfo = localtime ( &rawtime );
711         WriteServ(user->fd,"391 %s %s :%s",user->nick,ServerName, asctime (timeinfo) );
712   
713 }
714
715 void handle_whois(char **parameters, int pcnt, userrec *user)
716 {
717         userrec *dest;
718         char *t;
719
720         if (loop_call(handle_whois,parameters,pcnt,user,0,pcnt-1,0))
721                 return;
722         dest = Find(parameters[0]);
723         if (dest)
724         {
725                 // bug found by phidjit - were able to whois an incomplete connection if it had sent a NICK or USER
726                 if (dest->registered == 7)
727                 {
728                         WriteServ(user->fd,"311 %s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname);
729                         if ((user == dest) || (strchr(user->modes,'o')))
730                         {
731                                 WriteServ(user->fd,"378 %s %s :is connecting from *@%s",user->nick, dest->nick, dest->host);
732                         }
733                         if (strcmp(chlist(dest),""))
734                         {
735                                 WriteServ(user->fd,"319 %s %s :%s",user->nick, dest->nick, chlist(dest));
736                         }
737                         WriteServ(user->fd,"312 %s %s %s :%s",user->nick, dest->nick, dest->server, GetServerDescription(dest->server).c_str());
738                         if (strcmp(dest->awaymsg,""))
739                         {
740                                 WriteServ(user->fd,"301 %s %s :%s",user->nick, dest->nick, dest->awaymsg);
741                         }
742                         if (strchr(dest->modes,'o'))
743                         {
744                                 WriteServ(user->fd,"313 %s %s :is %s %s on %s",user->nick, dest->nick,
745                                 (strchr("aeiou",dest->oper[0]) ? "an" : "a"),dest->oper, Network);
746                         }
747                         FOREACH_MOD OnWhois(user,dest);
748                         if (!strcasecmp(user->server,dest->server))
749                         {
750                                 // idle time and signon line can only be sent if youre on the same server (according to RFC)
751                                 WriteServ(user->fd,"317 %s %s %d %d :seconds idle, signon time",user->nick, dest->nick, abs((dest->idle_lastmsg)-time(NULL)), dest->signon);
752                         }
753                         
754                         WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, dest->nick);
755                 }
756                 else
757                 {
758                         WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
759                 }
760         }
761         else
762         {
763                 /* no such nick/channel */
764                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
765         }
766 }
767
768 void handle_quit(char **parameters, int pcnt, userrec *user)
769 {
770         user_hash::iterator iter = clientlist.find(user->nick);
771         char* reason;
772
773         if (user->registered == 7)
774         {
775                 /* theres more to do here, but for now just close the socket */
776                 if (pcnt == 1)
777                 {
778                         if (parameters[0][0] == ':')
779                         {
780                                 *parameters[0]++;
781                         }
782                         reason = parameters[0];
783
784                         if (strlen(reason)>MAXQUIT)
785                         {
786                                 reason[MAXQUIT-1] = '\0';
787                         }
788
789                         Write(user->fd,"ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,parameters[0]);
790                         WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,parameters[0]);
791                         WriteCommonExcept(user,"QUIT :%s%s",PrefixQuit,parameters[0]);
792
793                         char buffer[MAXBUF];
794                         snprintf(buffer,MAXBUF,"Q %s :%s%s",user->nick,PrefixQuit,parameters[0]);
795                         NetSendToAll(buffer);
796                 }
797                 else
798                 {
799                         Write(user->fd,"ERROR :Closing link (%s@%s) [QUIT]",user->ident,user->host);
800                         WriteOpers("*** Client exiting: %s!%s@%s [Client exited]",user->nick,user->ident,user->host);
801                         WriteCommonExcept(user,"QUIT :Client exited");
802
803                         char buffer[MAXBUF];
804                         snprintf(buffer,MAXBUF,"Q %s :Client exited",user->nick);
805                         NetSendToAll(buffer);
806                 }
807                 FOREACH_MOD OnUserQuit(user);
808                 AddWhoWas(user);
809         }
810
811         /* push the socket on a stack of sockets due to be closed at the next opportunity */
812         fd_reap.push_back(user->fd);
813         
814         if (iter != clientlist.end())
815         {
816                 clientlist.erase(iter);
817                 log(DEBUG,"deleting user hash value %d",iter->second);
818                 //if ((user) && (user->registered == 7)) {
819                         //delete user;
820                 //}
821         }
822
823         if (user->registered == 7) {
824                 purge_empty_chans();
825         }
826 }
827
828 void handle_who(char **parameters, int pcnt, userrec *user)
829 {
830         chanrec* Ptr = NULL;
831         
832         /* theres more to do here, but for now just close the socket */
833         if (pcnt == 1)
834         {
835                 if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*")))
836                 {
837                         if (user->chans[0].channel)
838                         {
839                                 int n_list = 0;
840                                 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
841                                 {
842                                         Ptr = i->second->chans[0].channel;
843                                         // suggested by phidjit and FCS
844                                         if ((!common_channels(user,i->second)) && (isnick(i->second->nick)))
845                                         {
846                                                 WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, Ptr ? Ptr->name : "*", i->second->ident, i->second->dhost, i->second->server, i->second->nick, i->second->fullname);
847                                                 n_list++;
848                                                 if (n_list > MaxWhoResults)
849                                                         break;
850                                         }
851                                 }
852                         }
853                         if (Ptr)
854                         {
855                                 WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
856                         }
857                         else
858                         {
859                                 WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, user->nick);
860                         }
861                         return;
862                 }
863                 if (parameters[0][0] == '#')
864                 {
865                         Ptr = FindChan(parameters[0]);
866                         if (Ptr)
867                         {
868                                 for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
869                                 {
870                                         if ((has_channel(i->second,Ptr)) && (isnick(i->second->nick)))
871                                         {
872                                                 WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, Ptr->name, i->second->ident, i->second->dhost, i->second->server, i->second->nick, i->second->fullname);
873                                         }
874                                 }
875                                 WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, Ptr->name);
876                         }
877                         else
878                         {
879                                 WriteServ(user->fd,"401 %s %s :No suck nick/channel",user->nick, parameters[0]);
880                         }
881                 }
882                 else
883                 {
884                         userrec* u = Find(parameters[0]);
885                         if (u)
886                         {
887                                 WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, u->nick, u->ident, u->dhost, u->server, u->nick, u->fullname);
888                         }
889                         WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, parameters[0]);
890                 }
891         }
892         if (pcnt == 2)
893         {
894                 if ((!strcmp(parameters[0],"0")) || (!strcmp(parameters[0],"*")) && (!strcmp(parameters[1],"o")))
895                 {
896                         for (user_hash::const_iterator i = clientlist.begin(); i != clientlist.end(); i++)
897                         {
898                                 if ((common_channels(user,i->second)) && (isnick(i->second->nick)))
899                                 {
900                                         if (strchr(i->second->modes,'o'))
901                                         {
902                                                 WriteServ(user->fd,"352 %s %s %s %s %s %s Hr@ :0 %s",user->nick, user->nick, i->second->ident, i->second->dhost, i->second->server, i->second->nick, i->second->fullname);
903                                         }
904                                 }
905                         }
906                         WriteServ(user->fd,"315 %s %s :End of /WHO list.",user->nick, user->nick);
907                         return;
908                 }
909         }
910 }
911
912 void handle_wallops(char **parameters, int pcnt, userrec *user)
913 {
914         WriteWallOps(user,false,"%s",parameters[0]);
915 }
916
917 void handle_list(char **parameters, int pcnt, userrec *user)
918 {
919         chanrec* Ptr;
920         
921         WriteServ(user->fd,"321 %s Channel :Users Name",user->nick);
922         for (chan_hash::const_iterator i = chanlist.begin(); i != chanlist.end(); i++)
923         {
924                 if ((!i->second->c_private) && (!i->second->secret))
925                 {
926                         WriteServ(user->fd,"322 %s %s %d :[+%s] %s",user->nick,i->second->name,usercount_i(i->second),chanmodes(i->second),i->second->topic);
927                 }
928         }
929         WriteServ(user->fd,"323 %s :End of channel list.",user->nick);
930 }
931
932
933 void handle_rehash(char **parameters, int pcnt, userrec *user)
934 {
935         WriteServ(user->fd,"382 %s %s :Rehashing",user->nick,CleanFilename(CONFIG_FILE));
936         ReadConfig();
937         FOREACH_MOD OnRehash();
938         WriteOpers("%s is rehashing config file %s",user->nick,CleanFilename(CONFIG_FILE));
939 }
940
941 void handle_lusers(char **parameters, int pcnt, userrec *user)
942 {
943         WriteServ(user->fd,"251 %s :There are %d users and %d invisible on %d servers",user->nick,usercnt()-usercount_invisible(),usercount_invisible(),servercount());
944         WriteServ(user->fd,"252 %s %d :operator(s) online",user->nick,usercount_opers());
945         WriteServ(user->fd,"253 %s %d :unknown connections",user->nick,usercount_unknown());
946         WriteServ(user->fd,"254 %s %d :channels formed",user->nick,chancount());
947         WriteServ(user->fd,"254 %s :I have %d clients and %d servers",user->nick,local_count(),count_servs());
948 }
949
950 void handle_admin(char **parameters, int pcnt, userrec *user)
951 {
952         WriteServ(user->fd,"256 %s :Administrative info for %s",user->nick,ServerName);
953         WriteServ(user->fd,"257 %s :Name     - %s",user->nick,AdminName);
954         WriteServ(user->fd,"258 %s :Nickname - %s",user->nick,AdminNick);
955         WriteServ(user->fd,"258 %s :E-Mail   - %s",user->nick,AdminEmail);
956 }
957
958 void handle_ping(char **parameters, int pcnt, userrec *user)
959 {
960         WriteServ(user->fd,"PONG %s :%s",ServerName,parameters[0]);
961 }
962
963 void handle_pong(char **parameters, int pcnt, userrec *user)
964 {
965         // set the user as alive so they survive to next ping
966         user->lastping = 1;
967 }
968
969 void handle_motd(char **parameters, int pcnt, userrec *user)
970 {
971         ShowMOTD(user);
972 }
973
974 void handle_rules(char **parameters, int pcnt, userrec *user)
975 {
976         ShowRULES(user);
977 }
978
979 void handle_user(char **parameters, int pcnt, userrec *user)
980 {
981         if (user->registered < 3)
982         {
983                 if (isident(parameters[0]) == 0) {
984                         // This kinda Sucks, According to the RFC thou, its either this,
985                         // or "You have already registered" :p -- Craig
986                         WriteServ(user->fd,"461 %s USER :Not enough parameters",user->nick);
987                 }
988                 else {
989                         WriteServ(user->fd,"NOTICE Auth :No ident response, ident prefixed with ~");
990                         strcpy(user->ident,"~"); /* we arent checking ident... but these days why bother anyway? */
991                         strncat(user->ident,parameters[0],IDENTMAX);
992                         strncpy(user->fullname,parameters[3],128);
993                         user->registered = (user->registered | 1);
994                 }
995         }
996         else
997         {
998                 WriteServ(user->fd,"462 %s :You may not reregister",user->nick);
999                 return;
1000         }
1001         /* parameters 2 and 3 are local and remote hosts, ignored when sent by client connection */
1002         if (user->registered == 3)
1003         {
1004                 /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
1005                 ConnectUser(user);
1006         }
1007 }
1008
1009 void handle_userhost(char **parameters, int pcnt, userrec *user)
1010 {
1011         char Return[MAXBUF],junk[MAXBUF];
1012         sprintf(Return,"302 %s :",user->nick);
1013         for (int i = 0; i < pcnt; i++)
1014         {
1015                 userrec *u = Find(parameters[i]);
1016                 if (u)
1017                 {
1018                         if (strchr(u->modes,'o'))
1019                         {
1020                                 sprintf(junk,"%s*=+%s@%s ",u->nick,u->ident,u->host);
1021                                 strcat(Return,junk);
1022                         }
1023                         else
1024                         {
1025                                 sprintf(junk,"%s=+%s@%s ",u->nick,u->ident,u->host);
1026                                 strcat(Return,junk);
1027                         }
1028                 }
1029         }
1030         WriteServ(user->fd,Return);
1031 }
1032
1033
1034 void handle_ison(char **parameters, int pcnt, userrec *user)
1035 {
1036         char Return[MAXBUF];
1037         sprintf(Return,"303 %s :",user->nick);
1038         for (int i = 0; i < pcnt; i++)
1039         {
1040                 userrec *u = Find(parameters[i]);
1041                 if (u)
1042                 {
1043                         strcat(Return,u->nick);
1044                         strcat(Return," ");
1045                 }
1046         }
1047         WriteServ(user->fd,Return);
1048 }
1049
1050
1051 void handle_away(char **parameters, int pcnt, userrec *user)
1052 {
1053         if (pcnt)
1054         {
1055                 strcpy(user->awaymsg,parameters[0]);
1056                 WriteServ(user->fd,"306 %s :You have been marked as being away",user->nick);
1057         }
1058         else
1059         {
1060                 strcpy(user->awaymsg,"");
1061                 WriteServ(user->fd,"305 %s :You are no longer marked as being away",user->nick);
1062         }
1063 }
1064
1065 void handle_whowas(char **parameters, int pcnt, userrec* user)
1066 {
1067         user_hash::iterator i = whowas.find(parameters[0]);
1068
1069         if (i == whowas.end())
1070         {
1071                 WriteServ(user->fd,"406 %s %s :There was no such nickname",user->nick,parameters[0]);
1072                 WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]);
1073         }
1074         else
1075         {
1076                 time_t rawtime = i->second->signon;
1077                 tm *timeinfo;
1078                 char b[MAXBUF];
1079                 
1080                 timeinfo = localtime(&rawtime);
1081                 strcpy(b,asctime(timeinfo));
1082                 b[strlen(b)-1] = '\0';
1083                 
1084                 WriteServ(user->fd,"314 %s %s %s %s * :%s",user->nick,i->second->nick,i->second->ident,i->second->dhost,i->second->fullname);
1085                 WriteServ(user->fd,"312 %s %s %s :%s",user->nick,i->second->nick,i->second->server,b);
1086                 WriteServ(user->fd,"369 %s %s :End of WHOWAS",user->nick,parameters[0]);
1087         }
1088
1089 }
1090
1091 void handle_trace(char **parameters, int pcnt, userrec *user)
1092 {
1093         for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
1094         {
1095                 if (i->second)
1096                 {
1097                         if (isnick(i->second->nick))
1098                         {
1099                                 if (strchr(i->second->modes,'o'))
1100                                 {
1101                                         WriteServ(user->fd,"205 %s :Oper 0 %s",user->nick,i->second->nick);
1102                                 }
1103                                 else
1104                                 {
1105                                         WriteServ(user->fd,"204 %s :User 0 %s",user->nick,i->second->nick);
1106                                 }
1107                         }
1108                         else
1109                         {
1110                                 WriteServ(user->fd,"203 %s :???? 0 [%s]",user->nick,i->second->host);
1111                         }
1112                 }
1113         }
1114 }
1115
1116 void handle_modules(char **parameters, int pcnt, userrec *user)
1117 {
1118         for (int i = 0; i < module_names.size(); i++)
1119         {
1120                         Version V = modules[i]->GetVersion();
1121                         char modulename[MAXBUF];
1122                         strncpy(modulename,module_names[i].c_str(),256);
1123                         WriteServ(user->fd,"900 %s :0x%08lx %d.%d.%d.%d %s",user->nick,modules[i],V.Major,V.Minor,V.Revision,V.Build,CleanFilename(modulename));
1124         }
1125 }
1126
1127 void handle_stats(char **parameters, int pcnt, userrec *user)
1128 {
1129         char Link_ServerName[MAXBUF],Link_IPAddr[MAXBUF],Link_Port[MAXBUF];
1130         if (pcnt != 1)
1131         {
1132                 return;
1133         }
1134         if (strlen(parameters[0])>1)
1135         {
1136                 /* make the stats query 1 character long */
1137                 parameters[0][1] = '\0';
1138         }
1139
1140
1141         if (!strcasecmp(parameters[0],"c"))
1142         {
1143                 for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
1144                 {
1145                         ConfValue("link","name",i,Link_ServerName,&config_f);
1146                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
1147                         ConfValue("link","port",i,Link_Port,&config_f);
1148                         WriteServ(user->fd,"213 %s C *@%s * %s %s 0 M",user->nick,Link_IPAddr,Link_ServerName,Link_Port);
1149                         WriteServ(user->fd,"244 %s H * * %s",user->nick,Link_ServerName);
1150                 }
1151         }
1152         
1153         if (!strcasecmp(parameters[0],"i"))
1154         {
1155                 int idx = 0;
1156                 for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
1157                 {
1158                         WriteServ(user->fd,"215 %s I * * * %d %d %s *",user->nick,MAXCLIENTS,idx,ServerName);
1159                         idx++;
1160                 }
1161         }
1162         
1163         if (!strcasecmp(parameters[0],"y"))
1164         {
1165                 int idx = 0;
1166                 for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
1167                 {
1168                         WriteServ(user->fd,"218 %s Y %d %d 0 %d %d",user->nick,idx,120,i->flood,i->registration_timeout);
1169                         idx++;
1170                 }
1171         }
1172
1173         if (!strcmp(parameters[0],"U"))
1174         {
1175                 for (int i = 0; i < ConfValueEnum("uline",&config_f); i++)
1176                 {
1177                         ConfValue("uline","server",i,Link_ServerName,&config_f);
1178                         WriteServ(user->fd,"248 %s U %s",user->nick,Link_ServerName);
1179                 }
1180         }
1181         
1182         if (!strcmp(parameters[0],"P"))
1183         {
1184                 int idx = 0;
1185                 for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
1186                 {
1187                         if (strchr(i->second->modes,'o'))
1188                         {
1189                                 WriteServ(user->fd,"249 %s :%s (%s@%s) Idle: %d",user->nick,i->second->nick,i->second->ident,i->second->dhost,(time(NULL) - i->second->idle_lastmsg));
1190                                 idx++;
1191                         }
1192                 }
1193                 WriteServ(user->fd,"249 %s :%d OPER(s)",user->nick,idx);
1194         //249 [Brain] :bwoadway-monitor (~wgmon@204.152.186.58) Idle: 18
1195         }
1196         
1197         if (!strcmp(parameters[0],"k"))
1198         {
1199                 stats_k(user);
1200         }
1201
1202         if (!strcmp(parameters[0],"g"))
1203         {
1204                 stats_g(user);
1205         }
1206
1207         if (!strcmp(parameters[0],"q"))
1208         {
1209                 stats_q(user);
1210         }
1211
1212         if (!strcmp(parameters[0],"Z"))
1213         {
1214                 stats_z(user);
1215         }
1216
1217         /* stats m (list number of times each command has been used, plus bytecount) */
1218         if (!strcmp(parameters[0],"m"))
1219         {
1220                 for (int i = 0; i < cmdlist.size(); i++)
1221                 {
1222                         if (cmdlist[i].handler_function)
1223                         {
1224                                 if (cmdlist[i].use_count)
1225                                 {
1226                                         /* RPL_STATSCOMMANDS */
1227                                         WriteServ(user->fd,"212 %s %s %d %d",user->nick,cmdlist[i].command,cmdlist[i].use_count,cmdlist[i].total_bytes);
1228                                 }
1229                         }
1230                 }
1231                         
1232         }
1233
1234         /* stats z (debug and memory info) */
1235         if (!strcmp(parameters[0],"z"))
1236         {
1237                 WriteServ(user->fd,"249 %s :Users(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,clientlist.size(),clientlist.size()*sizeof(userrec),clientlist.bucket_count());
1238                 WriteServ(user->fd,"249 %s :Channels(HASH_MAP) %d (%d bytes, %d buckets)",user->nick,chanlist.size(),chanlist.size()*sizeof(chanrec),chanlist.bucket_count());
1239                 WriteServ(user->fd,"249 %s :Commands(VECTOR) %d (%d bytes)",user->nick,cmdlist.size(),cmdlist.size()*sizeof(command_t));
1240                 WriteServ(user->fd,"249 %s :MOTD(VECTOR) %d, RULES(VECTOR) %d",user->nick,MOTD.size(),RULES.size());
1241                 WriteServ(user->fd,"249 %s :address_cache(HASH_MAP) %d (%d buckets)",user->nick,IP.size(),IP.bucket_count());
1242                 WriteServ(user->fd,"249 %s :Modules(VECTOR) %d (%d)",user->nick,modules.size(),modules.size()*sizeof(Module));
1243                 WriteServ(user->fd,"249 %s :ClassFactories(VECTOR) %d (%d)",user->nick,factory.size(),factory.size()*sizeof(ircd_module));
1244                 WriteServ(user->fd,"249 %s :Ports(STATIC_ARRAY) %d",user->nick,boundPortCount);
1245         }
1246         
1247         /* stats o */
1248         if (!strcmp(parameters[0],"o"))
1249         {
1250                 for (int i = 0; i < ConfValueEnum("oper",&config_f); i++)
1251                 {
1252                         char LoginName[MAXBUF];
1253                         char HostName[MAXBUF];
1254                         char OperType[MAXBUF];
1255                         ConfValue("oper","name",i,LoginName,&config_f);
1256                         ConfValue("oper","host",i,HostName,&config_f);
1257                         ConfValue("oper","type",i,OperType,&config_f);
1258                         WriteServ(user->fd,"243 %s O %s * %s %s 0",user->nick,HostName,LoginName,OperType);
1259                 }
1260         }
1261         
1262         /* stats l (show user I/O stats) */
1263         if (!strcmp(parameters[0],"l"))
1264         {
1265                 WriteServ(user->fd,"211 %s :server:port nick bytes_in cmds_in bytes_out cmds_out",user->nick);
1266                 for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); i++)
1267                 {
1268                         if (isnick(i->second->nick))
1269                         {
1270                                 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);
1271                         }
1272                         else
1273                         {
1274                                 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);
1275                         }
1276                         
1277                 }
1278         }
1279         
1280         /* stats u (show server uptime) */
1281         if (!strcmp(parameters[0],"u"))
1282         {
1283                 time_t current_time = 0;
1284                 current_time = time(NULL);
1285                 time_t server_uptime = current_time - startup_time;
1286                 struct tm* stime;
1287                 stime = gmtime(&server_uptime);
1288                 /* i dont know who the hell would have an ircd running for over a year nonstop, but
1289                  * Craig suggested this, and it seemed a good idea so in it went */
1290                 if (stime->tm_year > 70)
1291                 {
1292                         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);
1293                 }
1294                 else
1295                 {
1296                         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);
1297                 }
1298         }
1299
1300         WriteServ(user->fd,"219 %s %s :End of /STATS report",user->nick,parameters[0]);
1301         WriteOpers("*** Notice: Stats '%s' requested by %s (%s@%s)",parameters[0],user->nick,user->ident,user->host);
1302         
1303 }
1304
1305 void handle_connect(char **parameters, int pcnt, userrec *user)
1306 {
1307         char Link_ServerName[1024];
1308         char Link_IPAddr[1024];
1309         char Link_Port[1024];
1310         char Link_Pass[1024];
1311         int LinkPort;
1312         bool found = false;
1313         
1314         for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
1315         {
1316                 if (!found)
1317                 {
1318                         ConfValue("link","name",i,Link_ServerName,&config_f);
1319                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
1320                         ConfValue("link","port",i,Link_Port,&config_f);
1321                         ConfValue("link","sendpass",i,Link_Pass,&config_f);
1322                         log(DEBUG,"(%d) Comparing against name='%s', ipaddr='%s', port='%s', recvpass='%s'",i,Link_ServerName,Link_IPAddr,Link_Port,Link_Pass);
1323                         LinkPort = atoi(Link_Port);
1324                         if (match(Link_ServerName,parameters[0])) {
1325                                 found = true;
1326                                 break;
1327                         }
1328                 }
1329         }
1330         
1331         if (!found) {
1332                 WriteServ(user->fd,"NOTICE %s :*** Failed to connect to %s: No servers matching this pattern are configured for linking.",user->nick,parameters[0]);
1333                 return;
1334         }
1335         
1336         // TODO: Perform a check here to stop a server being linked twice!
1337
1338         WriteServ(user->fd,"NOTICE %s :*** Connecting to %s (%s) port %s...",user->nick,Link_ServerName,Link_IPAddr,Link_Port);
1339
1340         if (me[defaultRoute])
1341         {
1342                 me[defaultRoute]->BeginLink(Link_IPAddr,LinkPort,Link_Pass,Link_ServerName,me[defaultRoute]->port);
1343                 return;
1344         }
1345         else
1346         {
1347                 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);
1348         }
1349 }
1350
1351 void handle_squit(char **parameters, int pcnt, userrec *user)
1352 {
1353         // send out an squit across the mesh and then clear the server list (for local squit)
1354         if (!pcnt)
1355         {
1356                 WriteOpers("SQUIT command issued by %s",user->nick);
1357                 char buffer[MAXBUF];
1358                 snprintf(buffer,MAXBUF,"& %s",ServerName);
1359                 NetSendToAll(buffer);
1360                 DoSplitEveryone();
1361         }
1362         else
1363         {
1364                 WriteServ(user->fd,"NOTICE :*** Remote SQUIT not supported yet.");
1365         }
1366 }
1367
1368 void handle_links(char **parameters, int pcnt, userrec *user)
1369 {
1370         WriteServ(user->fd,"364 %s %s %s :0 %s",user->nick,ServerName,ServerName,ServerDesc);
1371         for (int j = 0; j < 32; j++)
1372         {
1373                 if (me[j] != NULL)
1374                 {
1375                         for (int k = 0; k < me[j]->connectors.size(); k++)
1376                         {
1377                                 WriteServ(user->fd,"364 %s %s %s :1 %s",user->nick,me[j]->connectors[k].GetServerName().c_str(),ServerName,me[j]->connectors[k].GetDescription().c_str());
1378                         }
1379                 }
1380         }
1381         WriteServ(user->fd,"365 %s * :End of /LINKS list.",user->nick);
1382 }
1383
1384 void handle_map(char **parameters, int pcnt, userrec *user)
1385 {
1386         char line[MAXBUF];
1387         snprintf(line,MAXBUF,"006 %s :%s",user->nick,ServerName);
1388         while (strlen(line) < 50)
1389                 strcat(line," ");
1390         WriteServ(user->fd,"%s%d (%.2f%%)",line,local_count(),(float)(((float)local_count()/(float)usercnt())*100));
1391         for (int j = 0; j < 32; j++)
1392         {
1393                 if (me[j] != NULL)
1394                 {
1395                         for (int k = 0; k < me[j]->connectors.size(); k++)
1396                         {
1397                                 snprintf(line,MAXBUF,"006 %s :%c-%s",user->nick,islast(me[j]->connectors[k].GetServerName().c_str()),me[j]->connectors[k].GetServerName().c_str());
1398                                 while (strlen(line) < 50)
1399                                         strcat(line," ");
1400                                 WriteServ(user->fd,"%s%d (%.2f%%)",line,map_count(me[j]->connectors[k].GetServerName().c_str()),(float)(((float)map_count(me[j]->connectors[k].GetServerName().c_str())/(float)usercnt())*100));
1401                         }
1402                 }
1403         }
1404         WriteServ(user->fd,"007 %s :End of /MAP",user->nick);
1405 }
1406
1407 bool is_uline(const char* server)
1408 {
1409         char ServName[MAXBUF];
1410         int i,j;
1411
1412         for (int i = 0; i < ConfValueEnum("uline",&config_f); i++)
1413         {
1414                 ConfValue("uline","server",i,ServName,&config_f);
1415                 if (!strcasecmp(server,ServName))
1416                 {
1417                         return true;
1418                 }
1419         }
1420         return false;
1421 }
1422
1423
1424 void handle_oper(char **parameters, int pcnt, userrec *user)
1425 {
1426         char LoginName[MAXBUF];
1427         char Password[MAXBUF];
1428         char OperType[MAXBUF];
1429         char TypeName[MAXBUF];
1430         char Hostname[MAXBUF];
1431         int i,j;
1432
1433         for (int i = 0; i < ConfValueEnum("oper",&config_f); i++)
1434         {
1435                 ConfValue("oper","name",i,LoginName,&config_f);
1436                 ConfValue("oper","password",i,Password,&config_f);
1437                 if ((!strcmp(LoginName,parameters[0])) && (!strcmp(Password,parameters[1])))
1438                 {
1439                         /* correct oper credentials */
1440                         ConfValue("oper","type",i,OperType,&config_f);
1441                         WriteOpers("*** %s (%s@%s) is now an IRC operator of type %s",user->nick,user->ident,user->host,OperType);
1442                         WriteServ(user->fd,"381 %s :You are now an IRC operator of type %s",user->nick,OperType);
1443                         WriteServ(user->fd,"MODE %s :+o",user->nick);
1444                         char global[MAXBUF];
1445                         snprintf(global,MAXBUF,"M %s +o",user->nick);
1446                         NetSendToAll(global);
1447                         for (j =0; j < ConfValueEnum("type",&config_f); j++)
1448                         {
1449                                 ConfValue("type","name",j,TypeName,&config_f);
1450                                 if (!strcmp(TypeName,OperType))
1451                                 {
1452                                         /* found this oper's opertype */
1453                                         snprintf(global,MAXBUF,"| %s %s",user->nick,TypeName);
1454                                         NetSendToAll(global);
1455                                         ConfValue("type","host",j,Hostname,&config_f);
1456                                         ChangeDisplayedHost(user,Hostname);
1457                                         strncpy(user->oper,TypeName,NICKMAX);
1458                                 }
1459                         }
1460                         if (!strchr(user->modes,'o'))
1461                         {
1462                                 strcat(user->modes,"o");
1463                         }
1464                         FOREACH_MOD OnOper(user);
1465                         return;
1466                 }
1467         }
1468         /* no such oper */
1469         WriteServ(user->fd,"491 %s :Invalid oper credentials",user->nick);
1470         WriteOpers("*** WARNING! Failed oper attempt by %s!%s@%s!",user->nick,user->ident,user->host);
1471 }
1472
1473 void handle_nick(char **parameters, int pcnt, userrec *user)
1474 {
1475         if (pcnt < 1) 
1476         {
1477                 log(DEBUG,"not enough params for handle_nick");
1478                 return;
1479         }
1480         if (!parameters[0])
1481         {
1482                 log(DEBUG,"invalid parameter passed to handle_nick");
1483                 return;
1484         }
1485         if (!strlen(parameters[0]))
1486         {
1487                 log(DEBUG,"zero length new nick passed to handle_nick");
1488                 return;
1489         }
1490         if (!user)
1491         {
1492                 log(DEBUG,"invalid user passed to handle_nick");
1493                 return;
1494         }
1495         if (!user->nick)
1496         {
1497                 log(DEBUG,"invalid old nick passed to handle_nick");
1498                 return;
1499         }
1500         if (!strcasecmp(user->nick,parameters[0]))
1501         {
1502                 log(DEBUG,"old nick is new nick, skipping");
1503                 return;
1504         }
1505         else
1506         {
1507                 if (strlen(parameters[0]) > 1)
1508                 {
1509                         if (parameters[0][0] == ':')
1510                         {
1511                                 *parameters[0]++;
1512                         }
1513                 }
1514                 if (matches_qline(parameters[0]))
1515                 {
1516                         WriteOpers("*** Q-Lined nickname %s from %s!%s@%s: %s",parameters[0],user->nick,user->ident,user->host,matches_qline(parameters[0]));
1517                         WriteServ(user->fd,"432 %s %s :Invalid nickname: %s",user->nick,parameters[0],matches_qline(parameters[0]));
1518                         return;
1519                 }
1520                 if ((Find(parameters[0])) && (Find(parameters[0]) != user))
1521                 {
1522                         WriteServ(user->fd,"433 %s %s :Nickname is already in use.",user->nick,parameters[0]);
1523                         return;
1524                 }
1525         }
1526         if (isnick(parameters[0]) == 0)
1527         {
1528                 WriteServ(user->fd,"432 %s %s :Erroneous Nickname",user->nick,parameters[0]);
1529                 return;
1530         }
1531
1532         if (user->registered == 7)
1533         {
1534                 int MOD_RESULT = 0;
1535                 FOREACH_RESULT(OnUserPreNick(user,parameters[0]));
1536                 if (MOD_RESULT) {
1537                         // if a module returns true, the nick change is silently forbidden.
1538                         return;
1539                 }
1540
1541                 WriteCommon(user,"NICK %s",parameters[0]);
1542                 
1543                 // Q token must go to ALL servers!!!
1544                 char buffer[MAXBUF];
1545                 snprintf(buffer,MAXBUF,"n %s %s",user->nick,parameters[0]);
1546                 NetSendToAll(buffer);
1547                 
1548         }
1549         
1550         /* change the nick of the user in the users_hash */
1551         user = ReHashNick(user->nick, parameters[0]);
1552         /* actually change the nick within the record */
1553         if (!user) return;
1554         if (!user->nick) return;
1555
1556         strncpy(user->nick, parameters[0],NICKMAX);
1557
1558         log(DEBUG,"new nick set: %s",user->nick);
1559         
1560         if (user->registered < 3)
1561                 user->registered = (user->registered | 2);
1562         if (user->registered == 3)
1563         {
1564                 /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */
1565                 ConnectUser(user);
1566         }
1567 }
1568
1569
1570
1571 void handle_V(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1572 {
1573         char* src = strtok(params," ");
1574         char* dest = strtok(NULL," :");
1575         char* text = strtok(NULL,"\r\n");
1576         text++;
1577         
1578         userrec* user = Find(src);
1579         if (user)
1580         {
1581                 userrec* dst = Find(dest);
1582                 
1583                 if (dst)
1584                 {
1585                         WriteTo(user, dst, "NOTICE %s :%s", dst->nick, text);
1586                 }
1587                 else
1588                 {
1589                         chanrec* d = FindChan(dest);
1590                         if (d)
1591                         {
1592                                 ChanExceptSender(d, user, "NOTICE %s :%s", d->name, text);
1593                         }
1594                 }
1595         }
1596         
1597 }
1598
1599
1600 void handle_P(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1601 {
1602         char* src = strtok(params," ");
1603         char* dest = strtok(NULL," :");
1604         char* text = strtok(NULL,"\r\n");
1605         text++;
1606         
1607         userrec* user = Find(src);
1608         if (user)
1609         {
1610                 userrec* dst = Find(dest);
1611                 
1612                 if (dst)
1613                 {
1614                         WriteTo(user, dst, "PRIVMSG %s :%s", dst->nick, text);
1615                 }
1616                 else
1617                 {
1618                         chanrec* d = FindChan(dest);
1619                         if (d)
1620                         {
1621                                 ChanExceptSender(d, user, "PRIVMSG %s :%s", d->name, text);
1622                         }
1623                 }
1624         }
1625         
1626 }
1627
1628 void handle_i(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1629 {
1630         char* nick = strtok(params," ");
1631         char* from = strtok(NULL," ");
1632         char* channel = strtok(NULL," ");
1633         userrec* u = Find(nick);
1634         userrec* user = Find(from);
1635         chanrec* c = FindChan(channel);
1636         if ((c) && (u) && (user))
1637         {
1638                 u->InviteTo(c->name);
1639                 WriteFrom(u->fd,user,"INVITE %s :%s",u->nick,c->name);
1640         }
1641 }
1642
1643 void handle_t(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1644 {
1645         char* setby = strtok(params," ");
1646         char* channel = strtok(NULL," :");
1647         char* topic = strtok(NULL,"\r\n");
1648         topic++;
1649         userrec* u = Find(setby);
1650         chanrec* c = FindChan(channel);
1651         if ((c) && (u))
1652         {
1653                 WriteChannelLocal(c,u,"TOPIC %s :%s",c->name,topic);
1654                 strncpy(c->topic,topic,MAXTOPIC);
1655                 strncpy(c->setby,u->nick,NICKMAX);
1656                 c->topicset = time(NULL);
1657         }       
1658 }
1659         
1660
1661 void handle_T(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1662 {
1663         char* tm = strtok(params," ");
1664         char* setby = strtok(NULL," ");
1665         char* channel = strtok(NULL," :");
1666         char* topic = strtok(NULL,"\r\n");
1667         topic++;
1668         time_t TS = atoi(tm);
1669         chanrec* c = FindChan(channel);
1670         if (c)
1671         {
1672                 // in the case of topics and TS, the *NEWER* 
1673                 if (TS <= c->topicset)
1674                 {
1675                         WriteChannelLocal(c,NULL,"TOPIC %s :%s",c->name,topic);
1676                         strncpy(c->topic,topic,MAXTOPIC);
1677                         strncpy(c->setby,setby,NICKMAX);
1678                 }
1679         }       
1680 }
1681         
1682 void handle_M(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1683 {
1684         char* pars[128];
1685         char original[MAXBUF],target[MAXBUF];
1686         strncpy(original,params,MAXBUF);
1687         int index = 0;
1688         char* parameter = strtok(params," ");
1689         strncpy(target,parameter,MAXBUF);
1690         while (parameter)
1691         {
1692                 if (parameter[0] == ':')
1693                         parameter++;
1694                 pars[index++] = parameter;
1695                 parameter = strtok(NULL," ");
1696         }
1697         log(DEBUG,"*** MODE: %s %s",pars[0],pars[1]);
1698         merge_mode(pars,index);
1699         if (FindChan(target))
1700         {
1701                 WriteChannelLocal(FindChan(target), NULL, "MODE %s",original);
1702         }
1703         if (Find(target))
1704         {
1705                 Write(Find(target)->fd,":%s MODE %s",ServerName,original);
1706         }
1707 }
1708
1709 // m is modes set by users only (not servers) valid targets are channels or users.
1710
1711 void handle_m(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1712 {
1713         // m blah #chatspike +b *!test@*4
1714         char* pars[128];
1715         char original[MAXBUF];
1716         strncpy(original,params,MAXBUF);
1717         
1718         if (!strchr(params,' '))
1719         {
1720                 WriteOpers("WARNING! 'm' token in data stream without any parameters! Something fishy is going on!");
1721                 return;
1722         }
1723         
1724         int index = 0;
1725         
1726         char* src = strtok(params," ");
1727         userrec* user = Find(src);
1728         
1729         if (user)
1730         {
1731                 log(DEBUG,"Found user: %s",user->nick);
1732                 char* parameter = strtok(NULL," ");
1733                 while (parameter)
1734                 {
1735                         pars[index++] = parameter;
1736                         parameter = strtok(NULL," ");
1737                 }
1738                 
1739                 log(DEBUG,"Calling merge_mode2");
1740                 merge_mode2(pars,index,user);
1741         }
1742 }
1743
1744
1745 void handle_L(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1746 {
1747         char* nick = strtok(params," ");
1748         char* channel = strtok(NULL," :");
1749         char* reason = strtok(NULL,"\r\n");
1750         userrec* user = Find(nick);
1751         reason++;
1752         if (user)
1753         {
1754                 if (strcmp(reason,""))
1755                 {
1756                         del_channel(user,channel,reason,true);
1757                 }
1758                 else
1759                 {
1760                         del_channel(user,channel,NULL,true);
1761                 }
1762         }
1763 }
1764
1765 void handle_K(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1766 {
1767         char* src = strtok(params," ");
1768         char* nick = strtok(NULL," :");
1769         char* reason = strtok(NULL,"\r\n");
1770         char kreason[MAXBUF];
1771         reason++;
1772
1773         userrec* u = Find(nick);
1774         userrec* user = Find(src);
1775         
1776         if ((user) && (u))
1777         {
1778                 WriteTo(user, u, "KILL %s :%s!%s!%s!%s (%s)", u->nick, source->name, ServerName, user->dhost,user->nick,reason);
1779                 WriteOpers("*** Remote kill from %s by %s: %s!%s@%s (%s)",source->name,user->nick,u->nick,u->ident,u->host,reason);
1780                 snprintf(kreason,MAXBUF,"[%s] Killed (%s (%s))",source->name,user->nick,reason);
1781                 kill_link(u,kreason);
1782         }
1783 }
1784
1785 void handle_Q(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1786 {
1787         char* nick = strtok(params," :");
1788         char* reason = strtok(NULL,"\r\n");
1789         reason++;
1790
1791         userrec* user = Find(nick);
1792         
1793         if (user)
1794         {
1795                 if (strlen(reason)>MAXQUIT)
1796                 {
1797                         reason[MAXQUIT-1] = '\0';
1798                 }
1799
1800
1801                 WriteCommonExcept(user,"QUIT :%s",reason);
1802
1803                 user_hash::iterator iter = clientlist.find(user->nick);
1804         
1805                 if (iter != clientlist.end())
1806                 {
1807                         log(DEBUG,"deleting user hash value %d",iter->second);
1808                         if ((iter->second) && (user->registered == 7)) {
1809                                 delete iter->second;
1810                         }
1811                         clientlist.erase(iter);
1812                 }
1813
1814                 purge_empty_chans();
1815         }
1816 }
1817
1818 void handle_n(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1819 {
1820         char* oldnick = strtok(params," ");
1821         char* newnick = strtok(NULL," ");
1822         
1823         userrec* user = Find(oldnick);
1824         
1825         if (user)
1826         {
1827                 WriteCommon(user,"NICK %s",newnick);
1828                 if (is_uline(tcp_host))
1829                 {
1830                         int MOD_RESULT = 0;
1831                         FOREACH_RESULT(OnUserPreNick(user,newnick));
1832                         if (MOD_RESULT) {
1833                                 // if a module returns true, the nick change couldnt be allowed
1834                                 kill_link(user,"Nickname collision");
1835                                 return;
1836                         }
1837                         if (matches_qline(newnick))
1838                         {
1839                                 kill_link(user,"Nickname collision");
1840                                 return;
1841                         }
1842         
1843                         // broadcast this because its a services thingy
1844                         char buffer[MAXBUF];
1845                         snprintf(buffer,MAXBUF,"n %s %s",user->nick,newnick);
1846                         NetSendToAll(buffer);
1847                 }
1848                 user = ReHashNick(user->nick, newnick);
1849                 if (!user) return;
1850                 if (!user->nick) return;
1851                 strncpy(user->nick, newnick,NICKMAX);
1852                 log(DEBUG,"new nick set: %s",user->nick);
1853         }
1854 }
1855
1856 // k <SOURCE> <DEST> <CHANNEL> :<REASON>
1857 void handle_k(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1858 {
1859         char* src = strtok(params," ");
1860         char* dest = strtok(NULL," ");
1861         char* channel = strtok(NULL," :");
1862         char* reason = strtok(NULL,"\r\n");
1863         reason++;
1864         userrec* s = Find(src);
1865         userrec* d = Find(dest);
1866         chanrec* c = FindChan(channel);
1867         if ((s) && (d) && (c))
1868         {
1869                 kick_channel(s,d,c,reason);
1870                 return;
1871         }
1872         d = Find(channel);
1873         c = FindChan(dest);
1874         if ((s) && (d) && (c))
1875         {
1876                 kick_channel(s,d,c,reason);
1877                 return;
1878         }
1879 }
1880
1881 void handle_AT(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1882 {
1883         char* who = strtok(params," :");
1884         char* text = strtok(NULL,"\r\n");
1885         text++;
1886         userrec* s = Find(who);
1887         if (s)
1888         {
1889                 WriteWallOps(s,true,text);
1890         }
1891 }
1892
1893 void handle_H(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1894 {
1895         log(DEBUG,"Adding ULined server %s to my map",params);
1896         ircd_connector s;
1897         s.SetState(STATE_DISCONNECTED);
1898         s.SetServerName(params);
1899         source->connectors.push_back(s);
1900         WriteOpers("Non-Mesh server %s has joined the network",params);
1901 }
1902
1903 void handle_N(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1904 {
1905         char* tm = strtok(params," ");
1906         char* nick = strtok(NULL," ");
1907         char* host = strtok(NULL," ");
1908         char* dhost = strtok(NULL," ");
1909         char* ident = strtok(NULL," ");
1910         char* modes = strtok(NULL," ");
1911         char* ipaddr = strtok(NULL," ");
1912         char* server = strtok(NULL," :");
1913         char* gecos = strtok(NULL,"\r\n");
1914         gecos++;
1915         modes++;
1916         time_t TS = atoi(tm);
1917         user_hash::iterator iter = clientlist.find(nick);
1918         if (iter != clientlist.end())
1919         {
1920                 // nick collision
1921                 WriteOpers("Nickname collision: %s@%s != %s@%s",nick,server,iter->second->nick,iter->second->server);
1922                 char str[MAXBUF];
1923                 snprintf(str,MAXBUF,"Killed (Nick Collision (%s@%s < %s@%s))",nick,server,iter->second->nick,iter->second->server);
1924                 WriteServ(iter->second->fd, "KILL %s :%s",iter->second->nick,str);
1925                 kill_link(iter->second,str);
1926         }
1927         clientlist[nick] = new userrec();
1928         // remote users have an fd of -1. This is so that our Write abstraction
1929         // routines know to route any messages to this record away to whatever server
1930         // theyre on.
1931         clientlist[nick]->fd = -1;
1932         strncpy(clientlist[nick]->nick, nick,NICKMAX);
1933         strncpy(clientlist[nick]->host, host,160);
1934         strncpy(clientlist[nick]->dhost, dhost,160);
1935         strncpy(clientlist[nick]->server, server,256);
1936         strncpy(clientlist[nick]->ident, ident,10); // +1 char to compensate for tilde
1937         strncpy(clientlist[nick]->fullname, gecos,128);
1938         clientlist[nick]->signon = TS;
1939         clientlist[nick]->nping = 0; // this is ignored for a remote user anyway.
1940         clientlist[nick]->lastping = 1;
1941         clientlist[nick]->port = 0; // so is this...
1942         clientlist[nick]->registered = 7; // this however we need to set for them to receive messages and appear online
1943         clientlist[nick]->idle_lastmsg = time(NULL); // this is unrealiable and wont actually be used locally
1944         for (int i = 0; i < MAXCHANS; i++)
1945         {
1946                 clientlist[nick]->chans[i].channel = NULL;
1947                 clientlist[nick]->chans[i].uc_modes = 0;
1948         }
1949 }
1950
1951 void handle_F(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1952 {
1953         long tdiff = time(NULL) - atoi(params);
1954         if (tdiff)
1955                 WriteOpers("TS split for %s -> %s: %d",source->name,reply->name,tdiff);
1956 }
1957
1958 void handle_a(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1959 {
1960         char* nick = strtok(params," :");
1961         char* gecos = strtok(NULL,"\r\n");
1962         
1963         userrec* user = Find(nick);
1964
1965         if (user)
1966                 strncpy(user->fullname,gecos,MAXBUF);
1967 }
1968
1969 void handle_b(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1970 {
1971         char* nick = strtok(params," ");
1972         char* host = strtok(NULL," ");
1973         
1974         userrec* user = Find(nick);
1975
1976         if (user)
1977                 strncpy(user->dhost,host,160);
1978 }
1979
1980 void handle_plus(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
1981 {
1982         // %s %s %d %d
1983         // + test3.chatspike.net 7010 -2016508415
1984         char* servername = strtok(params," ");
1985         char* ipaddr = strtok(NULL," ");
1986         char* ipport = strtok(NULL," ");
1987         char* cookie = strtok(NULL," ");
1988         log(DEBUG,"*** Connecting back to %s:%d",ipaddr,atoi(ipport));
1989
1990
1991         bool conn_already = false;
1992         for (int i = 0; i < 32; i++)
1993         {
1994                 if (me[i] != NULL)
1995                 {
1996                         for (int j = 0; j < me[i]->connectors.size(); j++)
1997                         {
1998                                 if (!strcasecmp(me[i]->connectors[j].GetServerName().c_str(),servername))
1999                                 {
2000                                         if (me[i]->connectors[j].GetServerPort() == atoi(ipport))
2001                                         {
2002                                                 log(DEBUG,"Already got a connection to %s:%d, ignoring +",ipaddr,atoi(ipport));
2003                                                 conn_already = true;
2004                                         }
2005                                 }
2006                         }
2007                 }
2008         }
2009         if (!conn_already)
2010                 me[defaultRoute]->MeshCookie(ipaddr,atoi(ipport),atoi(cookie),servername);
2011 }
2012
2013 void handle_R(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2014 {
2015         char* server = strtok(params," ");
2016         char* data = strtok(NULL,"\r\n");
2017         if ((!data) || (!server))
2018         {
2019                 log(DEBUG,"Someones playing silly buggers, attempting to send to a null server or send a null message (BUG?)");
2020                 return;
2021         }
2022                 
2023         log(DEBUG,"Forwarded packet '%s' to '%s'",data,server);
2024         NetSendToOne(server,data);
2025 }
2026
2027 void handle_J(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2028 {
2029         // IMPORTANT NOTE
2030         // The J token currently has no timestamp - this needs looking at
2031         // because it will allow splitriding.
2032         char* nick = strtok(params," ");
2033         char* channel = strtok(NULL," ");
2034         userrec* user = Find(nick);
2035         while (channel)
2036         {
2037                 if ((user != NULL) && (strcmp(channel,"")))
2038                 {
2039                         char privilage = '\0';
2040                         if (channel[0] != '#')
2041                         {
2042                                 privilage = channel[0];
2043                                 channel++;
2044                         }
2045                         add_channel(user,channel,"",true);
2046
2047                         // now work out the privilages they should have on each channel
2048                         // and send the appropriate servermodes.
2049                         for (int i = 0; i != MAXCHANS; i++)
2050                         {
2051                                 if (user->chans[i].channel)
2052                                 {
2053                                         if (!strcasecmp(user->chans[i].channel->name,channel))
2054                                         {
2055                                                 if (privilage == '@')
2056                                                 {
2057                                                         user->chans[i].uc_modes = user->chans[i].uc_modes | UCMODE_OP;
2058                                                         WriteChannelLocal(user->chans[i].channel, NULL, "MODE %s +o %s",channel,user->nick);
2059                                                 }
2060                                                 if (privilage == '%')
2061                                                 {
2062                                                         user->chans[i].uc_modes = user->chans[i].uc_modes | UCMODE_HOP;
2063                                                         WriteChannelLocal(user->chans[i].channel, NULL, "MODE %s +h %s",channel,user->nick);
2064                                                 }
2065                                                 if (privilage == '+')
2066                                                 {
2067                                                         user->chans[i].uc_modes = user->chans[i].uc_modes | UCMODE_VOICE;
2068                                                         WriteChannelLocal(user->chans[i].channel, NULL, "MODE %s +v %s",channel,user->nick);
2069                                                 }
2070                                         }
2071                                 }
2072                         }
2073
2074                 }
2075                 channel = strtok(NULL," ");
2076         }
2077 }
2078
2079 void handle_dollar(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2080 {
2081         log(DEBUG,"Storing routing table...");
2082         char* sourceserver = strtok(params," ");
2083         char* server = strtok(NULL," ");
2084         for (int i = 0; i < 32; i++)
2085         {
2086                 if (me[i] != NULL)
2087                 {
2088                         for (int j = 0; j < me[i]->connectors.size(); j++)
2089                         {
2090                                 if (!strcasecmp(me[i]->connectors[j].GetServerName().c_str(),sourceserver))
2091                                 {
2092                                         me[i]->connectors[j].routes.clear();
2093                                         log(DEBUG,"Found entry for source server.");
2094                                         while (server)
2095                                         {
2096                                                 // store each route
2097                                                 me[i]->connectors[j].routes.push_back(server);
2098                                                 log(DEBUG,"*** Stored route: %s -> %s -> %s",ServerName,sourceserver,server);
2099                                                 server = strtok(NULL," ");
2100                                         }
2101                                         return;
2102                                 }
2103                         }
2104                 }
2105         }
2106         log(DEBUG,"Warning! routing table received from nonexistent server!");
2107 }
2108
2109 void handle_amp(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2110 {
2111         log(DEBUG,"Netsplit! %s split from mesh, removing!",params);
2112         WriteOpers("*** NOTICE - Controlled netsplit: %s split from %s",params,ServerName);
2113         bool go_again = true;
2114         while (go_again)
2115         {
2116                 go_again = false;
2117                 for (int i = 0; i < 32; i++)
2118                 {
2119                         if (me[i] != NULL)
2120                         {
2121                                 for (vector<ircd_connector>::iterator j = me[i]->connectors.begin(); j != me[i]->connectors.end(); j++)
2122                                 {
2123                                         if (!strcasecmp(j->GetServerName().c_str(),params))
2124                                         {
2125                                                 j->routes.clear();
2126                                                 j->CloseConnection();
2127                                                 me[i]->connectors.erase(j);
2128                                                 go_again = true;
2129                                                 break;
2130                                         }
2131                                 }
2132                         }
2133                 }
2134         }
2135         log(DEBUG,"Removed server. Will remove clients...");
2136         // iterate through the userlist and remove all users on this server.
2137         // because we're dealing with a mesh, we dont have to deal with anything
2138         // "down-route" from this server (nice huh)
2139         go_again = true;
2140         char reason[MAXBUF];
2141         snprintf(reason,MAXBUF,"%s %s",ServerName,params);
2142         while (go_again)
2143         {
2144                 go_again = false;
2145                 for (user_hash::const_iterator u = clientlist.begin(); u != clientlist.end(); u++)
2146                 {
2147                         if (!strcasecmp(u->second->server,params))
2148                         {
2149                                 kill_link(u->second,reason);
2150                                 go_again = true;
2151                                 break;
2152                         }
2153                 }
2154         }
2155 }
2156
2157 long authcookie;
2158
2159 void handle_hash(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2160 {
2161         // # <mask> <who-set-it> <time-set> <duration> :<reason>
2162         log(DEBUG,"Adding G-line");
2163         char* mask = strtok(params," ");
2164         char* who = strtok(NULL," ");
2165         char* create_time = strtok(NULL," ");
2166         char* duration = strtok(NULL," :");
2167         char* reason = strtok(NULL,"\r\n");
2168         add_gline(atoi(duration),who,reason,mask);
2169         // we must update the creation time on this gline
2170         // now that we've added it, or it wont expire at the right time.
2171         gline_set_creation_time(mask,atoi(create_time));
2172         if (!atoi(duration))
2173         {
2174                 WriteOpers("*** %s Added permenant G-Line on %s.",who,mask);
2175         }
2176         else
2177         {
2178                 WriteOpers("*** %s Added timed G-Line on %s to expire in %d seconds.",who,mask,atoi(duration));
2179         }
2180         apply_lines();
2181 }
2182
2183 void handle_dot(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2184 {
2185         log(DEBUG,"Removing G-line");
2186         char* mask = strtok(params," ");
2187         char* who = strtok(NULL," ");
2188         if (mask)
2189         {
2190                 if (del_gline(mask))
2191                 {
2192                         if (who)
2193                         {
2194                                 WriteOpers("*** %s Removed G-line on %s.",who,mask);
2195                         }
2196                 }
2197         }
2198 }
2199
2200 void handle_add_sqline(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2201 {
2202         // { <mask> <who-set-it> <time-set> <duration> :<reason>
2203         log(DEBUG,"Adding Q-line");
2204         char* mask = strtok(params," ");
2205         char* who = strtok(NULL," ");
2206         char* create_time = strtok(NULL," ");
2207         char* duration = strtok(NULL," :");
2208         char* reason = strtok(NULL,"\r\n");
2209         add_qline(atoi(duration),who,reason,mask);
2210         // we must update the creation time on this gline
2211         // now that we've added it, or it wont expire at the right time.
2212         qline_set_creation_time(mask,atoi(create_time));
2213         qline_make_global(mask);
2214         if (!atoi(duration))
2215         {
2216                 WriteOpers("*** %s Added permenant Q-Line on %s.",who,mask);
2217         }
2218         else
2219         {
2220                 WriteOpers("*** %s Added timed Q-Line on %s to expire in %d seconds.",who,mask,atoi(duration));
2221         }
2222         apply_lines();
2223 }
2224
2225 void handle_del_sqline(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2226 {
2227         log(DEBUG,"Removing Q-line");
2228         char* mask = strtok(params," ");
2229         char* who = strtok(NULL," ");
2230         if (mask)
2231         {
2232                 if (del_qline(mask))
2233                 {
2234                         if (who)
2235                         {
2236                                 WriteOpers("*** %s Removed Q-line on %s.",who,mask);
2237                         }
2238                 }
2239         }
2240 }
2241
2242 void handle_add_szline(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2243 {
2244         // } <mask> <who-set-it> <time-set> <duration> :<reason>
2245         log(DEBUG,"Adding Z-line");
2246         char* mask = strtok(params," ");
2247         char* who = strtok(NULL," ");
2248         char* create_time = strtok(NULL," ");
2249         char* duration = strtok(NULL," :");
2250         char* reason = strtok(NULL,"\r\n");
2251         add_zline(atoi(duration),who,reason,mask);
2252         // we must update the creation time on this gline
2253         // now that we've added it, or it wont expire at the right time.
2254         zline_set_creation_time(mask,atoi(create_time));
2255         zline_make_global(mask);
2256         if (!atoi(duration))
2257         {
2258                 WriteOpers("*** %s Added permenant Z-Line on %s.",who,mask);
2259         }
2260         else
2261         {
2262                 WriteOpers("*** %s Added timed Z-Line on %s to expire in %d seconds.",who,mask,atoi(duration));
2263         }
2264         apply_lines();
2265 }
2266
2267 void handle_del_szline(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2268 {
2269         log(DEBUG,"Removing Z-line");
2270         char* mask = strtok(params," ");
2271         char* who = strtok(NULL," ");
2272         if (mask)
2273         {
2274                 if (del_zline(mask))
2275                 {
2276                         if (who)
2277                         {
2278                                 WriteOpers("*** %s Removed Q-line on %s.",who,mask);
2279                         }
2280                 }
2281         }
2282 }
2283
2284 void handle_pipe(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host)
2285 {
2286         char* nick = strtok(params," ");
2287         char* type = strtok(params," ");
2288         userrec* u = Find(nick);
2289         if (u)
2290         {
2291                 strncpy(u->oper,type,NICKMAX);
2292         }
2293 }
2294
2295
2296 void process_restricted_commands(char token,char* params,serverrec* source,serverrec* reply, char* tcp_host,char* ipaddr,int port)
2297 {
2298         char buffer[MAXBUF];
2299
2300         switch(token)
2301         {
2302                 // Y <TS>
2303                 // start netburst
2304                 case 'Y':
2305                         nb_start = time(NULL);
2306                         WriteOpers("Server %s is starting netburst.",tcp_host);
2307                         // now broadcast this new servers address out to all servers that are linked to us,
2308                         // except the newcomer. They'll all attempt to connect back to it.
2309                         authcookie = rand()*rand();
2310                         snprintf(buffer,MAXBUF,"~ %d",authcookie);
2311                         NetSendToAll(buffer);
2312                 break;
2313                 // ~
2314                 // Store authcookie
2315                 // once stored, this authcookie permits other servers to log in
2316                 // without user or password, using it.
2317                 case '~':
2318                         auth_cookies.push_back(atoi(params));
2319                         log(DEBUG,"*** Stored auth cookie, will permit servers with auth-cookie %d",atoi(params));
2320                 break;
2321                 // connect back to a server using an authcookie
2322                 case '+':
2323                         handle_plus(token,params,source,reply,tcp_host);
2324                 break;
2325                 // routing table
2326                 case '$':
2327                         handle_dollar(token,params,source,reply,tcp_host);
2328                 break;
2329                 // node unreachable - we cant route to a server, sooooo we slit it off.
2330                 // servers can generate these for themselves for an squit.
2331                 case '&':
2332                         handle_amp(token,params,source,reply,tcp_host);
2333                 break;
2334                 // R <server> <data>
2335                 // redirect token, send all of <data> along to the given 
2336                 // server as this server has been found to still have a route to it
2337                 case 'R':
2338                         handle_R(token,params,source,reply,tcp_host);
2339                 break;
2340                 // ?
2341                 // ping
2342                 case '?':
2343                         reply->SendPacket("!",tcp_host);
2344                 break;
2345                 // ?
2346                 // pong
2347                 case '!':
2348                 break;
2349                 // *
2350                 // no operation
2351                 case '*':
2352                 break;
2353                 // N <TS> <NICK> <HOST> <DHOST> <IDENT> <MODES> <SERVER> :<GECOS>
2354                 // introduce remote client
2355                 case 'N':
2356                         handle_N(token,params,source,reply,tcp_host);
2357                 break;
2358                 // a <NICK> :<GECOS>
2359                 // change GECOS (SETNAME)
2360                 case 'a':
2361                         handle_a(token,params,source,reply,tcp_host);
2362                 break;
2363                 // b <NICK> :<HOST>
2364                 // change displayed host (SETHOST)
2365                 case 'b':
2366                         handle_b(token,params,source,reply,tcp_host);
2367                 break;
2368                 // t <NICK> <CHANNEL> :<TOPIC>
2369                 // change a channel topic
2370                 case 't':
2371                         handle_t(token,params,source,reply,tcp_host);
2372                 break;
2373                 // i <NICK> <CHANNEL>
2374                 // invite a user to a channel
2375                 case 'i':
2376                         handle_i(token,params,source,reply,tcp_host);
2377                 break;
2378                 // k <SOURCE> <DEST> <CHANNEL> :<REASON>
2379                 // kick a user from a channel
2380                 case 'k':
2381                         handle_k(token,params,source,reply,tcp_host);
2382                 break;
2383                 // n <NICK> <NEWNICK>
2384                 // change nickname of client -- a server should only be able to
2385                 // change the nicknames of clients that reside on it unless
2386                 // they are ulined.
2387                 case 'n':
2388                         handle_n(token,params,source,reply,tcp_host);
2389                 break;
2390                 // J <NICK> <CHANLIST>
2391                 // Join user to channel list, merge channel permissions
2392                 case 'J':
2393                         handle_J(token,params,source,reply,tcp_host);
2394                 break;
2395                 // T <TS> <CHANNEL> <TOPICSETTER> :<TOPIC>
2396                 // change channel topic (netburst only)
2397                 case 'T':
2398                         handle_T(token,params,source,reply,tcp_host);
2399                 break;
2400                 // M <TARGET> <MODES> [MODE-PARAMETERS]
2401                 // Server setting modes on an object
2402                 case 'M':
2403                         handle_M(token,params,source,reply,tcp_host);
2404                 break;
2405                 // m <SOURCE> <TARGET> <MODES> [MODE-PARAMETERS]
2406                 // User setting modes on an object
2407                 case 'm':
2408                         handle_m(token,params,source,reply,tcp_host);
2409                 break;
2410                 // P <SOURCE> <TARGET> :<TEXT>
2411                 // Send a private/channel message
2412                 case 'P':
2413                         handle_P(token,params,source,reply,tcp_host);
2414                 break;
2415                 // V <SOURCE> <TARGET> :<TEXT>
2416                 // Send a private/channel notice
2417                 case 'V':
2418                         handle_V(token,params,source,reply,tcp_host);
2419                 break;
2420                 // L <SOURCE> <CHANNEL> :<REASON>
2421                 // User parting a channel
2422                 case 'L':
2423                         handle_L(token,params,source,reply,tcp_host);
2424                 break;
2425                 // Q <SOURCE> :<REASON>
2426                 // user quitting
2427                 case 'Q':
2428                         handle_Q(token,params,source,reply,tcp_host);
2429                 break;
2430                 // H <SERVER>
2431                 // introduce non-meshable server (such as a services server)
2432                 case 'H':
2433                         handle_H(token,params,source,reply,tcp_host);
2434                 break;
2435                 // K <SOURCE> <DEST> :<REASON>
2436                 // remote kill
2437                 case 'K':
2438                         handle_K(token,params,source,reply,tcp_host);
2439                 break;
2440                 // @ <SOURCE> :<TEXT>
2441                 // wallops
2442                 case '@':
2443                         handle_AT(token,params,source,reply,tcp_host);
2444                 break;
2445                 // # <mask> <who-set-it> <time-set> <duration> :<reason>
2446                 // add gline
2447                 case '#':
2448                         handle_hash(token,params,source,reply,tcp_host);
2449                 break;
2450                 // . <mask> <who>
2451                 // remove gline
2452                 case '.':
2453                         handle_dot(token,params,source,reply,tcp_host);
2454                 break;
2455                 // # <mask> <who-set-it> <time-set> <duration> :<reason>
2456                 // add gline
2457                 case '{':
2458                         handle_add_sqline(token,params,source,reply,tcp_host);
2459                 break;
2460                 // . <mask> <who>
2461                 // remove gline
2462                 case '[':
2463                         handle_del_sqline(token,params,source,reply,tcp_host);
2464                 break;
2465                 // # <mask> <who-set-it> <time-set> <duration> :<reason>
2466                 // add gline
2467                 case '}':
2468                         handle_add_szline(token,params,source,reply,tcp_host);
2469                 break;
2470                 // . <mask> <who>
2471                 // remove gline
2472                 case ']':
2473                         handle_del_szline(token,params,source,reply,tcp_host);
2474                 break;
2475                 // | <nick> <opertype>
2476                 // set opertype
2477                 case '|':
2478                         handle_pipe(token,params,source,reply,tcp_host);
2479                 break;
2480                 // F <TS>
2481                 // end netburst
2482                 case 'F':
2483                         WriteOpers("Server %s has completed netburst. (%d secs)",tcp_host,time(NULL)-nb_start);
2484                         handle_F(token,params,source,reply,tcp_host);
2485                         nb_start = 0;
2486                         // tell all the other servers to use this authcookie to connect back again
2487                         // got '+ test3.chatspike.net 7010 -2016508415' from test.chatspike.net
2488                         snprintf(buffer,MAXBUF,"+ %s %s %d %d",tcp_host,ipaddr,port,authcookie);
2489                         NetSendToAllExcept(tcp_host,buffer);
2490                 break;
2491                 // F <TS>
2492                 // end netburst with no mesh creation
2493                 case 'f':
2494                         WriteOpers("Server %s has completed netburst. (%d secs)",tcp_host,time(NULL)-nb_start);
2495                         handle_F(token,params,source,reply,tcp_host);
2496                         nb_start = 0;
2497                         // tell everyone else about the new server name so they just add it in the disconnected
2498                         // state
2499                         snprintf(buffer,MAXBUF,"u %s :%s",tcp_host,GetServerDescription(tcp_host).c_str());
2500                         NetSendToAllExcept(tcp_host,buffer);
2501                 break;
2502                 // X <reserved>
2503                 // Send netburst now
2504                 case 'X':
2505                         WriteOpers("Sending my netburst to %s",tcp_host);
2506                         DoSync(source,tcp_host);
2507                         WriteOpers("Send of netburst to %s completed",tcp_host);
2508                         NetSendMyRoutingTable();
2509                 break;
2510                 // anything else
2511                 default:
2512                         WriteOpers("WARNING! Unknown datagram type '%c'",token);
2513                 break;
2514         }
2515 }
2516
2517
2518 void handle_link_packet(char* udp_msg, char* tcp_host, serverrec *serv)
2519 {
2520         if ((!strncmp(udp_msg,"USER ",5)) || (!strncmp(udp_msg,"NICK ",5)))
2521         {
2522                 // a user on a server port, just close their connection.
2523                 RemoveServer(tcp_host);
2524                 return;
2525         }
2526
2527         char response[10240];
2528         char token = udp_msg[0];
2529         char* old = udp_msg;
2530
2531         if (token == ':') // leading :servername or details - strip them off (services does this, sucky)
2532         {
2533                 char* src = udp_msg+1;
2534                 while (udp_msg[0] != ' ')
2535                         udp_msg++;
2536                 udp_msg[0] = 0;
2537                 udp_msg++;
2538                 char* comd = udp_msg;
2539                 while (udp_msg[0] != ' ')
2540                         udp_msg++;
2541                 udp_msg[0] = 0;
2542                 udp_msg++;
2543                 char data[MAXBUF];
2544                 char source[MAXBUF];
2545                 char command[MAXBUF];
2546                 strcpy(data,udp_msg);
2547                 strcpy(source,src);
2548                 strcpy(command,comd);
2549                 udp_msg = old;
2550                 
2551                 // unused numeric:
2552                 // :services-dev.chatspike.net 433 Craig Craig :Nickname is registered to someone else
2553                 if (!strcmp(command,"433"))
2554                 {
2555                         token = '*';
2556                 }
2557                 if (!strcmp(command,"432"))
2558                 {
2559                         token = '*';
2560                 }
2561                 if (!strcmp(command,"NOTICE"))
2562                 {
2563                         snprintf(udp_msg,MAXBUF,"V %s %s",source,data);
2564                         log(DEBUG,"Rewrote NOTICE from services to: '%s'",udp_msg);
2565                         token = udp_msg[0];
2566                 }
2567                 if (!strcmp(command,"QUIT"))
2568                 {
2569                         if ((!udp_msg) || (!strcmp(data,"")) || (strcmp(data,":")))
2570                         {
2571                                 strcpy(data,":No reason");
2572                         }
2573                         if (!strcmp(data,":"))
2574                         {
2575                                 strcpy(data,":No reason");
2576                         }
2577                         snprintf(udp_msg,MAXBUF,"Q %s %s",source,data);
2578                         log(DEBUG,"Rewrote QUIT from services to: '%s'",udp_msg);
2579                         token = udp_msg[0];
2580                 }
2581                 if (!strcmp(command,"SQUIT"))
2582                 {
2583                         snprintf(udp_msg,MAXBUF,"& %s",source);
2584                         log(DEBUG,"Rewrote SQUIT from services to: '%s'",udp_msg);
2585                         token = udp_msg[0];
2586                 }
2587                 if (!strcmp(command,"SVSMODE"))
2588                 {
2589                         snprintf(udp_msg,MAXBUF,"M %s",data);
2590                         log(DEBUG,"Rewrote SVSMODE from services to: '%s'",udp_msg);
2591                         token = udp_msg[0];
2592                 }
2593                 if (!strcmp(command,"SVS2MODE"))
2594                 {
2595                         snprintf(udp_msg,MAXBUF,"M %s",data);
2596                         log(DEBUG,"Rewrote SVSMODE from services to: '%s'",udp_msg);
2597                         token = udp_msg[0];
2598                 }
2599                 // todo: this wont work without u:lines
2600                 // in give_ops etc allow nick on a u:lined serv to do just about anything
2601                 if (!strcmp(command,"MODE"))
2602                 {
2603                         snprintf(udp_msg,MAXBUF,"m %s %s",source,data);
2604                         log(DEBUG,"Rewrote MODE from services to: '%s'",udp_msg);
2605                         token = udp_msg[0];
2606                 }
2607                 if (!strcmp(command,"KICK"))
2608                 {
2609                         snprintf(udp_msg,MAXBUF,"k %s %s",source,data);
2610                         log(DEBUG,"Rewrote KICK from services to: '%s'",udp_msg);
2611                         token = udp_msg[0];
2612                 }
2613                 if (!strcmp(command,"KILL"))
2614                 {
2615                         snprintf(udp_msg,MAXBUF,"K %s %s",source,data);
2616                         log(DEBUG,"Rewrote KILL from services to: '%s'",udp_msg);
2617                         token = udp_msg[0];
2618                 }
2619                 if (!strcmp(command,"SVSJOIN"))
2620                 {
2621                         snprintf(udp_msg,MAXBUF,"J %s",data);
2622                         NetSendToOne(tcp_host,udp_msg);
2623                         char* nick = strtok(data," ");
2624                         char* chan = strtok(NULL," ");
2625                         log(DEBUG,"Rewrote SVSJOIN from services to: '%s'",udp_msg);
2626                         userrec* u = Find(nick);
2627                         if (u)
2628                         {
2629                                 add_channel(u,chan,"",true);
2630                         }
2631                         token = udp_msg[0];
2632                 }
2633                 
2634         }
2635
2636
2637         char* params = udp_msg + 2;
2638         char finalparam[1024];
2639         strcpy(finalparam," :xxxx");
2640         if (strstr(udp_msg," :")) {
2641                 strncpy(finalparam,strstr(udp_msg," :"),1024);
2642         }
2643         
2644         
2645         if (token == '-') {
2646                 char* cookie = strtok(params," ");
2647                 char* servername = strtok(NULL," ");
2648                 char* serverdesc = finalparam+2;
2649
2650                 WriteOpers("AuthCookie CONNECT from %s (%s)",servername,tcp_host);
2651
2652                 for (int u = 0; u < auth_cookies.size(); u++)
2653                 {
2654                         if (auth_cookies[u] == atoi(cookie))
2655                         {
2656                                 WriteOpers("Allowed cookie from %s, is now part of the mesh",servername);
2657
2658
2659                                 for (int j = 0; j < 32; j++)
2660                                 {
2661                                         if (me[j] != NULL)
2662                                         {
2663                                                 for (int k = 0; k < me[j]->connectors.size(); k++)
2664                                                 {
2665                                                         if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),tcp_host))
2666                                                         {
2667                                                                 me[j]->connectors[k].SetServerName(servername);
2668                                                                 me[j]->connectors[k].SetDescription(serverdesc);
2669                                                                 me[j]->connectors[k].SetState(STATE_CONNECTED);
2670                                                                 NetSendMyRoutingTable();
2671                                                                 return;
2672                                                         }
2673                                                 }
2674                                         }
2675                                         WriteOpers("\2WARNING!\2 %s sent us an authentication packet but we are not authenticating with this server right now! Possible intrusion attempt!",tcp_host);
2676                                         return;
2677                                 }
2678
2679
2680                                 return;
2681                         }
2682                 }
2683                 // bad cookie, bad bad! go sit in the corner!
2684                 WriteOpers("Bad cookie from %s!",servername);
2685                 return;
2686         }
2687         else
2688         if (token == 'S') {
2689                 // S test.chatspike.net password portn :ChatSpike InspIRCd test server
2690                 char* servername = strtok(params," ");
2691                 char* password = strtok(NULL," ");
2692                 char* myport = strtok(NULL," ");
2693                 char* revision = strtok(NULL," ");
2694                 char* serverdesc = finalparam+2;
2695
2696                 WriteOpers("CONNECT from %s (%s) (their port: %d)",servername,tcp_host,atoi(myport));
2697                 
2698                 ircd_connector* cn = serv->FindHost(servername);
2699                 
2700                 if (cn)
2701                 {
2702                         WriteOpers("CONNECT aborted: Server %s already exists from %s",servername,ServerName);
2703                         char buffer[MAXBUF];
2704                         sprintf(buffer,"E :Server %s already exists!",servername);
2705                         serv->SendPacket(buffer,tcp_host);
2706                         RemoveServer(tcp_host);
2707                         return;
2708                 }
2709
2710                 if (atoi(revision) != GetRevision())
2711                 {
2712                         WriteOpers("CONNECT aborted: Could not link to %s, is an incompatible version %s, our version is %d",servername,revision,GetRevision());
2713                         char buffer[MAXBUF];
2714                         sprintf(buffer,"E :Version number mismatch");
2715                         serv->SendPacket(buffer,tcp_host);
2716                         RemoveServer(tcp_host);
2717                         RemoveServer(servername);
2718                         return;
2719                 }
2720
2721                 for (int j = 0; j < serv->connectors.size(); j++)
2722                 {
2723                         if (!strcasecmp(serv->connectors[j].GetServerName().c_str(),tcp_host))
2724                         {
2725                                 serv->connectors[j].SetServerName(servername);
2726                                 serv->connectors[j].SetDescription(serverdesc);
2727                                 serv->connectors[j].SetServerPort(atoi(myport));
2728                         }
2729                 }
2730                 
2731                 
2732                 char Link_ServerName[1024];
2733                 char Link_IPAddr[1024];
2734                 char Link_Port[1024];
2735                 char Link_Pass[1024];
2736                 char Link_SendPass[1024];
2737                 int LinkPort = 0;
2738                 
2739                 // search for a corresponding <link> block in the config files
2740                 for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
2741                 {
2742                         ConfValue("link","name",i,Link_ServerName,&config_f);
2743                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
2744                         ConfValue("link","port",i,Link_Port,&config_f);
2745                         ConfValue("link","recvpass",i,Link_Pass,&config_f);
2746                         ConfValue("link","sendpass",i,Link_SendPass,&config_f);
2747                         log(DEBUG,"(%d) Comparing against name='%s', ipaddr='%s', port='%s', recvpass='%s'",i,Link_ServerName,Link_IPAddr,Link_Port,Link_Pass);
2748                         LinkPort = atoi(Link_Port);
2749                         if (!strcasecmp(Link_ServerName,servername))
2750                         {
2751                                 // we have a matching link line -
2752                                 // send a 'diminutive' server message back...
2753                                 snprintf(response,10240,"s %s %s :%s",ServerName,Link_SendPass,ServerDesc);
2754                                 serv->SendPacket(response,servername);
2755
2756                                 for (int t = 0; t < serv->connectors.size(); t++)
2757                                 {
2758                                         if (!strcasecmp(serv->connectors[t].GetServerName().c_str(),servername))
2759                                         {
2760                                                 serv->connectors[t].SetState(STATE_CONNECTED);
2761                                         }
2762                                 }
2763                 
2764                                 return;
2765                         }
2766                 }
2767                 char buffer[MAXBUF];
2768                 sprintf(buffer,"E :Access is denied (no matching link block)");
2769                 serv->SendPacket(buffer,tcp_host);
2770                 WriteOpers("CONNECT from %s denied, no matching link block",servername);
2771                 RemoveServer(tcp_host);
2772                 RemoveServer(servername);
2773                 return;
2774         }
2775         else
2776         if (token == 's') {
2777                 // S test.chatspike.net password :ChatSpike InspIRCd test server
2778                 char* servername = strtok(params," ");
2779                 char* password = strtok(NULL," ");
2780                 char* serverdesc = finalparam+2;
2781                 
2782                 // TODO: we should do a check here to ensure that this server is one we recently initiated a
2783                 // link with, and didnt hear an 's' or 'E' back from yet (these are the only two valid responses
2784                 // to an 'S' command. If we didn't recently send an 'S' to this server, theyre trying to spoof
2785                 // a connect, so put out an oper alert!
2786                 
2787                 // for now, just accept all, we'll fix that later.
2788                 WriteOpers("%s accepted our link credentials ",servername);
2789                 
2790                 char Link_ServerName[1024];
2791                 char Link_IPAddr[1024];
2792                 char Link_Port[1024];
2793                 char Link_Pass[1024];
2794                 char Link_SendPass[1024];
2795                 int LinkPort = 0;
2796                 
2797                 // search for a corresponding <link> block in the config files
2798                 for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
2799                 {
2800                         ConfValue("link","name",i,Link_ServerName,&config_f);
2801                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
2802                         ConfValue("link","port",i,Link_Port,&config_f);
2803                         ConfValue("link","recvpass",i,Link_Pass,&config_f);
2804                         ConfValue("link","sendpass",i,Link_SendPass,&config_f);
2805                         log(DEBUG,"(%d) Comparing against name='%s', ipaddr='%s', port='%s', recvpass='%s'",i,Link_ServerName,Link_IPAddr,Link_Port,Link_Pass);
2806                         LinkPort = atoi(Link_Port);
2807                         if (!strcasecmp(Link_ServerName,servername))
2808                         {
2809                                 // matching link at this end too, we're all done!
2810                                 // at this point we must begin key exchange and insert this
2811                                 // server into our 'active' table.
2812                                 for (int j = 0; j < 32; j++)
2813                                 {
2814                                         if (me[j] != NULL)
2815                                         {
2816                                                 for (int k = 0; k < me[j]->connectors.size(); k++)
2817                                                 {
2818                                                         if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),tcp_host))
2819                                                         {
2820                                                                 char buffer[MAXBUF];
2821                                                                 me[j]->connectors[k].SetDescription(serverdesc);
2822                                                                 me[j]->connectors[k].SetState(STATE_CONNECTED);
2823                                                                 sprintf(buffer,"X 0");
2824                                                                 serv->SendPacket(buffer,tcp_host);
2825                                                                 DoSync(me[j],tcp_host);
2826                                                                 NetSendMyRoutingTable();
2827                                                                 return;
2828                                                         }
2829                                                 }
2830                                         }
2831                                         WriteOpers("\2WARNING!\2 %s sent us an authentication packet but we are not authenticating with this server right noe! Possible intrusion attempt!",tcp_host);
2832                                         return;
2833                                 }
2834                         }
2835                         else {
2836                                 log(DEBUG,"Server names '%s' and '%s' don't match",Link_ServerName,servername);
2837                         }
2838                 }
2839                 char buffer[MAXBUF];
2840                 sprintf(buffer,"E :Access is denied (no matching link block)");
2841                 serv->SendPacket(buffer,tcp_host);
2842                 WriteOpers("CONNECT from %s denied, no matching link block",servername);
2843                 RemoveServer(tcp_host);
2844                 RemoveServer(servername);
2845                 return;
2846         }
2847         else
2848         if (token == 'U') {
2849                 // U services.chatspike.net password :ChatSpike Services
2850                 //
2851                 // non-meshed link, used by services. Everything coming from a non-meshed link is auto broadcasted.
2852                 char* servername = strtok(params," ");
2853                 char* password = strtok(NULL," ");
2854                 char* serverdesc = finalparam+2;
2855                 
2856                 char Link_ServerName[1024];
2857                 char Link_IPAddr[1024];
2858                 char Link_Port[1024];
2859                 char Link_Pass[1024];
2860                 char Link_SendPass[1024];
2861                 int LinkPort = 0;
2862                 
2863                 // search for a corresponding <link> block in the config files
2864                 for (int i = 0; i < ConfValueEnum("link",&config_f); i++)
2865                 {
2866                         ConfValue("link","name",i,Link_ServerName,&config_f);
2867                         ConfValue("link","ipaddr",i,Link_IPAddr,&config_f);
2868                         ConfValue("link","port",i,Link_Port,&config_f);
2869                         ConfValue("link","recvpass",i,Link_Pass,&config_f);
2870                         ConfValue("link","sendpass",i,Link_SendPass,&config_f);
2871                         log(DEBUG,"(%d) Comparing against name='%s', ipaddr='%s', port='%s', recvpass='%s'",i,Link_ServerName,Link_IPAddr,Link_Port,Link_Pass);
2872                         LinkPort = atoi(Link_Port);
2873                         if (!strcasecmp(Link_ServerName,servername))
2874                         {
2875                                 // matching link at this end too, we're all done!
2876                                 // at this point we must begin key exchange and insert this
2877                                 // server into our 'active' table.
2878                                 for (int j = 0; j < 32; j++)
2879                                 {
2880                                         if (me[j] != NULL)
2881                                         {
2882                                                 for (int k = 0; k < me[j]->connectors.size(); k++)
2883                                                 {
2884                                                         if (!strcasecmp(me[j]->connectors[k].GetServerName().c_str(),tcp_host))
2885                                                         {
2886                                                                 char buffer[MAXBUF];
2887                                                                 me[j]->connectors[k].SetDescription(serverdesc);
2888                                                                 me[j]->connectors[k].SetServerName(servername);
2889                                                                 me[j]->connectors[k].SetState(STATE_SERVICES);
2890                                                                 sprintf(buffer,"X 0");
2891                                                                 serv->SendPacket(buffer,servername);
2892                                                                 DoSync(me[j],servername);
2893                                                                 NetSendMyRoutingTable();
2894                                                                 sprintf(buffer,"H %s",servername);
2895                                                                 NetSendToAllExcept(servername,buffer);
2896                                                                 WriteOpers("Non-Mesh server %s has joined the network",servername);
2897                                                                 return;
2898                                                         }
2899                                                 }
2900                                         }
2901                                         WriteOpers("\2WARNING!\2 %s sent us an authentication packet but we are not authenticating with this server right noe! Possible intrusion attempt!",tcp_host);
2902                                         return;
2903                                 }
2904                         }
2905                         else {
2906                                 log(DEBUG,"Server names '%s' and '%s' don't match",Link_ServerName,servername);
2907                         }
2908                 }
2909                 char buffer[MAXBUF];
2910                 sprintf(buffer,"E :Access is denied (no matching link block)");
2911                 serv->SendPacket(buffer,tcp_host);
2912                 WriteOpers("CONNECT from %s denied, no matching link block",servername);
2913                 RemoveServer(tcp_host);
2914                 RemoveServer(servername);
2915                 return;
2916         }
2917         else
2918         if (token == 'E') {
2919                 char* error_message = finalparam+2;
2920                 WriteOpers("ERROR from %s: %s",tcp_host,error_message);
2921                 return;
2922         }
2923         else {
2924
2925                 serverrec* source_server = NULL;
2926
2927                 for (int j = 0; j < 32; j++)
2928                 {
2929                         if (me[j] != NULL)
2930                         {
2931                                 for (int x = 0; x < me[j]->connectors.size(); x++)
2932                                 {
2933                                         log(DEBUG,"Servers are: '%s' '%s'",tcp_host,me[j]->connectors[x].GetServerName().c_str());
2934                                         if (!strcasecmp(me[j]->connectors[x].GetServerName().c_str(),tcp_host))
2935                                         {
2936                                                 if ((me[j]->connectors[x].GetState() == STATE_CONNECTED) || (me[j]->connectors[x].GetState() == STATE_SERVICES))
2937                                                 {
2938                                                         // found a valid ircd_connector.
2939                                                         process_restricted_commands(token,params,me[j],serv,tcp_host,me[j]->connectors[x].GetServerIP(),me[j]->connectors[x].GetServerPort());
2940                                                         return;
2941                                                 }
2942                                         }
2943                                 }
2944                         }
2945                 }
2946
2947                 log(DEBUG,"Unrecognised token or unauthenticated host in datagram from %s: %c",tcp_host,token);
2948         }
2949 }
2950
2951 long duration(char* str)
2952 {
2953         char n_field[MAXBUF];
2954         long total = 0;
2955         char* str_end = str + strlen(str);
2956         n_field[0] = 0;
2957         
2958         for (char* i = str; i < str_end; i++)
2959         {
2960                 // if we have digits, build up a string for the value in n_field,
2961                 // up to 10 digits in size.
2962                 if ((*i >= '0') && (*i <= '9'))
2963                 {
2964                         strncat(n_field,i,10);
2965                 }
2966                 else
2967                 {
2968                         // we dont have a digit, check for numeric tokens
2969                         switch (tolower(*i))
2970                         {
2971                                 case 's':
2972                                         total += atoi(n_field);
2973                                 break;
2974
2975                                 case 'm':
2976                                         total += (atoi(n_field)*duration_m);
2977                                 break;
2978
2979                                 case 'h':
2980                                         total += (atoi(n_field)*duration_h);
2981                                 break;
2982
2983                                 case 'd':
2984                                         total += (atoi(n_field)*duration_d);
2985                                 break;
2986
2987                                 case 'w':
2988                                         total += (atoi(n_field)*duration_w);
2989                                 break;
2990
2991                                 case 'y':
2992                                         total += (atoi(n_field)*duration_y);
2993                                 break;
2994                         }
2995                         n_field[0] = 0;
2996                 }
2997         }
2998         // add trailing seconds
2999         total += atoi(n_field);
3000         
3001         return total;
3002 }
3003
3004
3005 void handle_kline(char **parameters, int pcnt, userrec *user)
3006 {
3007         if (pcnt >= 3)
3008         {
3009                 add_kline(duration(parameters[1]),user->nick,parameters[2],parameters[0]);
3010                 if (!duration(parameters[1]))
3011                 {
3012                         WriteOpers("*** %s added permenant K-line for %s.",user->nick,parameters[0]);
3013                 }
3014                 else
3015                 {
3016                         WriteOpers("*** %s added timed K-line for %s, expires in %d seconds.",user->nick,parameters[0],duration(parameters[1]));
3017                 }
3018         }
3019         else
3020         {
3021                 if (del_kline(parameters[0]))
3022                 {
3023                         WriteOpers("*** %s Removed K-line on %s.",user->nick,parameters[0]);
3024                 }
3025                 else
3026                 {
3027                         WriteServ(user->fd,"NOTICE %s :*** K-Line %s not found in list, try /stats k.",user->nick,parameters[0]);
3028                 }
3029         }
3030         apply_lines();
3031 }
3032
3033 void handle_gline(char **parameters, int pcnt, userrec *user)
3034 {
3035         char netdata[MAXBUF];
3036         if (pcnt >= 3)
3037         {
3038                 add_gline(duration(parameters[1]),user->nick,parameters[2],parameters[0]);
3039                 // # <mask> <who-set-it> <time-set> <duration> :<reason>
3040                 snprintf(netdata,MAXBUF,"# %s %s %ld %ld :%s",parameters[0],user->nick,time(NULL),duration(parameters[1]),parameters[2]);
3041                 NetSendToAll(netdata);
3042                 if (!duration(parameters[1]))
3043                 {
3044                         WriteOpers("*** %s added permenant G-line for %s.",user->nick,parameters[0]);
3045                 }
3046                 else
3047                 {
3048                         WriteOpers("*** %s added timed G-line for %s, expires in %d seconds.",user->nick,parameters[0],duration(parameters[1]));
3049                 }
3050         }
3051         else
3052         {
3053                 if (del_gline(parameters[0]))
3054                 {
3055                         // . <mask> <who-removed-it>
3056                         snprintf(netdata,MAXBUF,". %s %s",parameters[0],user->nick);
3057                         WriteOpers("*** %s Removed G-line on %s.",user->nick,parameters[0]);
3058                 }
3059                 else
3060                 {
3061                         WriteServ(user->fd,"NOTICE %s :*** G-Line %s not found in list, try /stats g.",user->nick,parameters[0]);
3062                 }
3063         }
3064         apply_lines();
3065 }
3066
3067 void handle_zline(char **parameters, int pcnt, userrec *user)
3068 {
3069         if (pcnt >= 3)
3070         {
3071                 add_zline(duration(parameters[1]),user->nick,parameters[2],parameters[0]);
3072                 if (!duration(parameters[1]))
3073                 {
3074                         WriteOpers("*** %s added permenant Z-line for %s.",user->nick,parameters[0]);
3075                 }
3076                 else
3077                 {
3078                         WriteOpers("*** %s added timed Z-line for %s, expires in %d seconds.",user->nick,parameters[0],duration(parameters[1]));
3079                 }
3080         }
3081         else
3082         {
3083                 if (del_zline(parameters[0]))
3084                 {
3085                         WriteOpers("*** %s Removed Z-line on %s.",user->nick,parameters[0]);
3086                 }
3087                 else
3088                 {
3089                         WriteServ(user->fd,"NOTICE %s :*** Z-Line %s not found in list, try /stats Z.",user->nick,parameters[0]);
3090                 }
3091         }
3092         apply_lines();
3093 }
3094
3095 void handle_qline(char **parameters, int pcnt, userrec *user)
3096 {
3097         if (pcnt >= 3)
3098         {
3099                 add_qline(duration(parameters[1]),user->nick,parameters[2],parameters[0]);
3100                 if (!duration(parameters[1]))
3101                 {
3102                         WriteOpers("*** %s added permenant Q-line for %s.",user->nick,parameters[0]);
3103                 }
3104                 else
3105                 {
3106                         WriteOpers("*** %s added timed Q-line for %s, expires in %d seconds.",user->nick,parameters[0],duration(parameters[1]));
3107                 }
3108         }
3109         else
3110         {
3111                 if (del_qline(parameters[0]))
3112                 {
3113                         WriteOpers("*** %s Removed Q-line on %s.",user->nick,parameters[0]);
3114                 }
3115                 else
3116                 {
3117                         WriteServ(user->fd,"NOTICE %s :*** Q-Line %s not found in list, try /stats k.",user->nick,parameters[0]);
3118                 }
3119         }
3120         apply_lines();
3121 }
3122
3123