--- /dev/null
+/* cmd_neonserv_mode.c - NeonServ v5.3
+ * Copyright (C) 2011-2012 Philipp Kreil (pk910)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cmd_neonserv.h"
+
+/*
+* argv[0] - modes
+* argv[1-*] - parameters
+*/
+static USERLIST_CALLBACK(neonserv_cmd_mode_userlist_lookup);
+static void neonserv_cmd_mode_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *mode);
+
+struct neonserv_cmd_mode_cache {
+ struct ClientSocket *client, *textclient;
+ struct UserNode *user;
+ struct Event *event;
+ char *mode;
+};
+
+CMD_BIND(neonserv_cmd_mode) {
+ struct neonserv_cmd_mode_cache *cache = malloc(sizeof(*cache));
+ if (!cache) {
+ perror("malloc() failed");
+ return;
+ }
+ cache->client = client;
+ cache->textclient = getTextBot();
+ cache->user = user;
+ cache->event = event;
+ cache->mode = strdup(merge_argv(argv, 0, argc));
+ get_userlist_with_invisible(chan, neonserv_cmd_mode_userlist_lookup, cache);
+}
+
+static USERLIST_CALLBACK(neonserv_cmd_mode_userlist_lookup) {
+ struct neonserv_cmd_mode_cache *cache = data;
+ neonserv_cmd_mode_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->mode);
+ free(cache->mode);
+ free(cache);
+}
+
+static void neonserv_cmd_mode_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *mode) {
+ MYSQL_ROW row, defaults = NULL;
+ int i, arg, add = 1, skip = 0;
+ unsigned int modetype;
+ int db_canop, db_canvoice, db_canban, db_enfmodes;
+ struct ModeNode *modelock = createModeNode(NULL), *changemodes = createModeNode(NULL);
+ struct ModeBuffer *modeBuf;
+ struct UserNode *cuser;
+ struct ChanUser *chanuser;
+ modeBuf = initModeBuffer(client, chan);
+ printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
+ row = mysql_fetch_row(mysql_use());
+ if(row[0] == NULL || row[1] == NULL || row[2] == NULL || row[3] == NULL) {
+ printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes` FROM `channels` WHERE `channel_name` = 'defaults'");
+ defaults = mysql_fetch_row(mysql_use());
+ }
+ db_canop = atoi((row[0] ? row[0] : defaults[0]));
+ db_canvoice = atoi((row[1] ? row[1] : defaults[1]));
+ db_canban = atoi((row[2] ? row[2] : defaults[2]));
+ db_enfmodes = atoi((row[3] ? row[3] : defaults[3]));
+ if(row[4])
+ parseModeString(modelock, row[4]);
+ else if(defaults[4])
+ parseModeString(modelock, defaults[4]);
+ int uaccess = getChannelAccess(user, chan);
+ char *a, *b = mode;
+ char *argv[MAXNUMPARAMS];
+ char *carg;
+ char tmp[MAXLEN];
+ int argc = 0;
+ do {
+ a = strstr(b, " ");
+ if(a) *a = '\0';
+ argv[argc++] = b;
+ if(argc == MAXNUMPARAMS) break;
+ if(a) b = a+1;
+ } while(a);
+ arg = 0;
+ while(arg < argc) {
+ char *modeStr = argv[arg++];
+ for(i = 0; i < strlen(modeStr); i++) {
+ switch(modeStr[i]) {
+ case '+':
+ add = 1;
+ break;
+ case '-':
+ add = 0;
+ break;
+ case 'o':
+ case 'v':
+ if(arg == argc) {
+ reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
+ return;
+ }
+ carg = argv[arg++];
+ if(modeStr[i] == 'o') {
+ if(uaccess < db_canop) {
+ reply(textclient, user, "NS_MODE_ENFOPS", chan->name);
+ db_canop = -1;
+ break;
+ }
+ if(db_canop == -1) break;
+ } else {
+ if(uaccess < db_canvoice) {
+ reply(textclient, user, "NS_MODE_ENFVOICE", chan->name);
+ db_canvoice = -1;
+ break;
+ }
+ if(db_canvoice == -1) break;
+ }
+ cuser = searchUserByNick(carg);
+ if(!cuser) {
+ //check for an invisible user
+ for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
+ if(!stricmp(chanuser->user->nick, carg)) {
+ cuser = chanuser->user;
+ break;
+ }
+ }
+ if(!cuser) break;
+ } else {
+ chanuser = getChanUser(cuser, chan);
+ if(!chanuser) break;
+ }
+ if(!(add ^ (chanuser->flags & (modeStr[i] == 'o' ? CHANUSERFLAG_OPPED : CHANUSERFLAG_VOICED)))) break;
+ if(!add) {
+ //check protection
+ if(modeStr[i] == 'o' && isNetworkService(cuser)) {
+ reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick);
+ break;
+ }
+ if(isUserProtected(chan, cuser, user)) {
+ reply(textclient, user, "NS_USER_PROTECTED", cuser->nick);
+ break;
+ }
+ }
+ modeBufferSet(modeBuf, add, modeStr[i], carg);
+ break;
+ case 'b':
+ if(arg == argc) {
+ reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
+ return;
+ }
+ carg = argv[arg++];
+ if(uaccess < db_canban) {
+ reply(textclient, user, "NS_MODE_CANBAN", chan->name);
+ db_canban = -1;
+ break;
+ }
+ if(db_canban == -1) break;
+ char hostmask_buffer[NICKLEN+USERLEN+HOSTLEN+3];
+ char usermask[NICKLEN+USERLEN+HOSTLEN+3];
+ struct BanNode *ban;
+ int match_count = 0;
+ carg = make_banmask(carg, hostmask_buffer);
+ if(add) {
+ for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
+ cuser = chanuser->user;
+ sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host);
+ if(!match(carg, usermask)) {
+ if(isNetworkService(chanuser->user)) {
+ reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick);
+ skip = 1;
+ break;
+ }
+ if(isUserProtected(chan, cuser, user)) {
+ reply(textclient, user, "NS_USER_PROTECTED", cuser->nick);
+ skip = 1;
+ break;
+ }
+ match_count++;
+ if(match_count > 4 && (match_count * 3) > chan->usercount && !isGodMode(user)) {
+ skip = 1;
+ reply(textclient, user, "NS_LAME_MASK", carg);
+ break;
+ }
+ }
+ }
+ } else {
+ skip = 1;
+ for(ban = chan->bans; ban; ban = ban->next) {
+ if(!match(carg, ban->mask)) {
+ skip = 0;
+ break;
+ }
+ }
+ }
+ if(!skip) {
+ modeBufferSet(modeBuf, add, 'b', carg);
+ }
+ break;
+ default:
+ modetype = getModeType(modelock, modeStr[i]);
+ if(modetype == 0) {
+ reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
+ return;
+ }
+ if(isModeAffected(modelock, modeStr[i]) && add == !isModeSet(modelock, modeStr[i]) && uaccess < db_enfmodes) {
+ if(isGodMode(user))
+ event->flags |= CMDFLAG_OPLOG;
+ else {
+ getFullModeString(modelock, tmp);
+ reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name);
+ return;
+ }
+ }
+ if(add && (modetype & CHANNEL_MODE_TYPE) != CHANNEL_MODE_TYPE_D) {
+ if(arg == argc) {
+ reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
+ return;
+ }
+ carg = argv[arg++];
+ if((modetype & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING && isModeSet(modelock, modeStr[i])) {
+ char *modelock_val = getModeValue(modelock, modeStr[i]);
+ if(stricmp(carg, modelock_val)) {
+ if(isGodMode(user))
+ event->flags |= CMDFLAG_OPLOG;
+ else {
+ getFullModeString(modelock, tmp);
+ reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name);
+ return;
+ }
+ }
+ }
+ if((modetype & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING && isModeSet(modelock, modeStr[i])) {
+ int *modelock_val = getModeValue(modelock, modeStr[i]);
+ if(atoi(carg) != *modelock_val) {
+ if(isGodMode(user))
+ event->flags |= CMDFLAG_OPLOG;
+ else {
+ getFullModeString(modelock, tmp);
+ reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name);
+ return;
+ }
+ }
+ }
+ } else if(!add && (modetype & CHANNEL_MODE_TYPE) == CHANNEL_MODE_TYPE_B) {
+ if(arg == argc && !(modetype & CHANNEL_MODE_KEY)) {
+ reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
+ return;
+ }
+ carg = (arg == argc ? NULL : argv[arg++]);
+ } else
+ carg = NULL;
+ if((modetype & CHANNEL_MODE_TYPE) == CHANNEL_MODE_TYPE_D && isModeSet(chan->modes, modeStr[i]) == add)
+ break;
+ if(!isModeAffected(changemodes, modeStr[i])) {
+ if(!add && (modetype & CHANNEL_MODE_KEY)) {
+ if(isModeSet(chan->modes, modeStr[i])) {
+ char *current_val = getModeValue(chan->modes, modeStr[i]);
+ carg = current_val;
+ }
+ }
+ if(parseMode(changemodes, add, modeStr[i], carg)) {
+ if(carg) {
+ if(add && (modetype & CHANNEL_MODE_KEY) && isModeSet(chan->modes, modeStr[i])) {
+ char *current_val = getModeValue(chan->modes, modeStr[i]);
+ modeBufferSet(modeBuf, 0, modeStr[i], current_val);
+ flushModeBuffer(modeBuf);
+ }
+ if(!add && !isModeSet(chan->modes, modeStr[i])) break;
+ modeBufferSet(modeBuf, add, modeStr[i], carg);
+ } else {
+ modeBufferSimpleMode(modeBuf, add, modeStr[i]);
+ }
+ } else {
+ reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
+ return;
+ }
+ }
+ break;
+ }
+ }
+ }
+ getFullModeString(changemodes, tmp);
+ freeModeBuffer(modeBuf);
+ if(strcmp(tmp, "+"))
+ reply(textclient, user, "NS_MODE_DONE", tmp);
+
+ logEvent(event);
+ freeModeNode(modelock);
+ freeModeNode(changemodes);
+}