Add fake host support (assuming your ircd supports it)
[srvx.git] / src / chanserv.c
index 75f7ee24b47faa4f1a30350314b6b89c29bbdac7..60f8991681d7778b00ba994e46a4de1b61368eaa 100644 (file)
 #define KEY_EXPIRES             "expires"
 #define KEY_TRIGGERED          "triggered"
 
-#define CHANNEL_DEFAULT_FLAGS   (0)
+#define CHANNEL_DEFAULT_FLAGS   (CHANNEL_OFFCHANNEL)
 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
 
 /* Administrative messages */
@@ -252,6 +252,7 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_SET_MODES",         "$bModes       $b %s" },
     { "CSMSG_SET_NODELETE",      "$bNoDelete    $b %s" },
     { "CSMSG_SET_DYNLIMIT",      "$bDynLimit    $b %s" },
+    { "CSMSG_SET_OFFCHANNEL",    "$bOffChannel  $b %s" },
     { "CSMSG_SET_USERINFO",      "$bUserInfo    $b %d" },
     { "CSMSG_SET_GIVE_VOICE",    "$bGiveVoice   $b %d" },
     { "CSMSG_SET_TOPICSNARF",    "$bTopicSnarf  $b %d" },
@@ -471,6 +472,7 @@ static int eject_user(struct userNode *user, struct chanNode *channel, unsigned
 
 struct userNode *chanserv;
 dict_t note_types;
+int off_channel;
 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
 static struct log_type *CS_LOG;
 
@@ -1008,10 +1010,10 @@ mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *c
         return 1;
     if(orig->modes_clear & change->modes_set)
         return 1;
-    if((orig->modes_set & MODE_KEY)
+    if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
        && strcmp(orig->new_key, change->new_key))
         return 1;
-    if((orig->modes_set & MODE_LIMIT)
+    if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
        && (orig->new_limit != change->new_limit))
         return 1;
     return 0;
@@ -1268,6 +1270,7 @@ static void chanserv_expire_suspension(void *data);
 static void
 unregister_channel(struct chanData *channel, const char *reason)
 {
+    struct mod_chanmode change;
     char msgbuf[MAXLEN];
 
     /* After channel unregistration, the following must be cleaned
@@ -1284,22 +1287,29 @@ unregister_channel(struct chanData *channel, const char *reason)
 
     timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
 
+    mod_chanmode_init(&change);
+    change.modes_clear |= MODE_REGISTERED;
+    mod_chanmode_announce(chanserv, channel->channel, &change);
+
     while(channel->users)
        del_channel_user(channel->users, 0);
 
     while(channel->bans)
        del_channel_ban(channel->bans);
 
-    if(channel->topic) free(channel->topic);
-    if(channel->registrar) free(channel->registrar);
-    if(channel->greeting) free(channel->greeting);
-    if(channel->user_greeting) free(channel->user_greeting);
-    if(channel->topic_mask) free(channel->topic_mask);
+    free(channel->topic);
+    free(channel->registrar);
+    free(channel->greeting);
+    free(channel->user_greeting);
+    free(channel->topic_mask);
 
-    if(channel->prev) channel->prev->next = channel->next;
-    else channelList = channel->next;
+    if(channel->prev)
+        channel->prev->next = channel->next;
+    else
+        channelList = channel->next;
 
-    if(channel->next) channel->next->prev = channel->prev;
+    if(channel->next)
+        channel->next->prev = channel->prev;
 
     if(channel->suspended)
     {
@@ -1321,8 +1331,7 @@ unregister_channel(struct chanData *channel, const char *reason)
     }
     channel->channel->channel_info = NULL;
 
-    if(channel->notes)
-        dict_delete(channel->notes);
+    dict_delete(channel->notes);
     sprintf(msgbuf, "%s %s", channel->channel->name, reason);
     if(!IsSuspended(channel))
         DelChannelUser(chanserv, channel->channel, msgbuf, 0);
@@ -2931,7 +2940,7 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
     if(action & ACTION_KICK)
     {
         char kick_reason[MAXLEN];
-       sprintf(kick_reason, "%s (%s)", reason, user->nick);
+       sprintf(kick_reason, "(%s) %s", user->nick, reason);
 
        for(n = 0; n < victimCount; n++)
            KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
@@ -3429,14 +3438,15 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
     lData.lowest = lowest;
     lData.highest = highest;
     lData.search = (argc > 1) ? argv[1] : NULL;
-    send_list = zoot_list;
+    send_list = def_list;
+    (void)zoot_list; /* since it doesn't show user levels */
 
     if(user->handle_info)
     {
        switch(user->handle_info->userlist_style)
        {
        case HI_STYLE_DEF: send_list = def_list; break;
-        case HI_STYLE_ZOOT: send_list = zoot_list; break;
+        case HI_STYLE_ZOOT: send_list = def_list; break;
        }
     }
 
@@ -4925,6 +4935,54 @@ static MODCMD_FUNC(chan_opt_dynlimit)
     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
 }
 
+static MODCMD_FUNC(chan_opt_offchannel)
+{
+    struct chanData *cData = channel->channel_info;
+    int value;
+
+    if(argc > 1)
+    {
+       /* Set flag according to value. */
+       if(enabled_string(argv[1]))
+       {
+            if(!IsOffChannel(cData))
+                DelChannelUser(chanserv, channel, "Going off-channel.", 0);
+           cData->flags |= CHANNEL_OFFCHANNEL;
+           value = 1;
+       }
+       else if(disabled_string(argv[1]))
+       {
+            if(IsOffChannel(cData))
+            {
+                struct mod_chanmode change;
+                mod_chanmode_init(&change);
+                change.argc = 1;
+                change.args[0].mode = MODE_CHANOP;
+                change.args[0].member = AddChannelUser(chanserv, channel);
+                mod_chanmode_announce(chanserv, channel, &change);
+            }
+           cData->flags &= ~CHANNEL_OFFCHANNEL;
+           value = 0;
+       }
+       else
+       {
+           reply("MSG_INVALID_BINARY", argv[1]);
+           return 0;
+       }
+    }
+    else
+    {
+       /* Find current option value. */
+       value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
+    }
+
+    if(value)
+        reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
+    else
+        reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
+    return 1;
+}
+
 static MODCMD_FUNC(chan_opt_defaults)
 {
     struct userData *uData;
@@ -5774,7 +5832,7 @@ handle_join(struct modeNode *mNode)
         if(bData)
         {
             char kick_reason[MAXLEN];
-            sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
+            sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
 
             bData->triggered = now;
             if(bData != cData->bans)
@@ -5915,7 +5973,7 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
                && IsUserAutoInvite(channel)
                && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
                && !self->burst
-              && !user->uplink->burst)
+               && !user->uplink->burst)
                 irc_invite(chanserv, user, cn);
             continue;
         }
