added automatic multi-bot rejoin for opless channels
[NeonServV5.git] / src / IRCParser.c
index b757f5dc4d28ca18c9af2f356120ded843736a97..9b91d21552ed8f6dc3f2f463c0ded662ee7d374a 100644 (file)
@@ -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;
 }