fix possible crash on user deletion
[srvx.git] / src / hash.c
index c5aa49e0655b65c3c7f0c4b7ef724f3853146226..a55ad9ba92568bc06f6e3b031953a41bce38f273 100644 (file)
@@ -191,9 +191,11 @@ NickChange(struct userNode* user, const char *new_nick, int no_announce)
 #endif
 
     /* Make callbacks for nick changes.  Do this with new nick in
-     * place because that is slightly more useful.
+     * place because that is slightly more useful.  Stop if the user
+     * gets killed by any of the hooks, so that later hooks do not get
+     * confused by the user having disappeared.
      */
-    for (nn=0; nn<ncf2_used; nn++)
+    for (nn=0; (nn<ncf2_used) && !user->dead; nn++)
         ncf2_list[nn](user, old_nick);
     user->timestamp = now;
     if (IsLocal(user) && !no_announce)
@@ -219,7 +221,7 @@ reg_account_func(account_func_t handler)
 }
 
 void
-call_account_func(struct userNode *user, const char *stamp)
+call_account_func(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
 {
     /* We've received an account stamp for a user; notify
        NickServ, which registers the sole account_func
@@ -228,7 +230,7 @@ call_account_func(struct userNode *user, const char *stamp)
        P10 Protocol violation if (user->modes & FLAGS_STAMPED) here.
     */
     if (account_func)
-        account_func(user, stamp);
+        account_func(user, stamp, timestamp, serial);
 
 #ifdef WITH_PROTOCOL_P10
     /* Mark the user so we don't stamp it again. */
@@ -237,7 +239,7 @@ call_account_func(struct userNode *user, const char *stamp)
 }
 
 void
-StampUser(struct userNode *user, const char *stamp)
+StampUser(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
 {
 #ifdef WITH_PROTOCOL_P10
     /* The P10 protocol says we can't stamp users who already
@@ -246,16 +248,19 @@ StampUser(struct userNode *user, const char *stamp)
         return;
 #endif
 
-    irc_account(user, stamp);
+    irc_account(user, stamp, timestamp, serial);
     user->modes |= FLAGS_STAMPED;
 }
 
 void
-assign_fakehost(struct userNode *user, const char *host, int announce)
+assign_fakehost(struct userNode *user, const char *host, const char *ident, int force, int announce)
 {
-    safestrncpy(user->fakehost, host, sizeof(user->fakehost));
+    if (host)
+        safestrncpy(user->fakehost, host, sizeof(user->fakehost));
+    if (ident)
+        safestrncpy(user->fakeident, ident, sizeof(user->ident));
     if (announce)
-        irc_fakehost(user, host);
+        irc_fakehost(user, host, ident, force);
 }
 
 static new_channel_func_t *ncf_list;
@@ -377,6 +382,7 @@ AddChannel(const char *name, unsigned long time_, const char *modes, char *banli
         strcpy(cNode->name, name);
         banList_init(&cNode->banlist);
         modeList_init(&cNode->members);
+        userList_init(&cNode->invited);
         mod_chanmode(NULL, cNode, argv, nn, MCP_FROM_SERVER);
         dict_insert(channels, cNode->name, cNode);
         cNode->timestamp = time_;
@@ -464,6 +470,7 @@ DelChannel(struct chanNode *channel)
 
     modeList_clean(&channel->members);
     banList_clean(&channel->banlist);
+    userList_clean(&channel->invited);
     free(channel);
 }
 
@@ -483,7 +490,7 @@ AddChannelUser(struct userNode *user, struct chanNode* channel)
         mNode->channel = channel;
         mNode->user = user;
         mNode->modes = 0;
-        mNode->oplevel = -1;
+        mNode->oplevel = MAXOPLEVEL;
         mNode->idle_since = now;
 
         /* Add modeNode to channel and to user.
@@ -502,7 +509,7 @@ AddChannelUser(struct userNode *user, struct chanNode* channel)
             irc_join(user, channel);
         }
 
-        for (n=0; n<jf_used; n++) {
+        for (n=0; (n<jf_used) && !user->dead; n++) {
             /* Callbacks return true if they kick or kill the user,
              * and we can continue without removing mNode. */
             if (jf_list[n](mNode))
@@ -512,6 +519,33 @@ AddChannelUser(struct userNode *user, struct chanNode* channel)
         return mNode;
 }
 
+/* Return negative if *(struct modeNode**)pa is "less than" pb,
+ * positive if pa is "larger than" pb.  Comparison is based on sorting
+ * so that non-voiced/non-opped users are first, voiced-only users are
+ * next, and the "strongest" oplevels are before "weaker" oplevels.
+ * Within those sets, ordering is arbitrary.
+ */
+int
+modeNode_sort(const void *pa, const void *pb)
+{
+        struct modeNode *a = *(struct modeNode**)pa;
+        struct modeNode *b = *(struct modeNode**)pb;
+
+        if (a->modes & MODE_CHANOP) {
+            if (!(b->modes & MODE_CHANOP))
+                return -1;
+            else if ((b->modes & MODE_VOICE) != (a->modes & MODE_VOICE))
+                return (b->modes & MODE_VOICE) - (a->modes & MODE_VOICE);
+            else if (a->oplevel != b->oplevel)
+                return a->oplevel - b->oplevel;
+        } else if (b->modes & MODE_CHANOP)
+            return 1;
+        else if ((b->modes & MODE_VOICE) != (a->modes & MODE_VOICE))
+            return (b->modes & MODE_VOICE) - (a->modes & MODE_VOICE);
+
+        return irccasecmp(a->user->nick, b->user->nick);
+}
+
 static part_func_t *pf_list;
 static unsigned int pf_size = 0, pf_used = 0;
 
@@ -631,9 +665,9 @@ ChannelUserKicked(struct userNode* kicker, struct userNode* victim, struct chanN
     unsigned int n;
     struct modeNode *mn;
 
-    if (!victim || !channel || IsService(victim) || !GetUserMode(channel, victim))
+    if (!victim || !channel || !GetUserMode(channel, victim))
         return;
-
+    
     /* Update the kicker's idle time (kicker may be null if it was a server) */
     if (kicker && (mn = GetUserMode(channel, kicker)))
         mn->idle_since = now;
@@ -700,6 +734,10 @@ SetChannelTopic(struct chanNode *channel, struct userNode *user, const char *top
         irc_topic(user, channel, topic);
     } else {
         for (n=0; n<tf_used; n++)
+            /* A topic change handler can return non-zero to indicate
+             * that it has reverted the topic change, and that further
+             * hooks should not be called.
+             */
             if (tf_list[n](user, channel, old_topic))
                 break;
     }