@@ -5982,45 +6040,45 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
 }
 
 static void
-handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
+handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
 {
     struct chanData *cData;
     struct userData *uData;
 
-    cData = channel->channel_info;
-    if(!cData || IsSuspended(cData) || IsLocal(user))
+    cData = mn->channel->channel_info;
+    if(!cData || IsSuspended(cData) || IsLocal(mn->user))
         return;
 
-    if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
+    if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
     {
        /* Allow for a bit of padding so that the limit doesn't
           track the user count exactly, which could get annoying. */
-       if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
+       if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
        {
            timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
            timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
        }
     }
 
-    if((uData = GetTrueChannelAccess(cData, user->handle_info)))
+    if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
     {
-       scan_user_presence(uData, user);
+       scan_user_presence(uData, mn->user);
         uData->seen = now;
     }
 
-    if(IsHelping(user) && IsSupportHelper(user))
+    if(IsHelping(mn->user) && IsSupportHelper(mn->user))
     {
         unsigned int ii, jj;
         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
         {
-            for(jj = 0; jj < user->channels.used; ++jj)
-                if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
+            for(jj = 0; jj < mn->user->channels.used; ++jj)
+                if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
                     break;
-            if(jj < user->channels.used)
+            if(jj < mn->user->channels.used)
                 break;
         }
         if(ii == chanserv_conf.support_channels.used)
-            HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
+            HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
     }
 }
 
@@ -6374,6 +6432,10 @@ chanserv_conf_read(void)
     else
         strlist = alloc_string_list(2);
     chanserv_conf.old_ban_names = strlist;
+    /* the variable itself is actually declared in proto-common.c; this is equally 
+     * parse issue. */
+    str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
+    off_channel = (str && enabled_string(str)) ? 1 : 0;
 }
 
 static void
@@ -6625,7 +6687,7 @@ chanserv_channel_read(const char *key, struct record_data *hir)
         /* We could use suspended->expires and suspended->revoked to
          * set the CHANNEL_SUSPENDED flag, but we don't. */
     }
-    else if(cData->flags & CHANNEL_SUSPENDED)
+    else if(IsSuspended(cData))
     {
         suspended = calloc(1, sizeof(*suspended));
         suspended->issued = 0;
@@ -6640,17 +6702,16 @@ chanserv_channel_read(const char *key, struct record_data *hir)
         suspended->cData = cData;
     }
     else
-        suspended = NULL;
+        suspended = NULL; /* to squelch a warning */
 
-    if((cData->flags & CHANNEL_SUSPENDED)
-       && suspended->expires
-       && (suspended->expires <= now))
-    {
-        cData->flags &= ~CHANNEL_SUSPENDED;
+    if(IsSuspended(cData)) {
+        if(suspended->expires > now)
+            timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
+        else if(suspended->expires)
+            cData->flags &= ~CHANNEL_SUSPENDED;
     }
 
-    if(!(cData->flags & CHANNEL_SUSPENDED))
-    {
+    if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
         struct mod_chanmode change;
         mod_chanmode_init(&change);
         change.argc = 1;
@@ -6658,10 +6719,6 @@ chanserv_channel_read(const char *key, struct record_data *hir)
         change.args[0].member = AddChannelUser(chanserv, cNode);
         mod_chanmode_announce(chanserv, cNode, &change);
     }
-    else if(suspended->expires > now)
-    {
-        timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
-    }
 
     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
     cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
@@ -6678,14 +6735,15 @@ chanserv_channel_read(const char *key, struct record_data *hir)
     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
     cData->topic = str ? strdup(str) : NULL;
 
-    if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
+    if(!IsSuspended(cData)
+       && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
        && (argc = split_line(str, 0, ArrayLength(argv), argv))
        && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
         cData->modes = *modes;
+        cData->modes.modes_set |= MODE_REGISTERED;
         if(cData->modes.argc > 1)
             cData->modes.argc = 1;
-        if(!IsSuspended(cData))
-            mod_chanmode_announce(chanserv, cNode, &cData->modes);
+        mod_chanmode_announce(chanserv, cNode, &cData->modes);
         mod_chanmode_free(modes);
     }
 
@@ -7163,6 +7221,8 @@ init_chanserv(const char *nick)
     DEFINE_CHANNEL_OPTION(ctcpusers);
     DEFINE_CHANNEL_OPTION(ctcpreaction);
     DEFINE_CHANNEL_OPTION(inviteme);
+    if(off_channel)
+        DEFINE_CHANNEL_OPTION(offchannel);
     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
 
     /* Alias set topic to set defaulttopic for compatibility. */