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