From: pk910 Date: Thu, 12 Jan 2012 14:07:20 +0000 (+0100) Subject: added full half-op support X-Git-Tag: v5.3~64 X-Git-Url: http://git.pk910.de/?p=NeonServV5.git;a=commitdiff_plain;h=b013fcf166b6b84f7b946412dacfe84ba5cfe6b6 added full half-op support --- diff --git a/Makefile.am b/Makefile.am index 1f7d907..ea2f286 100644 --- a/Makefile.am +++ b/Makefile.am @@ -129,6 +129,10 @@ neonserv_SOURCES = src/version.c \ src/cmd_neonhelp_next.c \ src/cmd_neonhelp_delete.c \ src/cmd_neonhelp_requests.c \ + src/cmd_neonserv_halfop.c \ + src/cmd_neonserv_dehalfop.c \ + src/cmd_neonserv_halfopall.c \ + src/cmd_neonserv_dehalfopall.c \ src/cmd_funcmds.c \ src/ConfigParser.c diff --git a/database.sql b/database.sql index 9f3ee1c..2047290 100644 --- a/database.sql +++ b/database.sql @@ -102,8 +102,10 @@ CREATE TABLE IF NOT EXISTS `channels` ( `channel_staticban` smallint(3) DEFAULT NULL, `channel_protect` tinyint(1) DEFAULT NULL, `channel_canop` smallint(3) DEFAULT NULL, + `channel_canhalfop` smallint(3) DEFAULT NULL, `channel_canvoice` smallint(3) DEFAULT NULL, `channel_getop` smallint(3) DEFAULT NULL, + `channel_gethalfop` smallint(3) DEFAULT NULL, `channel_getvoice` smallint(3) DEFAULT NULL, `channel_greeting` varchar(512) NOT NULL, `channel_usergreeting` varchar(512) NOT NULL, diff --git a/database.upgrade.sql b/database.upgrade.sql index fcbb936..4f4697d 100644 --- a/database.upgrade.sql +++ b/database.upgrade.sql @@ -127,3 +127,10 @@ ALTER TABLE `helpserv_requests` ADD `botid` INT( 11 ) NOT NULL AFTER `id`; ALTER TABLE `helpserv_settings` ADD `helpserv_intern_announce` TINYINT( 1 ) NOT NULL; -- version: 13 + +ALTER TABLE `channels` ADD `channel_canhalfop` SMALLINT( 3 ) NULL AFTER `channel_canop`; +ALTER TABLE `channels` ADD `channel_gethalfop` SMALLINT( 3 ) NULL AFTER `channel_getop`; +UPDATE `channels` SET `channel_canhalfop` = '150', +`channel_gethalfop` = '150' WHERE `channel_name` = 'defaults'; + +-- version: 14 diff --git a/neonserv.example.conf b/neonserv.example.conf index 99775ae..abb149d 100644 --- a/neonserv.example.conf +++ b/neonserv.example.conf @@ -8,4 +8,8 @@ "user" = "neonserv"; "pass" = "password"; "base" = "neonserv"; +}; +"General" { + "alertchan" = ""; + "have_halfop" = 0; }; \ No newline at end of file diff --git a/src/bot_NeonServ.c b/src/bot_NeonServ.c index 3700d4b..00b87c2 100644 --- a/src/bot_NeonServ.c +++ b/src/bot_NeonServ.c @@ -34,6 +34,7 @@ #include "timeq.h" #include "EventLogger.h" #include "cmd_neonserv.h" +#include "ConfigParser.h" #define BOTID 1 #define BOTALIAS "NeonServ" @@ -115,16 +116,22 @@ static const struct default_language_entry msgtab[] = { {"NS_GIVEOWNER_DONE", "Ownership of $b%s$b has been transferred to account $b%s$b."}, /* {ARGS: "#TestChan", "TestUser"} */ {"NS_OP_FAIL", "$b%s$b could not op some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ {"NS_OP_DONE", "Opped users in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_HALFOP_FAIL", "$b%s$b could not halfop some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_HALFOP_DONE", "Half-Opped users in $b%s$b."}, /* {ARGS: "#TestChan"} */ {"NS_VOICE_FAIL", "$b%s$b could not voice some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ {"NS_VOICE_DONE", "Voiced users in $b%s$b."}, /* {ARGS: "#TestChan"} */ {"NS_DEOP_FAIL", "$b%s$b could not deop some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ {"NS_DEOP_DONE", "Deopped users in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_DEHALFOP_FAIL", "$b%s$b could not dehalfop some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_DEHALFOP_DONE", "Dehalfopped users in $b%s$b."}, /* {ARGS: "#TestChan"} */ {"NS_DEVOICE_FAIL", "$b%s$b could not devoice some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ {"NS_DEVOICE_DONE", "Devoiced users in $b%s$b."}, /* {ARGS: "#TestChan"} */ {"NS_OPALL_SECURITY", "$bWARNING$b: Opping all users on a channel is very insecure! If you still want do op all users on %s use: '$bopall FORCE$b [nick mask]'"}, {"NS_OPALL_DONE", "Opped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ + {"NS_HALFOPALL_DONE", "Half-Opped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ {"NS_VOICEALL_DONE", "Voiced $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ {"NS_DEOPALL_DONE", "Deopped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ + {"NS_DEHALFOPALL_DONE", "Dehalfopped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ {"NS_DEVOICEALL_DONE", "Devoiced $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ {"NS_KICK_DONE", "Kicked $b%d$b users from %s"}, /* {ARGS: 20, "#TestChan"} */ {"NS_KICK_FAIL", "$b%s$b could not kick some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ @@ -244,6 +251,7 @@ static const struct default_language_entry msgtab[] = { {"NS_PEEK_TOPIC", "Topic: %s"}, /* {ARGS: "TOPIC"} */ {"NS_PEEK_MODES", "Modes: %s"}, /* {ARGS: "+xyz"} */ {"NS_PEEK_USERS", "Total Users: %d (%d ops, %d voices, %d regulars, %d invisible)"}, /* {ARGS: 20, 4, 6, 8, 2} */ + {"NS_PEEK_USERS_HALFOP", "Total Users: %d (%d ops, %d halfops, %d voices, %d regulars, %d invisible)"}, /* {ARGS: 25, 5, 4, 6, 8, 2} */ {"NS_PEEK_OPS", "Ops:"}, {"NS_USET_GLOBAL", "$b--- Global ---$b"}, {"NS_USET_CHANNEL", "$b--- User options (channel) ---$b"}, diff --git a/src/cmd_neonserv.h b/src/cmd_neonserv.h index ac3fa54..0df1a7b 100644 --- a/src/cmd_neonserv.h +++ b/src/cmd_neonserv.h @@ -37,6 +37,7 @@ #include "EventLogger.h" #include "bots.h" #include "bot_NeonServ.h" +#include "ConfigParser.h" CMD_BIND(neonserv_cmd_access); CMD_BIND(neonserv_cmd_addban); @@ -50,6 +51,8 @@ CMD_BIND(neonserv_cmd_chanservsync); CMD_BIND(neonserv_cmd_clvl); CMD_BIND(neonserv_cmd_csuspend); CMD_BIND(neonserv_cmd_cunsuspend); +CMD_BIND(neonserv_cmd_dehalfop); +CMD_BIND(neonserv_cmd_dehalfopall); CMD_BIND(neonserv_cmd_delban); CMD_BIND(neonserv_cmd_delme); CMD_BIND(neonserv_cmd_delrank); @@ -63,6 +66,8 @@ CMD_BIND(neonserv_cmd_downall); CMD_BIND(neonserv_cmd_events); CMD_BIND(neonserv_cmd_extscript); CMD_BIND(neonserv_cmd_giveowner); +CMD_BIND(neonserv_cmd_halfop); +CMD_BIND(neonserv_cmd_halfopall); CMD_BIND(neonserv_cmd_help); CMD_BIND(neonserv_cmd_info); CMD_BIND(neonserv_cmd_invite); diff --git a/src/cmd_neonserv_dehalfop.c b/src/cmd_neonserv_dehalfop.c new file mode 100644 index 0000000..2253797 --- /dev/null +++ b/src/cmd_neonserv_dehalfop.c @@ -0,0 +1,93 @@ +/* cmd_neonserv_dehalfop.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 . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nicks +*/ +static USERLIST_CALLBACK(neonserv_cmd_dehalfop_userlist_lookup); +static void neonserv_cmd_dehalfop_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc); + +struct neonserv_cmd_dehalfop_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char **argv; + int argc; +}; + +CMD_BIND(neonserv_cmd_dehalfop) { + struct neonserv_cmd_dehalfop_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + perror("malloc() failed"); + return; + } + cache->client = client; + cache->textclient = getTextBot(); + cache->user = user; + cache->event = event; + cache->argv = calloc(argc, sizeof(char*)); + int i; + for(i = 0; i < argc; i++) { + cache->argv[i] = strdup(argv[i]); + } + cache->argc = argc; + get_userlist(chan, neonserv_cmd_dehalfop_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_dehalfop_userlist_lookup) { + struct neonserv_cmd_dehalfop_cache *cache = data; + neonserv_cmd_dehalfop_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->argv, cache->argc); + int i; + for(i = 0; i < cache->argc; i++) { + free(cache->argv[i]); + } + free(cache); +} + +static void neonserv_cmd_dehalfop_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc) { + int i, done_users = 0; + struct UserNode *cuser; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + for(i = 0; i < argc; i++) { + cuser = searchUserByNick(argv[i]); + if(!cuser) continue; + chanuser = getChanUser(cuser, chan); + if(!chanuser) continue; + if(isNetworkService(cuser)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick); + continue; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + continue; + } + done_users++; + if(!(chanuser->flags & CHANUSERFLAG_OPPED)) continue; + modeBufferDehalfop(modeBuf, argv[i]); + } + freeModeBuffer(modeBuf); + if(done_users == argc) + reply(textclient, user, "NS_DEHALFOP_DONE", chan->name); + else + reply(textclient, user, "NS_DEHALFOP_FAIL", client->user->nick); + if(done_users) + logEvent(event); +} diff --git a/src/cmd_neonserv_dehalfopall.c b/src/cmd_neonserv_dehalfopall.c new file mode 100644 index 0000000..8dd8d61 --- /dev/null +++ b/src/cmd_neonserv_dehalfopall.c @@ -0,0 +1,84 @@ +/* cmd_neonserv_dehalfopall.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 . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] (optional) nick mask +*/ +static USERLIST_CALLBACK(neonserv_cmd_dehalfopall_userlist_lookup); +static void neonserv_cmd_dehalfopall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc); + +struct neonserv_cmd_dehalfopall_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char **argv; + int argc; +}; + +CMD_BIND(neonserv_cmd_dehalfopall) { + struct neonserv_cmd_dehalfopall_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + perror("malloc() failed"); + return; + } + cache->client = client; + cache->textclient = getTextBot(); + cache->user = user; + cache->event = event; + cache->argv = calloc(argc, sizeof(char*)); + int i; + for(i = 0; i < argc; i++) { + cache->argv[i] = strdup(argv[i]); + } + cache->argc = argc; + get_userlist(chan, neonserv_cmd_dehalfopall_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_dehalfopall_userlist_lookup) { + struct neonserv_cmd_dehalfopall_cache *cache = data; + neonserv_cmd_dehalfopall_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->argv, cache->argc); + int i; + for(i = 0; i < cache->argc; i++) { + free(cache->argv[i]); + } + free(cache); +} + +static void neonserv_cmd_dehalfopall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc) { + int issuer_access, victim_access, done_users = 0; + char *nickmask = NULL; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + if(argc > 0) + nickmask = argv[0]; + modeBuf = initModeBuffer(client, chan); + issuer_access = getChannelAccess(user, chan); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(nickmask && match(nickmask, chanuser->user->nick)) continue; + victim_access = getChannelAccess(chanuser->user, chan); + if(victim_access >= issuer_access || isNetworkService(chanuser->user)) continue; + if(!(chanuser->flags & CHANUSERFLAG_OPPED)) continue; + modeBufferDehalfop(modeBuf, chanuser->user->nick); + done_users++; + } + freeModeBuffer(modeBuf); + reply(getTextBot(), user, "NS_DEHALFOPALL_DONE", done_users, chan->name); + if(done_users) + logEvent(event); +} diff --git a/src/cmd_neonserv_halfop.c b/src/cmd_neonserv_halfop.c new file mode 100644 index 0000000..ba684cb --- /dev/null +++ b/src/cmd_neonserv_halfop.c @@ -0,0 +1,93 @@ +/* cmd_neonserv_halfop.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 . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nicks +*/ +static USERLIST_CALLBACK(neonserv_cmd_halfop_userlist_lookup); +static void neonserv_cmd_halfop_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks); + +struct neonserv_cmd_halfop_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nicks; +}; + +CMD_BIND(neonserv_cmd_halfop) { + struct neonserv_cmd_halfop_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + perror("malloc() failed"); + return; + } + cache->client = client; + cache->textclient = getTextBot(); + cache->user = user; + cache->event = event; + cache->nicks = strdup(merge_argv(argv, 0, argc)); + get_userlist_if_invisible(chan, neonserv_cmd_halfop_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_halfop_userlist_lookup) { + struct neonserv_cmd_halfop_cache *cache = data; + neonserv_cmd_halfop_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nicks); + free(cache->nicks); + free(cache); +} + +static void neonserv_cmd_halfop_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks) { + int total_users = 0, done_users = 0; + struct UserNode *cuser; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + char *a, *b = nicks; + do { + a = strstr(b, " "); + if(a) *a = '\0'; + total_users++; + cuser = searchUserByNick(b); + if(!cuser) { + //check for an invisible user + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(!stricmp(chanuser->user->nick, b)) { + cuser = chanuser->user; + break; + } + } + if(!cuser) continue; + } else { + chanuser = getChanUser(cuser, chan); + if(!chanuser) continue; + } + done_users++; + if(chanuser->flags & CHANUSERFLAG_OPPED) continue; + modeBufferHalfop(modeBuf, b); + if(a) { + b = a+1; + } + } while(a); + freeModeBuffer(modeBuf); + if(done_users == total_users) + reply(textclient, user, "NS_HALFOP_DONE", chan->name); + else + reply(textclient, user, "NS_HALFOP_FAIL", client->user->nick); + if(done_users) + logEvent(event); +} diff --git a/src/cmd_neonserv_halfopall.c b/src/cmd_neonserv_halfopall.c new file mode 100644 index 0000000..a52ad6d --- /dev/null +++ b/src/cmd_neonserv_halfopall.c @@ -0,0 +1,74 @@ +/* cmd_neonserv_halfopall.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 . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] "force" +* argv[1] (optional) nick mask +*/ +static USERLIST_CALLBACK(neonserv_cmd_halfopall_userlist_lookup); +static void neonserv_cmd_halfopall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask); + +struct neonserv_cmd_halfopall_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nickmask; +}; + +CMD_BIND(neonserv_cmd_halfopall) { + struct neonserv_cmd_halfopall_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + perror("malloc() failed"); + return; + } + cache->client = client; + cache->textclient = getTextBot(); + cache->user = user; + cache->event = event; + if(argc) { + cache->nickmask = strdup(argv[0]); + } else + cache->nickmask = NULL; + get_userlist_if_invisible(chan, neonserv_cmd_halfopall_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_halfopall_userlist_lookup) { + struct neonserv_cmd_halfopall_cache *cache = data; + neonserv_cmd_halfopall_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nickmask); + if(cache->nickmask) + free(cache->nickmask); + free(cache); +} + +static void neonserv_cmd_halfopall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask) { + int done_users = 0; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(nickmask && match(nickmask, chanuser->user->nick)) continue; + if(chanuser->flags & CHANUSERFLAG_OPPED) continue; + modeBufferHalfop(modeBuf, chanuser->user->nick); + done_users++; + } + freeModeBuffer(modeBuf); + reply(textclient, user, "NS_HALFOPALL_DONE", done_users, chan->name); + if(done_users) + logEvent(event); +} diff --git a/src/cmd_neonserv_peek.c b/src/cmd_neonserv_peek.c index f32ce6c..51de508 100644 --- a/src/cmd_neonserv_peek.c +++ b/src/cmd_neonserv_peek.c @@ -53,10 +53,13 @@ static void neonserv_cmd_peek_async1(struct ClientSocket *client, struct ClientS getModeString(chan->modes, tmpStr); reply(textclient, user, "NS_PEEK_MODES", tmpStr); struct ChanUser *chanuser; - int op_count = 0, voice_count = 0, normal_count = 0, invi_count = 0; + int with_halfops = get_int_field("General.have_halfop"); + int op_count = 0, halfop_count = 0, voice_count = 0, normal_count = 0, invi_count = 0; for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { if(chanuser->flags & CHANUSERFLAG_OPPED) op_count++; + else if(with_halfops && (chanuser->flags & CHANUSERFLAG_HALFOPPED)) + halfop_count++; else if(chanuser->flags & CHANUSERFLAG_VOICED) voice_count++; else if(chanuser->flags & CHANUSERFLAG_INVISIBLE) @@ -64,7 +67,10 @@ static void neonserv_cmd_peek_async1(struct ClientSocket *client, struct ClientS else normal_count++; } - reply(textclient, user, "NS_PEEK_USERS", op_count+voice_count+invi_count+normal_count, op_count, voice_count, normal_count, invi_count); + if(with_halfops) + reply(textclient, user, "NS_PEEK_USERS_HALFOP", op_count+halfop_count+voice_count+invi_count+normal_count, op_count, halfop_count, voice_count, normal_count, invi_count); + else + reply(textclient, user, "NS_PEEK_USERS", op_count+voice_count+invi_count+normal_count, op_count, voice_count, normal_count, invi_count); int tmpStrPos = 0; int headerlen = 10 + strlen(user->nick); for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { diff --git a/src/cmd_neonserv_resync.c b/src/cmd_neonserv_resync.c index f2ee090..7c60a6e 100644 --- a/src/cmd_neonserv_resync.c +++ b/src/cmd_neonserv_resync.c @@ -77,26 +77,36 @@ static void neonserv_cmd_resync_async1(struct ClientSocket *client, struct Clien MYSQL_ROW row, defaults = NULL; int i; int resync_op = 1; + int with_halfop = get_int_field("General.have_halfop"); + int resync_halfop = with_halfop; int resync_voice = 1; if(usermask && usermask[0] == '@') { + resync_voice = 0; + resync_halfop = 0; + usermask++; + if(!*usermask) usermask = NULL; + } else if(usermask && with_halfop && usermask[0] == 'h') { + resync_op = 0; resync_voice = 0; usermask++; if(!*usermask) usermask = NULL; } else if(usermask && usermask[0] == '+') { resync_op = 0; + resync_halfop = 0; usermask++; if(!*usermask) usermask = NULL; } struct ChanUser *chanuser; - int db_enfops, db_enfvoice; - printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + int db_enfops, db_enfhalfop, db_enfvoice; + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice`, `channel_gethalfop` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); row = mysql_fetch_row(mysql_use()); if(row[0] == NULL || row[1] == NULL) { - printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_name` = 'defaults'"); + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice`, `channel_gethalfop` FROM `channels` WHERE `channel_name` = 'defaults'"); defaults = mysql_fetch_row(mysql_use()); } db_enfops = atoi((row[0] ? row[0] : defaults[0])); db_enfvoice = atoi((row[1] ? row[1] : defaults[1])); + db_enfhalfop = (with_halfop ? atoi((row[2] ? row[2] : defaults[2])) : 0); printf_mysql_query("SELECT `chanuser_access`, `user_user`, `chanuser_flags` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' ORDER BY `chanuser_access` DESC, `user_user` ASC", chan->channel_id); res = mysql_use(); char *db_users[mysql_num_rows(res)]; @@ -130,14 +140,23 @@ static void neonserv_cmd_resync_async1(struct ClientSocket *client, struct Clien if(caccess >= db_enfops) { if(!(chanuser->flags & CHANUSERFLAG_OPPED) && resync_op && (override_noautoop || !(cflags & DB_CHANUSER_NOAUTOOP))) modeBufferOp(modeBuf, chanuser->user->nick); + } else if(with_halfop && caccess >= db_enfhalfop) { + if((chanuser->flags & CHANUSERFLAG_OPPED) && resync_op && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) + modeBufferDeop(modeBuf, chanuser->user->nick); + if(!(chanuser->flags & CHANUSERFLAG_HALFOPPED) && resync_halfop && (override_noautoop || !(cflags & DB_CHANUSER_NOAUTOOP))) + modeBufferHalfop(modeBuf, chanuser->user->nick); } else if(caccess >= db_enfvoice) { if((chanuser->flags & CHANUSERFLAG_OPPED) && resync_op && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) modeBufferDeop(modeBuf, chanuser->user->nick); + if((chanuser->flags & CHANUSERFLAG_HALFOPPED) && resync_halfop && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) + modeBufferDehalfop(modeBuf, chanuser->user->nick); if(!(chanuser->flags & CHANUSERFLAG_VOICED) && resync_voice && (override_noautoop || !(cflags & DB_CHANUSER_NOAUTOOP))) modeBufferVoice(modeBuf, chanuser->user->nick); } else { if((chanuser->flags & CHANUSERFLAG_OPPED) && resync_op && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) modeBufferDeop(modeBuf, chanuser->user->nick); + if((chanuser->flags & CHANUSERFLAG_HALFOPPED) && resync_halfop && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) + modeBufferDehalfop(modeBuf, chanuser->user->nick); if((chanuser->flags & CHANUSERFLAG_VOICED) && resync_voice && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) modeBufferDevoice(modeBuf, chanuser->user->nick); } diff --git a/src/cmd_neonserv_set.c b/src/cmd_neonserv_set.c index f35dee9..2b3ddab 100644 --- a/src/cmd_neonserv_set.c +++ b/src/cmd_neonserv_set.c @@ -31,6 +31,7 @@ static char* neonserv_cmd_set_nodelete(struct ClientSocket *client, struct UserN #define NS_VALID_OPTIONS 0x10 #define NS_VALID_NUMERIC 0x20 #define NS_VALID_BOOLEAN 0x40 +#define NS_VALID_IF_HALFOP 0x80 #define NS_HAS_OPT 0x100 /* options (SET_OPTION_{NAME}_{VALUE}) */ #define NS_HAS_HELP 0x200 /* help (SET_HELP_{NAME}) - only shown if help is requested */ @@ -52,8 +53,10 @@ static const struct { {"MODES", "channel_modes", NS_VALID_FUNCTION, neonserv_cmd_set_modes}, {"INVITEME", "channel_getinvite", NS_VALID_ACCESS, NULL}, {"GIVEOPS", "channel_getop", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"GIVEHALFOPS", "channel_gethalfop", NS_VALID_ACCESS | NS_HAS_HELP | NS_VALID_IF_HALFOP, NULL}, {"GIVEVOICE", "channel_getvoice", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, {"ENFOPS", "channel_canop", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"ENFHALFOPS", "channel_canhalfop", NS_VALID_ACCESS | NS_HAS_HELP | NS_VALID_IF_HALFOP, NULL}, {"ENFVOICE", "channel_canvoice", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, {"KICK", "channel_cankick", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, {"BAN", "channel_canban", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, @@ -125,8 +128,9 @@ CMD_BIND(neonserv_cmd_set) { i = 0; j = 0; char *args = (argc > 1 ? merge_argv(argv, 1, argc) : NULL); + int with_halfops = get_int_field("General.have_halfop"); while(channel_settings[i].setting) { - if(!stricmp(channel_settings[i].setting, argv[0])) { + if(!stricmp(channel_settings[i].setting, argv[0]) && (!(channel_settings[i].valid & NS_VALID_IF_HALFOP) || with_halfops)) { //setting found if(channel_settings[i].valid & NS_VALID_FUNCTION) { neonserv_cmd_set_function *func = channel_settings[i].parameter; @@ -150,13 +154,19 @@ CMD_BIND(neonserv_cmd_set) { MYSQL_ROW row, defaults; struct Table *table; char *content[2]; + int with_halfops = get_int_field("General.have_halfop"); i = 0; + j = 0; while(channel_settings[i].setting) { + if((channel_settings[i].valid & NS_VALID_IF_HALFOP) && !with_halfops) { + j++; + continue; + } if(channel_settings[i].chanfield) querypos += sprintf(query + querypos, ", `%s`", channel_settings[i].chanfield); i++; } - table = table_init(2, i, 0); + table = table_init(2, i-j, 0); table_set_bold(table, 0, 1); printf_mysql_query("SELECT `channel_id` %s FROM `channels` WHERE `channel_name` = 'defaults'", query); defaults_res = mysql_use(); @@ -168,6 +178,10 @@ CMD_BIND(neonserv_cmd_set) { j = 0; reply(getTextBot(), user, "NS_SET_HEADER", chan->name); while(channel_settings[i].setting) { + if((channel_settings[i].valid & NS_VALID_IF_HALFOP) && !with_halfops) { + i++; + continue; + } if(channel_settings[i].chanfield) { j++; org_value = (row[j] ? row[j] : defaults[j]); diff --git a/src/commands.c b/src/commands.c index c7978d1..6412800 100644 --- a/src/commands.c +++ b/src/commands.c @@ -21,6 +21,7 @@ #include "cmd_funcmds.h" #include "cmd_neonhelp.h" #include "modcmd.h" +#include "ConfigParser.h" void register_commands() { @@ -88,6 +89,12 @@ void register_commands() { USER_COMMAND("deopall", neonserv_cmd_deopall, 0, "@#channel_getop,#channel_canop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); USER_COMMAND("voiceall", neonserv_cmd_voiceall, 0, "+#channel_getvoice,#channel_canvoice", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); USER_COMMAND("devoiceall", neonserv_cmd_devoiceall,0, "+#channel_getvoice,#channel_canvoice", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + if(get_int_field("General.have_halfop")) { + USER_COMMAND("halfop", neonserv_cmd_halfop, 1, "%#channel_gethalfop,#channel_canhalfop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("dehalfop", neonserv_cmd_dehalfop, 1, "%#channel_gethalfop,#channel_canhalfop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("halfopall", neonserv_cmd_halfopall, 0, "%#channel_gethalfop,#channel_canhalfop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("dehalfopall", neonserv_cmd_dehalfopall,0,"%#channel_gethalfop,#channel_canhalfop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + } USER_COMMAND("set", neonserv_cmd_set, 0, "#channel_setters", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); USER_COMMAND("kick", neonserv_cmd_kick, 1, "#channel_cankick", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); USER_COMMAND("kickban", neonserv_cmd_kickban, 1, "#channel_cankick,#channel_canban", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); diff --git a/src/event_neonserv_join.c b/src/event_neonserv_join.c index 84845f3..77d5de7 100644 --- a/src/event_neonserv_join.c +++ b/src/event_neonserv_join.c @@ -77,13 +77,14 @@ static void neonserv_event_join_async1(struct ClientSocket *client, struct ChanU struct ChanNode *chan = chanuser->chan; struct UserNode *user = chanuser->user; struct ModeBuffer *modeBuf; + int with_halfops = get_int_field("General.have_halfop"); MYSQL_RES *res; MYSQL_ROW row, chanuserrow, defaultrow = NULL; - printf_mysql_query("SELECT `channel_maxusers`, `channel_greeting`, `channel_usergreeting`, `channel_getop`, `channel_getvoice`, `channel_userinfo`, `channel_dynlimit` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + printf_mysql_query("SELECT `channel_maxusers`, `channel_greeting`, `channel_usergreeting`, `channel_getop`, `channel_getvoice`, `channel_userinfo`, `channel_dynlimit`, `channel_gethalfop` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); res = mysql_use(); if ((row = mysql_fetch_row(res)) == NULL) return; - if(!row[3] || !row[4] || !row[5]) { - printf_mysql_query("SELECT `channel_getop`, `channel_getvoice`, `channel_userinfo` FROM `channels` WHERE `channel_name` = 'defaults'"); + if(!row[3] || !row[4] || !row[5] || (!row[7] && with_halfops)) { + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice`, `channel_userinfo`, `channel_gethalfop` FROM `channels` WHERE `channel_name` = 'defaults'"); res = mysql_use(); defaultrow = mysql_fetch_row(res); } @@ -143,10 +144,13 @@ static void neonserv_event_join_async1(struct ClientSocket *client, struct ChanU //USER RIGHTS if(!(userflags & DB_CHANUSER_NOAUTOOP)) { int getop = atoi((row[3] ? row[3] : defaultrow[0])); + int gethalfop = (with_halfops ? atoi((row[7] ? row[7] : defaultrow[3])) : 0); int getvoice = atoi((row[4] ? row[4] : defaultrow[1])); modeBuf = initModeBuffer(client, chan); if(uaccess >= getop && uaccess != 0) { //we disallow auto op for all users modeBufferOp(modeBuf, user->nick); + } else if(with_halfops && uaccess >= gethalfop) { + modeBufferHalfop(modeBuf, user->nick); } else if(uaccess >= getvoice) { modeBufferVoice(modeBuf, user->nick); } diff --git a/src/event_neonserv_mode.c b/src/event_neonserv_mode.c index 5c87873..6813853 100644 --- a/src/event_neonserv_mode.c +++ b/src/event_neonserv_mode.c @@ -76,16 +76,17 @@ static void neonserv_event_mode_async1(struct ClientSocket *client, struct UserN MYSQL_ROW row, defaults = NULL; int i, arg, add = 1, skip = 0; unsigned int modetype; - int db_canop, db_canvoice, db_canban, db_enfmodes, db_getop, db_getvoice; + int db_canop, db_canhalfop, db_canvoice, db_canban, db_enfmodes, db_getop, db_gethalfop, db_getvoice; struct ModeNode *modelock = createModeNode(NULL); struct ModeBuffer *modeBuf; struct UserNode *cuser; struct ChanUser *chanuser; + int with_halfops = get_int_field("General.have_halfop"); modeBuf = initModeBuffer(client, chan); - printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes`, `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes`, `channel_getop`, `channel_getvoice`, `channel_gethalfop`, `channel_canhalfop` 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 || row[4] == NULL || row[5] == NULL || row[6] == NULL) { - printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes`, `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_name` = 'defaults'"); + if(row[0] == NULL || row[1] == NULL || row[2] == NULL || row[3] == NULL || row[4] == NULL || row[5] == NULL || row[6] == NULL || (row[7] == NULL && with_halfops) || (row[8] == NULL && with_halfops)) { + printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes`, `channel_getop`, `channel_getvoice`, `channel_gethalfop`, `channel_canhalfop` FROM `channels` WHERE `channel_name` = 'defaults'"); defaults = mysql_fetch_row(mysql_use()); } db_canop = atoi((row[0] ? row[0] : defaults[0])); @@ -94,6 +95,8 @@ static void neonserv_event_mode_async1(struct ClientSocket *client, struct UserN db_enfmodes = atoi((row[3] ? row[3] : defaults[3])); db_getop = atoi((row[5] ? row[5] : defaults[5])); db_getvoice = atoi((row[6] ? row[6] : defaults[6])); + db_gethalfop = (with_halfops ? atoi((row[7] ? row[7] : defaults[7])) : 0); + db_canhalfop = (with_halfops ? atoi((row[8] ? row[8] : defaults[8])) : 0); if(row[4]) parseModeString(modelock, row[4]); else if(defaults[4]) @@ -114,6 +117,7 @@ static void neonserv_event_mode_async1(struct ClientSocket *client, struct UserN add = 0; break; case 'o': + case 'h': case 'v': if(arg == argc) { break; @@ -128,7 +132,7 @@ static void neonserv_event_mode_async1(struct ClientSocket *client, struct UserN //someone deopped the bot??? requestOp(client->user, chan); } - if(modes[i] == 'o' && !(add && isBot(cuser))) { + if((modes[i] == 'o' || (modes[i] == 'h' && !with_halfops)) && !(add && isBot(cuser))) { if(uaccess < db_canop) { reply(textclient, user, "NS_MODE_ENFOPS", chan->name); db_canop = -1; @@ -140,7 +144,19 @@ static void neonserv_event_mode_async1(struct ClientSocket *client, struct UserN modeBufferSet(modeBuf, !add, modes[i], carg); break; } - } else if(modes[i] == 'v') { + } else if(modes[i] == 'h' && with_halfops && !(add && isBot(cuser))) { + if(uaccess < db_canhalfop) { + reply(textclient, user, "NS_MODE_ENFOPS", chan->name); + db_canhalfop = -1; + if(uaccess < db_gethalfop) + deop_user = 1; + } + if(db_canhalfop == -1 && caccess < db_gethalfop) { + if(!neonserv_cmd_mode_botwar_detect(client, user, chan)) + modeBufferSet(modeBuf, !add, modes[i], carg); + break; + } + } else if(modes[i] == 'v' && !(add && isBot(cuser))) { if(uaccess < db_canvoice) { reply(textclient, user, "NS_MODE_ENFVOICE", chan->name); db_canvoice = -1; @@ -301,13 +317,14 @@ static int neonserv_cmd_mode_botwar_detect(struct ClientSocket *client, struct U //BOTWAR! chanuser->changeTime = time(0); if(chanuser->chageEvents > 0) { - putsock(client, "NOTICE @%s :%s %s", chan->name, get_language_string(user, "NS_BOTWAR_DETECTED"), (BOTWAR_ALERT_CHAN ? get_language_string(user, "NS_BOTWAR_REPORTED") : "")); - if(BOTWAR_ALERT_CHAN) { - struct ChanNode *alertchan = getChanByName(BOTWAR_ALERT_CHAN); + char *alertchan = get_string_field("General.alertchan"); + putsock(client, "NOTICE @%s :%s %s", chan->name, get_language_string(user, "NS_BOTWAR_DETECTED"), (alertchan ? get_language_string(user, "NS_BOTWAR_REPORTED") : "")); + if(alertchan) { + struct ChanNode *alertchan_chan = getChanByName(alertchan); struct ClientSocket *alertclient; - if(alertchan && (alertclient = getBotForChannel(chan)) != NULL) { + if(alertchan_chan && (alertclient = getBotForChannel(chan)) != NULL) { char msgBuf[MAXLEN]; - putsock(alertclient, "PRIVMSG %s :%s", alertchan->name, build_language_string(NULL, msgBuf, "NS_BOTWAR_ALERT", chan->name, user->nick)); + putsock(alertclient, "PRIVMSG %s :%s", alertchan_chan->name, build_language_string(NULL, msgBuf, "NS_BOTWAR_ALERT", chan->name, user->nick)); } } } diff --git a/src/event_neonserv_topic.c b/src/event_neonserv_topic.c index 304ebea..577e0a3 100644 --- a/src/event_neonserv_topic.c +++ b/src/event_neonserv_topic.c @@ -90,13 +90,14 @@ static void neonserv_event_topic_async1(struct ClientSocket *client, struct User //BOTWAR! chanuser->changeTime = time(0); if(chanuser->chageEvents > 0) { - putsock(client, "NOTICE @%s :%s %s", chan->name, get_language_string(user, "NS_BOTWAR_DETECTED"), (BOTWAR_ALERT_CHAN ? get_language_string(user, "NS_BOTWAR_REPORTED") : "")); - if(BOTWAR_ALERT_CHAN) { - struct ChanNode *alertchan = getChanByName(BOTWAR_ALERT_CHAN); + char *alertchan = get_string_field("General.alertchan"); + putsock(client, "NOTICE @%s :%s %s", chan->name, get_language_string(user, "NS_BOTWAR_DETECTED"), (alertchan ? get_language_string(user, "NS_BOTWAR_REPORTED") : "")); + if(alertchan) { + struct ChanNode *alertchan_chan = getChanByName(alertchan); struct ClientSocket *alertclient; - if(alertchan && (alertclient = getBotForChannel(chan)) != NULL) { + if(alertchan_chan && (alertclient = getBotForChannel(chan)) != NULL) { char msgBuf[MAXLEN]; - putsock(alertclient, "PRIVMSG %s :%s", alertchan->name, build_language_string(NULL, msgBuf, "NS_BOTWAR_ALERT", chan->name, user->nick)); + putsock(alertclient, "PRIVMSG %s :%s", alertchan_chan->name, build_language_string(NULL, msgBuf, "NS_BOTWAR_ALERT", chan->name, user->nick)); } } } diff --git a/src/main.h b/src/main.h index 21a978c..bc2ff72 100644 --- a/src/main.h +++ b/src/main.h @@ -21,10 +21,6 @@ #define NEONSERV_VERSION "5.3" #define VERSION_PATCHLEVEL 543 -#ifndef BOTWAR_ALERT_CHAN -#define BOTWAR_ALERT_CHAN NULL -#endif - #include #include #include diff --git a/src/mysqlConn.c b/src/mysqlConn.c index a651b16..b82d317 100644 --- a/src/mysqlConn.c +++ b/src/mysqlConn.c @@ -16,7 +16,7 @@ */ #include "mysqlConn.h" -#define DATABASE_VERSION "13" +#define DATABASE_VERSION "14" struct used_result { MYSQL_RES *result; diff --git a/src/tools.h b/src/tools.h index 29cd337..7be2e0d 100644 --- a/src/tools.h +++ b/src/tools.h @@ -68,6 +68,8 @@ struct ModeBuffer* initModeBuffer(struct ClientSocket *client, struct ChanNode * #define modeBufferSimpleMode(MODEBUF,ADD,MODE) modeBufferSet(MODEBUF, ADD, MODE, NULL) #define modeBufferOp(MODEBUF,USER) modeBufferSet(MODEBUF, 1, 'o', USER) #define modeBufferDeop(MODEBUF,USER) modeBufferSet(MODEBUF, 0, 'o', USER) +#define modeBufferHalfop(MODEBUF,USER) modeBufferSet(MODEBUF, 1, 'h', USER) +#define modeBufferDehalfop(MODEBUF,USER) modeBufferSet(MODEBUF, 0, 'h', USER) #define modeBufferVoice(MODEBUF,USER) modeBufferSet(MODEBUF, 1, 'v', USER) #define modeBufferDevoice(MODEBUF,USER) modeBufferSet(MODEBUF, 0, 'v', USER) #define modeBufferBan(MODEBUF,MASK) modeBufferSet(MODEBUF, 1, 'b', MASK)