From 2ac843a40dfa151e8ccc12097e96b52ae1ae0826 Mon Sep 17 00:00:00 2001 From: pk910 Date: Sat, 27 Aug 2011 16:34:53 +0200 Subject: [PATCH] added cmd_kick, cmd_kickban & all the functions depending on --- ChanNode.c | 1 + ChanNode.h | 1 + ChanUser.c | 3 + IRCParser.c | 11 ++++ IRCParser.h | 1 + UserNode.c | 13 ++++ UserNode.h | 1 + bot_NeonServ.c | 11 +++- cmd_neonserv_deop.c | 2 +- cmd_neonserv_deopall.c | 2 +- cmd_neonserv_kick.c | 131 +++++++++++++++++++++++++++++++++++++++ cmd_neonserv_kickban.c | 136 +++++++++++++++++++++++++++++++++++++++++ tools.c | 40 ++++++++++++ tools.h | 5 ++ 14 files changed, 354 insertions(+), 4 deletions(-) create mode 100644 cmd_neonserv_kick.c create mode 100644 cmd_neonserv_kickban.c diff --git a/ChanNode.c b/ChanNode.c index 14ab084..e00acb6 100644 --- a/ChanNode.c +++ b/ChanNode.c @@ -156,6 +156,7 @@ struct ChanNode* addChannel(const char *name) { } strcpy(chan->name, name); chan->user = NULL; + chan->usercount = 0; chan->chanbot = NULL; chan->topic[0] = 0; chan->flags = 0; diff --git a/ChanNode.h b/ChanNode.h index c93625c..8b8808c 100644 --- a/ChanNode.h +++ b/ChanNode.h @@ -13,6 +13,7 @@ struct ChanNode { char name[CHANNELLEN+1]; char topic[TOPICLEN+1]; struct ChanUser *user; + unsigned int usercount; unsigned char flags; unsigned int modes; char **mode_str_args; diff --git a/ChanUser.c b/ChanUser.c index 9c98d46..69fc006 100644 --- a/ChanUser.c +++ b/ChanUser.c @@ -16,6 +16,7 @@ struct ChanUser* addChanUser(struct ChanNode *chan, struct UserNode *user) { chanuser->next_user = chan->user; chan->user = chanuser; + chan->usercount++; chanuser->next_chan = user->channel; user->channel = chanuser; @@ -74,6 +75,7 @@ void delChanUser(struct ChanUser *chanuser, int freeChanUser) { last = NULL; for(cchanuser = chanuser->chan->user; cchanuser; cchanuser = cchanuser->next_user) { if(cchanuser == chanuser) { + chanuser->chan->usercount--; if(last) last->next_user = chanuser->next_user; else @@ -98,6 +100,7 @@ void removeChanUserFromLists(struct ChanUser *chanuser, int remove_from_userlist last = NULL; for(cchanuser = chanuser->chan->user; cchanuser; cchanuser = cchanuser->next_user) { if(cchanuser == chanuser) { + chanuser->chan->usercount--; if(last) last->next_user = chanuser->next_user; else diff --git a/IRCParser.c b/IRCParser.c index ccfc67a..12fca57 100644 --- a/IRCParser.c +++ b/IRCParser.c @@ -390,3 +390,14 @@ void reply(struct ClientSocket *client, struct UserNode *user, const char *text, write_socket(client, sendBuf, pos+1); } +char* merge_argv(char **argv, int start, int end) { + int i; + char *p = NULL; + for(i = start; i < end; i++) { + p = argv[i]; + while(*p) p++; + *p = ' '; + } + if(p) *p = '\0'; + return argv[start]; +} diff --git a/IRCParser.h b/IRCParser.h index 5d21687..d0ab8d6 100644 --- a/IRCParser.h +++ b/IRCParser.h @@ -20,5 +20,6 @@ void bot_disconnect(struct ClientSocket *client); void init_parser(); void free_parser(); void reply(struct ClientSocket *client, struct UserNode *user, const char *text, ...); +char* merge_argv(char **argv, int start, int end); #endif \ No newline at end of file diff --git a/UserNode.c b/UserNode.c index 5b6c9c1..e857cb7 100644 --- a/UserNode.c +++ b/UserNode.c @@ -101,6 +101,19 @@ struct UserNode* searchUserByNick(const char *nick) { //case insensitive return NULL; } +int countUsersWithHost(char *host) { + int i, count = 0; + struct UserNode *user; + for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN+1; i++) { + for(user = userList[i]; user; user = user->next) { + if(!strcmp(user->host, host)) { + count++; + } + } + } + return count; +} + struct UserNode* addUser(const char *nick) { int userListIndex = get_nicklist_entry(*nick); if(userListIndex == -1 || !is_valid_nick(nick)) diff --git a/UserNode.h b/UserNode.h index 6347ef5..b98c063 100644 --- a/UserNode.h +++ b/UserNode.h @@ -36,6 +36,7 @@ void free_UserNode(); int is_valid_nick(const char *nick); struct UserNode* getUserByNick(const char *nick); struct UserNode* getUserByMask(const char *mask); +int countUsersWithHost(char *host); struct UserNode* searchUserByNick(const char *nick); struct UserNode* addUser(const char *nick); struct UserNode* addUserMask(const char *mask); diff --git a/bot_NeonServ.c b/bot_NeonServ.c index 53d8bbe..ad5d544 100644 --- a/bot_NeonServ.c +++ b/bot_NeonServ.c @@ -97,6 +97,11 @@ static const struct default_language_entry msgtab[] = { {"NS_VOICEALL_DONE", "Voiced \002%d\002 users in %s."}, {"NS_DEOPALL_DONE", "Deopped \002%d\002 users in %s."}, {"NS_DEVOICEALL_DONE", "Devoiced \002%d\002 users in %s."}, + {"NS_KICK_DONE", "Kicked \002%d\002 users from %s"}, + {"NS_KICK_FAIL", "\002%s\002 could not kick some of the nicks you provided."}, + {"NS_KICKBAN_DONE", "KickBanned \002%d\002 users from %s"}, + {"NS_KICKBAN_FAIL", "\002%s\002 could not kickban some of the nicks you provided."}, + {"NS_LAME_MASK", "\002%s\002 is a little too general. Try making it more specific."}, {NULL, NULL} }; @@ -127,8 +132,8 @@ INCLUDE ALL CMD's HERE #include "cmd_neonserv_devoice.c" #include "cmd_neonserv_devoiceall.c" //#include "cmd_neonserv_uset.c" -//#include "cmd_neonserv_kick.c" -//#include "cmd_neonserv_kickban.c" +#include "cmd_neonserv_kick.c" +#include "cmd_neonserv_kickban.c" //#include "cmd_neonserv_ban.c" //#include "cmd_neonserv_unban.c" //#include "cmd_neonserv_unbanall.c" @@ -274,6 +279,8 @@ void init_NeonServ() { register_command(BOTID, "voiceall", neonserv_cmd_voiceall, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH, "#channel_canvoice", 0); register_command(BOTID, "devoiceall", neonserv_cmd_devoiceall, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH, "#channel_canvoice", 0); register_command(BOTID, "set", neonserv_cmd_set, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH, "#channel_setters", 0); + register_command(BOTID, "kick", neonserv_cmd_kick, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH, "#channel_cankick", 0); + register_command(BOTID, "kickban", neonserv_cmd_kickban, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH, "#channel_cankick,#channel_canban", 0); start_bots(); bind_bot_ready(neonserv_bot_ready); diff --git a/cmd_neonserv_deop.c b/cmd_neonserv_deop.c index 993a011..65b42fc 100644 --- a/cmd_neonserv_deop.c +++ b/cmd_neonserv_deop.c @@ -36,7 +36,7 @@ static CMD_BIND(neonserv_cmd_deop) { } static USERLIST_CALLBACK(neonserv_cmd_deop_userlist_lookup) { - struct neonserv_cmd_kick_cache *cache = data; + struct neonserv_cmd_deop_cache *cache = data; neonserv_cmd_deop_async1(cache->client, cache->textclient, cache->user, chan, cache->argv, cache->argc); int i; for(i = 0; i < cache->argc; i++) { diff --git a/cmd_neonserv_deopall.c b/cmd_neonserv_deopall.c index 1f1f390..c6f044c 100644 --- a/cmd_neonserv_deopall.c +++ b/cmd_neonserv_deopall.c @@ -36,7 +36,7 @@ static CMD_BIND(neonserv_cmd_deopall) { } static USERLIST_CALLBACK(neonserv_cmd_deopall_userlist_lookup) { - struct neonserv_cmd_kick_cache *cache = data; + struct neonserv_cmd_deopall_cache *cache = data; neonserv_cmd_deopall_async1(cache->client, cache->textclient, cache->user, chan, cache->argv, cache->argc); int i; for(i = 0; i < cache->argc; i++) { diff --git a/cmd_neonserv_kick.c b/cmd_neonserv_kick.c new file mode 100644 index 0000000..4ff4466 --- /dev/null +++ b/cmd_neonserv_kick.c @@ -0,0 +1,131 @@ + +/* +* argv[0] nick[,*auth[,*!*@mask[...]]] +* argv[1-*] reason +*/ +static USERLIST_CALLBACK(neonserv_cmd_kick_userlist_lookup); +static void neonserv_cmd_kick_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *nicks, char *reason); + +struct neonserv_cmd_kick_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + char *nicks; + char *reason; +}; + +static CMD_BIND(neonserv_cmd_kick) { + struct neonserv_cmd_kick_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + perror("malloc() failed"); + return; + } + cache->client = client; + cache->textclient = getTextBot(); + cache->user = user; + cache->nicks = strdup(argv[0]); + if(argc > 1) { + cache->reason = strdup(merge_argv(argv, 1, argc)); + } else + cache->reason = NULL; + get_userlist(chan, neonserv_cmd_kick_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_kick_userlist_lookup) { + struct neonserv_cmd_kick_cache *cache = data; + neonserv_cmd_kick_async1(cache->client, cache->textclient, cache->user, chan, cache->nicks, (cache->reason ? cache->reason : "Bye.")); + free(cache->nicks); + if(cache->reason) + free(cache->reason); + free(cache); +} + +static void neonserv_cmd_kick_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *nicks, char *reason) { + int i, kicked_users = 0, provided_nicks = 0; + char *nick, *nextnick; + struct UserNode *cuser; + struct ChanUser *chanuser; + nextnick = nicks; + check_mysql(); + while((nick = nextnick)) { + nextnick = strstr(nick, ","); + if(nextnick) { + *nextnick = '\0'; + nextnick++; + } + if(!*nick) continue; + if(is_ircmask(nick)) { + //KICK HOSTMASK + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + struct ChanUser *kick_chanuser[chan->usercount]; + int kick_chanuser_pos = 0; + for(chanuser = getChannelUsers(chan, NULL); NULL; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(nick, usermask)) { + provided_nicks++; + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + continue; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + continue; + } + kick_chanuser[kick_chanuser_pos++] = chanuser; + if(kick_chanuser_pos > 4 && (kick_chanuser_pos * 3) > chan->usercount && !isGodMode(user)) { + kick_chanuser_pos = 0; + reply(textclient, user, "NS_LAME_MASK", nick); + break; + } + } + } + for(i = 0; i < kick_chanuser_pos; i++) { + kicked_users++; + putsock(client, "KICK %s %s :%s", chan->name, kick_chanuser[i]->user->nick, reason); + } + } else if(*nick == '*') { + //KICK AUTH + nick++; + cuser = NULL; + for(chanuser = getChannelUsers(chan, NULL); NULL; chanuser = getChannelUsers(chan, chanuser)) { + if((chanuser->user->flags & USERFLAG_ISAUTHED) && !stricmp(chanuser->user->auth, nick)) { + provided_nicks++; + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + continue; + } + if(!cuser) { + //check if the user is protected + if(isUserProtected(chan, chanuser->user, user)) { + reply(textclient, user, "NS_USER_PROTECTED", chanuser->user->nick); + break; //all other users are also protected... + } + cuser = chanuser->user; + } + kicked_users++; + putsock(client, "KICK %s %s :%s", chan->name, cuser->nick, reason); + } + } + } else { + provided_nicks++; + cuser = searchUserByNick(nick); + 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; + } + kicked_users++; + putsock(client, "KICK %s %s :%s", chan->name, cuser->nick, reason); + } + } + if(kicked_users == provided_nicks) + reply(getTextBot(), user, "NS_KICK_DONE", kicked_users, chan->name); + else + reply(getTextBot(), user, "NS_KICK_FAIL", client->user->nick); +} diff --git a/cmd_neonserv_kickban.c b/cmd_neonserv_kickban.c new file mode 100644 index 0000000..4ed5ce0 --- /dev/null +++ b/cmd_neonserv_kickban.c @@ -0,0 +1,136 @@ + +/* +* argv[0] nick[,*auth[,*!*@mask[...]]] +* argv[1-*] reason +*/ +static USERLIST_CALLBACK(neonserv_cmd_kickban_userlist_lookup); +static void neonserv_cmd_kickban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *nicks, char *reason); + +struct neonserv_cmd_kickban_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + char *nicks; + char *reason; +}; + +static CMD_BIND(neonserv_cmd_kickban) { + struct neonserv_cmd_kickban_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + perror("malloc() failed"); + return; + } + cache->client = client; + cache->textclient = getTextBot(); + cache->user = user; + cache->nicks = strdup(argv[0]); + if(argc > 1) { + cache->reason = strdup(merge_argv(argv, 1, argc)); + } else + cache->reason = NULL; + get_userlist(chan, neonserv_cmd_kickban_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_kickban_userlist_lookup) { + struct neonserv_cmd_kickban_cache *cache = data; + neonserv_cmd_kickban_async1(cache->client, cache->textclient, cache->user, chan, cache->nicks, (cache->reason ? cache->reason : "Bye.")); + free(cache->nicks); + if(cache->reason) + free(cache->reason); + free(cache); +} + +static void neonserv_cmd_kickban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *nicks, char *reason) { + int i, kicked_users = 0, provided_nicks = 0; + char *nick, *nextnick; + struct UserNode *cuser; + struct ChanUser *chanuser; + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + nextnick = nicks; + check_mysql(); + while((nick = nextnick)) { + nextnick = strstr(nick, ","); + if(nextnick) { + *nextnick = '\0'; + nextnick++; + } + if(!*nick) continue; + if(is_ircmask(nick)) { + //KICK HOSTMASK + struct ChanUser *kickban_chanuser[chan->usercount]; + int kick_chanuser_pos = 0; + for(chanuser = getChannelUsers(chan, NULL); NULL; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(nick, usermask)) { + provided_nicks++; + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + continue; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + continue; + } + kickban_chanuser[kick_chanuser_pos++] = chanuser; + if(kick_chanuser_pos > 4 && (kick_chanuser_pos * 3) > chan->usercount && !isGodMode(user)) { + kick_chanuser_pos = 0; + reply(textclient, user, "NS_LAME_MASK", nick); + break; + } + } + } + for(i = 0; i < kick_chanuser_pos; i++) { + if(i == 0) { + putsock(client, "MODE %s +b %s", chan->name, nick); + } + kicked_users++; + putsock(client, "KICK %s %s :%s", chan->name, kickban_chanuser[i]->user->nick, reason); + } + } else if(*nick == '*') { + //KICK AUTH + nick++; + cuser = NULL; + for(chanuser = getChannelUsers(chan, NULL); NULL; chanuser = getChannelUsers(chan, chanuser)) { + if((chanuser->user->flags & USERFLAG_ISAUTHED) && !stricmp(chanuser->user->auth, nick)) { + provided_nicks++; + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + continue; + } + if(!cuser) { + //check if the user is protected + if(isUserProtected(chan, chanuser->user, user)) { + reply(textclient, user, "NS_USER_PROTECTED", chanuser->user->nick); + break; //all other users are also protected... + } + cuser = chanuser->user; + } + kicked_users++; + putsock(client, "MODE %s +b %s", chan->name, generate_banmask(cuser, usermask)); + putsock(client, "KICK %s %s :%s", chan->name, cuser->nick, reason); + } + } + } else { + provided_nicks++; + cuser = searchUserByNick(nick); + 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; + } + kicked_users++; + putsock(client, "MODE %s +b %s", chan->name, generate_banmask(cuser, usermask)); + putsock(client, "KICK %s %s :%s", chan->name, cuser->nick, reason); + } + } + if(kicked_users == provided_nicks) + reply(getTextBot(), user, "NS_KICKBAN_DONE", kicked_users, chan->name); + else + reply(getTextBot(), user, "NS_KICKBAN_FAIL", client->user->nick); +} diff --git a/tools.c b/tools.c index 2741872..938bf79 100644 --- a/tools.c +++ b/tools.c @@ -370,6 +370,46 @@ void freeModeBuffer(struct ModeBuffer *modeBuf) { free(modeBuf); } +int is_ircmask(const char *text) { + while (*text && (isalnum((char)*text) || strchr("-_[]|\\`^{}?*", *text))) + text++; + if (*text++ != '!') + return 0; + while (*text && *text != '@' && !isspace((char)*text)) + text++; + if (*text++ != '@') + return 0; + while (*text && !isspace((char)*text)) + text++; + return !*text; +} + +char* generate_banmask(struct UserNode *user, char *buffer) { + char *userhost = user->host; + char *p1, *p2 = userhost; + + //find the last dot to identify if the hostmask is a hidden host + while((p1 = strstr(userhost, "."))) { + p2 = p1; + } + //TLD database: http://www.iana.org/domains/root/db/ + //the longest TLD i found was 6 chars long (ignoring the stange exotic ones :D) + //but we even ignore '.museum' and '.travel' so we can say that the TLD of our mask needs to be less than 4 chars to be a real domain + if(strlen(p2+1) > 4) { + sprintf(buffer, "*!*@%s", userhost); + return buffer; + } + + //check if the hostname has more than 4 connections (trusted host) + if(countUsersWithHost(userhost) > 4) { + sprintf(buffer, "*!%s@%s", user->ident, userhost); + return buffer; + } else { + sprintf(buffer, "*!*@%s", userhost); + return buffer; + } +} + void init_tools() { register_default_language_table(msgtab); } diff --git a/tools.h b/tools.h index d589ade..03bdb64 100644 --- a/tools.h +++ b/tools.h @@ -51,10 +51,15 @@ struct ModeBuffer* initModeBuffer(struct ClientSocket *client, struct ChanNode * #define modeBufferDeop(MODEBUF,USER) modeBufferSet(MODEBUF, 0, 'o', 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) void modeBufferSet(struct ModeBuffer *modeBuf, int add, char mode, char *param); void flushModeBuffer(struct ModeBuffer *modeBuf); void freeModeBuffer(struct ModeBuffer *modeBuf); +int is_ircmask(const char *text); + +char* generate_banmask(struct UserNode *user, char *buffer); + void init_tools(); #endif \ No newline at end of file -- 2.20.1