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