Merge branch 'development'
[NeonServV5.git] / src / modcmd.c
1 /* modcmd.c - NeonServ v5.6
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #include "modcmd.h"
19 #include "IRCEvents.h"
20 #include "IRCParser.h"
21 #include "ClientSocket.h"
22 #include "UserNode.h"
23 #include "ChanNode.h"
24 #include "ChanUser.h"
25 #include "WHOHandler.h"
26 #include "lang.h"
27 #include "mysqlConn.h"
28 #include "DBHelper.h"
29 #include "EventLogger.h"
30 #include "tools.h"
31 #include "log.h"
32
33 struct trigger_callback {
34     int botid;
35     int module_id;
36     trigger_callback_t *func;
37     
38     struct trigger_callback *next;
39 };
40
41 struct cmd_bot_alias {
42     int botid;
43     char *alias;
44     
45     struct cmd_bot_alias *next;
46 };
47
48 struct command_check_user_cache {
49     struct ClientSocket *client, *textclient;
50     struct UserNode *user;
51     struct ChanNode *chan, *sent_chan;
52     char **argv;
53     int argc;
54     char *message, *args_buffer;
55     struct cmd_binding *cbind;
56 };
57
58 static struct cmd_binding **cmd_binds;
59 static struct cmd_function *cmd_functions = NULL;
60 static struct trigger_callback *trigger_callbacks = NULL;
61 static struct cmd_bot_alias *bot_aliases = NULL;
62 static int total_triggered = 0;
63 int statistics_commands = 0;
64
65 static const struct default_language_entry msgtab[] = {
66     {"MODCMD_LESS_PARAM_COUNT", "This command requires more parameters."},
67     {"MODCMD_CHAN_REQUIRED",    "You must provide the name of a channel that exists and the bot is on."},
68     {"MODCMD_AUTH_REQUIRED",    "You need to be authenticated with AuthServ to use this command."},
69     {"MODCMD_CHAN_SUSPENDED",   "This channel is currently suspended."},
70     {"MODCMD_PRIVILEGED",       "$b%s$b is a privileged command."}, /* {ARGS: "god"} */
71     {"MODCMD_PUBCMD",           "Public commands in $b%s$b are restricted."}, /* {ARGS: "#TestChan"} */
72     {"MODCMD_ACCESS_DENIED",    "Access denied."},
73     {"MODCMD_SUBCOMMANDS",      "Subcommands of %s: %s"}, /* {ARGS: "bot", "ADD, DEL, EDIT"} */
74     {"MODCMD_CROSSCHAN",        "You must be in %s (or on its userlist) to use this command."}, /* {ARGS: "#TestChan"} */
75     {"MODCMD_UNKNOWN",          "$b%s$b is an unknown command."}, /* {ARGS: "bla"} */
76     {NULL, NULL}
77 };
78
79 static int get_binds_index(char first_char) {
80     if(tolower(first_char) >= 'a' && tolower(first_char) <= 'z') {
81         return tolower(first_char) - 'a';
82     }
83     return 26;
84 }
85
86 struct ClientSocket* get_botwise_prefered_bot(int botid, int clientid) {
87     struct ClientSocket *client, *lowbot = NULL;
88     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
89         if(client->botid == botid && (botid || clientid == client->clientid)) {
90             if((client->flags & SOCKET_FLAG_PREFERRED))
91                 return client;
92             else
93                 lowbot = client;
94         }
95     }
96     return lowbot;
97 }
98
99 static char* get_channel_trigger(int botid, int clientid, struct ChanNode *chan) {
100     struct trigger_cache *trigger;
101     for(trigger = chan->trigger; trigger; trigger = trigger->next) {
102         if(trigger->botid == botid && (botid || trigger->clientid == clientid))
103             return trigger->trigger;
104     }
105     struct trigger_callback *cb;
106     for(cb = trigger_callbacks; cb; cb = cb->next) {
107         if(cb->botid == botid)
108             break;
109     }
110     char triggerStr[TRIGGERLEN];
111     if(cb)
112         cb->func(clientid, chan, triggerStr);
113     else
114         triggerStr[0] = '\0';
115     trigger = malloc(sizeof(*trigger));
116     if (!trigger) {
117         printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
118         return 0;
119     }
120     trigger->botid = botid;
121     trigger->clientid = clientid;
122     trigger->trigger = (triggerStr[0] ? strdup(triggerStr) : NULL);
123     trigger->next = chan->trigger;
124     chan->trigger = trigger;
125     return trigger->trigger;
126 }
127
128 static void handle_command_async(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct ChanNode *sent_chan, struct cmd_binding *cbind, char **argv, int argc);
129
130 static USERAUTH_CALLBACK(command_checked_auth) {
131     struct command_check_user_cache *cache = data;
132     if(user)
133         handle_command_async(cache->client, cache->textclient, user, cache->chan, cache->sent_chan, cache->cbind, cache->argv, cache->argc);
134     free(cache->message);
135     if(cache->args_buffer)
136         free(cache->args_buffer);
137     free(cache->argv);
138     free(cache);
139 }
140
141 static CMD_BIND(modcmd_linker) {
142     //fake command for subcommands
143     //just empty
144 }
145
146 static struct cmd_binding *modcmd_linker_command(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, int bind_index, char **args_ptr) {
147     //links subcommands
148     char command[MAXLEN];
149     struct cmd_binding *parent_bind = cbind;
150     char *args = *args_ptr;
151     if(args) {
152         char *subcmd = args;
153         args = strstr(args, " ");
154         if(args) {
155             *args = '\0';
156             args++;
157         }
158         sprintf(command, "%s %s", parent_bind->cmd, subcmd);
159         for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
160             if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmp(cbind->cmd, command) == 0)
161                 break;
162         }
163         *args_ptr = args;
164         if(cbind && cbind->func->func == modcmd_linker) {
165             return modcmd_linker_command(client, textclient, user, cbind, bind_index, args_ptr);
166         }
167         return cbind;
168     } else {
169         //list all sub commands
170         int commandlen = sprintf(command, "%s ", parent_bind->cmd);
171         char subcommands[MAXLEN];
172         int subcompos = 0;
173         for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
174             if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmplen(cbind->cmd, command, commandlen) == 0) {
175                 if(strstr(cbind->cmd + commandlen, " ")) continue; //ignore if there is another space in the command (sub-sub-command :D)
176                 subcompos += sprintf(subcommands + subcompos, (subcompos ? ", %s" : "%s"), cbind->cmd + commandlen);
177             }
178         }
179         reply(textclient, user, "MODCMD_SUBCOMMANDS", parent_bind->cmd, (subcompos ? subcommands : "\1dnone\1d"));
180         return NULL;
181     }
182 }
183
184 static struct cmd_binding *modcmd_sub_linker_command(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, int bind_index, char **args_ptr) {
185     //links subcommands
186     char command[MAXLEN];
187     struct cmd_binding *parent_bind = cbind;
188     char *args = *args_ptr;
189     if(args) {
190         char *subcmd = args;
191         args = strchr(args, ' ');
192         if(args) {
193             *args = '\0';
194             args++;
195         }
196         sprintf(command, "%s %s", parent_bind->cmd, subcmd);
197         if(args)
198             args[-1] = ' ';
199         for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
200             if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmp(cbind->cmd, command) == 0)
201                 break;
202         }
203         if(cbind) {
204             *args_ptr = args;
205             if(cbind->func->func == modcmd_linker)
206                 parent_bind = modcmd_linker_command(client, textclient, user, cbind, bind_index, args_ptr);
207             else
208                 parent_bind = cbind;
209         }
210     }
211     return parent_bind;
212 }
213
214 static void handle_command(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *message) {
215     struct ChanNode *sent_chan = chan;
216     if(message[0] == '#') {
217         char *chanName = message;
218         message = strstr(message, " ");
219         if(!message) return;
220         *message = '\0';
221         message++;
222         struct ChanNode *chan2 = getChanByName(chanName);
223         if(chan2)
224             chan = chan2;
225     }
226     message = strdup(message);
227     int bind_index = get_binds_index(message[0]);
228     char *args = strchr(message, ' ');
229     char *args_buffer = NULL; //we need this to save a possible pointer to a allocation we need to free
230     if(args) {
231         *args = '\0';
232         args++;
233     }
234     struct cmd_binding *cbind;
235     int found_cmd = 0;
236     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
237         if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmp(cbind->cmd, message) == 0) {
238             found_cmd = 1;
239             //get a text bot
240             struct ClientSocket *textclient = get_botwise_prefered_bot(client->botid, (client->botid == 0 ? client->clientid : 0));
241             if(cbind->func->func == modcmd_linker) {
242                 cbind = modcmd_linker_command(client, textclient, user, cbind, bind_index, &args);
243                 if(cbind == NULL) break;
244             } else if(cbind->flags & CMDFLAG_SUB_LINKER)
245                 cbind = modcmd_sub_linker_command(client, textclient, user, cbind, bind_index, &args);
246             statistics_commands++;
247             total_triggered++;
248             cbind->triggered++;
249             if((BIND_FLAGS(cbind) & CMDFLAG_FUNCMD)) {
250                 if(!sent_chan)
251                     break;
252                 chan = sent_chan;
253             }
254             //parse the arguments...
255             char *arga[MAXNUMPARAMS];
256             char **argv;
257             int argc = 0;
258             int escape = 0;
259             int offset = 0;
260             if(args) {
261                 while(*args) {
262                     //skip leading spaces
263                     while (*args == ' ')
264                         *args++ = 0;
265                     arga[argc++] = args;
266                     if (argc >= MAXNUMPARAMS)
267                         break;
268                     while ((escape || *args != ' ') && *args) {
269                         if((BIND_FLAGS(cbind) & CMDFLAG_ESCAPE_ARGS) && *args == '\\') {
270                             escape = 1;
271                             offset++;
272                         } else if(escape)
273                             escape = 0;
274                         if(!escape && offset) {
275                             args[0 - offset] = args[0];
276                         }
277                         args++;
278                         
279                     }
280                     if(offset) {
281                         args[0-offset] = '\0';
282                         offset = 0;
283                     }
284                 }
285             }
286             argv = arga;
287             if(cbind->paramcount) {
288                 //userdefined parameters...
289                 args_buffer = malloc(MAXLEN * 2 * sizeof(*args_buffer));
290                 int args_pos = 0;
291                 char *uargs[MAXNUMPARAMS];
292                 int uargc = 0;
293                 char *b, *c;
294                 int i;
295                 int allargs, argi;
296                 for(i = 0; i < cbind->paramcount; i++) {
297                     b = cbind->parameters[i];
298                     if(b[0] == '%') {
299                         b++;
300                         c = b;
301                         while(*c && *c != ' ') {
302                             if(*c == '|') break;
303                             c++;
304                         }
305                         if(!*c || *c == ' ') c = NULL;
306                         else {
307                             *c = '\0';
308                             c++;
309                         }
310                         if(b[strlen(b)-1] == '-') {
311                             allargs = strlen(b)-1;
312                             b[allargs] = '\0';
313                             argi = atoi(b);
314                             b[allargs] = '-';
315                             allargs = 1;
316                         } else if(b[strlen(b)-1] == '+') {
317                             allargs = strlen(b)-1;
318                             b[allargs] = '\0';
319                             argi = atoi(b);
320                             b[allargs] = '+';
321                             allargs = 2;
322                         } else {
323                             allargs = 0;
324                             argi = atoi(b);
325                         }
326                         if(argi > 0) {
327                             if(argi <= argc) {
328                                 uargs[uargc++] = args_buffer + args_pos;
329                                 if(allargs == 0) {
330                                     args_pos += sprintf(args_buffer + args_pos, "%s", argv[argi-1]) + 1;
331                                 } else if(allargs == 1) {
332                                     args_pos += sprintf(args_buffer + args_pos, "%s", argv[argi-1]) + 1;
333                                     for(argi++; argi <= argc; argi++) {
334                                         uargs[uargc++] = args_buffer + args_pos;
335                                         args_pos += sprintf(args_buffer + args_pos, "%s", argv[argi-1]) + 1;
336                                     }
337                                 } else if(allargs == 2) {
338                                     for(;argi <= argc; argi++) {
339                                         args_pos += sprintf(args_buffer + args_pos, (allargs ? "%s" : " %s"), argv[argi-1]);
340                                         allargs = 0;
341                                     }
342                                     args_pos++;
343                                 }
344                             } else if((BIND_FLAGS(cbind) & CMDFLAG_EMPTY_ARGS)) {
345                                 uargs[uargc++] = args_buffer + args_pos;
346                                 args_buffer[args_pos++] = '\0';
347                             } else if(c) {
348                                 uargs[uargc++] = args_buffer + args_pos;
349                                 args_pos += sprintf(args_buffer + args_pos, "%s", c) + 1;
350                             }
351                         } else if(!strcmp(b, "c")) {
352                             uargs[uargc++] = args_buffer + args_pos;
353                             args_pos += sprintf(args_buffer + args_pos, "%s", (chan ? chan->name : "")) + 1;
354                         } else if(!strcmp(b, "n")) {
355                             uargs[uargc++] = args_buffer + args_pos;
356                             args_pos += sprintf(args_buffer + args_pos, "%s", user->nick) + 1;
357                         }
358                         if(c) c[-1] = '|'; //reset \0 to |
359                     } else {
360                         uargs[uargc++] = args_buffer + args_pos;
361                         args_pos += sprintf(args_buffer + args_pos, "%s", b) + 1;
362                     }
363                 }
364                 argv = uargs;
365                 argc = uargc;
366             }
367             if(argc != 0 && argv[0][0] == '#' && !(BIND_FLAGS(cbind) & CMDFLAG_CHAN_PARAM)) {
368                 struct ChanNode *chan2 = getChanByName(argv[0]);
369                 if(chan2) {
370                     argv += 1;
371                     argc -= 1;
372                     chan = chan2;
373                 }
374             }
375             if(argc < cbind->func->paramcount) {
376                 reply(textclient, user, "MODCMD_LESS_PARAM_COUNT");
377                 break;
378             }
379             if((BIND_FLAGS(cbind) & CMDFLAG_REQUIRE_CHAN) && !chan) {
380                 reply(textclient, user, "MODCMD_CHAN_REQUIRED");
381                 break;
382             }
383             if(((BIND_FLAGS(cbind) & CMDFLAG_CHECK_AUTH) || (chan && chan != sent_chan && !isUserOnChan(user, chan))) && !(user->flags & USERFLAG_ISAUTHED)) {
384                 //check auth...
385                 struct command_check_user_cache *data = malloc(sizeof(*data));
386                 char **temp_argv = malloc(argc*sizeof(*temp_argv));
387                 if (!data || !temp_argv) {
388                     printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
389                     break;
390                 }
391                 memcpy(temp_argv, argv, argc*sizeof(*temp_argv));
392                 data->argv = temp_argv;
393                 data->argc = argc;
394                 data->client = client;
395                 data->user = user;
396                 data->chan = chan;
397                 data->sent_chan = sent_chan;
398                 data->message = message;
399                 data->args_buffer = args_buffer;
400                 data->cbind = cbind;
401                 data->textclient = textclient;
402                 get_userauth(user, 0, command_checked_auth, data);
403                 return;
404             } else
405                 handle_command_async(client, textclient, user, chan, sent_chan, cbind, argv, argc);
406             break;
407         }
408     }
409     if(!found_cmd && !sent_chan && !(client->flags & SOCKET_FLAG_SILENT))
410         reply(get_botwise_prefered_bot(client->botid, (client->botid == 0 ? client->clientid : 0)), user, "MODCMD_UNKNOWN", message);
411     free(message);
412     if(args_buffer)
413         free(args_buffer);
414 }
415
416 static void handle_command_async(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct ChanNode *sent_chan, struct cmd_binding *cbind, char **argv, int argc) {
417     MYSQL_RES *res;
418     MYSQL_ROW row;
419     int uaccess;
420     char requested_uaccess = 0;
421     int eventflags = (BIND_FLAGS(cbind) & (CMDFLAG_LOG | CMDFLAG_OPLOG));
422     if((BIND_FLAGS(cbind) & CMDFLAG_REQUIRE_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) {
423         reply(textclient, user, "MODCMD_AUTH_REQUIRED");
424         return;
425     }
426     if(chan && sent_chan != chan && (BIND_FLAGS(cbind) & CMDFLAG_NO_CROSSCHAN) && !isUserOnChan(user, chan)) {
427         char user_in_chan = 0;
428         if((user->flags & USERFLAG_ISAUTHED)) {
429             //maybe there's another user authed to user->auth on the channel...
430             struct ChanUser *cchanuser;
431             for(cchanuser = getChannelUsers(chan, NULL); cchanuser; cchanuser = getChannelUsers(chan, cchanuser)) {
432                 if((cchanuser->user->flags & USERFLAG_ISAUTHED) && !stricmp(user->auth, cchanuser->user->auth)) {
433                     user_in_chan = 1;
434                     break;
435                 }
436             }
437         }
438         if(!user_in_chan) {
439             //check if we are allowed to execute commands in this channel
440             requested_uaccess = 1;
441             uaccess = getChannelAccess(user, chan);
442             if(!uaccess) {
443                 if(isGodMode(user)) {
444                     eventflags |= CMDFLAG_OPLOG;
445                 } else {
446                     reply(textclient, user, "MODCMD_CROSSCHAN", chan->name);
447                     return;
448                 }
449             }
450         }
451     }
452     if(sent_chan && sent_chan != chan) {
453         //check pubcmd of this channel
454         printf_mysql_query("SELECT `channel_pubcmd` FROM `channels` WHERE `channel_name` = '%s'", escape_string(sent_chan->name));
455         res = mysql_use();
456         if ((row = mysql_fetch_row(res)) != NULL) {
457             int saccess = getChannelAccess(user, sent_chan);
458             if(row[0] && saccess < atoi(row[0]) && !isGodMode(user)) { //NOTE: HARDCODED DEFAULT: pubcmd = 0
459                 reply(textclient, user, "MODCMD_PUBCMD", sent_chan->name);
460                 return;
461             }
462         }
463     }
464     int global_access = ((cbind->flags & CMDFLAG_OVERRIDE_GLOBAL_ACCESS) ? cbind->global_access : cbind->func->global_access);
465     if(global_access > 0) {
466         int user_global_access = 0;
467         printf_mysql_query("SELECT `user_access` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth));
468         res = mysql_use();
469         if ((row = mysql_fetch_row(res)) != NULL) {
470             user_global_access = atoi(row[0]);
471         }
472         if(user_global_access < global_access) {
473             if(!user_global_access)
474                 reply(textclient, user, "MODCMD_PRIVILEGED", cbind->cmd);
475             else
476                 reply(textclient, user, "MODCMD_ACCESS_DENIED");
477             return;
478         }
479     }
480     if((BIND_FLAGS(cbind) & CMDFLAG_REGISTERED_CHAN)) {
481         MYSQL_ROW defaults = NULL;
482         char access_list[256];
483         int access_pos = 0;
484         int access_count = 0;
485         int minaccess = 0;
486         char *str_a, *str_b = cbind->func->channel_access, *str_c;
487         if(cbind->flags & CMDFLAG_OVERRIDE_CHANNEL_ACCESS)
488             str_b = cbind->channel_access;
489         access_list[0] = '\0';
490         if(str_b) {
491             struct ChanUser *chanuser = getChanUser(user, chan);
492             str_c = strdup(str_b);
493             str_b = str_c;
494             while((str_a = str_b)) {
495                 str_b = strstr(str_a, ",");
496                 if(str_b) {
497                     *str_b = '\0';
498                     str_b++;
499                 }
500                 if(*str_a == '@' || *str_a == '+') {
501                     //privs can override this access requirement
502                     int priv = 0;
503                     if(*str_a == '@') priv = CHANUSERFLAG_OPPED;
504                     else if(*str_a == '%') priv = CHANUSERFLAG_HALFOPPED;
505                     else if(*str_a == '+') priv = CHANUSERFLAG_VOICED;
506                     if(chanuser && (chanuser->flags & priv)) continue;
507                     str_a++;
508                 }
509                 if(*str_a == '#') {
510                     str_a++;
511                     access_pos += sprintf(access_list+access_pos, ", `%s`", str_a);
512                     access_count++;
513                 } else {
514                     if(atoi(str_a) > minaccess)
515                         minaccess = atoi(str_a);
516                 }
517             }
518             free(str_c);
519         }
520         if(!(chan->flags & CHANFLAG_REQUESTED_CHANINFO) || (sent_chan && sent_chan == chan) || access_count || minaccess) {
521             printf_mysql_query("SELECT `channel_id`, `channel_pubcmd` %s FROM `channels` WHERE `channel_name` = '%s'", access_list, escape_string(chan->name));
522             res = mysql_use();
523             if ((row = mysql_fetch_row(res)) != NULL) {
524                 chan->flags |= CHANFLAG_CHAN_REGISTERED;
525                 chan->channel_id = atoi(row[0]);
526                 if((sent_chan && sent_chan == chan) || access_count || minaccess) {
527                     if(!requested_uaccess) uaccess = getChannelAccess(user, chan);
528                     if(uaccess < minaccess && isGodMode(user)) {
529                         eventflags |= CMDFLAG_OPLOG;
530                     } else if(uaccess < minaccess) {
531                         //ACCESS DENIED
532                         reply(textclient, user, "MODCMD_ACCESS_DENIED");
533                         return;
534                     }
535                     if(!row[1] && !defaults) {
536                         printf_mysql_query("SELECT `channel_id`, `channel_pubcmd` %s FROM `channels` WHERE `channel_name` = 'defaults'", access_list);
537                         defaults = mysql_fetch_row(mysql_use());
538                     }
539                     if(sent_chan && (sent_chan == chan) && uaccess < (row[1] ? atoi(row[1]) : atoi(defaults[1]))) {
540                         if(isGodMode(user)) {
541                             eventflags |= CMDFLAG_OPLOG;
542                         } else {
543                             //PUBCMD
544                             reply(textclient, user, "MODCMD_PUBCMD", chan->name);
545                             return;
546                         }
547                     }
548                     int i;
549                     for(i = 0; i < access_count; i++) {
550                         if(!row[2+i] && !defaults) {
551                             printf_mysql_query("SELECT `channel_id`, `channel_pubcmd` %s FROM `channels` WHERE `channel_name` = 'defaults'", access_list);
552                             defaults = mysql_fetch_row(mysql_use());
553                         }
554                         if(uaccess < (row[2+i] ? atoi(row[2+i]) : atoi(defaults[2+i]))) {
555                             if(isGodMode(user)) {
556                                 eventflags |= CMDFLAG_OPLOG;
557                             } else {
558                                 reply(textclient, user, "MODCMD_ACCESS_DENIED");
559                                 return;
560                             }
561                         }
562                     }
563                 }
564             }
565             chan->flags |= CHANFLAG_REQUESTED_CHANINFO;
566         }
567         if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) {
568             reply(textclient, user, "MODCMD_CHAN_REQUIRED");
569             return;
570         }
571         printf_mysql_query("SELECT `botid`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, client->botid);
572         res = mysql_use();
573         if ((row = mysql_fetch_row(res)) == NULL) {
574             reply(textclient, user, "MODCMD_CHAN_REQUIRED");
575             return;
576         } else if(!strcmp(row[1], "1")) {
577             reply(textclient, user, "MODCMD_CHAN_SUSPENDED");
578             return;
579         }
580     }
581     if((BIND_FLAGS(cbind) & CMDFLAG_REQUIRE_GOD) && !isGodMode(user)) {
582         reply(textclient, user, "MODCMD_PRIVILEGED", cbind->cmd);
583         return;
584     }
585     struct Event *event = createEvent(client, user, chan, cbind, argv, argc, eventflags);
586     cbind->func->func(client, textclient, user, chan, argv, argc, event);
587 }
588
589 static void got_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) {
590     fd_set fds, fds2;
591     char *trigger;
592     struct ClientSocket *client;
593     FD_ZERO(&fds);
594     FD_ZERO(&fds2);
595     got_chanmsg_loop1:
596     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
597         if(isUserOnChan(client->user, chan) && (client->flags & SOCKET_FLAG_PREFERRED) && ((client->botid == 0 && !FD_ISSET(client->clientid, &fds)) || (client->botid && !FD_ISSET(client->botid, &fds2))))  {
598             FD_SET(client->clientid, &fds);
599             FD_SET(client->botid, &fds2);
600             trigger = get_channel_trigger(client->botid, client->clientid, chan);
601             if(trigger && stricmplen(message, trigger, strlen(trigger)) == 0) {
602                 handle_command(client, user, chan, message + strlen(trigger));
603                 goto got_chanmsg_loop1; //Thats really really bad, i know... But we can't count on the "getBots" list anymore after executing a command
604             }
605         }
606     }
607     got_chanmsg_loop2:
608     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
609         if(isUserOnChan(client->user, chan) && ((client->botid == 0 && !FD_ISSET(client->clientid, &fds)) || (client->botid && !FD_ISSET(client->botid, &fds2)))) {
610             FD_SET(client->clientid, &fds);
611             FD_SET(client->botid, &fds2);
612             trigger = get_channel_trigger(client->botid, client->clientid, chan);
613             if(trigger && stricmplen(message, trigger, strlen(trigger)) == 0) {
614                 handle_command(client, user, chan, message + strlen(trigger));
615                 goto got_chanmsg_loop2; //Thats really really bad, i know... But we can't count on the "getBots" list anymore after executing a command
616             }
617         }
618     }
619 }
620
621 static void got_privmsg(struct UserNode *user, struct UserNode *target, char *message) {
622     struct ClientSocket *client;
623     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
624         if(client->user == target) {
625             handle_command(client, user, NULL, message);
626         }
627     }
628 }
629
630 int register_command(int botid, char *name, int module_id, cmd_bind_t *func, int paramcount, char *channel_access, int global_access, unsigned int flags) {
631     struct cmd_function *cmdfunc;
632     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
633         if((cmdfunc->botid == botid || cmdfunc->botid == 0) && !stricmp(cmdfunc->name, name))
634             return 0;
635     }
636     cmdfunc = malloc(sizeof(*cmdfunc));
637     if (!cmdfunc) {
638         printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
639         return 0;
640     }
641     cmdfunc->botid = botid;
642     cmdfunc->name = strdup(name);
643     cmdfunc->module_id = module_id;
644     cmdfunc->func = func;
645     cmdfunc->flags = flags;
646     cmdfunc->paramcount = paramcount;
647     cmdfunc->channel_access = channel_access;
648     cmdfunc->global_access = global_access;
649     cmdfunc->next = cmd_functions;
650     cmd_functions = cmdfunc;
651     return 1;
652 }
653
654 int set_trigger_callback(int botid, int module_id, trigger_callback_t *func) {
655     static struct trigger_callback *cb = NULL;
656     for(cb = trigger_callbacks; cb; cb = cb->next) {
657         if(cb->botid == botid)
658             break;
659     }
660     if(!cb) {
661         cb = malloc(sizeof(*cb));
662         if (!cb) {
663             printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
664             return 0;
665         }
666         cb->botid = botid;
667         cb->next = trigger_callbacks;
668         trigger_callbacks = cb;
669     }
670     cb->module_id = module_id;
671     cb->func = func;
672     return 1;
673 }
674
675 int flush_trigger_cache(int botid, int clientid) {
676     struct ChanNode *chan;
677     struct trigger_cache *trigger, *last;
678     for(chan = getAllChans(NULL); chan; chan = getAllChans(chan)) {
679         last = NULL;
680         for(trigger = chan->trigger; trigger; trigger = trigger->next) {
681             if(trigger->botid == botid && (botid || trigger->clientid == clientid)) {
682                 if(last)
683                     last->next = trigger->next;
684                 else
685                     chan->trigger = trigger->next;
686                 free(trigger->trigger);
687                 free(trigger);
688                 break;
689             } else
690                 last = trigger;
691         }
692     }
693     return 1;
694 }
695
696 int changeBotwiseChannelTrigger(int botid, int clientid, struct ChanNode *chan, char *new_trigger) {
697     struct trigger_cache *trigger;
698     for(trigger = chan->trigger; trigger; trigger = trigger->next) {
699         if(trigger->botid == botid && (botid || trigger->clientid == clientid)) {
700             free(trigger->trigger);
701             trigger->trigger = strdup(new_trigger);
702             return 1;
703         }
704     }
705     return 0;
706 }
707
708 int bind_botwise_cmd_to_function(int botid, int clientid, char *cmd, struct cmd_function *func) {
709     int bind_index = get_binds_index(cmd[0]);
710     struct cmd_binding *cbind;
711     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
712         if(((botid && cbind->botid == botid) || (botid == 0 && clientid == cbind->clientid)) && !stricmp(cbind->cmd, cmd))
713             return 0;
714     }
715     cbind = malloc(sizeof(*cbind));
716     if (!cbind) {
717         printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
718         return 0;
719     }
720     cbind->botid = botid;
721     cbind->clientid = clientid;
722     cbind->cmd = strdup(cmd);
723     cbind->func = func;
724     cbind->paramcount = 0;
725     cbind->global_access = 0;
726     cbind->channel_access = NULL;
727     cbind->flags = 0;
728     cbind->triggered = 0;
729     cbind->next = cmd_binds[bind_index];
730     cmd_binds[bind_index] = cbind;
731     return 1;
732 }
733
734 int bind_botwise_cmd_to_command(int botid, int clientid, char *cmd, char *func) {
735     struct cmd_function *cmdfunc;
736     int fbotid = botid;
737     char *c;
738     if((c = strstr(func, "."))) {
739         *c = '\0';
740         struct cmd_bot_alias *botalias;
741         for(botalias = bot_aliases; botalias; botalias = botalias->next) {
742             if(!stricmp(botalias->alias, func)) {
743                 fbotid = botalias->botid;
744                 break;
745             }
746         }
747         *c = '.';
748         func = c+1;
749     }
750     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
751         if((cmdfunc->botid == fbotid || cmdfunc->botid == 0) && !stricmp(cmdfunc->name, func))
752             break;
753     }
754     if(!cmdfunc) return 0;
755     int bind_index = get_binds_index(cmd[0]);
756     struct cmd_binding *cbind;
757     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
758         if(((botid && cbind->botid == botid) || (botid == 0 && clientid == cbind->clientid)) && !stricmp(cbind->cmd, cmd))
759             return 0;
760     }
761     cbind = malloc(sizeof(*cbind));
762     if (!cbind) {
763         printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
764         return 0;
765     }
766     cbind->botid = botid;
767     cbind->clientid = clientid;
768     cbind->cmd = strdup(cmd);
769     cbind->func = cmdfunc;
770     cbind->next = cmd_binds[bind_index];
771     cbind->paramcount = 0;
772     cbind->global_access = 0;
773     cbind->channel_access = NULL;
774     cbind->flags = 0;
775     cbind->triggered = 0;
776     cmd_binds[bind_index] = cbind;
777     return 1;
778 }
779
780 int unbind_botwise_cmd(int botid, int clientid, char *cmd) {
781     int bind_index = get_binds_index(cmd[0]);
782     struct cmd_binding *cbind, *last = NULL;
783     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
784         if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) {
785             if(last)
786                 last->next = cbind->next;
787             else
788                 cmd_binds[bind_index] = cbind->next;
789             free(cbind->cmd);
790             if(cbind->paramcount) {
791                 int i;
792                 for(i = 0; i < cbind->paramcount; i++)
793                     free(cbind->parameters[i]);
794             }
795             if(cbind->channel_access)
796                 free(cbind->channel_access);
797             free(cbind);
798             return 1;
799         } else
800             last = cbind;
801     }
802     return 0;
803 }
804
805 int unbind_botwise_allcmd(int botid, int clientid) {
806     int i;
807     for(i = 0; i < 27; i++) {
808         struct cmd_binding *cbind, *next, *last = NULL;
809         for(cbind = cmd_binds[i]; cbind; cbind = next) {
810             next = cbind->next;
811             if((botid == 0 && clientid == cbind->clientid) || (botid && botid == cbind->botid)) {
812                 if(last)
813                     last->next = cbind->next;
814                 else
815                     cmd_binds[i] = cbind->next;
816                 free(cbind->cmd);
817                 if(cbind->paramcount) {
818                     int j;
819                     for(j = 0; j < cbind->paramcount; j++)
820                         free(cbind->parameters[j]);
821                 }
822                 if(cbind->channel_access)
823                     free(cbind->channel_access);
824                 free(cbind);
825             } else
826                 last = cbind;
827         }
828     }
829     return 1;
830 }
831
832 struct cmd_function *find_cmd_function(int botid, char *name) {
833     struct cmd_function *cmdfunc;
834     char *c;
835     if((c = strstr(name, "."))) {
836         *c = '\0';
837         struct cmd_bot_alias *botalias;
838         for(botalias = bot_aliases; botalias; botalias = botalias->next) {
839             if(!stricmp(botalias->alias, name)) {
840                 botid = botalias->botid;
841                 break;
842             }
843         }
844         *c = '.';
845         name = c+1;
846     }
847     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
848         if((cmdfunc->botid == botid || cmdfunc->botid == 0) && stricmp(cmdfunc->name, name) == 0)
849             break;
850     }
851     return cmdfunc;
852 }
853
854 void init_modcmd() {
855     cmd_binds = calloc(27, sizeof(*cmd_binds));
856     bind_chanmsg(got_chanmsg, 0);
857     bind_privmsg(got_privmsg, 0);
858     register_default_language_table(msgtab);
859     register_command(0, "linker", 0, modcmd_linker, 0, 0, 0, 0); //fake command for subcommands
860 }
861
862 void free_modcmd() {
863     int i;
864     for(i = 0; i < 27; i++) {
865         struct cmd_binding *cbind, *next;
866         for(cbind = cmd_binds[i]; cbind; cbind = next) {
867             next = cbind->next;
868             free(cbind->cmd);
869             if(cbind->paramcount) {
870                 int j;
871                 for(j = 0; j < cbind->paramcount; j++)
872                     free(cbind->parameters[j]);
873             }
874             if(cbind->channel_access)
875                 free(cbind->channel_access);
876             free(cbind);
877         }
878     }
879     free(cmd_binds);
880     struct cmd_function *cmdfunct, *next;
881     for(cmdfunct = cmd_functions; cmdfunct; cmdfunct = next) {
882         next = cmdfunct->next;
883         free(cmdfunct->name);
884         free(cmdfunct);
885     }
886     struct trigger_callback *cb, *next_cb;
887     for(cb = trigger_callbacks; cb; cb = next_cb) {
888         next_cb = cb->next;
889         free(next_cb);
890     }
891     struct cmd_bot_alias *botalias, *next_botalias;
892     for(botalias = bot_aliases; botalias; botalias = next_botalias) {
893         next_botalias = botalias->next;
894         free(botalias->alias);
895         free(botalias);
896     }
897     cmd_functions = NULL;
898     trigger_callbacks = NULL;
899     bot_aliases = NULL;
900 }
901
902 void bind_botwise_set_parameters(int botid, int clientid, char *cmd, char *parameters) {
903     int bind_index = get_binds_index(cmd[0]);
904     struct cmd_binding *cbind;
905     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
906         if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) {
907             if(cbind->paramcount) {
908                 int i;
909                 for(i = 0; i < cbind->paramcount; i++)
910                     free(cbind->parameters[i]);
911                 cbind->paramcount = 0;
912             }
913             if(parameters) {
914                 char *a, *b = parameters;
915                 do {
916                     a = strstr(b, " ");
917                     if(a) *a = '\0';
918                     cbind->parameters[cbind->paramcount++] = strdup(b);
919                     if(a) b = a+1;
920                 } while(a);
921             }
922             return;
923         }
924     }
925 }
926
927 void bind_botwise_set_global_access(int botid, int clientid, char *cmd, int gaccess) {
928     int bind_index = get_binds_index(cmd[0]);
929     struct cmd_binding *cbind;
930     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
931         if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) {
932             if(gaccess > -1) {
933                 cbind->global_access = gaccess;
934                 cbind->flags |= CMDFLAG_OVERRIDE_GLOBAL_ACCESS;
935             } else {
936                 cbind->flags &= ~CMDFLAG_OVERRIDE_GLOBAL_ACCESS;
937             }
938             return;
939         }
940     }
941 }
942
943 void bind_botwise_set_channel_access(int botid, int clientid, char *cmd, char *chanaccess) {
944     int bind_index = get_binds_index(cmd[0]);
945     struct cmd_binding *cbind;
946     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
947         if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) {
948             if(cbind->channel_access)
949                 free(cbind->channel_access);
950             if(chanaccess) {
951                 cbind->channel_access = strdup(chanaccess);
952                 cbind->flags |= CMDFLAG_OVERRIDE_CHANNEL_ACCESS;
953             } else {
954                 cbind->channel_access = NULL;
955                 cbind->flags &= ~CMDFLAG_OVERRIDE_CHANNEL_ACCESS;
956             }
957             return;
958         }
959     }
960 }
961
962 void bind_botwise_set_bind_flags(int botid, int clientid, char *cmd, unsigned int flags) {
963     int bind_index = get_binds_index(cmd[0]);
964     struct cmd_binding *cbind;
965     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
966         if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) {
967             cbind->flags |= flags;
968             return;
969         }
970     }
971 }
972
973 struct cmd_binding *find_botwise_cmd_binding(int botid, int clientid, char *cmd) {
974     int bind_index = get_binds_index(cmd[0]);
975     struct cmd_binding *cbind;
976     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
977         if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) {
978             return cbind;
979         }
980     }
981     return NULL;
982 }
983
984 void bind_botwise_unbound_required_functions(int botid, int clientid) {
985     struct cmd_function *cmdfunc;
986     int i, found;
987     struct cmd_binding *cbind;
988     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
989         if((cmdfunc->flags & CMDFLAG_REQUIRED)) {
990             found = 0;
991             for(i = 0; i < 27; i++) {
992                 for(cbind = cmd_binds[i]; cbind; cbind = cbind->next) {
993                     if(cbind->botid == botid && (botid || clientid == cbind->clientid) && cbind->func == cmdfunc) {
994                         found = 1;
995                         break;
996                     }
997                 }
998                 if(found)
999                     break;
1000             }
1001             if(!found && bind_botwise_cmd_to_function(botid, clientid, cmdfunc->name, cmdfunc)) {
1002                 cbind = find_botwise_cmd_binding(botid, clientid, cmdfunc->name);
1003                 cbind->flags |= CMDFLAG_TEMPONARY_BIND;
1004             }
1005         }
1006     }
1007 }
1008
1009 void register_command_alias(int botid, char *alias) {
1010     struct cmd_bot_alias *botalias;
1011     for(botalias = bot_aliases; botalias; botalias = botalias->next) {
1012         if(!stricmp(botalias->alias, alias))
1013             return;
1014     }
1015     botalias = malloc(sizeof(*botalias));
1016     if (!botalias) {
1017         printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
1018         return;
1019     }
1020     botalias->botid = botid;
1021     botalias->alias = strdup(alias);
1022     botalias->next = bot_aliases;
1023     bot_aliases = botalias;
1024 }
1025
1026 struct cmd_binding *getAllBinds(struct cmd_binding *last) {
1027     int bind_index;
1028     if(last) {
1029         if(last->next)
1030             return last->next;
1031         bind_index = get_binds_index(last->cmd[0]) + 1;
1032         if(bind_index > 26)
1033             return NULL;
1034     } else
1035         bind_index = 0;
1036     do {
1037         if(cmd_binds[bind_index])
1038             return cmd_binds[bind_index];
1039         bind_index++;
1040     } while(bind_index < 27);
1041     return NULL;
1042 }
1043
1044 void unregister_module_commands(int module_id) {
1045     struct cmd_function *cmdfunct, *nextfunct, *prevfunct = NULL;
1046     int i;
1047     for(cmdfunct = cmd_functions; cmdfunct; cmdfunct = nextfunct) {
1048         nextfunct = cmdfunct->next;
1049         if(cmdfunct->module_id == module_id) {
1050             for(i = 0; i < 27; i++) {
1051                 struct cmd_binding *cbind, *next, *last = NULL;
1052                 for(cbind = cmd_binds[i]; cbind; cbind = next) {
1053                     next = cbind->next;
1054                     if(cmdfunct == cbind->func) {
1055                         if(last)
1056                             last->next = cbind->next;
1057                         else
1058                             cmd_binds[i] = cbind->next;
1059                         free(cbind->cmd);
1060                         if(cbind->paramcount) {
1061                             int j;
1062                             for(j = 0; j < cbind->paramcount; j++)
1063                                 free(cbind->parameters[j]);
1064                         }
1065                         if(cbind->channel_access)
1066                             free(cbind->channel_access);
1067                         free(cbind);
1068                     } else
1069                         last = cbind;
1070                 }
1071             }
1072             if(prevfunct) 
1073                 prevfunct->next = nextfunct;
1074             else
1075                 cmd_functions = nextfunct;
1076             free(cmdfunct->name);
1077             free(cmdfunct);
1078         } else
1079             prevfunct = cmdfunct;
1080     }
1081     struct trigger_callback *cb, *prevcb = NULL, *nextcb;
1082     for(cb = trigger_callbacks; cb; cb = nextcb) {
1083         nextcb = cb->next;
1084         if(cb->module_id == module_id) {
1085             if(prevcb)
1086                 prevcb->next = nextcb;
1087             else
1088                 trigger_callbacks = nextcb;
1089             free(cb);
1090         } else
1091             prevcb = cb;
1092     }
1093 }