applied giveownership-history patch by ThiefMaster
[srvx.git] / src / chanserv.c
index fa1827fa6e59d68c9455901faf960246d406c11f..1dbf346c3cfb528d1a47d55b956ea7bae36aa18a 100644 (file)
@@ -58,6 +58,7 @@
 #define KEY_MAX_USERINFO_LENGTH     "max_userinfo_length"
 #define KEY_GIVEOWNERSHIP_PERIOD    "giveownership_timeout"
 #define KEY_INVITED_INTERVAL           "invite_timeout"
+#define KEY_REVOKE_MODE_A           "revoke_mode_a"
 #define KEY_NEW_CHANNEL_AUTHED      "new_channel_authed_join"
 #define KEY_NEW_CHANNEL_UNAUTHED    "new_channel_unauthed_join"
 #define KEY_NEW_CHANNEL_MSG         "new_channel_message"
 #define KEY_REVOKED         "revoked"
 #define KEY_SUSPEND_EXPIRES "suspend_expires"
 #define KEY_SUSPEND_REASON  "suspend_reason"
+#define KEY_GIVEOWNERSHIP   "giveownership"
+#define KEY_STAFF_ISSUER    "staff_issuer"
+#define KEY_OLD_OWNER       "old_owner"
+#define KEY_TARGET          "target"
+#define KEY_TARGET_ACCESS   "target_access"
 #define KEY_VISITED         "visited"
 #define KEY_TOPIC           "topic"
 #define KEY_GREETING        "greeting"
@@ -568,6 +574,8 @@ static struct
     unsigned int    max_chan_users;
     unsigned int    max_chan_bans;
     unsigned int    max_userinfo_length;
+    
+    unsigned int    revoke_mode_a;
 
     struct string_list  *set_shows;
     struct string_list  *eightball;
@@ -1438,11 +1446,13 @@ unregister_channel(struct chanData *channel, const char *reason)
 
     timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
 
-    if(off_channel > 0)
-    {
-      mod_chanmode_init(&change);
-      change.modes_clear |= MODE_REGISTERED;
-      mod_chanmode_announce(chanserv, channel->channel, &change);
+    if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
+        mod_chanmode_init(&change);
+        if(off_channel > 0)
+            change.modes_clear |= MODE_REGISTERED;
+        if(chanserv_conf.revoke_mode_a)
+            change.modes_clear |= MODE_ACCESS;
+        mod_chanmode_announce(chanserv, channel->channel, &change);
     }
 
     while(channel->users)
@@ -1498,7 +1508,7 @@ unregister_channel(struct chanData *channel, const char *reason)
 }
 
 static void
-expire_channels(UNUSED_ARG(void *data))
+expire_channels(void *data)
 {
     struct chanData *channel, *next;
     struct userData *user;
@@ -1529,7 +1539,7 @@ expire_channels(UNUSED_ARG(void *data))
         unregister_channel(channel, "registration expired.");
     }
 
-    if(chanserv_conf.channel_expire_frequency)
+    if(chanserv_conf.channel_expire_frequency && !data)
         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
 }
 
@@ -2990,7 +3000,7 @@ cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short m
     unsigned int count;
     unsigned long limit;
 
-    actor = GetChannelAccess(channel->channel_info, user->handle_info);
+    actor = GetChannelUser(channel->channel_info, user->handle_info);
     if(min_access > max_access)
     {
         send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
@@ -3885,6 +3895,7 @@ static CHANSERV_FUNC(cmd_myaccess)
     {
         struct chanData *cData = uData->channel;
         ccount++;
+        unsigned int base_len;
 
         if(uData->access > UL_OWNER)
             continue;
@@ -3897,9 +3908,8 @@ static CHANSERV_FUNC(cmd_myaccess)
            && !IsNetworkHelper(user))
             continue;
         sbuf.used = 0;
-        string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
-        if(uData->flags != USER_AUTO_OP)
-            string_buffer_append(&sbuf, ',');
+        string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
+        base_len = sbuf.used;
         if(IsUserSuspended(uData))
             string_buffer_append(&sbuf, 's');
         if(IsUserAutoOp(uData))
@@ -3911,6 +3921,8 @@ static CHANSERV_FUNC(cmd_myaccess)
         }
         if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
             string_buffer_append(&sbuf, 'i');
+        if(sbuf.used==base_len)
+            sbuf.used--;
         if(uData->info)
             string_buffer_append_printf(&sbuf, ")] %s", uData->info);
         else
@@ -4602,6 +4614,30 @@ show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended
     }
 }
 
+static void
+show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
+{
+    char buf[MAXLEN];
+    const char *fmt = "%a %b %d %H:%M %Y";
+    strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
+
+    if(giveownership->staff_issuer)
+    {
+        if(giveownership->reason)
+            reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
+                  giveownership->target, giveownership->target_access,
+                  giveownership->staff_issuer, buf, giveownership->reason);
+        else
+            reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
+                  giveownership->target, giveownership->target_access,
+                  giveownership->staff_issuer, buf);
+    }
+    else
+    {
+        reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
+    }
+}
+
 static CHANSERV_FUNC(cmd_info)
 {
     char modes[MAXLEN], buffer[INTERVALLEN];
@@ -4668,6 +4704,14 @@ static CHANSERV_FUNC(cmd_info)
         reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
         show_suspension_info(cmd, user, cData->suspended);
     }
