]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/command_parse.cpp
Fix for parameters which contain a colon (which is not the first char in the string)
[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 #include "inspircd.h"
18 #include "configreader.h"
19 #include <algorithm>
20 #include "users.h"
21 #include "modules.h"
22 #include "wildcard.h"
23 #include "xline.h"
24 #include "socketengine.h"
25 #include "userprocess.h"
26 #include "socket.h"
27 #include "command_parse.h"
28 #define nspace __gnu_cxx
29
30 /*       XXX Serious WTFness XXX
31  *
32  * Well, unless someone invents a wildcard or
33  * regexp #include, and makes it a standard,
34  * we're stuck with this way of including all
35  * the commands.
36  */
37
38 #include "commands/cmd_admin.h"
39 #include "commands/cmd_away.h"
40 #include "commands/cmd_commands.h"
41 #include "commands/cmd_connect.h"
42 #include "commands/cmd_die.h"
43 #include "commands/cmd_eline.h"
44 #include "commands/cmd_gline.h"
45 #include "commands/cmd_info.h"
46 #include "commands/cmd_invite.h"
47 #include "commands/cmd_ison.h"
48 #include "commands/cmd_join.h"
49 #include "commands/cmd_kick.h"
50 #include "commands/cmd_kill.h"
51 #include "commands/cmd_kline.h"
52 #include "commands/cmd_links.h"
53 #include "commands/cmd_list.h"
54 #include "commands/cmd_loadmodule.h"
55 #include "commands/cmd_lusers.h"
56 #include "commands/cmd_map.h"
57 #include "commands/cmd_modules.h"
58 #include "commands/cmd_motd.h"
59 #include "commands/cmd_names.h"
60 #include "commands/cmd_nick.h"
61 #include "commands/cmd_notice.h"
62 #include "commands/cmd_oper.h"
63 #include "commands/cmd_part.h"
64 #include "commands/cmd_pass.h"
65 #include "commands/cmd_ping.h"
66 #include "commands/cmd_pong.h"
67 #include "commands/cmd_privmsg.h"
68 #include "commands/cmd_qline.h"
69 #include "commands/cmd_quit.h"
70 #include "commands/cmd_rehash.h"
71 #include "commands/cmd_restart.h"
72 #include "commands/cmd_rules.h"
73 #include "commands/cmd_server.h"
74 #include "commands/cmd_squit.h"
75 #include "commands/cmd_stats.h"
76 #include "commands/cmd_summon.h"
77 #include "commands/cmd_time.h"
78 #include "commands/cmd_topic.h"
79 #include "commands/cmd_trace.h"
80 #include "commands/cmd_unloadmodule.h"
81 #include "commands/cmd_user.h"
82 #include "commands/cmd_userhost.h"
83 #include "commands/cmd_users.h"
84 #include "commands/cmd_version.h"
85 #include "commands/cmd_wallops.h"
86 #include "commands/cmd_who.h"
87 #include "commands/cmd_whois.h"
88 #include "commands/cmd_whowas.h"
89 #include "commands/cmd_zline.h"
90
91 bool InspIRCd::ULine(const char* server)
92 {
93         if (!server)
94                 return false;
95         if (!*server)
96                 return true;
97
98         return (find(Config->ulines.begin(),Config->ulines.end(),server) != Config->ulines.end());
99 }
100
101 int InspIRCd::OperPassCompare(const char* data,const char* input)
102 {
103         int MOD_RESULT = 0;
104         FOREACH_RESULT_I(this,I_OnOperCompare,OnOperCompare(data,input))
105         Log(DEBUG,"OperPassCompare: %d",MOD_RESULT);
106         if (MOD_RESULT == 1)
107                 return 0;
108         if (MOD_RESULT == -1)
109                 return 1;
110         Log(DEBUG,"strcmp fallback: '%s' '%s' %d",data,input,strcmp(data,input));
111         return strcmp(data,input);
112 }
113
114 long InspIRCd::Duration(const char* str)
115 {
116         char n_field[MAXBUF];
117         long total = 0;
118         n_field[0] = 0;
119
120         if ((!strchr(str,'s')) && (!strchr(str,'m')) && (!strchr(str,'h')) && (!strchr(str,'d')) && (!strchr(str,'w')) && (!strchr(str,'y')))
121         {
122                 std::string n = str;
123                 n += 's';
124                 return Duration(n.c_str());
125         }
126         
127         for (char* i = (char*)str; *i; i++)
128         {
129                 // if we have digits, build up a string for the value in n_field,
130                 // up to 10 digits in size.
131                 if ((*i >= '0') && (*i <= '9'))
132                 {
133                         strlcat(n_field,i,10);
134                 }
135                 else
136                 {
137                         // we dont have a digit, check for numeric tokens
138                         switch (tolower(*i))
139                         {
140                                 case 's':
141                                         total += atoi(n_field);
142                                 break;
143
144                                 case 'm':
145                                         total += (atoi(n_field)*duration_m);
146                                 break;
147
148                                 case 'h':
149                                         total += (atoi(n_field)*duration_h);
150                                 break;
151
152                                 case 'd':
153                                         total += (atoi(n_field)*duration_d);
154                                 break;
155
156                                 case 'w':
157                                         total += (atoi(n_field)*duration_w);
158                                 break;
159
160                                 case 'y':
161                                         total += (atoi(n_field)*duration_y);
162                                 break;
163                         }
164                         n_field[0] = 0;
165                 }
166         }
167         // add trailing seconds
168         total += atoi(n_field);
169         
170         return total;
171 }
172
173 /* All other ircds when doing this check usually just look for a string of *@* or *. We're smarter than that, though. */
174
175 bool InspIRCd::HostMatchesEveryone(const std::string &mask, userrec* user)
176 {
177         char buffer[MAXBUF];
178         char itrigger[MAXBUF];
179         long matches = 0;
180         
181         if (!Config->ConfValue(Config->config_data, "insane","trigger", 0, itrigger, MAXBUF))
182                 strlcpy(itrigger,"95.5",MAXBUF);
183         
184         if (Config->ConfValueBool(Config->config_data, "insane","hostmasks", 0))
185                 return false;
186         
187         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
188         {
189                 strlcpy(buffer,u->second->ident,MAXBUF);
190                 charlcat(buffer,'@',MAXBUF);
191                 strlcat(buffer,u->second->host,MAXBUF);
192                 if (match(buffer,mask.c_str()))
193                         matches++;
194         }
195         float percent = ((float)matches / (float)clientlist.size()) * 100;
196         if (percent > (float)atof(itrigger))
197         {
198                 WriteOpers("*** \2WARNING\2: %s tried to set a G/K/E line mask of %s, which covers %.2f%% of the network!",user->nick,mask.c_str(),percent);
199                 return true;
200         }
201         return false;
202 }
203
204 bool InspIRCd::IPMatchesEveryone(const std::string &ip, userrec* user)
205 {
206         char itrigger[MAXBUF];
207         long matches = 0;
208         
209         if (!Config->ConfValue(Config->config_data, "insane","trigger",0,itrigger,MAXBUF))
210                 strlcpy(itrigger,"95.5",MAXBUF);
211         
212         if (Config->ConfValueBool(Config->config_data, "insane","ipmasks",0))
213                 return false;
214         
215         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
216         {
217                 if (match(u->second->GetIPString(),ip.c_str(),true))
218                         matches++;
219         }
220         
221         float percent = ((float)matches / (float)clientlist.size()) * 100;
222         if (percent > (float)atof(itrigger))
223         {
224                 WriteOpers("*** \2WARNING\2: %s tried to set a Z line mask of %s, which covers %.2f%% of the network!",user->nick,ip.c_str(),percent);
225                 return true;
226         }
227         return false;
228 }
229
230 bool InspIRCd::NickMatchesEveryone(const std::string &nick, userrec* user)
231 {
232         char itrigger[MAXBUF];
233         long matches = 0;
234         
235         if (!Config->ConfValue(Config->config_data, "insane","trigger",0,itrigger,MAXBUF))
236                 strlcpy(itrigger,"95.5",MAXBUF);
237         
238         if (Config->ConfValueBool(Config->config_data, "insane","nickmasks",0))
239                 return false;
240
241         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
242         {
243                 if (match(u->second->nick,nick.c_str()))
244                         matches++;
245         }
246         
247         float percent = ((float)matches / (float)clientlist.size()) * 100;
248         if (percent > (float)atof(itrigger))
249         {
250                 WriteOpers("*** \2WARNING\2: %s tried to set a Q line mask of %s, which covers %.2f%% of the network!",user->nick,nick.c_str(),percent);
251                 return true;
252         }
253         return false;
254 }
255
256
257
258
259
260 /* Special commands which may occur without registration of the user */
261 cmd_user* command_user;
262 cmd_nick* command_nick;
263 cmd_pass* command_pass;
264
265
266 /* LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
267  * There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
268  * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
269  * the channel names and their keys as follows:
270  * JOIN #chan1,#chan2,#chan3 key1,,key3
271  * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
272  * two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
273  * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
274  * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
275  */
276 int CommandParser::LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere, unsigned int extra)
277 {
278         /* First check if we have more than one item in the list, if we don't we return zero here and the handler
279          * which called us just carries on as it was.
280          */
281         if (!strchr(parameters[splithere],','))
282                 return 0;
283
284         /* Create two lists, one for channel names, one for keys
285          */
286         irc::commasepstream items1(parameters[splithere]);
287         irc::commasepstream items2(parameters[extra]);
288         std::string item = "";
289         unsigned int max = 0;
290
291         /* Attempt to iterate these lists and call the command objech
292          * which called us, for every parameter pair until there are
293          * no more left to parse.
294          */
295         while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets))
296         {
297                 std::string extrastuff = items2.GetToken();
298                 parameters[splithere] = item.c_str();
299                 parameters[extra] = extrastuff.c_str();
300                 CommandObj->Handle(parameters,pcnt,user);
301         }
302         return 1;
303 }
304
305 int CommandParser::LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere)
306 {
307         /* First check if we have more than one item in the list, if we don't we return zero here and the handler
308          * which called us just carries on as it was.
309          */
310         if (!strchr(parameters[splithere],','))
311                 return 0;
312
313         /* Only one commasepstream here */
314         irc::commasepstream items1(parameters[splithere]);
315         std::string item = "";
316         unsigned int max = 0;
317
318         /* Parse the commasepstream until there are no tokens remaining.
319          * Each token we parse out, call the command handler that called us
320          * with it
321          */
322         while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets))
323         {
324                 parameters[splithere] = item.c_str();
325                 CommandObj->Handle(parameters,pcnt,user);
326         }
327         /* By returning 1 we tell our caller that nothing is to be done,
328          * as all the previous calls handled the data. This makes the parent
329          * return without doing any processing.
330          */
331         return 1;
332 }
333
334 bool CommandParser::IsValidCommand(const std::string &commandname, int pcnt, userrec * user)
335 {
336         nspace::hash_map<std::string,command_t*>::iterator n = cmdlist.find(commandname);
337
338         if (n != cmdlist.end())
339         {
340                 if ((pcnt>=n->second->min_params) && (n->second->source != "<core>"))
341                 {
342                         if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65]))
343                         {
344                                 if (n->second->flags_needed)
345                                 {
346                                         return ((user->HasPermission(commandname)) || (ServerInstance->ULine(user->server)));
347                                 }
348                                 return true;
349                         }
350                 }
351         }
352         return false;
353 }
354
355 // calls a handler function for a command
356
357 bool CommandParser::CallHandler(const std::string &commandname,const char** parameters, int pcnt, userrec *user)
358 {
359         nspace::hash_map<std::string,command_t*>::iterator n = cmdlist.find(commandname);
360
361         if (n != cmdlist.end())
362         {
363                 if (pcnt >= n->second->min_params)
364                 {
365                         if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65]))
366                         {
367                                 if (n->second->flags_needed)
368                                 {
369                                         if ((user->HasPermission(commandname)) || (!IS_LOCAL(user)))
370                                         {
371                                                 n->second->Handle(parameters,pcnt,user);
372                                                 return true;
373                                         }
374                                 }
375                                 else
376                                 {
377                                         n->second->Handle(parameters,pcnt,user);
378                                         return true;
379                                 }
380                         }
381                 }
382         }
383         return false;
384 }
385
386 void CommandParser::ProcessCommand(userrec *user, std::string &cmd)
387 {
388         const char *command_p[127];
389         int items = 0;
390         std::string para[127];
391         irc::tokenstream tokens(cmd);
392         std::string command = tokens.GetToken();
393
394         while (((para[items] = tokens.GetToken()) != "") && (items < 127))
395                 command_p[items] = para[items++].c_str();
396
397         std::transform(command.begin(), command.end(), command.begin(), ::toupper);
398                 
399         int MOD_RESULT = 0;
400         FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,false));
401         if (MOD_RESULT == 1) {
402                 return;
403         }
404
405         nspace::hash_map<std::string,command_t*>::iterator cm = cmdlist.find(command);
406         
407         if (cm != cmdlist.end())
408         {
409                 if (user)
410                 {
411                         /* activity resets the ping pending timer */
412                         user->nping = ServerInstance->Time() + user->pingmax;
413                         if (cm->second->flags_needed)
414                         {
415                                 if (!user->IsModeSet(cm->second->flags_needed))
416                                 {
417                                         user->WriteServ("481 %s :Permission Denied- You do not have the required operator privilages",user->nick);
418                                         return;
419                                 }
420                                 if (!user->HasPermission(command))
421                                 {
422                                         user->WriteServ("481 %s :Permission Denied- Oper type %s does not have access to command %s",user->nick,user->oper,command.c_str());
423                                         return;
424                                 }
425                         }
426                         if ((user->registered == REG_ALL) && (!*user->oper) && (cm->second->IsDisabled()))
427                         {
428                                 /* command is disabled! */
429                                 user->WriteServ("421 %s %s :This command has been disabled.",user->nick,command.c_str());
430                                 return;
431                         }
432                         if (items < cm->second->min_params)
433                         {
434                                 user->WriteServ("461 %s %s :Not enough parameters.", user->nick, command.c_str());
435                                 /* If syntax is given, display this as the 461 reply */
436                                 if ((ServerInstance->Config->SyntaxHints) && (cm->second->syntax.length()))
437                                         user->WriteServ("304 %s :SYNTAX %s %s", user->nick, cm->second->command.c_str(), cm->second->syntax.c_str());
438                                 return;
439                         }
440                         if ((user->registered == REG_ALL) || (cm->second == command_user) || (cm->second == command_nick) || (cm->second == command_pass))
441                         {
442                                 /* ikky /stats counters */
443                                 cm->second->use_count++;
444                                 cm->second->total_bytes += cmd.length();
445
446                                 int MOD_RESULT = 0;
447                                 FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,true));
448                                 if (MOD_RESULT == 1)
449                                         return;
450
451                                 /*
452                                  * WARNING: nothing may come after the
453                                  * command handler call, as the handler
454                                  * may free the user structure!
455                                  */
456
457                                 cm->second->Handle(command_p,items,user);
458                                 return;
459                         }
460                         else
461                         {
462                                 user->WriteServ("451 %s :You have not registered",command.c_str());
463                                 return;
464                         }
465                 }
466         }
467         else if (user)
468         {
469                 ServerInstance->stats->statsUnknown++;
470                 user->WriteServ("421 %s %s :Unknown command",user->nick,command.c_str());
471         }
472 }
473
474 bool CommandParser::RemoveCommands(const char* source)
475 {
476         bool go_again = true;
477
478         while (go_again)
479         {
480                 go_again = false;
481
482                 for (nspace::hash_map<std::string,command_t*>::iterator i = cmdlist.begin(); i != cmdlist.end(); i++)
483                 {
484                         command_t* x = i->second;
485                         if (x->source == std::string(source))
486                         {
487                                 ServerInstance->Log(DEBUG,"removecommands(%s) Removing dependent command: %s",x->source.c_str(),x->command.c_str());
488                                 cmdlist.erase(i);
489                                 go_again = true;
490                                 break;
491                         }
492                 }
493         }
494
495         return true;
496 }
497
498 void CommandParser::ProcessBuffer(std::string &buffer,userrec *user)
499 {
500         std::string::size_type a;
501
502         if (!user)
503                 return;
504
505         while ((a = buffer.rfind("\n")) != std::string::npos)
506                 buffer.erase(a);
507         while ((a = buffer.rfind("\r")) != std::string::npos)
508                 buffer.erase(a);
509
510         if (buffer.length())
511         {
512                 ServerInstance->Log(DEBUG,"CMDIN: %s %s",user->nick,buffer.c_str());
513                 this->ProcessCommand(user,buffer);
514         }
515 }
516
517 bool CommandParser::CreateCommand(command_t *f)
518 {
519         /* create the command and push it onto the table */
520         if (cmdlist.find(f->command) == cmdlist.end())
521         {
522                 cmdlist[f->command] = f;
523                 ServerInstance->Log(DEBUG,"Added command %s (%lu parameters)",f->command.c_str(),(unsigned long)f->min_params);
524                 return true;
525         }
526         else return false;
527 }
528
529 CommandParser::CommandParser(InspIRCd* Instance) : ServerInstance(Instance)
530 {
531         this->SetupCommandTable();
532 }
533
534 void CommandParser::SetupCommandTable()
535 {
536         /* These three are special (can occur without
537          * full user registration) and so are saved
538          * for later use.
539          */
540         command_user = new cmd_user(ServerInstance);
541         command_nick = new cmd_nick(ServerInstance);
542         command_pass = new cmd_pass(ServerInstance);
543         this->CreateCommand(command_user);
544         this->CreateCommand(command_nick);
545         this->CreateCommand(command_pass);
546
547         /* The rest of these arent special. boo hoo.
548          */
549         this->CreateCommand(new cmd_quit(ServerInstance));
550         this->CreateCommand(new cmd_version(ServerInstance));
551         this->CreateCommand(new cmd_ping(ServerInstance));
552         this->CreateCommand(new cmd_pong(ServerInstance));
553         this->CreateCommand(new cmd_admin(ServerInstance));
554         this->CreateCommand(new cmd_privmsg(ServerInstance));
555         this->CreateCommand(new cmd_info(ServerInstance));
556         this->CreateCommand(new cmd_time(ServerInstance));
557         this->CreateCommand(new cmd_whois(ServerInstance));
558         this->CreateCommand(new cmd_wallops(ServerInstance));
559         this->CreateCommand(new cmd_notice(ServerInstance));
560         this->CreateCommand(new cmd_join(ServerInstance));
561         this->CreateCommand(new cmd_names(ServerInstance));
562         this->CreateCommand(new cmd_part(ServerInstance));
563         this->CreateCommand(new cmd_kick(ServerInstance));
564         this->CreateCommand(new cmd_mode(ServerInstance));
565         this->CreateCommand(new cmd_topic(ServerInstance));
566         this->CreateCommand(new cmd_who(ServerInstance));
567         this->CreateCommand(new cmd_motd(ServerInstance));
568         this->CreateCommand(new cmd_rules(ServerInstance));
569         this->CreateCommand(new cmd_oper(ServerInstance));
570         this->CreateCommand(new cmd_list(ServerInstance));
571         this->CreateCommand(new cmd_die(ServerInstance));
572         this->CreateCommand(new cmd_restart(ServerInstance));
573         this->CreateCommand(new cmd_kill(ServerInstance));
574         this->CreateCommand(new cmd_rehash(ServerInstance));
575         this->CreateCommand(new cmd_lusers(ServerInstance));
576         this->CreateCommand(new cmd_stats(ServerInstance));
577         this->CreateCommand(new cmd_userhost(ServerInstance));
578         this->CreateCommand(new cmd_away(ServerInstance));
579         this->CreateCommand(new cmd_ison(ServerInstance));
580         this->CreateCommand(new cmd_summon(ServerInstance));
581         this->CreateCommand(new cmd_users(ServerInstance));
582         this->CreateCommand(new cmd_invite(ServerInstance));
583         this->CreateCommand(new cmd_trace(ServerInstance));
584         this->CreateCommand(new cmd_whowas(ServerInstance));
585         this->CreateCommand(new cmd_connect(ServerInstance));
586         this->CreateCommand(new cmd_squit(ServerInstance));
587         this->CreateCommand(new cmd_modules(ServerInstance));
588         this->CreateCommand(new cmd_links(ServerInstance));
589         this->CreateCommand(new cmd_map(ServerInstance));
590         this->CreateCommand(new cmd_kline(ServerInstance));
591         this->CreateCommand(new cmd_gline(ServerInstance));
592         this->CreateCommand(new cmd_zline(ServerInstance));
593         this->CreateCommand(new cmd_qline(ServerInstance));
594         this->CreateCommand(new cmd_eline(ServerInstance));
595         this->CreateCommand(new cmd_loadmodule(ServerInstance));
596         this->CreateCommand(new cmd_unloadmodule(ServerInstance));
597         this->CreateCommand(new cmd_server(ServerInstance));
598         this->CreateCommand(new cmd_commands(ServerInstance));
599 }
600