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