removed some doubled checks & added cmd_topic with ADVANCEDTOPIC
[NeonServV5.git] / cmd_neonserv_set.c
1
2 typedef char* neonserv_cmd_set_function(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, const char *setting, char *argument);
3 static void neonserv_cmd_set_setting(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, int setting, char *argument);
4 static char* neonserv_cmd_set_trigger(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, const char *setting, char *argument);
5 static char* neonserv_cmd_set_modes(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, const char *setting, char *argument);
6 static char* neonserv_cmd_set_dynlimit(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, const char *setting, char *argument);
7 static char* neonserv_cmd_set_nodelete(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, 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, 1);
71         if(uaccess < 500) {
72             reply(getTextBot(), user, "NS_SET_DEFAULTS_OWNER", chan->name);
73             return;
74         }
75         int seed = 0;
76         char *tmp;
77         static char defaultskey[16];
78         for(tmp = user->auth; *tmp; tmp++)
79             seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp));
80         for(tmp = chan->name; *tmp; tmp++)
81             seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp));
82         sprintf(defaultskey, "%08x", seed);
83         if(argc > 1 && !strcmp(argv[1], defaultskey)) {
84             char query[MAX_QUERY_LEN];
85             int querypos = 0;
86             check_mysql();
87             i = 0;
88             while(channel_settings[i].setting) {
89                 if(channel_settings[i].chanfield)
90                     querypos += sprintf(query + querypos, "`%s` = NULL, ", channel_settings[i].chanfield);
91                 i++;
92             }
93             if(querypos) {
94                 query[querypos-2] = '\0';
95             }
96             printf_mysql_query("UPDATE `channels` SET %s WHERE `channel_id` = '%d'", query, chan->channel_id);
97             reply(getTextBot(), user, "NS_SET_DEFAULTS_DONE", chan->name);
98         } else {
99             reply(getTextBot(), user, "NS_SET_DEFAULTS_CODE", chan->name, defaultskey);
100         }
101     } else if(argc && strcmp(argv[0], "help")) {
102         //find the correct command
103         i = 0;
104         j = 0;
105         char *args = (argc > 1 ? merge_argv(argv, 1, argc) : NULL);
106         while(channel_settings[i].setting) {
107             if(!stricmp(channel_settings[i].setting, argv[0])) {
108                 //setting found
109                 if(channel_settings[i].valid & NS_VALID_FUNCTION) {
110                     neonserv_cmd_set_function *func = channel_settings[i].parameter;
111                     func(client, user, chan, channel_settings[i].setting, args);
112                 } else {
113                     neonserv_cmd_set_setting(client, user, chan, i, args);
114                 }
115                 j = 1;
116                 break;
117             }
118             i++;
119         }
120         if(j == 0) {
121             //unknown setting
122             reply(getTextBot(), user, "NS_SET_UNKNOWN_SETTING", argv[0]);
123         }
124     } else {
125         check_mysql();
126         char query[MAX_QUERY_LEN], *value, *org_value, *tmp, nameBuf[64];
127         int querypos = 0;
128         MYSQL_RES *res, *defaults_res;
129         MYSQL_ROW row, defaults;
130         struct Table *table;
131         char *content[2];
132         i = 0;
133         while(channel_settings[i].setting) {
134             if(channel_settings[i].chanfield)
135                 querypos += sprintf(query + querypos, ", `%s`", channel_settings[i].chanfield);
136             i++;
137         }
138         table = table_init(2, i, 0);
139         table_set_bold(table, 0, 1);
140         printf_mysql_query("SELECT `channel_id` %s FROM `channels` WHERE `channel_name` = 'defaults'", query);
141         defaults_res = mysql_use();
142         defaults = mysql_fetch_row(defaults_res);
143         printf_mysql_query("SELECT `channel_name` %s FROM `channels` WHERE `channel_id` = '%d'", query, chan->channel_id);
144         res = mysql_use();
145         row = mysql_fetch_row(res);
146         i = 0;
147         j = 0;
148         reply(getTextBot(), user, "NS_SET_HEADER", chan->name);
149         while(channel_settings[i].setting) {
150             if(channel_settings[i].chanfield) {
151                 j++;
152                 org_value = (row[j] ? row[j] : defaults[j]);
153             } else if(channel_settings[i].valid & NS_VALID_FUNCTION) {
154                 neonserv_cmd_set_function *func = channel_settings[i].parameter;
155                 org_value = func(client, user, chan, NULL, NULL);
156             } else
157                 org_value = "0";
158             value = org_value;
159             if(channel_settings[i].valid & NS_VALID_BOOLEAN) {
160                 if(!strcmp(value, "0"))
161                     value = get_language_string(user, "NS_SET_OFF");
162                 else
163                     value = get_language_string(user, "NS_SET_ON");
164             }
165             strcpy(query, value);
166             querypos = strlen(query);
167             if(channel_settings[i].valid & NS_HAS_OPT) {
168                 sprintf(nameBuf, "NS_SET_OPTION_%s_%s", channel_settings[i].setting, org_value);
169                 tmp = get_language_string(user, nameBuf);
170                 if(tmp) {
171                     querypos += sprintf(query+querypos, " - %s", tmp);
172                 }
173             }
174             if(argc && channel_settings[i].valid & NS_HAS_HELP) {
175                 sprintf(nameBuf, "NS_SET_HELP_%s", channel_settings[i].setting);
176                 tmp = get_language_string(user, nameBuf);
177                 if(tmp) {
178                     querypos += sprintf(query+querypos, " - %s", tmp);
179                 }
180             }
181             content[0] = (char*)channel_settings[i].setting;
182             content[1] = query;
183             table_add(table, content);
184             i++;
185         }
186         char **table_lines = table_end(table);
187         for(i = 0; i < table->entrys; i++) {
188             reply(getTextBot(), user, table_lines[i]);
189         }
190         table_free(table);
191     }
192 }
193
194 static void neonserv_cmd_set_setting(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, int setting, char *args) {
195     char *value;
196     char nameBuf[64];
197     //get current value
198     MYSQL_RES *res;
199     MYSQL_ROW row;
200     check_mysql();
201     printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", channel_settings[setting].chanfield, chan->channel_id);
202     res = mysql_use();
203     row = mysql_fetch_row(res);
204     if(row[0] == NULL) {
205         printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_name` = 'defaults'", channel_settings[setting].chanfield);
206         res = mysql_use();
207         row = mysql_fetch_row(res);
208     }
209     value = row[0];
210     if(args) {
211         //change the channel setting
212         //check the new argument
213         int valid = channel_settings[setting].valid;
214         if(valid & NS_VALID_STRING) {
215             if(!strcmp(args, "*")) {
216                 args = "";
217             }
218         }
219         if(valid & NS_VALID_ACCESS) {
220             int caccess = atoi(args);
221             int max = ((valid & NS_VALID_NO501) ? 500 : 501);
222             if(caccess < 0 || caccess > max) {
223                 reply(getTextBot(), user, "NS_INVALID_ACCESS", caccess);
224                 return;
225             }
226             int uaccess = getChannelAccess(user, chan, 1);
227             if(uaccess == 500) uaccess++;
228             if(atoi(value) > uaccess) {
229                 reply(getTextBot(), user, "NS_SET_CANNOT_SET");
230                 return;
231             }
232             if(caccess > uaccess) {
233                 reply(getTextBot(), user, "NS_SET_BADLEVEL");
234                 return;
235             }
236             sprintf(nameBuf, "%d", caccess);
237             args = nameBuf;
238         }
239         if(valid & NS_VALID_OPTIONS) {
240             int options = atoi((char *) channel_settings[setting].parameter);
241             int coption = atoi(args);
242             if(coption < 0 || coption >= options) {
243                 reply(getTextBot(), user, "NS_SET_INVALID_OPTION", args);
244                 int i;
245                 int nameBufPos = 0;
246                 if(valid & NS_HAS_OPT) {
247                     for(i = 0; i < options; i++) {
248                         sprintf(nameBuf, "NS_SET_OPTION_%s_%d", channel_settings[setting].setting, i);
249                         reply(getTextBot(), user, "\002%d\002 - %s", i, get_language_string(user, nameBuf));
250                     }
251                 } else {
252                     for(i = 0; i < options; i++) {
253                         nameBufPos += sprintf(nameBuf + nameBufPos, "\002%d\002, ", i);
254                     }
255                     if(nameBufPos) {
256                         nameBuf[nameBufPos-2] = '\0';
257                         reply(getTextBot(), user, nameBuf);
258                     }
259                 }
260                 return;
261             }
262         }
263         if(valid & NS_VALID_NUMERIC) {
264             sprintf(nameBuf, "%d", atoi(args));
265             args = nameBuf;
266         }
267         if(valid & NS_VALID_BOOLEAN) {
268             if(!strcmp(args, "0") || !stricmp(args, "off") || !stricmp(args, get_language_string(user, "NS_SET_OFF"))) {
269                 args = "0";
270             } else if(!strcmp(args, "1") || !stricmp(args, "on") || !stricmp(args, get_language_string(user, "NS_SET_ON"))) {
271                 args = "1";
272             } else {
273                 reply(getTextBot(), user, "NS_SET_INVALID_BOOLEAN", args);
274                 return;
275             }
276         }
277         //valid - set it
278         value = args;
279         printf_mysql_query("UPDATE `channels` SET `%s` = '%s' WHERE `channel_id` = '%d'", channel_settings[setting].chanfield, escape_string(value), chan->channel_id);
280     }
281     reply(getTextBot(), user, "\002%s\002 %s", channel_settings[setting].setting, value);
282     if(channel_settings[setting].valid & NS_HAS_HELP) {
283          sprintf(nameBuf, "NS_SET_HELP_%s", channel_settings[setting].setting);
284          reply(getTextBot(), user, "  %s", get_language_string(user, nameBuf));
285     }
286 }
287
288 static char* neonserv_cmd_set_trigger(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, const char *setting, char *argument) {
289     char *trigger;
290     //get current trigger
291     MYSQL_RES *res;
292     MYSQL_ROW row;
293     check_mysql();
294     printf_mysql_query("SELECT `trigger` FROM `bot_channels` WHERE `chanid` = '%d' AND `botid` = '%d'", chan->channel_id, client->clientid);
295     res = mysql_use();
296     row = mysql_fetch_row(res);
297     trigger = row[0];
298     if(argument) {
299         int uaccess = getChannelAccess(user, chan, 1);
300         if(uaccess < 500) {
301             reply(getTextBot(), user, "NS_SET_TRIGGER_OWNER", chan->name);
302             return NULL;
303         }
304         if(strlen(argument) > 15)
305             argument[15] = '\0';
306         printf_mysql_query("UPDATE `bot_channels` SET `trigger` = '%s' WHERE `chanid` = '%d' AND `botid` = '%d'", escape_string(argument), chan->channel_id, client->clientid);
307         trigger = argument;
308         changeChannelTrigger(client->botid, chan, trigger);
309     }
310     if(setting) {
311         reply(getTextBot(), user, "\002%s\002 %s", setting, trigger);
312     }
313     return trigger;
314 }
315
316 static char* neonserv_cmd_set_modes(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, const char *setting, char *argument) {
317     char *value;
318     //get current value
319     MYSQL_RES *res;
320     MYSQL_ROW row;
321     check_mysql();
322     printf_mysql_query("SELECT `channel_modes` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
323     res = mysql_use();
324     row = mysql_fetch_row(res);
325     if(row[0] == NULL) {
326         printf_mysql_query("SELECT `channel_modes` FROM `channels` WHERE `channel_name` = 'defaults'");
327         res = mysql_use();
328         row = mysql_fetch_row(res);
329     }
330     value = row[0];
331     if(argument) {
332         //change the channel setting
333         //TODO: parse, check and set modelock
334     }
335     if(setting) {
336         reply(getTextBot(), user, "\002%s\002 %s", setting, value);
337     }
338     return value;
339 }
340
341 static char* neonserv_cmd_set_dynlimit(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, const char *setting, char *argument) {
342     char *value;
343     char tmp[64];
344     //get current value
345     MYSQL_RES *res;
346     MYSQL_ROW row;
347     check_mysql();
348     printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
349     res = mysql_use();
350     row = mysql_fetch_row(res);
351     if(row[0] == NULL) {
352         printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_name` = 'defaults'");
353         res = mysql_use();
354         row = mysql_fetch_row(res);
355     }
356     value = row[0];
357     if(argument) {
358         //change the channel setting
359         sprintf(tmp, "%d", atoi(argument));
360         argument = tmp;
361         printf_mysql_query("UPDATE `channels` SET `channel_dynlimit` = '%s' WHERE `channel_id` = '%d'", escape_string(argument), chan->channel_id);
362         if(strcmp(argument, "0"))
363             putsock(client, "MODE %s +l %d", chan->name, (chan->usercount + atoi(argument)));
364         else if(isModeSet(chan, 'l'))
365             putsock(client, "MODE %s -l", chan->name);
366     }
367     if(setting) {
368         reply(getTextBot(), user, "\002%s\002 %s", setting, value);
369     }
370     return value;
371 }
372
373 static char* neonserv_cmd_set_nodelete(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, const char *setting, char *argument) {
374     char *value;
375     //get current value
376     MYSQL_RES *res;
377     MYSQL_ROW row;
378     check_mysql();
379     printf_mysql_query("SELECT `channel_nodelete` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
380     res = mysql_use();
381     row = mysql_fetch_row(res);
382     if(row[0] == NULL) {
383         printf_mysql_query("SELECT `channel_nodelete` FROM `channels` WHERE `channel_name` = 'defaults'");
384         res = mysql_use();
385         row = mysql_fetch_row(res);
386     }
387     value = row[0];
388     if(argument && isGodMode(user)) {
389         //change the channel setting
390         if(!strcmp(argument, "0") || !strcmp(argument, "off") || !strcmp(argument, get_language_string(user, "NS_SET_OFF"))) {
391             argument = "0";
392         } else if(!strcmp(argument, "0") || !strcmp(argument, "off") || !strcmp(argument, get_language_string(user, "NS_SET_OFF"))) {
393             argument = "1";
394         } else {
395             reply(getTextBot(), user, "NS_SET_INVALID_BOOLEAN", argument);
396             return NULL;
397         }
398         printf_mysql_query("UPDATE `channels` SET `channel_nodelete` = '%s' WHERE `channel_id` = '%d'", escape_string(argument), chan->channel_id);
399     }
400     if(setting) {
401         reply(getTextBot(), user, "\002%s\002 %s", setting, value);
402     }
403     return value;
404 }
405
406 #undef MAX_QUERY_LEN