f35dee91a7c05cab266065e85d67f509f473b58a
[NeonServV5.git] / src / cmd_neonserv_set.c
1 /* cmd_neonserv_set.c - NeonServ v5.3
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #include "cmd_neonserv.h"
19
20 typedef char* neonserv_cmd_set_function(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
21 static void neonserv_cmd_set_setting(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, int setting, char *argument);
22 static char* neonserv_cmd_set_trigger(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
23 static char* neonserv_cmd_set_modes(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
24 static char* neonserv_cmd_set_dynlimit(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
25 static char* neonserv_cmd_set_nodelete(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
26
27 #define NS_VALID_FUNCTION 0x01
28 #define NS_VALID_STRING   0x02
29 #define NS_VALID_ACCESS   0x04
30 #define NS_VALID_NO501    0x08
31 #define NS_VALID_OPTIONS  0x10
32 #define NS_VALID_NUMERIC  0x20
33 #define NS_VALID_BOOLEAN  0x40
34
35 #define NS_HAS_OPT  0x100 /* options (SET_OPTION_{NAME}_{VALUE}) */
36 #define NS_HAS_HELP 0x200 /* help    (SET_HELP_{NAME}) - only shown if help is requested */
37
38 static const struct {
39     const char *setting;
40     const char *chanfield;
41     unsigned int valid;
42     void *parameter;
43 } channel_settings[] = {
44     {"TRIGGER",         NULL,                   NS_VALID_FUNCTION,                  neonserv_cmd_set_trigger},
45     {"DEFAULTTOPIC",    "channel_defaulttopic", NS_VALID_STRING,                    NULL},
46     {"TOPICMASK",       "channel_topicmask",    NS_VALID_STRING,                    NULL},
47     {"ADVANCEDTOPIC",   "channel_exttopic",     NS_VALID_BOOLEAN | NS_HAS_OPT,      NULL},
48     {"GREETING",        "channel_greeting",     NS_VALID_STRING,                    NULL},
49     {"USERGREETING",    "channel_usergreeting", NS_VALID_STRING,                    NULL},
50     {"USERINFO",        "channel_userinfo",     NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
51     {"WIPEINFO",        "channel_wipeinfo",     NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
52     {"MODES",           "channel_modes",        NS_VALID_FUNCTION,                  neonserv_cmd_set_modes},
53     {"INVITEME",        "channel_getinvite",    NS_VALID_ACCESS,                    NULL},
54     {"GIVEOPS",         "channel_getop",        NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
55     {"GIVEVOICE",       "channel_getvoice",     NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
56     {"ENFOPS",          "channel_canop",        NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
57     {"ENFVOICE",        "channel_canvoice",     NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
58     {"KICK",            "channel_cankick",      NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
59     {"BAN",             "channel_canban",       NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
60     {"STATICBAN",       "channel_staticban",    NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
61     {"PUBCMD",          "channel_pubcmd",       NS_VALID_ACCESS,                    NULL},
62     {"ENFMODES",        "channel_enfmodes",     NS_VALID_ACCESS,                    NULL},
63     {"ENFTOPIC",        "channel_enftopic",     NS_VALID_ACCESS,                    NULL},
64     {"TOPICSNARF",      "channel_topicsnarf",   NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
65     {"CHANGETOPIC",     "channel_changetopic",  NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
66     {"SETTERS",         "channel_setters",      NS_VALID_ACCESS | NS_VALID_NO501 | NS_HAS_HELP, NULL},
67     {"ADDUSER",         "channel_canadd",       NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
68     {"DELUSER",         "channel_candel",       NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
69     {"CLVL",            "channel_canclvl",      NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
70     {"RESYNC",          "channel_canresync",    NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
71     {"SUSPEND",         "channel_cansuspend",   NS_VALID_ACCESS | NS_HAS_HELP,      NULL},
72     {"NOTICEUSERS",     "channel_notice",       NS_VALID_ACCESS,                    NULL},
73     {"NOTICEREACTION",  "channel_noticereaction", NS_VALID_OPTIONS | NS_HAS_OPT,    "4"},
74     {"CTCPUSERS",       "channel_ctcp",         NS_VALID_ACCESS,                    NULL},
75     {"CTCPREACTION",    "channel_ctcpreaction", NS_VALID_OPTIONS | NS_HAS_OPT,      "4"},
76     {"PROTECT",         "channel_protect",      NS_VALID_OPTIONS | NS_HAS_OPT,      "4"},
77     {"TOYS",            "channel_toys",         NS_VALID_OPTIONS | NS_HAS_OPT,      "3"},
78     {"DYNLIMIT",        "channel_dynlimit",     NS_VALID_NUMERIC | NS_VALID_FUNCTION | NS_HAS_OPT, neonserv_cmd_set_dynlimit},
79     {"NODELETE",        "channel_nodelete",     NS_VALID_BOOLEAN | NS_VALID_FUNCTION, neonserv_cmd_set_nodelete},
80     {NULL, NULL, 0, NULL}
81 };
82
83 #define MAX_QUERY_LEN 1024
84 CMD_BIND(neonserv_cmd_set) {
85     int i, j;
86     if(argc && !strcmp(argv[0], "defaults")) {
87         //reset channel settings
88         int uaccess = getChannelAccess(user, chan);
89         if(uaccess < 500) {
90             if(isGodMode(user)) {
91                 event->flags |= CMDFLAG_OPLOG;
92             } else {
93                 reply(getTextBot(), user, "NS_SET_DEFAULTS_OWNER", chan->name);
94                 return;
95             }
96         }
97         int seed = 0;
98         char *tmp;
99         static char defaultskey[16];
100         for(tmp = user->auth; *tmp; tmp++)
101             seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp));
102         for(tmp = chan->name; *tmp; tmp++)
103             seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp));
104         sprintf(defaultskey, "%08x", seed);
105         if(argc > 1 && !strcmp(argv[1], defaultskey)) {
106             char query[MAX_QUERY_LEN];
107             int querypos = 0;
108             i = 0;
109             while(channel_settings[i].setting) {
110                 if(channel_settings[i].chanfield)
111                     querypos += sprintf(query + querypos, "`%s` = NULL, ", channel_settings[i].chanfield);
112                 i++;
113             }
114             if(querypos) {
115                 query[querypos-2] = '\0';
116             }
117             printf_mysql_query("UPDATE `channels` SET %s WHERE `channel_id` = '%d'", query, chan->channel_id);
118             reply(getTextBot(), user, "NS_SET_DEFAULTS_DONE", chan->name);
119             logEvent(event);
120         } else {
121             reply(getTextBot(), user, "NS_SET_DEFAULTS_CODE", chan->name, defaultskey);
122         }
123     } else if(argc && strcmp(argv[0], "help")) {
124         //find the correct command
125         i = 0;
126         j = 0;
127         char *args = (argc > 1 ? merge_argv(argv, 1, argc) : NULL);
128         while(channel_settings[i].setting) {
129             if(!stricmp(channel_settings[i].setting, argv[0])) {
130                 //setting found
131                 if(channel_settings[i].valid & NS_VALID_FUNCTION) {
132                     neonserv_cmd_set_function *func = channel_settings[i].parameter;
133                     func(client, user, chan, event, channel_settings[i].setting, args);
134                 } else {
135                     neonserv_cmd_set_setting(client, user, chan, event, i, args);
136                 }
137                 j = 1;
138                 break;
139             }
140             i++;
141         }
142         if(j == 0) {
143             //unknown setting
144             reply(getTextBot(), user, "NS_SET_UNKNOWN_SETTING", argv[0]);
145         }
146     } else {
147         char query[MAX_QUERY_LEN], *value, *org_value, *tmp, nameBuf[64];
148         int querypos = 0;
149         MYSQL_RES *res, *defaults_res;
150         MYSQL_ROW row, defaults;
151         struct Table *table;
152         char *content[2];
153         i = 0;
154         while(channel_settings[i].setting) {
155             if(channel_settings[i].chanfield)
156                 querypos += sprintf(query + querypos, ", `%s`", channel_settings[i].chanfield);
157             i++;
158         }
159         table = table_init(2, i, 0);
160         table_set_bold(table, 0, 1);
161         printf_mysql_query("SELECT `channel_id` %s FROM `channels` WHERE `channel_name` = 'defaults'", query);
162         defaults_res = mysql_use();
163         defaults = mysql_fetch_row(defaults_res);
164         printf_mysql_query("SELECT `channel_name` %s FROM `channels` WHERE `channel_id` = '%d'", query, chan->channel_id);
165         res = mysql_use();
166         row = mysql_fetch_row(res);
167         i = 0;
168         j = 0;
169         reply(getTextBot(), user, "NS_SET_HEADER", chan->name);
170         while(channel_settings[i].setting) {
171             if(channel_settings[i].chanfield) {
172                 j++;
173                 org_value = (row[j] ? row[j] : defaults[j]);
174             } else if(channel_settings[i].valid & NS_VALID_FUNCTION) {
175                 neonserv_cmd_set_function *func = channel_settings[i].parameter;
176                 org_value = func(client, user, chan, event, NULL, NULL);
177             } else
178                 org_value = "0";
179             value = org_value;
180             if(channel_settings[i].valid & NS_VALID_BOOLEAN) {
181                 if(!strcmp(value, "0"))
182                     value = get_language_string(user, "NS_SET_OFF");
183                 else
184                     value = get_language_string(user, "NS_SET_ON");
185             }
186             strcpy(query, value);
187             querypos = strlen(query);
188             if(channel_settings[i].valid & NS_HAS_OPT) {
189                 sprintf(nameBuf, "NS_SET_OPTION_%s_%s", channel_settings[i].setting, org_value);
190                 tmp = get_language_string(user, nameBuf);
191                 if(tmp) {
192                     querypos += sprintf(query+querypos, " - %s", tmp);
193                 }
194             }
195             if(argc && channel_settings[i].valid & NS_HAS_HELP) {
196                 sprintf(nameBuf, "NS_SET_HELP_%s", channel_settings[i].setting);
197                 tmp = get_language_string(user, nameBuf);
198                 if(tmp) {
199                     querypos += sprintf(query+querypos, " - %s", tmp);
200                 }
201             }
202             content[0] = (char*)channel_settings[i].setting;
203             content[1] = query;
204             table_add(table, content);
205             i++;
206         }
207         char **table_lines = table_end(table);
208         for(i = 0; i < table->entrys; i++) {
209             reply(getTextBot(), user, table_lines[i]);
210         }
211         table_free(table);
212     }
213 }
214
215 static void neonserv_cmd_set_setting(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, int setting, char *args) {
216     char *value;
217     char nameBuf[64];
218     //get current value
219     MYSQL_RES *res;
220     MYSQL_ROW row;
221     printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", channel_settings[setting].chanfield, chan->channel_id);
222     res = mysql_use();
223     row = mysql_fetch_row(res);
224     if(row[0] == NULL) {
225         printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_name` = 'defaults'", channel_settings[setting].chanfield);
226         res = mysql_use();
227         row = mysql_fetch_row(res);
228     }
229     value = row[0];
230     if(args) {
231         //change the channel setting
232         //check the new argument
233         int valid = channel_settings[setting].valid;
234         if(valid & NS_VALID_STRING) {
235             if(!strcmp(args, "*")) {
236                 args = "";
237             }
238         }
239         if(valid & NS_VALID_ACCESS) {
240             int caccess = atoi(args);
241             int max = ((valid & NS_VALID_NO501) ? 500 : 501);
242             if(caccess < 0 || caccess > max) {
243                 reply(getTextBot(), user, "NS_INVALID_ACCESS", caccess);
244                 return;
245             }
246             int uaccess = getChannelAccess(user, chan);
247             if(uaccess == 500) uaccess++;
248             if(atoi(value) > uaccess) {
249                 if(isGodMode(user)) {
250                     event->flags |= CMDFLAG_OPLOG;
251                 } else {
252                     reply(getTextBot(), user, "NS_SET_CANNOT_SET");
253                     return;
254                 }
255             }
256             if(caccess > uaccess) {
257                 if(isGodMode(user)) {
258                     event->flags |= CMDFLAG_OPLOG;
259                 } else {
260                     reply(getTextBot(), user, "NS_SET_BADLEVEL");
261                     return;
262                 }
263             }
264             sprintf(nameBuf, "%d", caccess);
265             args = nameBuf;
266         }
267         if(valid & NS_VALID_OPTIONS) {
268             int options = atoi((char *) channel_settings[setting].parameter);
269             int coption = atoi(args);
270             if(coption < 0 || coption >= options) {
271                 reply(getTextBot(), user, "NS_SET_INVALID_OPTION", coption);
272                 int i;
273                 int nameBufPos = 0;
274                 if(valid & NS_HAS_OPT) {
275                     for(i = 0; i < options; i++) {
276                         sprintf(nameBuf, "NS_SET_OPTION_%s_%d", channel_settings[setting].setting, i);
277                         reply(getTextBot(), user, "\002%d\002 - %s", i, get_language_string(user, nameBuf));
278                     }
279                 } else {
280                     for(i = 0; i < options; i++) {
281                         nameBufPos += sprintf(nameBuf + nameBufPos, "\002%d\002, ", i);
282                     }
283                     if(nameBufPos) {
284                         nameBuf[nameBufPos-2] = '\0';
285                         reply(getTextBot(), user, nameBuf);
286                     }
287                 }
288                 return;
289             }
290         }
291         if(valid & NS_VALID_NUMERIC) {
292             sprintf(nameBuf, "%d", atoi(args));
293             args = nameBuf;
294         }
295         if(valid & NS_VALID_BOOLEAN) {
296             if(!strcmp(args, "0") || !stricmp(args, "off") || !stricmp(args, get_language_string(user, "NS_SET_OFF"))) {
297                 args = "0";
298             } else if(!strcmp(args, "1") || !stricmp(args, "on") || !stricmp(args, get_language_string(user, "NS_SET_ON"))) {
299                 args = "1";
300             } else {
301                 reply(getTextBot(), user, "NS_SET_INVALID_BOOLEAN", args);
302                 return;
303             }
304         }
305         //valid - set it
306         value = args;
307         printf_mysql_query("UPDATE `channels` SET `%s` = '%s' WHERE `channel_id` = '%d'", channel_settings[setting].chanfield, escape_string(value), chan->channel_id);
308         logEvent(event);
309     }
310     if(channel_settings[setting].valid & NS_HAS_OPT) {
311         sprintf(nameBuf, "NS_SET_OPTION_%s_%s", channel_settings[setting].setting, value);
312         char *tmp = get_language_string(user, nameBuf);
313         if(tmp)
314             reply(getTextBot(), user, "\002%s\002 %s - %s", channel_settings[setting].setting, value, tmp);
315         else
316             reply(getTextBot(), user, "\002%s\002 %s", channel_settings[setting].setting, value);
317     } else
318         reply(getTextBot(), user, "\002%s\002 %s", channel_settings[setting].setting, value);
319     if(channel_settings[setting].valid & NS_HAS_HELP) {
320          sprintf(nameBuf, "NS_SET_HELP_%s", channel_settings[setting].setting);
321          reply(getTextBot(), user, "  %s", get_language_string(user, nameBuf));
322     }
323 }
324
325 static char* neonserv_cmd_set_trigger(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
326     char *trigger;
327     //get current trigger
328     MYSQL_RES *res;
329     MYSQL_ROW row;
330     printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botid` = '%d'", chan->channel_id, client->clientid);
331     res = mysql_use();
332     row = mysql_fetch_row(res);
333     trigger = (row[0] ? row[0] : row[1]);
334     if(argument) {
335         int uaccess = getChannelAccess(user, chan);
336         if(uaccess < 500) {
337             if(isGodMode(user)) {
338                 event->flags |= CMDFLAG_OPLOG;
339             } else {
340                 reply(getTextBot(), user, "NS_SET_TRIGGER_OWNER", chan->name);
341                 return NULL;
342             }
343         }
344         if(strlen(argument) > 15)
345             argument[15] = '\0';
346         printf_mysql_query("UPDATE `bot_channels` SET `trigger` = '%s' WHERE `chanid` = '%d' AND `botid` = '%d'", escape_string(argument), chan->channel_id, client->clientid);
347         trigger = argument;
348         changeChannelTrigger(client->botid, chan, trigger);
349         logEvent(event);
350     }
351     if(setting) {
352         reply(getTextBot(), user, "\002%s\002 %s", setting, trigger);
353     }
354     return trigger;
355 }
356
357 static char* neonserv_cmd_set_modes(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
358     char *value;
359     char valueBuf[MAXLEN];
360     //get current value
361     MYSQL_RES *res;
362     MYSQL_ROW row;
363     printf_mysql_query("SELECT `channel_modes` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
364     res = mysql_use();
365     row = mysql_fetch_row(res);
366     if(row[0] == NULL) {
367         printf_mysql_query("SELECT `channel_modes` FROM `channels` WHERE `channel_name` = 'defaults'");
368         res = mysql_use();
369         row = mysql_fetch_row(res);
370     }
371     value = row[0];
372     if(argument) {
373         //change the channel setting
374         struct ModeNode *modenode = createModeNode(NULL);
375         parseModeString(modenode, argument);
376         getFullModeString(modenode, valueBuf);
377         value = valueBuf;
378         printf_mysql_query("UPDATE `channels` SET `channel_modes` = '%s' WHERE `channel_id` = '%d'", escape_string(value), chan->channel_id);
379         //TODO: set modelock
380         freeModeNode(modenode);
381     }
382     if(setting) {
383         reply(getTextBot(), user, "\002%s\002 %s", setting, value);
384     }
385     return value;
386 }
387
388 static char* neonserv_cmd_set_dynlimit(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
389     char *value;
390     char tmp[64];
391     //get current value
392     MYSQL_RES *res;
393     MYSQL_ROW row;
394     printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
395     res = mysql_use();
396     row = mysql_fetch_row(res);
397     if(row[0] == NULL) {
398         printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_name` = 'defaults'");
399         res = mysql_use();
400         row = mysql_fetch_row(res);
401     }
402     value = row[0];
403     if(argument) {
404         //change the channel setting
405         sprintf(tmp, "%d", atoi(argument));
406         argument = tmp;
407         printf_mysql_query("UPDATE `channels` SET `channel_dynlimit` = '%s' WHERE `channel_id` = '%d'", escape_string(argument), chan->channel_id);
408         if(strcmp(argument, "0"))
409             putsock(client, "MODE %s +l %d", chan->name, (chan->usercount + atoi(argument)));
410         else if(isModeSet(chan->modes, 'l'))
411             putsock(client, "MODE %s -l", chan->name);
412         value = argument;
413         logEvent(event);
414     }
415     if(setting) {
416         reply(getTextBot(), user, "\002%s\002 %s", setting, value);
417     }
418     return value;
419 }
420
421 static char* neonserv_cmd_set_nodelete(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
422     char *value;
423     //get current value
424     MYSQL_RES *res;
425     MYSQL_ROW row;
426     printf_mysql_query("SELECT `channel_nodelete` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
427     res = mysql_use();
428     row = mysql_fetch_row(res);
429     if(row[0] == NULL) {
430         printf_mysql_query("SELECT `channel_nodelete` FROM `channels` WHERE `channel_name` = 'defaults'");
431         res = mysql_use();
432         row = mysql_fetch_row(res);
433     }
434     value = row[0];
435     if(argument && isGodMode(user)) {
436         //change the channel setting
437         if(!strcmp(argument, "0") || !strcmp(argument, "off") || !strcmp(argument, get_language_string(user, "NS_SET_OFF"))) {
438             argument = "0";
439         } else if(!strcmp(argument, "0") || !strcmp(argument, "off") || !strcmp(argument, get_language_string(user, "NS_SET_OFF"))) {
440             argument = "1";
441         } else {
442             reply(getTextBot(), user, "NS_SET_INVALID_BOOLEAN", argument);
443             return NULL;
444         }
445         printf_mysql_query("UPDATE `channels` SET `channel_nodelete` = '%s' WHERE `channel_id` = '%d'", escape_string(argument), chan->channel_id);
446         event->flags |= CMDFLAG_OPLOG;
447         value = argument;
448         logEvent(event);
449     }
450     if(setting) {
451         reply(getTextBot(), user, "\002%s\002 %s", setting, value);
452     }
453     return value;
454 }
455
456 #undef MAX_QUERY_LEN