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