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