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