completed cmd_adduser and added CMDFLAG_REGISTERED_CHAN flag
[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
12 struct trigger_callback {
13     int botid;
14     trigger_callback_t *func;
15     
16     struct trigger_callback *next;
17 };
18
19 struct command_check_user_cache {
20     struct ClientSocket *client, *textclient;
21     struct UserNode *user;
22     struct ChanNode *chan;
23     char **argv;
24     int argc;
25     char *message;
26     struct cmd_binding *cbind;
27 };
28
29 static struct cmd_binding **cmd_binds;
30 static struct cmd_function *cmd_functions = NULL;
31 static struct trigger_callback *trigger_callbacks = NULL;
32 static struct ClientSocket *tmp_text_client;
33
34 static const struct default_language_entry msgtab[] = {
35     {"MODCMD_LESS_PARAM_COUNT", "This command requires more parameters."},
36     {"MODCMD_CHAN_REQUIRED",    "You must provide the name of a channel that exists."},
37     {"MODCMD_AUTH_REQUIRED",    "You need to be authenticated with AuthServ to use this command."},
38     {"MODCMD_PRIVILEGED",       "§b%s§b is a privileged command."},
39     
40     
41     {NULL, NULL}
42 };
43
44 static int get_binds_index(char first_char) {
45     if(tolower(first_char) >= 'a' && tolower(first_char) <= 'z') {
46         return tolower(first_char - 'a');
47     }
48     return 26;
49 }
50
51 struct ClientSocket* get_prefered_bot(int botid) {
52     struct ClientSocket *client;
53     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
54         if(client->botid == botid && (client->flags & SOCKET_FLAG_PREFERRED))
55             return client;
56     }
57     return NULL;
58 }
59
60 static char* get_channel_trigger(int botid, struct ChanNode *chan) {
61     struct trigger_cache *trigger;
62     for(trigger = chan->trigger; trigger; trigger = trigger->next) {
63         if(trigger->botid == botid)
64             return trigger->trigger;
65     }
66     struct trigger_callback *cb;
67     for(cb = trigger_callbacks; cb; cb = cb->next) {
68         if(cb->botid == botid)
69             break;
70     }
71     char triggerStr[TRIGGERLEN];
72     if(cb)
73         cb->func(chan, triggerStr);
74     else
75         strcpy(triggerStr, "+");
76     trigger = malloc(sizeof(*trigger));
77     if (!trigger) {
78         perror("malloc() failed");
79         return 0;
80     }
81     trigger->botid = botid;
82     trigger->trigger = strdup(triggerStr);
83     trigger->next = chan->trigger;
84     chan->trigger = trigger;
85     return trigger->trigger;
86 }
87
88 static USERAUTH_CALLBACK(command_checked_auth) {
89     struct command_check_user_cache *cache = data;
90     int execute_cmd = 1;
91     tmp_text_client = cache->textclient;
92     if((cache->cbind->func->flags & CMDFLAG_REQUIRE_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) {
93         reply(tmp_text_client, user, "MODCMD_AUTH_REQUIRED");
94         execute_cmd = 0;
95     }
96     else if((cache->cbind->func->flags & CMDFLAG_REQUIRE_GOD) && !isGodMode(user)) {
97         reply(tmp_text_client, user, "MODCMD_PRIVILEGED", cache->cbind->cmd);
98         execute_cmd = 0;
99     }
100     if(execute_cmd) {
101         cache->cbind->func->func(cache->client, user, cache->chan, cache->argv, cache->argc);
102     }
103     free(cache->message);
104     free(cache);
105 }
106
107 static void handle_command(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *message) {
108     if(message[0] == '#') {
109         char *chanName = message;
110         message = strstr(message, " ");
111         if(!message) return;
112         *message = '\0';
113         message++;
114         struct ChanNode *chan2 = getChanByName(chanName);
115         if(chan2)
116             chan = chan2;
117     }
118     message = strdup(message);
119     int bind_index = get_binds_index(message[0]);
120     char *args = strstr(message, " ");
121     if(args) {
122         *args = '\0';
123         args++;
124     }
125     struct cmd_binding *cbind;
126     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
127         if(cbind->botid == client->botid && strcmp(cbind->cmd, message) == 0) {
128             //get a text bot
129             tmp_text_client = get_prefered_bot(client->botid);
130             //parse the arguments...
131             char *arga[MAXNUMPARAMS];
132             char **argv;
133             int argc = 0;
134             if(args) {
135                 while(*args) {
136                     //skip leading spaces
137                     while (*args == ' ')
138                         *args++ = 0;
139                     arga[argc++] = args;
140                     if (argc >= MAXNUMPARAMS)
141                         break;
142                     while (*args != ' ' && *args)
143                         args++;
144                 }
145             }
146             argv = arga;
147             if(argc != 0 && argv[0][0] == '#') {
148                 struct ChanNode *chan2 = getChanByName(argv[0]);
149                 if(chan2) {
150                     argv += 1;
151                     argc -= 1;
152                     chan = chan2;
153                 }
154             }
155             if(cbind->parameters) {
156                 //userdefined parameters...
157                 char *uarga[MAXNUMPARAMS];
158                 char params[strlen(cbind->parameters)+1];
159                 strcpy(params, cbind->parameters);
160                 int uargpos = 0, argi, allargs = 0;
161                 char *ppos = params;
162                 char *prev_ppos = params;
163                 while((ppos = strstr(ppos, " "))) {
164                     *ppos = '\0';
165                     if(prev_ppos[0] == '%') {
166                         prev_ppos++;
167                         if(prev_ppos[strlen(prev_ppos)-1] == '-') {
168                             allargs = 1;
169                             prev_ppos[strlen(prev_ppos)-1] = '\0';
170                         } else
171                             allargs = 0;
172                         argi = atoi(prev_ppos);
173                         if(argi > 0) {
174                             if(argi <= argc) continue;
175                             uarga[uargpos++] = argv[argi-1];
176                             if(allargs) {
177                                 for(;argi < argc; argi++)
178                                     uarga[uargpos++] = argv[argi-1];
179                             }
180                         } else if(!strcmp(prev_ppos, "c"))
181                             uarga[uargpos++] = (chan ? chan->name : NULL);
182                         else if(!strcmp(prev_ppos, "n")) 
183                             uarga[uargpos++] = user->nick;
184                     } else {
185                         uarga[uargpos++] = prev_ppos;
186                     }
187                     ppos++;
188                     prev_ppos = ppos;
189                 }
190                 argv = uarga;
191                 argc = uargpos;
192             }
193             if(argc < cbind->func->paramcount) {
194                 reply(tmp_text_client, user, "MODCMD_LESS_PARAM_COUNT");
195                 break;
196             }
197             if((cbind->func->flags & CMDFLAG_REQUIRE_CHAN) && !chan) {
198                 reply(tmp_text_client, user, "MODCMD_CHAN_REQUIRED");
199                 break;
200             }
201             if((cbind->func->flags & CMDFLAG_REGISTERED_CHAN)) {
202                 load_channel_settings(chan);
203                 if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) {
204                     reply(tmp_text_client, user, "MODCMD_CHAN_REQUIRED");
205                     break;
206                 }
207                 check_mysql();
208                 MYSQL_RES *res;
209                 MYSQL_ROW row;
210                 printf_mysql_query("SELECT `botid` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, client->botid);
211                 res = mysql_use();
212                 if ((row = mysql_fetch_row(res)) == NULL) {
213                     reply(tmp_text_client, user, "MODCMD_CHAN_REQUIRED");
214                     break;
215                 }
216             }
217             if((cbind->func->flags & CMDFLAG_CHECK_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) {
218                 //check auth...
219                 struct command_check_user_cache *data = malloc(sizeof(*data));
220                 char **temp_argv = malloc(argc*sizeof(*temp_argv));
221                 if (!data || !temp_argv) {
222                     perror("malloc() failed");
223                     break;
224                 }
225                 memcpy(temp_argv, argv, argc*sizeof(*temp_argv));
226                 data->argv = temp_argv;
227                 data->argc = argc;
228                 data->client = client;
229                 data->user = user;
230                 data->chan = chan;
231                 data->message = message;
232                 data->cbind = cbind;
233                 data->textclient = tmp_text_client;
234                 get_userauth(user, command_checked_auth, data);
235                 return;
236             }
237             if((cbind->func->flags & CMDFLAG_REQUIRE_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) {
238                 reply(tmp_text_client, user, "MODCMD_AUTH_REQUIRED");
239                 break;
240             }
241             if((cbind->func->flags & CMDFLAG_REQUIRE_GOD) && !isGodMode(user)) {
242                 reply(tmp_text_client, user, "MODCMD_PRIVILEGED", cbind->cmd);
243                 break;
244             }
245             cbind->func->func(client, user, chan, argv, argc);
246             break;
247         }
248     }
249     free(message);
250 }
251
252 static void got_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) {
253     fd_set fds;
254     char *trigger;
255     struct ClientSocket *client;
256     FD_ZERO(&fds);
257     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
258         if(isUserOnChan(client->user, chan) && (client->flags & SOCKET_FLAG_PREFERRED) && !FD_ISSET(client->botid, &fds)) {
259             FD_SET(client->botid, &fds);
260             trigger = get_channel_trigger(client->botid, chan);
261             if(stricmplen(message, trigger, strlen(trigger)) == 0) {
262                 handle_command(client, user, chan, message + strlen(trigger));
263             }
264         }
265     }
266     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
267         if(isUserOnChan(client->user, chan) && !FD_ISSET(client->botid, &fds)) {
268             FD_SET(client->botid, &fds);
269             trigger = get_channel_trigger(client->botid, chan);
270             if(stricmplen(message, trigger, strlen(trigger)) == 0) {
271                 handle_command(client, user, chan, message + strlen(trigger));
272             }
273         }
274     }
275 }
276
277 static void got_privmsg(struct UserNode *user, struct UserNode *target, char *message) {
278     struct ClientSocket *client;
279     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
280         if(client->user == target) {
281             handle_command(client, user, NULL, message);
282         }
283     }
284 }
285
286 int register_command(int botid, char *name, cmd_bind_t *func, int paramcount, unsigned int flags) {
287     struct cmd_function *cmdfunc;
288     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
289         if(cmdfunc->botid == botid && strcmp(cmdfunc->name, name) == 0)
290             return 0;
291     }
292     cmdfunc = malloc(sizeof(*cmdfunc));
293     if (!cmdfunc) {
294         perror("malloc() failed");
295         return 0;
296     }
297     cmdfunc->botid = botid;
298     cmdfunc->name = strdup(name);
299     cmdfunc->func = func;
300     cmdfunc->flags = 0;
301     cmdfunc->paramcount = paramcount;
302     cmdfunc->next = cmd_functions;
303     cmd_functions = cmdfunc;
304     return 1;
305 }
306
307 int set_trigger_callback(int botid, trigger_callback_t *func) {
308     static struct trigger_callback *cb = NULL;
309     for(cb = trigger_callbacks; cb; cb = cb->next) {
310         if(cb->botid == botid)
311             break;
312     }
313     if(!cb) {
314         cb = malloc(sizeof(*cb));
315         if (!cb) {
316             perror("malloc() failed");
317             return 0;
318         }
319         cb->botid = botid;
320         cb->next = trigger_callbacks;
321         trigger_callbacks = cb;
322     }
323     cb->func = func;
324     return 1;
325 }
326
327 int changeChannelTrigger(int botid, struct ChanNode *chan, char *new_trigger) {
328     struct trigger_cache *trigger;
329     for(trigger = chan->trigger; trigger; trigger = trigger->next) {
330         if(trigger->botid == botid) {
331             free(trigger->trigger);
332             trigger->trigger = strdup(new_trigger);
333             return 1;
334         }
335     }
336     return 0;
337 }
338
339 int bind_cmd_to_function(int botid, char *cmd, struct cmd_function *func) {
340     int bind_index = get_binds_index(cmd[0]);
341     struct cmd_binding *cbind;
342     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
343         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0)
344             return 0;
345     }
346     cbind = malloc(sizeof(*cbind));
347     if (!cbind) {
348         perror("malloc() failed");
349         return 0;
350     }
351     cbind->botid = botid;
352     cbind->cmd = strdup(cmd);
353     cbind->func = func;
354     cbind->parameters = NULL;
355     cbind->gaccess = 0;
356     cbind->flags = 0;
357     cbind->next = cmd_binds[bind_index];
358     cmd_binds[bind_index] = cbind;
359     return 1;
360 }
361
362 int bind_cmd_to_command(int botid, char *cmd, char *func) {
363     struct cmd_function *cmdfunc;
364     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
365         if(cmdfunc->botid == botid && strcmp(cmdfunc->name, func) == 0)
366             break;
367     }
368     if(!cmdfunc) return 0;
369     int bind_index = get_binds_index(cmd[0]);
370     struct cmd_binding *cbind;
371     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
372         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0)
373             return 0;
374     }
375     cbind = malloc(sizeof(*cbind));
376     if (!cbind) {
377         perror("malloc() failed");
378         return 0;
379     }
380     cbind->botid = botid;
381     cbind->cmd = strdup(cmd);
382     cbind->func = cmdfunc;
383     cbind->next = cmd_binds[bind_index];
384     cbind->parameters = NULL;
385     cbind->gaccess = 0;
386     cbind->flags = 0;
387     cmd_binds[bind_index] = cbind;
388     return 1;
389 }
390
391 int unbind_cmd(int botid, char *cmd) {
392     int bind_index = get_binds_index(cmd[0]);
393     struct cmd_binding *cbind, *last = NULL;
394     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
395         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
396             if(last)
397                 last->next = cbind->next;
398             else
399                 cmd_binds[bind_index] = cbind->next;
400             free(cbind->cmd);
401             if(cbind->parameters)
402                 free(cbind->parameters);
403             free(cbind);
404             return 1;
405         } else
406             last = cbind;
407     }
408     return 0;
409 }
410
411 struct ClientSocket *getTextBot() {
412     return tmp_text_client;
413 }
414
415 void init_modcmd() {
416     cmd_binds = calloc(27, sizeof(*cmd_binds));
417     bind_chanmsg(got_chanmsg);
418     bind_privmsg(got_privmsg);
419     register_default_language_table(msgtab);
420 }
421
422 void free_modcmd() {
423     int i;
424     for(i = 0; i < 27; i++) {
425         struct cmd_binding *cbind, *next;
426         for(cbind = cmd_binds[i]; cbind; cbind = next) {
427             next = cbind->next;
428             free(cbind->cmd);
429             if(cbind->parameters)
430                 free(cbind->parameters);
431             free(cbind);
432         }
433     }
434     free(cmd_binds);
435     struct cmd_function *cmdfunct, *next;
436     for(cmdfunct = cmd_functions; cmdfunct; cmdfunct = next) {
437         next = cmdfunct->next;
438         free(cmdfunct->name);
439         free(cmdfunct);
440     }
441     struct trigger_callback *cb, *next_cb;
442     for(cb = trigger_callbacks; cb; cb = next_cb) {
443         next_cb = cb->next;
444         free(next_cb);
445     }
446     cmd_functions = NULL;
447     trigger_callbacks = NULL;
448 }
449
450 void bind_set_parameters(int botid, char *cmd, char *parameters) {
451     
452 }
453
454 void bind_set_gaccess(int botid, char *cmd, int gaccess) {
455     
456 }
457