]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/command_parse.cpp
Fix (tested) for failing dlsym on win32
[user/henk/code/inspircd.git] / src / command_parse.cpp
1         /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "configreader.h"
16 #include <algorithm>
17 #include "users.h"
18 #include "modules.h"
19 #include "wildcard.h"
20 #include "xline.h"
21 #include "socketengine.h"
22 #include "socket.h"
23 #include "command_parse.h"
24
25 /* Directory Searching for Unix-Only */
26 #ifndef WIN32
27 #include <dirent.h>
28 #include <dlfcn.h>
29 #endif
30
31 bool InspIRCd::ULine(const char* server)
32 {
33         if (!server)
34                 return false;
35         if (!*server)
36                 return true;
37
38         return (Config->ulines.find(server) != Config->ulines.end());
39 }
40
41 bool InspIRCd::SilentULine(const char* server)
42 {
43         std::map<irc::string,bool>::iterator n = Config->ulines.find(server);
44         if (n != Config->ulines.end())
45                 return n->second;
46         else return false;
47 }
48
49 int InspIRCd::OperPassCompare(const char* data,const char* input, int tagnumber)
50 {
51         int MOD_RESULT = 0;
52         FOREACH_RESULT_I(this,I_OnOperCompare,OnOperCompare(data, input, tagnumber))
53         if (MOD_RESULT == 1)
54                 return 0;
55         if (MOD_RESULT == -1)
56                 return 1;
57         return strcmp(data,input);
58 }
59
60 std::string InspIRCd::TimeString(time_t curtime)
61 {
62         return std::string(ctime(&curtime),24);
63 }
64
65 long InspIRCd::Duration(const char* str)
66 {
67         char n_field[MAXBUF];
68         long total = 0;
69         n_field[0] = 0;
70
71         if ((!strchr(str,'s')) && (!strchr(str,'m')) && (!strchr(str,'h')) && (!strchr(str,'d')) && (!strchr(str,'w')) && (!strchr(str,'y')))
72         {
73                 std::string n = str;
74                 n += 's';
75                 return Duration(n.c_str());
76         }
77         
78         for (char* i = (char*)str; *i; i++)
79         {
80                 // if we have digits, build up a string for the value in n_field,
81                 // up to 10 digits in size.
82                 if ((*i >= '0') && (*i <= '9'))
83                 {
84                         strlcat(n_field,i,10);
85                 }
86                 else
87                 {
88                         // we dont have a digit, check for numeric tokens
89                         switch (tolower(*i))
90                         {
91                                 case 's':
92                                         total += atoi(n_field);
93                                 break;
94
95                                 case 'm':
96                                         total += (atoi(n_field)*duration_m);
97                                 break;
98
99                                 case 'h':
100                                         total += (atoi(n_field)*duration_h);
101                                 break;
102
103                                 case 'd':
104                                         total += (atoi(n_field)*duration_d);
105                                 break;
106
107                                 case 'w':
108                                         total += (atoi(n_field)*duration_w);
109                                 break;
110
111                                 case 'y':
112                                         total += (atoi(n_field)*duration_y);
113                                 break;
114                         }
115                         n_field[0] = 0;
116                 }
117         }
118         // add trailing seconds
119         total += atoi(n_field);
120         
121         return total;
122 }
123
124 /* LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
125  * There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
126  * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
127  * the channel names and their keys as follows:
128  * JOIN #chan1,#chan2,#chan3 key1,,key3
129  * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
130  * two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
131  * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
132  * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
133  */
134 int CommandParser::LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere, unsigned int extra)
135 {
136         /* First check if we have more than one item in the list, if we don't we return zero here and the handler
137          * which called us just carries on as it was.
138          */
139         if (!strchr(parameters[splithere],','))
140                 return 0;
141
142         /** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm.
143          * By using std::map (thanks for the idea w00t) we can cut this down a ton.
144          * ...VOOODOOOO!
145          */
146         std::map<irc::string, bool> dupes;
147
148         /* Create two lists, one for channel names, one for keys
149          */
150         irc::commasepstream items1(parameters[splithere]);
151         irc::commasepstream items2(parameters[extra]);
152         std::string item = "*";
153         unsigned int max = 0;
154
155         /* Attempt to iterate these lists and call the command objech
156          * which called us, for every parameter pair until there are
157          * no more left to parse.
158          */
159         while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets))
160         {
161                 if (dupes.find(item.c_str()) == dupes.end())
162                 {
163                         const char* new_parameters[127];
164
165                         for (int t = 0; (t < pcnt) && (t < 127); t++)
166                                 new_parameters[t] = parameters[t];
167
168                         std::string extrastuff = items2.GetToken();
169
170                         new_parameters[splithere] = item.c_str();
171                         new_parameters[extra] = extrastuff.c_str();
172
173                         CommandObj->Handle(new_parameters,pcnt,user);
174
175                         dupes[item.c_str()] = true;
176                 }
177         }
178         return 1;
179 }
180
181 int CommandParser::LoopCall(userrec* user, command_t* CommandObj, const char** parameters, int pcnt, unsigned int splithere)
182 {
183         /* First check if we have more than one item in the list, if we don't we return zero here and the handler
184          * which called us just carries on as it was.
185          */
186         if (!strchr(parameters[splithere],','))
187                 return 0;
188
189         std::map<irc::string, bool> dupes;
190
191         /* Only one commasepstream here */
192         irc::commasepstream items1(parameters[splithere]);
193         std::string item = "*";
194         unsigned int max = 0;
195
196         /* Parse the commasepstream until there are no tokens remaining.
197          * Each token we parse out, call the command handler that called us
198          * with it
199          */
200         while (((item = items1.GetToken()) != "") && (max++ < ServerInstance->Config->MaxTargets))
201         {
202                 if (dupes.find(item.c_str()) == dupes.end())
203                 {
204                         const char* new_parameters[127];
205
206                         for (int t = 0; (t < pcnt) && (t < 127); t++)
207                                 new_parameters[t] = parameters[t];
208
209                         new_parameters[splithere] = item.c_str();
210
211                         parameters[splithere] = item.c_str();
212
213                         /* Execute the command handler over and over. If someone pulls our user
214                          * record out from under us (e.g. if we /kill a comma sep list, and we're
215                          * in that list ourselves) abort if we're gone.
216                          */
217                         CommandObj->Handle(new_parameters,pcnt,user);
218
219                         dupes[item.c_str()] = true;
220                 }
221         }
222         /* By returning 1 we tell our caller that nothing is to be done,
223          * as all the previous calls handled the data. This makes the parent
224          * return without doing any processing.
225          */
226         return 1;
227 }
228
229 bool CommandParser::IsValidCommand(const std::string &commandname, int pcnt, userrec * user)
230 {
231         command_table::iterator n = cmdlist.find(commandname);
232
233         if (n != cmdlist.end())
234         {
235                 if ((pcnt>=n->second->min_params) && (n->second->source != "<core>"))
236                 {
237                         if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65]))
238                         {
239                                 if (n->second->flags_needed)
240                                 {
241                                         return ((user->HasPermission(commandname)) || (ServerInstance->ULine(user->server)));
242                                 }
243                                 return true;
244                         }
245                 }
246         }
247         return false;
248 }
249
250 command_t* CommandParser::GetHandler(const std::string &commandname)
251 {
252         command_table::iterator n = cmdlist.find(commandname);
253         if (n != cmdlist.end())
254                 return n->second;
255
256         return NULL;
257 }
258
259 // calls a handler function for a command
260
261 CmdResult CommandParser::CallHandler(const std::string &commandname,const char** parameters, int pcnt, userrec *user)
262 {
263         command_table::iterator n = cmdlist.find(commandname);
264
265         if (n != cmdlist.end())
266         {
267                 if (pcnt >= n->second->min_params)
268                 {
269                         if ((!n->second->flags_needed) || (user->modes[n->second->flags_needed-65]))
270                         {
271                                 if (n->second->flags_needed)
272                                 {
273                                         if ((user->HasPermission(commandname)) || (!IS_LOCAL(user)))
274                                         {
275                                                 return n->second->Handle(parameters,pcnt,user);
276                                         }
277                                 }
278                                 else
279                                 {
280                                         return n->second->Handle(parameters,pcnt,user);
281                                 }
282                         }
283                 }
284         }
285         return CMD_INVALID;
286 }
287
288 void CommandParser::ProcessCommand(userrec *user, std::string &cmd)
289 {
290         const char *command_p[127];
291         int items = 0;
292         irc::tokenstream tokens(cmd);
293         std::string command;
294         tokens.GetToken(command);
295
296         /* A client sent a nick prefix on their command (ick)
297          * rhapsody and some braindead bouncers do this --
298          * the rfc says they shouldnt but also says the ircd should
299          * discard it if they do.
300          */
301         if (*command.c_str() == ':')
302                 tokens.GetToken(command);
303
304         while (tokens.GetToken(para[items]) && (items < 127))
305         {
306                 command_p[items] = para[items].c_str();
307                 items++;
308         }
309
310         std::transform(command.begin(), command.end(), command.begin(), ::toupper);
311                 
312         int MOD_RESULT = 0;
313         FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,false,cmd));
314         if (MOD_RESULT == 1) {
315                 return;
316         }
317
318         command_table::iterator cm = cmdlist.find(command);
319         
320         if (cm != cmdlist.end())
321         {
322                 if (user)
323                 {
324                         /* activity resets the ping pending timer */
325                         user->nping = ServerInstance->Time() + user->pingmax;
326                         if (cm->second->flags_needed)
327                         {
328                                 if (!user->IsModeSet(cm->second->flags_needed))
329                                 {
330                                         user->WriteServ("481 %s :Permission Denied- You do not have the required operator privileges",user->nick);
331                                         return;
332                                 }
333                                 if (!user->HasPermission(command))
334                                 {
335                                         user->WriteServ("481 %s :Permission Denied- Oper type %s does not have access to command %s",user->nick,user->oper,command.c_str());
336                                         return;
337                                 }
338                         }
339                         if ((user->registered == REG_ALL) && (!IS_OPER(user)) && (cm->second->IsDisabled()))
340                         {
341                                 /* command is disabled! */
342                                 user->WriteServ("421 %s %s :This command has been disabled.",user->nick,command.c_str());
343                                 return;
344                         }
345                         if (items < cm->second->min_params)
346                         {
347                                 user->WriteServ("461 %s %s :Not enough parameters.", user->nick, command.c_str());
348                                 /* If syntax is given, display this as the 461 reply */
349                                 if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (cm->second->syntax.length()))
350                                         user->WriteServ("304 %s :SYNTAX %s %s", user->nick, cm->second->command.c_str(), cm->second->syntax.c_str());
351                                 return;
352                         }
353                         if ((user->registered == REG_ALL) || (cm->second->WorksBeforeReg()))
354                         {
355                                 /* ikky /stats counters */
356                                 cm->second->use_count++;
357                                 cm->second->total_bytes += cmd.length();
358
359                                 int MOD_RESULT = 0;
360                                 FOREACH_RESULT(I_OnPreCommand,OnPreCommand(command,command_p,items,user,true,cmd));
361                                 if (MOD_RESULT == 1)
362                                         return;
363
364                                 /*
365                                  * WARNING: nothing may come after the
366                                  * command handler call, as the handler
367                                  * may free the user structure!
368                                  */
369                                 CmdResult result = cm->second->Handle(command_p,items,user);
370
371                                 FOREACH_MOD(I_OnPostCommand,OnPostCommand(command, command_p, items, user, result,cmd));
372                                 return;
373                         }
374                         else
375                         {
376                                 user->WriteServ("451 %s :You have not registered",command.c_str());
377                                 return;
378                         }
379                 }
380         }
381         else if (user)
382         {
383                 ServerInstance->stats->statsUnknown++;
384                 user->WriteServ("421 %s %s :Unknown command",user->nick,command.c_str());
385         }
386 }
387
388 bool CommandParser::RemoveCommands(const char* source)
389 {
390         command_table::iterator i,safei;
391         for (i = cmdlist.begin(); i != cmdlist.end(); i++)
392         {
393                 safei = i;
394                 safei++;
395                 if (safei != cmdlist.end())
396                 {
397                         RemoveCommand(safei, source);
398                 }
399         }
400         safei = cmdlist.begin();
401         if (safei != cmdlist.end())
402         {
403                 RemoveCommand(safei, source);
404         }
405         return true;
406 }
407
408 void CommandParser::RemoveCommand(command_table::iterator safei, const char* source)
409 {
410         command_t* x = safei->second;
411         if (x->source == std::string(source))
412         {
413                 cmdlist.erase(safei);
414         }
415 }
416
417 void CommandParser::ProcessBuffer(std::string &buffer,userrec *user)
418 {
419         std::string::size_type a;
420
421         if (!user)
422                 return;
423
424         while ((a = buffer.rfind("\n")) != std::string::npos)
425                 buffer.erase(a);
426         while ((a = buffer.rfind("\r")) != std::string::npos)
427                 buffer.erase(a);
428
429         if (buffer.length())
430         {
431                 if (!user->muted)
432                 {
433                         ServerInstance->Log(DEBUG,"C[%d] -> :%s %s",user->GetFd(), user->nick, buffer.c_str());
434                         this->ProcessCommand(user,buffer);
435                 }
436         }
437 }
438
439 bool CommandParser::CreateCommand(command_t *f, void* so_handle)
440 {
441         if (so_handle)
442         {
443                 if (RFCCommands.find(f->command) == RFCCommands.end())
444                         RFCCommands[f->command] = so_handle;
445                 else
446                 {
447                         ServerInstance->Log(DEFAULT,"ERK! Somehow, we loaded a cmd_*.so file twice! Only the first instance is being recorded.");
448                         return false;
449                 }
450         }
451
452         /* create the command and push it onto the table */
453         if (cmdlist.find(f->command) == cmdlist.end())
454         {
455                 cmdlist[f->command] = f;
456                 return true;
457         }
458         else return false;
459 }
460
461 CommandParser::CommandParser(InspIRCd* Instance) : ServerInstance(Instance)
462 {
463         para.resize(128);
464         this->SetupCommandTable();
465 }
466
467 bool CommandParser::FindSym(void** v, void* h)
468 {
469         *v = dlsym(h, "init_command");
470         const char* err = dlerror();
471         if (err && !(*v))
472         {
473                 ServerInstance->Log(SPARSE, "Error loading core command: %s\n", err);
474                 return false;
475         }
476         return true;
477 }
478
479 bool CommandParser::ReloadCommand(const char* cmd)
480 {
481         char filename[MAXBUF];
482         char commandname[MAXBUF];
483         int y = 0;
484
485         for (const char* x = cmd; *x; x++, y++)
486                 commandname[y] = toupper(*x);
487
488         commandname[y] = 0;
489
490         SharedObjectList::iterator command = RFCCommands.find(commandname);
491
492         if (command != RFCCommands.end())
493         {
494                 command_t* cmdptr = cmdlist.find(commandname)->second;
495                 cmdlist.erase(cmdlist.find(commandname));
496
497                 for (char* x = commandname; *x; x++)
498                         *x = tolower(*x);
499
500
501                 delete cmdptr;
502                 dlclose(command->second);
503                 RFCCommands.erase(command);
504
505                 snprintf(filename, MAXBUF, "cmd_%s.so", commandname);
506                 this->LoadCommand(filename);
507
508                 return true;
509         }
510
511         return false;
512 }
513
514 CmdResult cmd_reload::Handle(const char** parameters, int pcnt, userrec *user)
515 {
516         user->WriteServ("NOTICE %s :*** Reloading command '%s'",user->nick, parameters[0]);
517         if (ServerInstance->Parser->ReloadCommand(parameters[0]))
518         {
519                 user->WriteServ("NOTICE %s :*** Successfully reloaded command '%s'", user->nick, parameters[0]);
520                 ServerInstance->WriteOpers("*** RELOAD: %s reloaded the '%s' command.", user->nick, parameters[0]);
521                 return CMD_SUCCESS;
522         }
523         else
524         {
525                 user->WriteServ("NOTICE %s :*** Could not reload command '%s'", user->nick, parameters[0]);
526                 return CMD_FAILURE;
527         }
528 }
529
530 void CommandParser::LoadCommand(const char* name)
531 {
532         char filename[MAXBUF];
533         void* h;
534         command_t* (*cmd_factory_func)(InspIRCd*);
535
536         snprintf(filename, MAXBUF, "%s/%s", LIBRARYDIR, name);
537         h = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
538
539         if (!h)
540         {
541                 ServerInstance->Log(SPARSE, "Error loading core command: %s", dlerror());
542                 return;
543         }
544
545         if (this->FindSym((void **)&cmd_factory_func, h))
546         {
547                 command_t* newcommand = cmd_factory_func(ServerInstance);
548                 this->CreateCommand(newcommand, h);
549         }
550 }
551
552 void CommandParser::SetupCommandTable()
553 {
554         RFCCommands.clear();
555
556         printf("\nLoading core commands");
557         fflush(stdout);
558
559         DIR* library = opendir(LIBRARYDIR);
560         if (library)
561         {
562                 dirent* entry = NULL;
563                 while ((entry = readdir(library)))
564                 {
565                         if (match(entry->d_name, "cmd_*.so"))
566                         {
567                                 printf(".");
568                                 fflush(stdout);
569                                 this->LoadCommand(entry->d_name);
570                         }
571                 }
572                 closedir(library);
573                 printf("\n");
574         }
575
576         this->CreateCommand(new cmd_reload(ServerInstance));
577 }
578