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