From 24dba98b5136867bd06d4d5397e11456ccc9aed2 Mon Sep 17 00:00:00 2001 From: pk910 Date: Sat, 10 Dec 2011 08:53:21 +0100 Subject: [PATCH] added automatic multi-bot rejoin for opless channels --- src/ChanNode.c | 5 ++- src/ChanNode.h | 5 ++- src/IRCParser.c | 87 +++++++++++++++++++++++++++++++++++++-- src/bots.c | 18 ++++++++ src/bots.h | 1 + src/event_neonserv_join.c | 5 ++- src/event_neonspam_join.c | 5 +++ 7 files changed, 117 insertions(+), 9 deletions(-) diff --git a/src/ChanNode.c b/src/ChanNode.c index bafc3bd..a24de3a 100644 --- a/src/ChanNode.c +++ b/src/ChanNode.c @@ -229,12 +229,12 @@ void freeChanNode(struct ChanNode* chan) { free(chan); } -void checkChannelVisibility(struct ChanNode* chan) { +int checkChannelVisibility(struct ChanNode* chan) { struct ChanUser *chanuser, *next; for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { if(chanuser->user->flags & USERFLAG_ISBOT) { chan->chanbot = chanuser->user; - return; + return 1; } } //free the channel... @@ -250,4 +250,5 @@ void checkChannelVisibility(struct ChanNode* chan) { } chan->user = NULL; delChannel(chan, 1); + return 0; } diff --git a/src/ChanNode.h b/src/ChanNode.h index 9cb67e3..74e50a8 100644 --- a/src/ChanNode.h +++ b/src/ChanNode.h @@ -27,6 +27,7 @@ struct NeonSpamSettings; #define CHANFLAG_REQUESTED_CHANINFO 0x02 #define CHANFLAG_CHAN_REGISTERED 0x04 #define CHANFLAG_HAVE_INVISIBLES 0x08 +#define CHANFLAG_REJOINING 0x10 struct ChanNode { char name[CHANNELLEN+1]; @@ -43,6 +44,8 @@ struct ChanNode { struct NeonSpamSettings *spam_settings; + void *rejoin_array; + struct ChanNode *next; }; @@ -57,6 +60,6 @@ int getChanUserCount(); int getChanBanCount(); void delChannel(struct ChanNode* chan, int freeChan); void freeChanNode(struct ChanNode* chan); -void checkChannelVisibility(struct ChanNode* chan); +int checkChannelVisibility(struct ChanNode* chan); #endif \ No newline at end of file diff --git a/src/IRCParser.c b/src/IRCParser.c index b757f5d..9b91d21 100644 --- a/src/IRCParser.c +++ b/src/IRCParser.c @@ -213,10 +213,58 @@ static IRC_CMD(raw_join) { if(wasRegistering) user->flags &= ~USERFLAG_WAS_REGISTRING; } + if(!(user->flags & USERFLAG_ISBOT) && (chan->flags & CHANFLAG_REJOINING)) { + //ABORT REJOIN (security break) + struct ClientSocket **clients = chan->rejoin_array; + while(*clients) { + putsock(*clients, "JOIN %s", chan->name); + clients++; + } + free(chan->rejoin_array); + chan->flags &= ~CHANFLAG_REJOINING; + } + } else if(chan->usercount == 1 && isUserOnChan(user, chan)) { + //first bot rejoined + struct ChanUser *chanuser = getChanUser(user, chan); + chanuser->flags &= ~CHANUSERFLAG_VOICED; + chanuser->flags |= CHANUSERFLAG_OPPED; } return 1; } +static void check_full_rejoin(struct ChanNode *chan) { + struct ChanUser *chanuser; + char do_rejoin = 1; + int botcount = 0; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if((chanuser->flags & CHANUSERFLAG_OPPED) || !(chanuser->user->flags & USERFLAG_ISBOT)) { + do_rejoin = 0; + break; + } + if((chanuser->user->flags & USERFLAG_ISBOT)) + botcount++; + } + if(do_rejoin) { + struct ClientSocket **clients = calloc(botcount, sizeof(*clients)); + struct ClientSocket *bot, *chanbot; + int i = 0; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->user != chan->chanbot && isUserOnChan(bot->user, chan)) { + clients[i++] = bot; + putsock(bot, "PART %s :rejoining", chan->name); + } else if(bot->user == chan->chanbot) + chanbot = bot; + } + chan->flags |= CHANFLAG_REJOINING; + chan->rejoin_array = clients; + if(botcount == 1) { + //we're alone + putsock(chanbot, "PART %s :magic hop", chan->name); + putsock(chanbot, "JOIN %s", chan->name); + } + } +} + static IRC_CMD(raw_part) { if(from == NULL || argc < 1) return 0; struct UserNode *user = getUserByMask(from); @@ -224,20 +272,38 @@ static IRC_CMD(raw_part) { struct ChanNode *chan = getChanByName(argv[0]); if(chan == NULL) return 0; if(chan->chanbot != client->user) return 1; //we ignore it - but it's not a parse error - if(isUserOnChan(user, chan) && (chan->flags & CHANFLAG_RECEIVED_USERLIST)) { + int keep_channel = 1; + if(chan->chanbot == user && (chan->flags & CHANFLAG_REJOINING)) { + struct ClientSocket **clients = chan->rejoin_array; + while(*clients) { + putsock(*clients, "JOIN %s", chan->name); + clients++; + } + free(chan->rejoin_array); + chan->flags &= ~CHANFLAG_REJOINING; + return 0; + } else if(isUserOnChan(user, chan) && (chan->flags & CHANFLAG_RECEIVED_USERLIST)) { struct ChanUser *chanuser = getChanUser(user, chan); delChanUser(chanuser, 0); //we need to free the chanuser manually! event_part(chanuser, (argc > 1 ? argv[1] : NULL)); freeChanUser(chanuser); if(chan->chanbot == user) { //check if theres another bot in the channel - otherwise free it - checkChannelVisibility(chan); + keep_channel = checkChannelVisibility(chan); } } if(user->channel == NULL && !(user->flags & USERFLAG_ISBOT)) { //remove the user delUser(user, 1); } + if(keep_channel && (chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING)) { + check_full_rejoin(chan); + } + else if(keep_channel && (chan->flags & CHANFLAG_REJOINING) && chan->usercount == 1) { + //bot is alone... rejoin! + putsock(client, "PART %s :magic hop", chan->name); + putsock(client, "JOIN %s", chan->name); + } return 1; } @@ -246,6 +312,7 @@ static IRC_CMD(raw_quit) { struct UserNode *user = getUserByMask(from); if(user == NULL) return 0; if(!is_firstBotSeeUser(client, user)) return 1; //we ignore it - but it's not a parse error + int keep_channel = 1; int registering = !stricmp(argv[0], "Registered"); if((registering && (user->flags & USERFLAG_ISBOT))) return 1; //bot is registering - just ignore it delUser(user, 0); //a little bit crazy, but we want to delete the user on the channel's userlists - but not the users channel list @@ -256,7 +323,7 @@ static IRC_CMD(raw_quit) { for(chanuser = getUserChannels(user, NULL); chanuser; chanuser = next) { next = getUserChannels(user, chanuser); if(chanuser->chan->chanbot == user) - checkChannelVisibility(chanuser->chan); + keep_channel = checkChannelVisibility(chanuser->chan); } //search the user representing the bot in the world of IRC struct ClientSocket *bot; @@ -266,6 +333,14 @@ static IRC_CMD(raw_quit) { break; } } + } else if(!registering) { + struct ChanUser *chanuser; + struct ChanNode *chan; + for(chanuser = user->channel; chanuser; chanuser = chanuser->next_chan) { + chan = chanuser->chan; + if((chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING)) + check_full_rejoin(chan); + } } if(registering && !(user->flags & USERFLAG_ISBOT)) { user->next = registering_users; @@ -312,6 +387,7 @@ static IRC_CMD(raw_kick) { struct ChanNode *chan = getChanByName(argv[0]); if(chan == NULL || target == NULL) return 0; if(chan->chanbot != client->user) return 1; //we ignore it - but it's not a parse error + int keep_channel = 1; if(isUserOnChan(target, chan) && (chan->flags & CHANFLAG_RECEIVED_USERLIST)) { if(user == NULL) { user = createTempUser(from); @@ -322,7 +398,7 @@ static IRC_CMD(raw_kick) { event_kick(user, chanuser, argv[1]); if(chanuser->chan->chanbot == user) { //check if theres another bot in the channel - otherwise free it - checkChannelVisibility(chan); + keep_channel = checkChannelVisibility(chan); } freeChanUser(chanuser); } @@ -330,6 +406,9 @@ static IRC_CMD(raw_kick) { //remove the user delUser(target, 1); } + if(keep_channel && (chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING)) { + check_full_rejoin(chan); + } return 1; } diff --git a/src/bots.c b/src/bots.c index b30b533..aece825 100644 --- a/src/bots.c +++ b/src/bots.c @@ -94,6 +94,24 @@ struct ClientSocket *getChannelBot(struct ChanNode *chan, int botid) { return use_bot; } +void requestOp(struct UserNode *user, struct ChanNode *chan) { + struct ClientSocket *bot; + struct ChanUser *chanuser = getChanUser(user, chan); + char opped = 0; + if(!chanuser) return; + if((chanuser->flags & CHANUSERFLAG_OPPED)) return; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if((chanuser = getChanUser(bot->user, chan)) != NULL && (chanuser->flags & CHANUSERFLAG_OPPED)) { + opped = 1; + putsock(bot, "MODE %s +o %s", chan->name, user->nick); + break; + } + } + if(!opped) { + //self op? + } +} + TIMEQ_CALLBACK(channel_ban_timeout) { char *str_banid = data; MYSQL_RES *res; diff --git a/src/bots.h b/src/bots.h index 0fead87..35f09db 100644 --- a/src/bots.h +++ b/src/bots.h @@ -29,6 +29,7 @@ void loop_bots(); void free_bots(); struct ClientSocket *getChannelBot(struct ChanNode *chan, int botid); +void requestOp(struct UserNode *user, struct ChanNode *chan); TIMEQ_CALLBACK(channel_ban_timeout); void general_event_privctcp(struct UserNode *user, struct UserNode *target, char *command, char *text); void set_bot_alias(int botid, char *alias); diff --git a/src/event_neonserv_join.c b/src/event_neonserv_join.c index f3e94ae..98bea3b 100644 --- a/src/event_neonserv_join.c +++ b/src/event_neonserv_join.c @@ -29,10 +29,11 @@ static void neonserv_event_join(struct ChanUser *chanuser) { struct UserNode *user = chanuser->user; struct ClientSocket *client = getBotForChannel(chanuser->chan); if(!client) return; //we can't "see" this event - if(user->flags & USERFLAG_ISBOT) { - putsock(client, "MODE %s +o %s", chanuser->chan->name, chanuser->user->nick); + if(chanuser->user == client->user) { + requestOp(client->user, chanuser->chan); return; } + if(chanuser->user->flags & USERFLAG_ISBOT) return; loadChannelSettings(chanuser->chan); if(!(chanuser->chan->flags & CHANFLAG_CHAN_REGISTERED)) return; char *ban; diff --git a/src/event_neonspam_join.c b/src/event_neonspam_join.c index a192d79..fc501e7 100644 --- a/src/event_neonspam_join.c +++ b/src/event_neonspam_join.c @@ -30,6 +30,11 @@ static void neonspam_event_join(struct ChanUser *chanuser) { if(chanuser->user->flags & USERFLAG_WAS_REGISTRING) return; struct ClientSocket *client = getChannelBot(chanuser->chan, BOTID); if(!client) return; //we can't "see" this event + if(chanuser->user == client->user) { + requestOp(client->user, chanuser->chan); + return; + } + if(chanuser->user->flags & USERFLAG_ISBOT) return; loadNeonSpamSettings(chanuser->chan); struct NeonSpamSettings *settings = chanuser->chan->spam_settings; if(!settings || !(settings->flags & SPAMSETTINGS_JOINSCAN)) return; -- 2.20.1