fixed last commit
[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         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                     ppos++;
169                     prev_ppos = ppos;
170                 }
171                 argv = uarga;
172                 argc = uargpos;
173             }
174             if(argc < cbind->func->paramcount) {
175                 //LESS_PARAM_COUNT
176                 break;
177             }
178             if((cbind->func->flags & CMDFLAG_REQUIRE_CHAN) && !chan) {
179                 //CHAN_REQUIRED
180                 break;
181             } 
182             if((cbind->func->flags & CMDFLAG_CHECK_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) {
183                 //check auth...
184                 struct command_check_user_cache *data = malloc(sizeof(*data));
185                 char **temp_argv = malloc(argc*sizeof(*temp_argv));
186                 if (!data || !temp_argv) {
187                     perror("malloc() failed");
188                     break;
189                 }
190                 memcpy(temp_argv, argv, argc*sizeof(*temp_argv));
191                 data->argv = temp_argv;
192                 data->argc = argc;
193                 data->client = client;
194                 data->user = user;
195                 data->chan = chan;
196                 data->message = message;
197                 data->cbind = cbind;
198                 get_userauth(user, command_checked_auth, data);
199                 return;
200             }
201             if((cbind->func->flags & CMDFLAG_REQUIRE_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) {
202                 //AUTH_REQUIRED
203                 break;
204             }
205             cbind->func->func(client, user, chan, argv, argc);
206             break;
207         }
208     }
209     free(message);
210 }
211
212 static void got_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) {
213     fd_set fds;
214     char *trigger;
215     struct ClientSocket *client;
216     FD_ZERO(&fds);
217     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
218         if(isUserOnChan(client->user, chan) && (client->flags & SOCKET_FLAG_PREFERRED) && !FD_ISSET(client->botid, &fds)) {
219             FD_SET(client->botid, &fds);
220             trigger = get_channel_trigger(client->botid, chan);
221             if(stricmplen(message, trigger, strlen(trigger)) == 0) {
222                 handle_command(client, user, chan, message + strlen(trigger));
223             }
224         }
225     }
226     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
227         if(isUserOnChan(client->user, chan) && !FD_ISSET(client->botid, &fds)) {
228             FD_SET(client->botid, &fds);
229             trigger = get_channel_trigger(client->botid, chan);
230             if(stricmplen(message, trigger, strlen(trigger)) == 0) {
231                 handle_command(client, user, chan, message + strlen(trigger));
232             }
233         }
234     }
235 }
236
237 static void got_privmsg(struct UserNode *user, struct UserNode *target, char *message) {
238     struct ClientSocket *client;
239     for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
240         if(client->user == target) {
241             handle_command(client, user, NULL, message);
242         }
243     }
244 }
245
246 int register_command(int botid, char *name, cmd_bind_t *func, int paramcount, unsigned int flags) {
247     struct cmd_function *cmdfunc;
248     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
249         if(cmdfunc->botid == botid && strcmp(cmdfunc->name, name) == 0)
250             return 0;
251     }
252     cmdfunc = malloc(sizeof(*cmdfunc));
253     if (!cmdfunc) {
254         perror("malloc() failed");
255         return 0;
256     }
257     cmdfunc->botid = botid;
258     cmdfunc->name = strdup(name);
259     cmdfunc->func = func;
260     cmdfunc->flags = 0;
261     cmdfunc->paramcount = paramcount;
262     cmdfunc->next = cmd_functions;
263     cmd_functions = cmdfunc;
264     return 1;
265 }
266
267 int set_trigger_callback(int botid, trigger_callback_t *func) {
268     static struct trigger_callback *cb = NULL;
269     for(cb = trigger_callbacks; cb; cb = cb->next) {
270         if(cb->botid == botid)
271             break;
272     }
273     if(!cb) {
274         cb = malloc(sizeof(*cb));
275         if (!cb) {
276             perror("malloc() failed");
277             return 0;
278         }
279         cb->botid = botid;
280         cb->next = trigger_callbacks;
281         trigger_callbacks = cb;
282     }
283     cb->func = func;
284     return 1;
285 }
286
287 int changeChannelTrigger(int botid, struct ChanNode *chan, char *new_trigger) {
288     struct trigger_cache *trigger;
289     for(trigger = chan->trigger; trigger; trigger = trigger->next) {
290         if(trigger->botid == botid) {
291             free(trigger->trigger);
292             trigger->trigger = strdup(new_trigger);
293             return 1;
294         }
295     }
296     return 0;
297 }
298
299 int bind_cmd_to_function(int botid, char *cmd, struct cmd_function *func) {
300     int bind_index = get_binds_index(cmd[0]);
301     struct cmd_binding *cbind;
302     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
303         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0)
304             return 0;
305     }
306     cbind = malloc(sizeof(*cbind));
307     if (!cbind) {
308         perror("malloc() failed");
309         return 0;
310     }
311     cbind->botid = botid;
312     cbind->cmd = strdup(cmd);
313     cbind->func = func;
314     cbind->parameters = NULL;
315     cbind->flags = 0;
316     cbind->next = cmd_binds[bind_index];
317     cmd_binds[bind_index] = cbind;
318     return 1;
319 }
320
321 int bind_cmd_to_command(int botid, char *cmd, char *func) {
322     struct cmd_function *cmdfunc;
323     for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) {
324         if(cmdfunc->botid == botid && strcmp(cmdfunc->name, func) == 0)
325             break;
326     }
327     if(!cmdfunc) return 0;
328     int bind_index = get_binds_index(cmd[0]);
329     struct cmd_binding *cbind;
330     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
331         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0)
332             return 0;
333     }
334     cbind = malloc(sizeof(*cbind));
335     if (!cbind) {
336         perror("malloc() failed");
337         return 0;
338     }
339     cbind->botid = botid;
340     cbind->cmd = strdup(cmd);
341     cbind->func = cmdfunc;
342     cbind->next = cmd_binds[bind_index];
343     cbind->parameters = NULL;
344     cbind->flags = 0;
345     cmd_binds[bind_index] = cbind;
346     return 1;
347 }
348
349 int unbind_cmd(int botid, char *cmd) {
350     int bind_index = get_binds_index(cmd[0]);
351     struct cmd_binding *cbind, *last = NULL;
352     for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
353         if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
354             if(last)
355                 last->next = cbind->next;
356             else
357                 cmd_binds[bind_index] = cbind->next;
358             free(cbind->cmd);
359             if(cbind->parameters)
360                 free(cbind->parameters);
361             free(cbind);
362             return 1;
363         } else
364             last = cbind;
365     }
366     return 0;
367 }
368
369 void init_modcmd() {
370     cmd_binds = calloc(27, sizeof(*cmd_binds));
371     bind_chanmsg(got_chanmsg);
372     bind_privmsg(got_privmsg);
373 }
374
375 void free_modcmd() {
376     int i;
377     for(i = 0; i < 27; i++) {
378         struct cmd_binding *cbind, *next;
379         for(cbind = cmd_binds[i]; cbind; cbind = next) {
380             next = cbind->next;
381             free(cbind->cmd);
382             if(cbind->parameters)
383                 free(cbind->parameters);
384             free(cbind);
385         }
386     }
387     free(cmd_binds);
388     struct cmd_function *cmdfunct, *next;
389     for(cmdfunct = cmd_functions; cmdfunct; cmdfunct = next) {
390         next = cmdfunct->next;
391         free(cmdfunct->name);
392         free(cmdfunct);
393     }
394     struct trigger_callback *cb, *next_cb;
395     for(cb = trigger_callbacks; cb; cb = next_cb) {
396         next_cb = cb->next;
397         free(next_cb);
398     }
399     cmd_functions = NULL;
400     trigger_callbacks = NULL;
401 }
402