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