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