+    
+    if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
+    {
+        struct giveownership *giveownership;
+        reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
+        for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
+            show_giveownership_info(cmd, user, giveownership);
+    }
     return 1;
 }
 
@@ -5209,7 +5253,7 @@ chanserv_support_channels(void)
 static CHANSERV_FUNC(cmd_expire)
 {
     int channel_count = registered_channels;
-    expire_channels(NULL);
+    expire_channels(chanserv);
     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
     return 1;
 }
@@ -6301,13 +6345,21 @@ static CHANSERV_FUNC(cmd_giveownership)
     struct chanData *cData = channel->channel_info;
     struct do_not_register *dnr;
     const char *confirm;
-    unsigned int force;
-    unsigned short co_access;
-    char reason[MAXLEN];
+    struct giveownership *giveownership;
+    unsigned int force, override;
+    unsigned short co_access, new_owner_old_access;
+    char reason[MAXLEN], transfer_reason[MAXLEN];
 
     REQUIRE_PARAMS(2);
     curr_user = GetChannelAccess(cData, user->handle_info);
     force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
+
+    struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
+    override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
+                && (uData->access > 500)
+                && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
+                    || uData->access < 500));
+
     if(!curr_user || (curr_user->access != UL_OWNER))
     {
         struct userData *owner = NULL;
@@ -6375,6 +6427,7 @@ static CHANSERV_FUNC(cmd_giveownership)
             return 0;
         }
     }
+    new_owner_old_access = new_owner->access;
     if(new_owner->access >= UL_COOWNER)
         co_access = new_owner->access;
     else
@@ -6383,6 +6436,23 @@ static CHANSERV_FUNC(cmd_giveownership)
     if(curr_user)
         curr_user->access = co_access;
     cData->ownerTransfer = now;
+    giveownership = calloc(1, sizeof(*giveownership));
+    giveownership->issued = now;
+    giveownership->old_owner = curr_user->handle->handle;
+    giveownership->target = new_owner_hi->handle;
+    giveownership->target_access = new_owner_old_access;
+    if(override)
+    {
+        if(argc > (2 + force))
+        {
+            unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
+            giveownership->reason = strdup(transfer_reason);
+        }
+        giveownership->staff_issuer = strdup(user->handle_info->handle);
+    }
+
+    giveownership->previous = channel->channel_info->giveownership;
+    channel->channel_info->giveownership = giveownership;
     reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
     sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
@@ -7657,6 +7727,8 @@ chanserv_conf_read(void)
     chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
     str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
     chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
+    str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
+    chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
@@ -7685,11 +7757,11 @@ chanserv_conf_read(void)
     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
     str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
-    chanserv_conf.new_channel_authed = str ? str : NULL;
+    chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
     str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
-    chanserv_conf.new_channel_unauthed = str ? str : NULL;
+    chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
     str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
-    chanserv_conf.new_channel_msg = str ? str : NULL;
+    chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
     if(!str)
         str = "+nt";
@@ -7934,10 +8006,36 @@ chanserv_read_suspended(dict_t obj)
     return suspended;
 }
 
+static struct giveownership *
+chanserv_read_giveownership(dict_t obj)
+{
+    struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
+    char *str;
+    dict_t previous;
+
+    str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
+    giveownership->staff_issuer = str ? strdup(str) : NULL;
+
+    giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
+
+    giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
+    giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
+
+    str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
+    giveownership->reason = str ? strdup(str) : NULL;
+    str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
+    giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
+
+    previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
+    giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
+    return giveownership;
+}
+
 static int
 chanserv_channel_read(const char *key, struct record_data *hir)
 {
     struct suspended *suspended;
+    struct giveownership *giveownership;
     struct mod_chanmode *modes;
     struct chanNode *cNode;
     struct chanData *cData;
@@ -8076,6 +8174,12 @@ chanserv_channel_read(const char *key, struct record_data *hir)
             cData->flags &= ~CHANNEL_SUSPENDED;
     }
 
+    if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
+    {
+        giveownership = chanserv_read_giveownership(obj);
+        cData->giveownership = giveownership;
+    }
+
     if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
         struct mod_chanmode change;
         mod_chanmode_init(&change);
@@ -8296,6 +8400,27 @@ chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct sus
     saxdb_end_record(ctx);
 }
 
+static void
+chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
+{
+    saxdb_start_record(ctx, name, 0);
+    if(giveownership->staff_issuer)
+      saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
+    if(giveownership->old_owner)
+      saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
+    if(giveownership->target)
+      saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
+    if(giveownership->target_access)
+      saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
+    if(giveownership->reason)
+      saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
+    if(giveownership->issued)
+        saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
+    if(giveownership->previous)
+        chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
+    saxdb_end_record(ctx);
+}
+
 static void
 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
 {
@@ -8322,6 +8447,8 @@ chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
     if(channel->suspended)
         chanserv_write_suspended(ctx, "suspended", channel->suspended);
+    if(channel->giveownership)
+        chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
     if(channel->expiry)
         saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);