]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/command_parse.cpp
Decide that it wasn't quite appropriate :(
[user/henk/code/inspircd.git] / src / command_parse.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 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 using namespace std;
18
19 #include "inspircd_config.h"
20 #include "inspircd.h"
21 #include "inspircd_io.h"
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/errno.h>
25 #include <sys/ioctl.h>
26 #include <sys/utsname.h>
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 <deque>
38 #include <sched.h>
39 #ifdef THREADED_DNS
40 #include <pthread.h>
41 #endif
42 #include "users.h"
43 #include "globals.h"
44 #include "modules.h"
45 #include "dynamic.h"
46 #include "wildcard.h"
47 #include "message.h"
48 #include "mode.h"
49 #include "commands.h"
50 #include "xline.h"
51 #include "inspstring.h"
52 #include "dnsqueue.h"
53 #include "helperfuncs.h"
54 #include "hashcomp.h"
55 #include "socketengine.h"
56 #include "userprocess.h"
57 #include "socket.h"
58 #include "dns.h"
59 #include "typedefs.h"
60 #include "command_parse.h"
61 #include "ctables.h"
62
63 #ifdef GCC3
64 #define nspace __gnu_cxx
65 #else
66 #define nspace std
67 #endif
68
69
70 extern InspIRCd* ServerInstance;
71
72 extern std::vector<Module*> modules;
73 extern std::vector<ircd_module*> factory;
74 extern std::vector<InspSocket*> module_sockets;
75 extern std::vector<userrec*> local_users;
76
77 extern int MODCOUNT;
78 extern InspSocket* socket_ref[MAX_DESCRIPTORS];
79 extern time_t TIME;
80
81 // This table references users by file descriptor.
82 // its an array to make it VERY fast, as all lookups are referenced
83 // by an integer, meaning there is no need for a scan/search operation.
84 extern userrec* fd_ref_table[MAX_DESCRIPTORS];
85
86 extern Server* MyServer;
87 extern ServerConfig *Config;
88
89 extern user_hash clientlist;
90 extern chan_hash chanlist;
91
92 /* This function pokes and hacks at a parameter list like the following:
93  *
94  * PART #winbot,#darkgalaxy :m00!
95  *
96  * to turn it into a series of individual calls like this:
97  *
98  * PART #winbot :m00!
99  * PART #darkgalaxy :m00!
100  *
101  * The seperate calls are sent to a callback function provided by the caller
102  * (the caller will usually call itself recursively). The callback function
103  * must be a command handler. Calling this function on a line with no list causes
104  * no action to be taken. You must provide a starting and ending parameter number
105  * where the range of the list can be found, useful if you have a terminating
106  * parameter as above which is actually not part of the list, or parameters
107  * before the actual list as well. This code is used by many functions which
108  * can function as "one to list" (see the RFC) */
109
110 int CommandParser::LoopCall(command_t* fn, char **parameters, int pcnt, userrec *u, int start, int end, int joins)
111 {
112         char plist[MAXBUF];
113         char *param;
114         char *pars[32];
115         char blog[32][MAXBUF];
116         char blog2[32][MAXBUF];
117         int j = 0, q = 0, total = 0, t = 0, t2 = 0, total2 = 0;
118         char keystr[MAXBUF];
119         char moo[MAXBUF];
120
121         for (int i = 0; i <32; i++)
122         {
123                 *blog[i] = 0;
124                 *blog2[i] = 0;
125         }
126
127         *moo = 0;
128         for (int i = 0; i <10; i++)
129         {
130                 if (!parameters[i])
131                 {
132                         parameters[i] = moo;
133                 }
134         }
135         if (joins)
136         {
137                 if (pcnt > 1) /* we have a key to copy */
138                 {
139                         strlcpy(keystr,parameters[1],MAXBUF);
140                 }
141         }
142
143         if (!parameters[start] || (!strchr(parameters[start],',')))
144         {
145                 return 0;
146         }
147
148         *plist = 0;
149
150         for (int i = start; i <= end; i++)
151         {
152                 if (parameters[i])
153                 {
154                         strlcat(plist,parameters[i],MAXBUF);
155                 }
156         }
157
158         j = 0;
159         param = plist;
160
161         t = strlen(plist);
162
163         for (int i = 0; i < t; i++)
164         {
165                 if (plist[i] == ',')
166                 {
167                         plist[i] = '\0';
168                         strlcpy(blog[j++],param,MAXBUF);
169                         param = plist+i+1;
170                         if ((unsigned int)j > Config->MaxTargets)
171                         {
172                                 WriteServ(u->fd,"407 %s %s :Too many targets in list, message not delivered.",u->nick,blog[j-1]);
173                                 return 1;
174                         }
175                 }
176         }
177
178         strlcpy(blog[j++],param,MAXBUF);
179         total = j;
180
181         if ((joins) && (keystr) && (total>0)) // more than one channel and is joining
182         {
183                 strcat(keystr,",");
184         }
185
186         if ((joins) && (keystr))
187         {
188                 if (strchr(keystr,','))
189                 {
190                         j = 0;
191                         param = keystr;
192                         t2 = strlen(keystr);
193
194                         for (int i = 0; i < t2; i++)
195                         {
196                                 if (keystr[i] == ',')
197                                 {
198                                         keystr[i] = '\0';
199                                         strlcpy(blog2[j++],param,MAXBUF);
200                                         param = keystr+i+1;
201                                 }
202                         }
203
204                         strlcpy(blog2[j++],param,MAXBUF);
205                         total2 = j;
206                 }
207         }
208
209         for (j = 0; j < total; j++)
210         {
211                 if (blog[j])
212                 {
213                         pars[0] = blog[j];
214                 }
215
216                 for (q = end; q < pcnt-1; q++)
217                 {
218                         if (parameters[q+1])
219                         {
220                                 pars[q-end+1] = parameters[q+1];
221                         }
222                 }
223
224                 if ((joins) && (parameters[1]))
225                 {
226                         if (pcnt > 1)
227                         {
228                                 pars[1] = blog2[j];
229                         }
230                         else
231                         {
232                                 pars[1] = NULL;
233                         }
234                 }
235
236                 /* repeatedly call the function with the hacked parameter list */
237                 if ((joins) && (pcnt > 1))
238                 {
239                         if (pars[1])
240                         {
241                                 // pars[1] already set up and containing key from blog2[j]
242                                 fn->Handle(pars,2,u);
243                         }
244                         else
245                         {
246                                 pars[1] = parameters[1];
247                                 fn->Handle(pars,2,u);
248                         }
249                 }
250                 else
251                 {
252                         fn->Handle(pars,pcnt-(end-start),u);
253                 }
254         }
255
256         return 1;
257 }
258
259 bool CommandParser::IsValidCommand(std::string &commandname, int pcnt, userrec * user)
260 {
261         nspace::hash_map<std::string,command_t*>::iterator n = cmdlist.find(commandname);
262
263         if (n != cmdlist.end())
264         {
265                 if ((pcnt>=n->second->min_params) && (n->second->source != "<core>"))
266                 {
267                         if ((strchr(user->modes,n->second->flags_needed)) || (!n->second->flags_needed))
268                         {
269                                 if (n->second->flags_needed)
270                                 {
271                                         if ((user->HasPermission(commandname)) || (is_uline(user->server)))
272                                         {
273                                                 return true;
274                                         }
275                                         else
276                                         {
277                                                 return false;
278                                         }
279                                 }
280
281                                 return true;
282                         }
283                 }
284         }
285
286         return false;
287 }
288
289 // calls a handler function for a command
290
291 void CommandParser::CallHandler(std::string &commandname,char **parameters, int pcnt, userrec *user)
292 {
293         nspace::hash_map<std::string,command_t*>::iterator n = cmdlist.find(commandname);
294
295         if (n != cmdlist.end())
296         {
297                 if (pcnt >= n->second->min_params)
298                 {
299                         if ((strchr(user->modes,n->second->flags_needed)) || (!n->second->flags_needed))
300                         {
301                                 if (n->second->flags_needed)
302                                 {
303                                         if ((user->HasPermission(commandname)) || (is_uline(user->server)))
304                                         {
305                                                 n->second->Handle(parameters,pcnt,user);
306                                         }
307                                 }
308                                 else
309                                 {
310                                         n->second->Handle(parameters,pcnt,user);
311                                 }
312                         }
313                 }
314         }
315 }
316
317 int CommandParser::ProcessParameters(char **command_p,char *parameters)
318 {
319         int j = 0;
320         int q = strlen(parameters);
321
322         if (!q)
323         {
324                 /* no parameters, command_p invalid! */
325                 return 0;
326         }
327
328         if (parameters[0] == ':')
329         {
330                 command_p[0] = parameters+1;
331                 return 1;
332         }
333
334         if (q)
335         {
336                 if ((strchr(parameters,' ')==NULL) || (parameters[0] == ':'))
337                 {
338                         /* only one parameter */
339                         command_p[0] = parameters;
340                         if (parameters[0] == ':')
341                         {
342                                 if (strchr(parameters,' ') != NULL)
343                                 {
344                                         command_p[0]++;
345                                 }
346                         }
347
348                         return 1;
349                 }
350         }
351
352         command_p[j++] = parameters;
353
354         for (int i = 0; i <= q; i++)
355         {
356                 if (parameters[i] == ' ')
357                 {
358                         command_p[j++] = parameters+i+1;
359                         parameters[i] = '\0';
360
361                         if (command_p[j-1][0] == ':')
362                         {
363                                 *command_p[j-1]++; /* remove dodgy ":" */
364                                 break;
365                                 /* parameter like this marks end of the sequence */
366                         }
367                 }
368         }
369
370         return j; /* returns total number of items in the list */
371 }
372
373 void CommandParser::ProcessCommand(userrec *user, char* cmd)
374 {
375         char *parameters;
376         char *command;
377         char *command_p[127];
378         char p[MAXBUF], temp[MAXBUF];
379         int j, items, cmd_found;
380         int total_params = 0;
381         unsigned int xl;
382
383         for (int i = 0; i < 127; i++)
384                 command_p[i] = NULL;
385
386         if (!user || !cmd || !*cmd)
387         {
388                 return;
389         }
390
391         xl = strlen(cmd);
392
393         if (xl > 2)
394         {
395                 for (unsigned int q = 0; q < xl - 1; q++)
396                 {
397                         if (cmd[q] == ' ')
398                         {
399                                 if (cmd[q+1] == ':')
400                                 {
401                                         total_params++;
402                                         // found a 'trailing', we dont count them after this.
403                                         break;
404                                 }
405                                 else
406                                         total_params++;
407                         }
408                 }
409         }
410
411         // another phidjit bug...
412         if (total_params > 126)
413         {
414                 *(strchr(cmd,' ')) = '\0';
415                 WriteServ(user->fd,"421 %s %s :Too many parameters given",user->nick,cmd);
416                 return;
417         }
418
419         strlcpy(temp,cmd,MAXBUF);
420
421         std::string tmp = cmd;
422
423         for (int i = 0; i <= MODCOUNT; i++)
424         {
425                 std::string oldtmp = tmp;
426                 modules[i]->OnServerRaw(tmp,true,user);
427                 if (oldtmp != tmp)
428                 {
429                         log(DEBUG,"A Module changed the input string!");
430                         log(DEBUG,"New string: %s",tmp.c_str());
431                         log(DEBUG,"Old string: %s",oldtmp.c_str());
432                         break;
433                 }
434         }
435
436         strlcpy(cmd,tmp.c_str(),MAXBUF);
437         strlcpy(temp,cmd,MAXBUF);
438
439         if (!strchr(cmd,' '))
440         {
441                 /*
442                  * no parameters, lets skip the formalities and not chop up
443                  * the string
444                  */
445                 log(DEBUG,"About to preprocess command with no params");
446                 items = 0;
447                 command_p[0] = NULL;
448                 parameters = NULL;
449                 unsigned int tl = strlen(cmd);
450                 for (unsigned int i = 0; i <= tl; i++)
451                 {
452                         cmd[i] = toupper(cmd[i]);
453                 }
454                 command = cmd;
455         }
456         else
457         {
458                 *cmd = 0;
459                 j = 0;
460
461                 unsigned int vl = strlen(temp);
462                 /* strip out extraneous linefeeds through mirc's crappy pasting (thanks Craig) */
463                 for (unsigned int i = 0; i < vl; i++)
464                 {
465                         if ((temp[i] != 10) && (temp[i] != 13) && (temp[i] != 0) && (temp[i] != 7))
466                         {
467                                 cmd[j++] = temp[i];
468                                 cmd[j] = 0;
469                         }
470                 }
471
472                 /* split the full string into a command plus parameters */
473                 parameters = p;
474                 p[0] = ' ';
475                 p[1] = 0;
476
477                 command = cmd;
478
479                 if (strchr(cmd,' '))
480                 {
481                         unsigned int cl = strlen(cmd);
482
483                         for (unsigned int i = 0; i <= cl; i++)
484                         {
485                                 /* capitalise the command ONLY, leave params intact */
486                                 cmd[i] = toupper(cmd[i]);
487                                 /* are we nearly there yet?! :P */
488                                 if (cmd[i] == ' ')
489                                 {
490                                         command = cmd;
491                                         parameters = cmd+i+1;
492                                         cmd[i] = '\0';
493                                         break;
494                                 }
495                         }
496                 }
497                 else
498                 {
499                         for (char* i = cmd; *i; i++)
500                         {
501                                 *i = toupper(*i);
502                         }
503                 }
504         }
505
506         if (strlen(command)>MAXCOMMAND)
507         {
508                 WriteServ(user->fd,"421 %s %s :Command too long",user->nick,command);
509                 return;
510         }
511
512         for (char* x = command; *x; x++)
513         {
514                 if (((*x < 'A') || (*x > 'Z')) && (*x != '.'))
515                 {
516                         if (((*x < '0') || (*x> '9')) && (*x != '-'))
517                         {
518                                 if (strchr("@!\"$%^&*(){}[]_=+;:'#~,<>/?\\|`",*x))
519                                 {
520                                         ServerInstance->stats->statsUnknown++;
521                                         WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
522                                         return;
523                                 }
524                         }
525                 }
526         }
527
528         std::string xcommand = command;
529         if ((user->registered != 7) && (xcommand == "SERVER"))
530         {
531                 kill_link(user,"Server connection to non-server port");
532                 return;
533         }
534         
535         /* Tweak by brain - why was this INSIDE the mainloop? */
536         if (parameters)
537         {
538                  if (parameters[0])
539                  {
540                          items = this->ProcessParameters(command_p,parameters);
541                  }
542                  else
543                  {
544                          items = 0;
545                          command_p[0] = NULL;
546                  }
547         }
548         else
549         {
550                 items = 0;
551                 command_p[0] = NULL;
552         }
553
554         int MOD_RESULT = 0;
555         FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,false));
556         if (MOD_RESULT == 1) {
557                 return;
558         }
559         
560         nspace::hash_map<std::string,command_t*>::iterator cm = cmdlist.find(xcommand);
561         
562         if (cm != cmdlist.end())
563         {
564                 if (user)
565                 {
566                         /* activity resets the ping pending timer */
567                         user->nping = TIME + user->pingmax;
568                         if ((items) < cm->second->min_params)
569                         {
570                                 log(DEBUG,"not enough parameters: %s %s",user->nick,command);
571                                 WriteServ(user->fd,"461 %s %s :Not enough parameters",user->nick,command);
572                                 return;
573                         }
574                         if ((!strchr(user->modes,cm->second->flags_needed)) && (cm->second->flags_needed))
575                         {
576                                 log(DEBUG,"permission denied: %s %s",user->nick,command);
577                                 WriteServ(user->fd,"481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
578                                 cmd_found = 1;
579                                 return;
580                         }
581                         if ((cm->second->flags_needed) && (!user->HasPermission(xcommand)))
582                         {
583                                 log(DEBUG,"permission denied: %s %s",user->nick,command);
584                                 WriteServ(user->fd,"481 %s :Permission Denied- Oper type %s does not have access to command %s",user->nick,user->oper,command);
585                                 cmd_found = 1;
586                                 return;
587                         }
588                         /* if the command isnt USER, PASS, or NICK, and nick is empty,
589                          * deny command! */
590                         if ((strncmp(command,"USER",4)) && (strncmp(command,"NICK",4)) && (strncmp(command,"PASS",4)))
591                         {
592                                 if ((!isnick(user->nick)) || (user->registered != 7))
593                                 {
594                                         log(DEBUG,"not registered: %s %s",user->nick,command);
595                                         WriteServ(user->fd,"451 %s :You have not registered",command);
596                                         return;
597                                 }
598                         }
599                         if ((user->registered == 7) && (!*user->oper) && (*Config->DisabledCommands))
600                         {
601                                 std::stringstream dcmds(Config->DisabledCommands);
602                                 std::string thiscmd;
603                                 while (dcmds >> thiscmd)
604                                 {
605                                         if (!strcasecmp(thiscmd.c_str(),command))
606                                         {
607                                                 // command is disabled!
608                                                 WriteServ(user->fd,"421 %s %s :This command has been disabled.",user->nick,command);
609                                                 return;
610                                         }
611                                 }
612                         }
613                         if ((user->registered == 7) || (!strncmp(command,"USER",4)) || (!strncmp(command,"NICK",4)) || (!strncmp(command,"PASS",4)))
614                         {
615                                 /* ikky /stats counters */
616                                 if (temp)
617                                 {
618                                         cm->second->use_count++;
619                                         cm->second->total_bytes+=strlen(temp);
620                                 }
621
622                                 int MOD_RESULT = 0;
623                                 FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,true));
624                                 if (MOD_RESULT == 1)
625                                 {
626                                         return;
627                                 }
628
629                                 /*
630                                  * WARNING: nothing may come after the
631                                  * command handler call, as the handler
632                                  * may free the user structure!
633                                  */
634
635                                 cm->second->Handle(command_p,items,user);
636                                 return;
637                         }
638                         else
639                         {
640                                 WriteServ(user->fd,"451 %s :You have not registered",command);
641                                 return;
642                         }
643                 }
644         }
645         else if (user)
646         {
647                 ServerInstance->stats->statsUnknown++;
648                 WriteServ(user->fd,"421 %s %s :Unknown command",user->nick,command);
649         }
650 }
651
652 bool CommandParser::RemoveCommands(const char* source)
653 {
654         bool go_again = true;
655
656         while (go_again)
657         {
658                 go_again = false;
659
660                 for (nspace::hash_map<std::string,command_t*>::iterator i = cmdlist.begin(); i != cmdlist.end(); i++)
661                 {
662                         command_t* x = i->second;
663                         if (x->source == std::string(source))
664                         {
665                                 log(DEBUG,"removecommands(%s) Removing dependent command: %s",x->source.c_str(),x->command.c_str());
666                                 cmdlist.erase(i);
667                                 go_again = true;
668                                 break;
669                         }
670                 }
671         }
672
673         return true;
674 }
675
676 void CommandParser::ProcessBuffer(const char* cmdbuf,userrec *user)
677 {
678         char cmd[MAXBUF];
679
680         if (!user)
681         {
682                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
683                 return;
684         }
685         if (!cmdbuf)
686         {
687                 log(DEFAULT,"*** BUG *** process_buffer was given an invalid parameter");
688                 return;
689         }
690         if (!cmdbuf[0])
691         {
692                 return;
693         }
694
695         while (*cmdbuf == ' ') cmdbuf++; // strip leading spaces
696
697         strlcpy(cmd,cmdbuf,MAXBUF);
698
699         if (!cmd[0])
700         {
701                 return;
702         }
703
704
705         /* XXX - This is ugly as sin, and incomprehensible - why are we doing this twice, for instance? --w00t */
706         int sl = strlen(cmd)-1;
707
708         if ((cmd[sl] == 13) || (cmd[sl] == 10))
709         {
710                 cmd[sl] = '\0';
711         }
712
713         sl = strlen(cmd)-1;
714
715         if ((cmd[sl] == 13) || (cmd[sl] == 10))
716         {
717                 cmd[sl] = '\0';
718         }
719
720         sl = strlen(cmd)-1;
721
722
723         while (cmd[sl] == ' ') // strip trailing spaces
724         {
725                 cmd[sl] = '\0';
726                 sl = strlen(cmd)-1;
727         }
728
729         if (!cmd[0])
730         {
731                 return;
732         }
733
734         log(DEBUG,"CMDIN: %s %s",user->nick,cmd);
735         tidystring(cmd);
736         if ((user) && (cmd))
737         {
738                 this->ProcessCommand(user,cmd);
739         }
740 }
741
742 bool CommandParser::CreateCommand(command_t *f)
743 {
744         /* create the command and push it onto the table */
745         if (cmdlist.find(f->command) == cmdlist.end())
746         {
747                 cmdlist[f->command] = f;
748                 log(DEBUG,"Added command %s (%lu parameters)",f->command.c_str(),(unsigned long)f->min_params);
749                 return true;
750         }
751         else return false;
752 }
753
754 CommandParser::CommandParser()
755 {
756         this->SetupCommandTable();
757 }
758
759 void CommandParser::SetupCommandTable()
760 {
761         this->CreateCommand(new cmd_user);
762         this->CreateCommand(new cmd_nick);
763         this->CreateCommand(new cmd_quit);
764         this->CreateCommand(new cmd_version);
765         this->CreateCommand(new cmd_ping);
766         this->CreateCommand(new cmd_pong);
767         this->CreateCommand(new cmd_admin);
768         this->CreateCommand(new cmd_privmsg);
769         this->CreateCommand(new cmd_info);
770         this->CreateCommand(new cmd_time);
771         this->CreateCommand(new cmd_whois);
772         this->CreateCommand(new cmd_wallops);
773         this->CreateCommand(new cmd_notice);
774         this->CreateCommand(new cmd_join);
775         this->CreateCommand(new cmd_names);
776         this->CreateCommand(new cmd_part);
777         this->CreateCommand(new cmd_kick);
778         this->CreateCommand(new cmd_mode);
779         this->CreateCommand(new cmd_topic);
780         this->CreateCommand(new cmd_who);
781         this->CreateCommand(new cmd_motd);
782         this->CreateCommand(new cmd_rules);
783         this->CreateCommand(new cmd_oper);
784         this->CreateCommand(new cmd_list);
785         this->CreateCommand(new cmd_die);
786         this->CreateCommand(new cmd_restart);
787         this->CreateCommand(new cmd_kill);
788         this->CreateCommand(new cmd_rehash);
789         this->CreateCommand(new cmd_lusers);
790         this->CreateCommand(new cmd_stats);
791         this->CreateCommand(new cmd_userhost);
792         this->CreateCommand(new cmd_away);
793         this->CreateCommand(new cmd_ison);
794         this->CreateCommand(new cmd_summon);
795         this->CreateCommand(new cmd_users);
796         this->CreateCommand(new cmd_invite);
797         this->CreateCommand(new cmd_pass);
798         this->CreateCommand(new cmd_trace);
799         this->CreateCommand(new cmd_whowas);
800         this->CreateCommand(new cmd_connect);
801         this->CreateCommand(new cmd_squit);
802         this->CreateCommand(new cmd_modules);
803         this->CreateCommand(new cmd_links);
804         this->CreateCommand(new cmd_map);
805         this->CreateCommand(new cmd_kline);
806         this->CreateCommand(new cmd_gline);
807         this->CreateCommand(new cmd_zline);
808         this->CreateCommand(new cmd_qline);
809         this->CreateCommand(new cmd_eline);
810         this->CreateCommand(new cmd_loadmodule);
811         this->CreateCommand(new cmd_unloadmodule);
812         this->CreateCommand(new cmd_server);
813         this->CreateCommand(new cmd_commands);
814 }
815