1 /* cmd_neonserv_set.c - NeonServ v5.6
2 * Copyright (C) 2011-2012 Philipp Kreil (pk910)
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.
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.
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/>.
18 #include "cmd_neonserv.h"
20 typedef char* neonserv_cmd_set_function(struct ClientSocket *client, struct ClientSocket *textclient, 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 ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int setting, char *argument);
22 static char* neonserv_cmd_set_trigger(struct ClientSocket *client, struct ClientSocket *textclient, 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 ClientSocket *textclient, 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 ClientSocket *textclient, 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 ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
26 static char* neonserv_cmd_set_backupbot(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument);
28 #define NS_VALID_FUNCTION 0x01
29 #define NS_VALID_STRING 0x02
30 #define NS_VALID_ACCESS 0x04
31 #define NS_VALID_NO501 0x08
32 #define NS_VALID_OPTIONS 0x10
33 #define NS_VALID_NUMERIC 0x20
34 #define NS_VALID_BOOLEAN 0x40
35 #define NS_VALID_IF_HALFOP 0x80
37 #define NS_HAS_OPT 0x100 /* options (SET_OPTION_{NAME}_{VALUE}) */
38 #define NS_HAS_HELP 0x200 /* help (SET_HELP_{NAME}) - only shown if help is requested */
42 const char *chanfield;
45 } channel_settings[] = {
46 {"TRIGGER", NULL, NS_VALID_FUNCTION, neonserv_cmd_set_trigger},
47 {"DEFAULTTOPIC", "channel_defaulttopic", NS_VALID_STRING, NULL},
48 {"TOPICMASK", "channel_topicmask", NS_VALID_STRING, NULL},
49 {"ADVANCEDTOPIC", "channel_exttopic", NS_VALID_BOOLEAN | NS_HAS_OPT, NULL},
50 {"GREETING", "channel_greeting", NS_VALID_STRING, NULL},
51 {"USERGREETING", "channel_usergreeting", NS_VALID_STRING, NULL},
52 {"USERINFO", "channel_userinfo", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
53 {"WIPEINFO", "channel_wipeinfo", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
54 {"MODES", "channel_modes", NS_VALID_FUNCTION, neonserv_cmd_set_modes},
55 {"INVITEME", "channel_getinvite", NS_VALID_ACCESS, NULL},
56 {"GIVEOPS", "channel_getop", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
57 {"GIVEHALFOPS", "channel_gethalfop", NS_VALID_ACCESS | NS_HAS_HELP | NS_VALID_IF_HALFOP, NULL},
58 {"GIVEVOICE", "channel_getvoice", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
59 {"ENFOPS", "channel_canop", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
60 {"ENFHALFOPS", "channel_canhalfop", NS_VALID_ACCESS | NS_HAS_HELP | NS_VALID_IF_HALFOP, NULL},
61 {"ENFVOICE", "channel_canvoice", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
62 {"KICK", "channel_cankick", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
63 {"BAN", "channel_canban", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
64 {"STATICBAN", "channel_staticban", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
65 {"PUBCMD", "channel_pubcmd", NS_VALID_ACCESS, NULL},
66 {"ENFMODES", "channel_enfmodes", NS_VALID_ACCESS, NULL},
67 {"ENFTOPIC", "channel_enftopic", NS_VALID_ACCESS, NULL},
68 {"TOPICSNARF", "channel_topicsnarf", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
69 {"CHANGETOPIC", "channel_changetopic", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
70 {"SETTERS", "channel_setters", NS_VALID_ACCESS | NS_VALID_NO501 | NS_HAS_HELP, NULL},
71 {"ADDUSER", "channel_canadd", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
72 {"DELUSER", "channel_candel", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
73 {"CLVL", "channel_canclvl", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
74 {"RESYNC", "channel_canresync", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
75 {"SUSPEND", "channel_cansuspend", NS_VALID_ACCESS | NS_HAS_HELP, NULL},
76 {"NOTICEUSERS", "channel_notice", NS_VALID_ACCESS, NULL},
77 {"NOTICEREACTION", "channel_noticereaction", NS_VALID_OPTIONS | NS_HAS_OPT, "4"},
78 {"CTCPUSERS", "channel_ctcp", NS_VALID_ACCESS, NULL},
79 {"CTCPREACTION", "channel_ctcpreaction", NS_VALID_OPTIONS | NS_HAS_OPT, "4"},
80 {"PROTECT", "channel_protect", NS_VALID_OPTIONS | NS_HAS_OPT, "4"},
81 {"TOYS", "channel_toys", NS_VALID_OPTIONS | NS_HAS_OPT, "3"},
82 {"DYNLIMIT", "channel_dynlimit", NS_VALID_NUMERIC | NS_VALID_FUNCTION | NS_HAS_OPT, neonserv_cmd_set_dynlimit},
83 {"NODELETE", "channel_nodelete", NS_VALID_BOOLEAN | NS_VALID_FUNCTION, neonserv_cmd_set_nodelete},
84 {"BACKUPBOT", NULL, NS_VALID_BOOLEAN | NS_VALID_FUNCTION, neonserv_cmd_set_backupbot},
88 #define MAX_QUERY_LEN 1024
89 CMD_BIND(neonserv_cmd_set) {
91 if(argc && !strcmp(argv[0], "defaults")) {
92 //reset channel settings
93 int uaccess = getChannelAccess(user, chan);
96 event->flags |= CMDFLAG_OPLOG;
98 reply(textclient, user, "NS_SET_DEFAULTS_OWNER", chan->name);
104 static char defaultskey[16];
105 for(tmp = user->auth; *tmp; tmp++)
106 seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp));
107 for(tmp = chan->name; *tmp; tmp++)
108 seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp));
109 sprintf(defaultskey, "%08x", seed);
110 if(argc > 1 && !strcmp(argv[1], defaultskey)) {
111 char query[MAX_QUERY_LEN];
114 while(channel_settings[i].setting) {
115 if(channel_settings[i].chanfield)
116 querypos += sprintf(query + querypos, "`%s` = NULL, ", channel_settings[i].chanfield);
120 query[querypos-2] = '\0';
122 printf_mysql_query("UPDATE `channels` SET %s WHERE `channel_id` = '%d'", query, chan->channel_id);
123 reply(textclient, user, "NS_SET_DEFAULTS_DONE", chan->name);
126 reply(textclient, user, "NS_SET_DEFAULTS_CODE", chan->name, defaultskey);
128 } else if(argc && strcmp(argv[0], "help")) {
129 //find the correct command
132 char *args = (argc > 1 ? merge_argv(argv, 1, argc) : NULL);
133 int with_halfops = get_int_field("General.have_halfop");
134 while(channel_settings[i].setting) {
135 if(!stricmp(channel_settings[i].setting, argv[0]) && (!(channel_settings[i].valid & NS_VALID_IF_HALFOP) || with_halfops)) {
137 if(!stricmp(channel_settings[i].setting, "BACKUPBOT")) {
139 sprintf(setting, "modules.%s.channel_backup_setting", get_module_name(module_id));
140 if(!get_int_field(setting)) {
145 if(channel_settings[i].valid & NS_VALID_FUNCTION) {
146 neonserv_cmd_set_function *func = channel_settings[i].parameter;
147 func(client, textclient, user, chan, event, channel_settings[i].setting, args);
149 neonserv_cmd_set_setting(client, textclient, user, chan, event, i, args);
158 reply(textclient, user, "NS_SET_UNKNOWN_SETTING", argv[0]);
161 char query[MAX_QUERY_LEN], *value, *org_value, *tmp, nameBuf[64];
163 MYSQL_RES *res, *defaults_res;
164 MYSQL_ROW row, defaults;
167 int with_halfops = get_int_field("General.have_halfop");
168 int channel_backup_setting;
171 while(channel_settings[i].setting) {
172 if((channel_settings[i].valid & NS_VALID_IF_HALFOP) && !with_halfops) {
177 if(!stricmp(channel_settings[i].setting, "BACKUPBOT")) {
179 sprintf(setting, "modules.%s.channel_backup_setting", get_module_name(module_id));
180 channel_backup_setting = get_int_field(setting);
181 if(!channel_backup_setting) {
187 if(channel_settings[i].chanfield)
188 querypos += sprintf(query + querypos, ", `%s`", channel_settings[i].chanfield);
191 table = table_init(2, i-j, 0);
192 table_set_bold(table, 0, 1);
193 printf_mysql_query("SELECT `channel_id` %s FROM `channels` WHERE `channel_name` = 'defaults'", query);
194 defaults_res = mysql_use();
195 defaults = mysql_fetch_row(defaults_res);
196 printf_mysql_query("SELECT `channel_name` %s FROM `channels` WHERE `channel_id` = '%d'", query, chan->channel_id);
198 row = mysql_fetch_row(res);
201 reply(textclient, user, "NS_SET_HEADER", chan->name);
202 while(channel_settings[i].setting) {
203 if((channel_settings[i].valid & NS_VALID_IF_HALFOP) && !with_halfops) {
207 if(!stricmp(channel_settings[i].setting, "BACKUPBOT") && !channel_backup_setting) {
211 if(channel_settings[i].chanfield) {
213 org_value = (row[j] ? row[j] : defaults[j]);
214 } else if(channel_settings[i].valid & NS_VALID_FUNCTION) {
215 neonserv_cmd_set_function *func = channel_settings[i].parameter;
216 org_value = func(client, textclient, user, chan, event, NULL, NULL);
220 if(channel_settings[i].valid & NS_VALID_BOOLEAN) {
221 if(!strcmp(value, "0"))
222 value = get_language_string(user, "NS_SET_OFF");
224 value = get_language_string(user, "NS_SET_ON");
226 strcpy(query, value);
227 querypos = strlen(query);
228 if(channel_settings[i].valid & NS_HAS_OPT) {
229 sprintf(nameBuf, "NS_SET_OPTION_%s_%s", channel_settings[i].setting, org_value);
230 tmp = get_language_string(user, nameBuf);
232 querypos += sprintf(query+querypos, " - %s", tmp);
235 if(argc && channel_settings[i].valid & NS_HAS_HELP) {
236 sprintf(nameBuf, "NS_SET_HELP_%s", channel_settings[i].setting);
237 tmp = get_language_string(user, nameBuf);
239 querypos += sprintf(query+querypos, " - %s", tmp);
242 content[0] = (char*)channel_settings[i].setting;
244 table_add(table, content);
247 char **table_lines = table_end(table);
248 for(i = 0; i < table->entrys; i++) {
249 reply(textclient, user, table_lines[i]);
255 static void neonserv_cmd_set_setting(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int setting, char *args) {
261 printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", channel_settings[setting].chanfield, chan->channel_id);
263 row = mysql_fetch_row(res);
265 printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_name` = 'defaults'", channel_settings[setting].chanfield);
267 row = mysql_fetch_row(res);
271 //change the channel setting
272 //check the new argument
273 int valid = channel_settings[setting].valid;
274 if(valid & NS_VALID_STRING) {
275 if(!strcmp(args, "*")) {
279 if(valid & NS_VALID_ACCESS) {
280 int caccess = atoi(args);
281 int max = ((valid & NS_VALID_NO501) ? 500 : 501);
282 if(caccess < 0 || caccess > max) {
283 reply(textclient, user, "NS_INVALID_ACCESS", caccess);
286 int uaccess = getChannelAccess(user, chan);
287 if(uaccess == 500) uaccess++;
288 if(atoi(value) > uaccess) {
289 if(isGodMode(user)) {
290 event->flags |= CMDFLAG_OPLOG;
292 reply(textclient, user, "NS_SET_CANNOT_SET");
296 if(caccess > uaccess) {
297 if(isGodMode(user)) {
298 event->flags |= CMDFLAG_OPLOG;
300 reply(textclient, user, "NS_SET_BADLEVEL");
304 sprintf(nameBuf, "%d", caccess);
307 if(valid & NS_VALID_OPTIONS) {
308 int options = atoi((char *) channel_settings[setting].parameter);
309 int coption = atoi(args);
310 if(coption < 0 || coption >= options) {
311 reply(textclient, user, "NS_SET_INVALID_OPTION", coption);
314 if(valid & NS_HAS_OPT) {
315 for(i = 0; i < options; i++) {
316 sprintf(nameBuf, "NS_SET_OPTION_%s_%d", channel_settings[setting].setting, i);
317 reply(textclient, user, "\002%d\002 - %s", i, get_language_string(user, nameBuf));
320 for(i = 0; i < options; i++) {
321 nameBufPos += sprintf(nameBuf + nameBufPos, "\002%d\002, ", i);
324 nameBuf[nameBufPos-2] = '\0';
325 reply(textclient, user, nameBuf);
331 if(valid & NS_VALID_NUMERIC) {
332 sprintf(nameBuf, "%d", atoi(args));
335 if(valid & NS_VALID_BOOLEAN) {
336 if(!strcmp(args, "0") || !stricmp(args, "off") || !stricmp(args, get_language_string(user, "NS_SET_OFF"))) {
338 } else if(!strcmp(args, "1") || !stricmp(args, "on") || !stricmp(args, get_language_string(user, "NS_SET_ON"))) {
341 reply(textclient, user, "NS_SET_INVALID_BOOLEAN", args);
347 printf_mysql_query("UPDATE `channels` SET `%s` = '%s' WHERE `channel_id` = '%d'", channel_settings[setting].chanfield, escape_string(value), chan->channel_id);
350 if(channel_settings[setting].valid & NS_HAS_OPT) {
351 sprintf(nameBuf, "NS_SET_OPTION_%s_%s", channel_settings[setting].setting, value);
352 char *tmp = get_language_string(user, nameBuf);
354 reply(textclient, user, "\002%s\002 %s - %s", channel_settings[setting].setting, value, tmp);
356 reply(textclient, user, "\002%s\002 %s", channel_settings[setting].setting, value);
358 reply(textclient, user, "\002%s\002 %s", channel_settings[setting].setting, value);
359 if(channel_settings[setting].valid & NS_HAS_HELP) {
360 sprintf(nameBuf, "NS_SET_HELP_%s", channel_settings[setting].setting);
361 reply(textclient, user, " %s", get_language_string(user, nameBuf));
365 static char* neonserv_cmd_set_trigger(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
367 //get current trigger
371 printf_mysql_query("SELECT `trigger`, `defaulttrigger`, `bot_channels`.`id` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, client->botid);
373 printf_mysql_query("SELECT `trigger`, `defaulttrigger`, `bot_channels`.`id` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botid` = '%d'", chan->channel_id, client->clientid);
375 row = mysql_fetch_row(res);
376 trigger = (row[0] ? row[0] : row[1]);
378 int uaccess = getChannelAccess(user, chan);
380 if(isGodMode(user)) {
381 event->flags |= CMDFLAG_OPLOG;
383 reply(textclient, user, "NS_SET_TRIGGER_OWNER", chan->name);
387 if(strlen(argument) > 15)
389 printf_mysql_query("UPDATE `bot_channels` SET `trigger` = '%s' WHERE `id` = '%d'", escape_string(argument), row[2]);
392 changeChannelTrigger(client->botid, chan, trigger);
394 changeBotwiseChannelTrigger(client->botid, client->clientid, chan, trigger);
398 reply(textclient, user, "\002%s\002 %s", setting, trigger);
403 static char* neonserv_cmd_set_modes(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
405 char valueBuf[MAXLEN];
409 printf_mysql_query("SELECT `channel_modes` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
411 row = mysql_fetch_row(res);
413 printf_mysql_query("SELECT `channel_modes` FROM `channels` WHERE `channel_name` = 'defaults'");
415 row = mysql_fetch_row(res);
419 //change the channel setting
420 struct ModeNode *modenode = createModeNode(NULL);
421 parseModeString(modenode, argument);
422 getFullModeString(modenode, valueBuf);
424 printf_mysql_query("UPDATE `channels` SET `channel_modes` = '%s' WHERE `channel_id` = '%d'", escape_string(value), chan->channel_id);
426 freeModeNode(modenode);
429 reply(textclient, user, "\002%s\002 %s", setting, value);
434 static char* neonserv_cmd_set_dynlimit(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
440 printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
442 row = mysql_fetch_row(res);
444 printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_name` = 'defaults'");
446 row = mysql_fetch_row(res);
450 //change the channel setting
451 sprintf(tmp, "%d", atoi(argument));
453 printf_mysql_query("UPDATE `channels` SET `channel_dynlimit` = '%s' WHERE `channel_id` = '%d'", escape_string(argument), chan->channel_id);
454 if(strcmp(argument, "0"))
455 putsock(client, "MODE %s +l %d", chan->name, (chan->usercount + atoi(argument)));
456 else if(isModeSet(chan->modes, 'l'))
457 putsock(client, "MODE %s -l", chan->name);
462 reply(textclient, user, "\002%s\002 %s", setting, value);
467 static char* neonserv_cmd_set_nodelete(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
472 printf_mysql_query("SELECT `channel_nodelete` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
474 row = mysql_fetch_row(res);
476 printf_mysql_query("SELECT `channel_nodelete` FROM `channels` WHERE `channel_name` = 'defaults'");
478 row = mysql_fetch_row(res);
481 if(argument && isGodMode(user)) {
482 //change the channel setting
483 if(!strcmp(argument, "0") || !strcmp(argument, "off") || !strcmp(argument, get_language_string(user, "NS_SET_OFF"))) {
485 } else if(!strcmp(argument, "1") || !strcmp(argument, "on") || !strcmp(argument, get_language_string(user, "NS_SET_ON"))) {
488 reply(textclient, user, "NS_SET_INVALID_BOOLEAN", argument);
491 printf_mysql_query("UPDATE `channels` SET `channel_nodelete` = '%s' WHERE `channel_id` = '%d'", escape_string(argument), chan->channel_id);
492 event->flags |= CMDFLAG_OPLOG;
497 reply(textclient, user, "\002%s\002 %s", setting, value);
502 static char* neonserv_cmd_set_backupbot(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) {
504 //get current trigger
506 printf_mysql_query("SELECT `botid` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, NEONBACKUP_BOTID);
508 if(mysql_fetch_row(res))
513 //change the channel setting
514 if(!strcmp(argument, "0") || !strcmp(argument, "off") || !strcmp(argument, get_language_string(user, "NS_SET_OFF"))) {
516 module_global_cmd_unregister_neonbackup(chan->name);
519 } else if(!strcmp(argument, "1") || !strcmp(argument, "on") || !strcmp(argument, get_language_string(user, "NS_SET_ON"))) {
521 module_global_cmd_register_neonbackup(chan->name);
525 reply(textclient, user, "NS_SET_INVALID_BOOLEAN", argument);
531 reply(textclient, user, "\002%s\002 %s", setting, (backupbot ? "1" : "0"));
533 return (backupbot ? "1" : "0");