tidied up command list
[NeonServV5.git] / modcmd.c
1
2 #include "modcmd.h"
3 #include "IRCEvents.h"
4 #include "IRCParser.h"
5 #include "ClientSocket.h"
6 #include "UserNode.h"
7 #include "ChanNode.h"
8 #include "ChanUser.h"
9 #include "WHOHandler.h"
10 #include "lang.h"
11 #include "mysqlConn.h"
12 #include "DBHelper.h"
13 #include "EventLogger.h"
14
15 struct trigger_callback {
16     int botid;
17     trigger_callback_t *func;
18     
19     struct trigger_callback *next;
20 };
21
22 struct command_check_user_cache {
23     struct ClientSocket *client, *textclient;
24     struct UserNode *user;
25     struct ChanNode *chan, *sent_chan;
26     char **argv;
27     int argc;
28     char *message;
29     struct cmd_binding *cbind;
30 };
31
32 static struct cmd_binding **cmd_binds;
33 static struct cmd_function *cmd_functions = NULL;
34 static struct trigger_callback *trigger_callbacks = NULL;
35 static struct ClientSocket *tmp_text_client;
36
37 static const struct default_language_entry msgtab[] = {
38     {"MODCMD_LESS_PARAM_COUNT", "This command requires more parameters."},
39     {"MODCMD_CHAN_REQUIRED",    "You must provide the name of a channel that exists and the bot is on."},
40     {"MODCMD_AUTH_REQUIRED",    "You need to be authenticated with AuthServ to use this command."},
41     {"MODCMD_CHAN_SUSPENDED",   "This channel is currently suspended."},
42     {"MODCMD_PRIVILEGED",       "$b%s$b is a privileged command."}, /* {ARGS: "god"} */
43     {"MODCMD_PUBCMD",           "Public commands in $b%s$b are restricted."}, /* {ARGS: "#TestChan"} */
44     {"MODCMD_ACCESS_DENIED",    "Access denied."},
45     {NULL, NULL}
46 };
47
48 static int get_binds_index(char first_char) {
49     if(tolower(first_char) >= 'a' && tolower(first_char) <= 'z') {
50         return tolower(first_char) - 'a';
51     }
52     return 26;
53 }
54
55 struct ClientSocket* get_prefered_bot(int botid) {
56     struct ClientSocket *client;
57     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
58         if(client->botid == botid && (client->flags & SOCKET_FLAG_PREFERRED))
59             return client;
60     }
61     return NULL;
62 }
63
64 static char* get_channel_trigger(int botid, struct ChanNode *chan) {
65     struct trigger_cache *trigger;
66     for(trigger = chan->trigger; trigger; trigger = trigger->next) {
67         if(trigger->botid == botid)
68             return trigger->trigger;
69     }
70     struct trigger_callback *cb;
71     for(cb = trigger_callbacks; cb; cb = cb->next) {
72         if(cb->botid == botid)
73             break;
74     }
75     char triggerStr[TRIGGERLEN];
76     if(cb)
77         cb->func(chan, triggerStr);
78     else
79         strcpy(triggerStr, "+");
80     trigger = malloc(sizeof(*trigger));
81     if (!trigger) {
82         perror("malloc() failed");
83         return 0;
84     }
85     trigger->botid = botid;
86     trigger->trigger = strdup(triggerStr);
87     trigger->next = chan->trigger;
88     chan->trigger = trigger;
89     return trigger->trigger;
90 }
91
92 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);
93
94 static USERAUTH_CALLBACK(command_checked_auth) {
95     struct command_check_user_cache *cache = data;
96     tmp_text_client = cache->textclient;
97     handle_command_async(cache->client, user, cache->chan, cache->sent_chan, cache->cbind, cache->argv, cache->argc);
98     free(cache->message);
99     free(cache);
100 }
101
102 static void handle_command(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *message) {
103     struct ChanNode *sent_chan = chan;
104     if(message[0] == '#') {
105         char *chanName = message;
106         message = strstr(message, " ");
107         if(!message) return;
108         *message = '\0';
109         message++;
110         struct ChanNode *chan2 = getChanByName(chanName);
111         if(chan2)
112             chan = chan2;
113     }
114     message = strdup(message);
115     int bind_index = get_binds_index(message[0]);
116     char *args = strstr(message, " ");
117     if(args) {
118         *args = '\0';
119         args++;
120     }
121     struct cmd_binding *cbind;
122     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
123         if(cbind->botid == client->botid && stricmp(cbind->cmd, message) == 0) {
124             //get a text bot
125             tmp_text_client = get_prefered_bot(client->botid);
126             //parse the arguments...
127             char *arga[MAXNUMPARAMS];
128             char **argv;
129             int argc = 0;
130             if(args) {
131                 while(*args) {
132                     //skip leading spaces
133                     while (*args == ' ')
134                         *args++ = 0;
135                     arga[argc++] = args;
136                     if (argc >= MAXNUMPARAMS)
137                         break;
138                     while (*args != ' ' && *args)
139                         args++;
140                 }
141             }
142             argv = arga;
143             if(argc != 0 && argv[0][0] == '#' && !(cbind->func->flags & CMDFLAG_CHAN_PARAM)) {
144                 struct ChanNode *chan2 = getChanByName(argv[0]);
145                 if(chan2) {
146                     argv += 1;
147                     argc -= 1;
148                     chan = chan2;
149                 }
150             }
151             if(cbind->parameters) {
152                 //userdefined parameters...
153                 char *uarga[MAXNUMPARAMS];
154                 char params[strlen(cbind->parameters)+1];
155                 strcpy(params, cbind->parameters);
156                 int uargpos = 0, argi, allargs = 0;
157                 char *ppos = params;
158                 char *prev_ppos = params;
159                 while((ppos = strstr(ppos, " "))) {
160                     *ppos = '\0';
161                     if(prev_ppos[0] == '%') {
162                         prev_ppos++;
163                         if(prev_ppos[strlen(prev_ppos)-1] == '-') {
164                             allargs = 1;
165                             prev_ppos[strlen(prev_ppos)-1] = '\0';
166                         } else
167                             allargs = 0;
168                         argi = atoi(prev_ppos);
169                         if(argi > 0) {
170                             if(argi <= argc) continue;
171                             uarga[uargpos++] = argv[argi-1];
172                             if(allargs) {
173                                 for(;argi < argc; argi++)
174                                     uarga[uargpos++] = argv[argi-1];
175                             }
176                         } else if(!strcmp(prev_ppos, "c"))
177                             uarga[uargpos++] = (chan ? chan->name : NULL);
178                         else if(!strcmp(prev_ppos, "n")) 
179                             uarga[uargpos++] = user->nick;
180                     } else {
181                         uarga[uargpos++] = prev_ppos;
182                     }
183                     ppos++;
184                     prev_ppos = ppos;
185                 }
186                 argv = uarga;
187                 argc = uargpos;
188             }
189             if(argc < cbind->func->paramcount) {
190                 reply(tmp_text_client, user, "MODCMD_LESS_PARAM_COUNT");
191                 break;
192             }
193             if((cbind->func->flags & CMDFLAG_REQUIRE_CHAN) && !chan) {
194                 reply(tmp_text_client, user, "MODCMD_CHAN_REQUIRED");
195                 break;
196             }
197             if((cbind->func->flags & CMDFLAG_CHECK_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) {
198                 //check auth...
199                 struct command_check_user_cache *data = malloc(sizeof(*data));
200                 char **temp_argv = malloc(argc*sizeof(*temp_argv));
201                 if (!data || !temp_argv) {
202                     perror("malloc() failed");
203                     break;
204                 }
205                 memcpy(temp_argv, argv, argc*sizeof(*temp_argv));
206                 data->argv = temp_argv;
207                 data->argc = argc;
208                 data->client = client;
209                 data->user = user;
210                 data->chan = chan;
211                 data->sent_chan = sent_chan;
212                 data->message = message;
213                 data->cbind = cbind;
214                 data->textclient = tmp_text_client;
215                 get_userauth(user, command_checked_auth, data);
216                 return;
217             } else
218                 handle_command_async(client, user, chan, sent_chan, cbind, argv, argc);
219             break;
220         }
221     }
222     free(message);
223 }
224
225 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) {
226     MYSQL_RES *res;
227     MYSQL_ROW row;
228     int uaccess;
229     int eventflags = (cbind->func->flags & (CMDFLAG_LOG | CMDFLAG_OPLOG));
230     if((cbind->func->flags & CMDFLAG_REQUIRE_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) {
231         reply(tmp_text_client, user, "MODCMD_AUTH_REQUIRED");
232         return;
233     }
234     if(sent_chan && sent_chan != chan) {
235         //check pubcmd of this channel
236         printf_mysql_query("SELECT `channel_pubcmd` FROM `channels` WHERE `channel_name` = '%s'", escape_string(sent_chan->name));
237         res = mysql_use();
238         if ((row = mysql_fetch_row(res)) != NULL) {
239             uaccess = getChannelAccess(user, sent_chan, 1);
240             if(row[0] && uaccess < atoi(row[0])) { //NOTE: HARDCODED DEFAULT: pubcmd = 0
241                 reply(tmp_text_client, user, "MODCMD_PUBCMD", sent_chan->name);
242                 return;
243             }
244         }
245     }
246     int global_access = ((cbind->flags & CMDFLAG_OVERRIDE_GLOBAL_ACCESS) ? cbind->global_access : cbind->func->global_access);
247     if(global_access > 0) {
248         int user_global_access = 0;
249         printf_mysql_query("SELECT `user_access` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth));
250         res = mysql_use();
251         if ((row = mysql_fetch_row(res)) != NULL) {
252             user_global_access = atoi(row[0]);
253         }
254         if(user_global_access < global_access) {
255             if(!user_global_access)
256                 reply(tmp_text_client, user, "MODCMD_PRIVILEGED", cbind->cmd);
257             else
258                 reply(tmp_text_client, user, "MODCMD_ACCESS_DENIED");
259             return;
260         }
261     }
262     if((cbind->func->flags & CMDFLAG_REGISTERED_CHAN)) {
263         MYSQL_ROW defaults = NULL;
264         char access_list[256];
265         int access_pos = 0;
266         int access_count = 0;
267         int minaccess = 0;
268         char *str_a, *str_b = cbind->func->channel_access, *str_c;
269         if(cbind->flags & CMDFLAG_OVERRIDE_CHANNEL_ACCESS)
270             str_b = cbind->channel_access;
271         access_list[0] = '\0';
272         if(str_b) {
273             str_c = strdup(str_b);
274             str_b = str_c;
275             while((str_a = str_b)) {
276                 str_b = strstr(str_a, ",");
277                 if(str_b) {
278                     *str_b = '\0';
279                     str_b++;
280                 }
281                 if(*str_a == '#') {
282                     str_a++;
283                     access_pos += sprintf(access_list+access_pos, ", `%s`", str_a);
284                     access_count++;
285                 } else {
286                     if(atoi(str_a) > minaccess)
287                         minaccess = atoi(str_a);
288                 }
289             }
290             free(str_c);
291         }
292         if(!(chan->flags & CHANFLAG_REQUESTED_CHANINFO) || (sent_chan && sent_chan == chan) || access_count || minaccess) {
293             printf_mysql_query("SELECT `channel_id`, `channel_pubcmd` %s FROM `channels` WHERE `channel_name` = '%s'", access_list, escape_string(chan->name));
294             res = mysql_use();
295             if ((row = mysql_fetch_row(res)) != NULL) {
296                 chan->flags |= CHANFLAG_CHAN_REGISTERED;
297                 chan->channel_id = atoi(row[0]);
298                 if((sent_chan && sent_chan == chan) || access_count || minaccess) {
299                     uaccess = getChannelAccess(user, chan, 0);
300                     if(uaccess < minaccess && isGodMode(user)) {
301                         eventflags |= CMDFLAG_OPLOG;
302                     } else if(uaccess < minaccess) {
303                         //ACCESS DENIED
304                         reply(tmp_text_client, user, "MODCMD_ACCESS_DENIED");
305                         return;
306                     }
307                     if(!row[1] && !defaults) {
308                         printf_mysql_query("SELECT `channel_id`, `channel_pubcmd` %s FROM `channels` WHERE `channel_name` = 'defaults'", access_list);
309                         defaults = mysql_fetch_row(mysql_use());
310                     }
311                     if(sent_chan && (sent_chan == chan) && uaccess < (row[1] ? atoi(row[1]) : atoi(defaults[1]))) {
312                         if(isGodMode(user)) {
313                             eventflags |= CMDFLAG_OPLOG;
314                         } else {
315                             //PUBCMD
316                             reply(tmp_text_client, user, "MODCMD_PUBCMD", chan->name);
317                             return;
318                         }
319                     }
320                     int i;
321                     for(i = 0; i < access_count; i++) {
322                         if(!row[2+i] && !defaults) {
323                             printf_mysql_query("SELECT `channel_id`, `channel_pubcmd` %s FROM `channels` WHERE `channel_name` = 'defaults'", access_list);
324                             defaults = mysql_fetch_row(mysql_use());
325                         }
326                         if(uaccess < (row[2+i] ? atoi(row[2+i]) : atoi(defaults[2+i]))) {
327                             if(isGodMode(user)) {
328                                 eventflags |= CMDFLAG_OPLOG;
329                             } else {
330                                 reply(tmp_text_client, user, "MODCMD_ACCESS_DENIED");
331                                 return;
332                             }
333                         }
334                     }
335                 }
336             }
337             chan->flags |= CHANFLAG_REQUESTED_CHANINFO;
338         }
339         if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) {
340             reply(tmp_text_client, user, "MODCMD_CHAN_REQUIRED");
341             return;
342         }
343         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);
344         res = mysql_use();
345         if ((row = mysql_fetch_row(res)) == NULL) {
346             reply(tmp_text_client, user, "MODCMD_CHAN_REQUIRED");
347             return;
348         } else if(!strcmp(row[1], "1")) {
349             reply(tmp_text_client, user, "MODCMD_CHAN_SUSPENDED");
350             return;
351         }
352     }
353     if((cbind->func->flags & CMDFLAG_REQUIRE_GOD) && !isGodMode(user)) {
354         reply(tmp_text_client, user, "MODCMD_PRIVILEGED", cbind->cmd);
355         return;
356     }
357     struct Event *event = createEvent(client, user, chan, cbind->func->name, argv, argc, eventflags);
358     cbind->func->func(client, user, chan, argv, argc, event);
359 }
360
361 static void got_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) {
362     fd_set fds;
363     char *trigger;
364     struct ClientSocket *client;
365     FD_ZERO(&fds);
366     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
367         if(isUserOnChan(client->user, chan) && (client->flags & SOCKET_FLAG_PREFERRED) && !FD_ISSET(client->botid, &fds)) {
368             FD_SET(client->botid, &fds);
369             trigger = get_channel_trigger(client->botid, chan);
370             if(stricmplen(message, trigger, strlen(trigger)) == 0) {
371                 handle_command(client, user, chan, message + strlen(trigger));
372             }
373         }
374     }
375     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
376         if(isUserOnChan(client->user, chan) && !FD_ISSET(client->botid, &fds)) {
377             FD_SET(client->botid, &fds);
378             trigger = get_channel_trigger(client->botid, chan);
379             if(stricmplen(message, trigger, strlen(trigger)) == 0) {
380                 handle_command(client, user, chan, message + strlen(trigger));
381             }
382         }
383     }
384 }
385
386 static void got_privmsg(struct UserNode *user, struct UserNode *target, char *message) {
387     struct ClientSocket *client;
388     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
389         if(client->user == target) {
390             handle_command(client, user, NULL, message);
391         }
392     }
393 }
394
395 int register_command(int botid, char *name, cmd_bind_t *func, int paramcount, char *channel_access, int global_access, unsigned int flags) {
396     struct cmd_function *cmdfunc;
397     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
398         if(cmdfunc->botid == botid && strcmp(cmdfunc->name, name) == 0)
399             return 0;
400     }
401     cmdfunc = malloc(sizeof(*cmdfunc));
402     if (!cmdfunc) {
403         perror("malloc() failed");
404         return 0;
405     }
406     cmdfunc->botid = botid;
407     cmdfunc->name = strdup(name);
408     cmdfunc->func = func;
409     cmdfunc->flags = flags;
410     cmdfunc->paramcount = paramcount;
411     cmdfunc->channel_access = channel_access;
412     cmdfunc->global_access = global_access;
413     cmdfunc->next = cmd_functions;
414     cmd_functions = cmdfunc;
415     return 1;
416 }
417
418 int set_trigger_callback(int botid, trigger_callback_t *func) {
419     static struct trigger_callback *cb = NULL;
420     for(cb = trigger_callbacks; cb; cb = cb->next) {
421         if(cb->botid == botid)
422             break;
423     }
424     if(!cb) {
425         cb = malloc(sizeof(*cb));
426         if (!cb) {
427             perror("malloc() failed");
428             return 0;
429         }
430         cb->botid = botid;
431         cb->next = trigger_callbacks;
432         trigger_callbacks = cb;
433     }
434     cb->func = func;
435     return 1;
436 }
437
438 int changeChannelTrigger(int botid, struct ChanNode *chan, char *new_trigger) {
439     struct trigger_cache *trigger;
440     for(trigger = chan->trigger; trigger; trigger = trigger->next) {
441         if(trigger->botid == botid) {
442             free(trigger->trigger);
443             trigger->trigger = strdup(new_trigger);
444             return 1;
445         }
446     }
447     return 0;
448 }
449
450 int bind_cmd_to_function(int botid, char *cmd, struct cmd_function *func) {
451     int bind_index = get_binds_index(cmd[0]);
452     struct cmd_binding *cbind;
453     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
454         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0)
455             return 0;
456     }
457     cbind = malloc(sizeof(*cbind));
458     if (!cbind) {
459         perror("malloc() failed");
460         return 0;
461     }
462     cbind->botid = botid;
463     cbind->cmd = strdup(cmd);
464     cbind->func = func;
465     cbind->parameters = NULL;
466     cbind->global_access = 0;
467     cbind->channel_access = NULL;
468     cbind->flags = 0;
469     cbind->next = cmd_binds[bind_index];
470     cmd_binds[bind_index] = cbind;
471     return 1;
472 }
473
474 int bind_cmd_to_command(int botid, char *cmd, char *func) {
475     struct cmd_function *cmdfunc;
476     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
477         if(cmdfunc->botid == botid && strcmp(cmdfunc->name, func) == 0)
478             break;
479     }
480     if(!cmdfunc) return 0;
481     int bind_index = get_binds_index(cmd[0]);
482     struct cmd_binding *cbind;
483     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
484         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0)
485             return 0;
486     }
487     cbind = malloc(sizeof(*cbind));
488     if (!cbind) {
489         perror("malloc() failed");
490         return 0;
491     }
492     cbind->botid = botid;
493     cbind->cmd = strdup(cmd);
494     cbind->func = cmdfunc;
495     cbind->next = cmd_binds[bind_index];
496     cbind->parameters = NULL;
497     cbind->global_access = 0;
498     cbind->channel_access = NULL;
499     cbind->flags = 0;
500     cmd_binds[bind_index] = cbind;
501     return 1;
502 }
503
504 int unbind_cmd(int botid, char *cmd) {
505     int bind_index = get_binds_index(cmd[0]);
506     struct cmd_binding *cbind, *last = NULL;
507     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
508         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
509             if(last)
510                 last->next = cbind->next;
511             else
512                 cmd_binds[bind_index] = cbind->next;
513             free(cbind->cmd);
514             if(cbind->parameters)
515                 free(cbind->parameters);
516             free(cbind);
517             return 1;
518         } else
519             last = cbind;
520     }
521     return 0;
522 }
523
524 struct cmd_function *find_cmd_function(int botid, char *name) {
525     struct cmd_function *cmdfunc;
526     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
527         if(cmdfunc->botid == botid && stricmp(cmdfunc->name, name) == 0)
528             break;
529     }
530     return cmdfunc;
531 }
532
533 struct ClientSocket *getTextBot() {
534     return tmp_text_client;
535 }
536
537 void init_modcmd() {
538     cmd_binds = calloc(27, sizeof(*cmd_binds));
539     bind_chanmsg(got_chanmsg);
540     bind_privmsg(got_privmsg);
541     register_default_language_table(msgtab);
542 }
543
544 void free_modcmd() {
545     int i;
546     for(i = 0; i < 27; i++) {
547         struct cmd_binding *cbind, *next;
548         for(cbind = cmd_binds[i]; cbind; cbind = next) {
549             next = cbind->next;
550             free(cbind->cmd);
551             if(cbind->parameters)
552                 free(cbind->parameters);
553             if(cbind->channel_access)
554                 free(cbind->channel_access);
555             free(cbind);
556         }
557     }
558     free(cmd_binds);
559     struct cmd_function *cmdfunct, *next;
560     for(cmdfunct = cmd_functions; cmdfunct; cmdfunct = next) {
561         next = cmdfunct->next;
562         free(cmdfunct->name);
563         free(cmdfunct);
564     }
565     struct trigger_callback *cb, *next_cb;
566     for(cb = trigger_callbacks; cb; cb = next_cb) {
567         next_cb = cb->next;
568         free(next_cb);
569     }
570     cmd_functions = NULL;
571     trigger_callbacks = NULL;
572 }
573
574 void bind_set_parameters(int botid, char *cmd, char *parameters) {
575     int bind_index = get_binds_index(cmd[0]);
576     struct cmd_binding *cbind;
577     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
578         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
579             if(cbind->parameters)
580                 free(cbind->parameters);
581             cbind->parameters = strdup(parameters);
582             return;
583         }
584     }
585 }
586
587 void bind_set_global_access(int botid, char *cmd, int gaccess) {
588     int bind_index = get_binds_index(cmd[0]);
589     struct cmd_binding *cbind;
590     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
591         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
592             if(gaccess > -1) {
593                 cbind->global_access = gaccess;
594                 cbind->flags |= CMDFLAG_OVERRIDE_GLOBAL_ACCESS;
595             } else {
596                 cbind->flags &= ~CMDFLAG_OVERRIDE_GLOBAL_ACCESS;
597             }
598             return;
599         }
600     }
601 }
602
603 void bind_set_channel_access(int botid, char *cmd, char *chanaccess) {
604     int bind_index = get_binds_index(cmd[0]);
605     struct cmd_binding *cbind;
606     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
607         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
608             if(cbind->channel_access)
609                 free(cbind->channel_access);
610             if(chanaccess) {
611                 cbind->channel_access = strdup(chanaccess);
612                 cbind->flags |= CMDFLAG_OVERRIDE_CHANNEL_ACCESS;
613             } else {
614                 cbind->channel_access = NULL;
615                 cbind->flags &= ~CMDFLAG_OVERRIDE_CHANNEL_ACCESS;
616             }
617             return;
618         }
619     }
620 }