X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/d1a656759d2d523b9094105f41854e2a533281bc..4ec2950270b838a0d2e2d8127dfd68319616e594:/src/chanserv.c?ds=sidebyside diff --git a/src/chanserv.c b/src/chanserv.c index 5a4ae60..7b10a69 100644 --- a/src/chanserv.c +++ b/src/chanserv.c @@ -92,6 +92,11 @@ #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" @@ -427,6 +432,10 @@ static const struct message_entry msgtab[] = { { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" }, { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." }, { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." }, + { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" }, + { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" }, + { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" }, + { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" }, { "CSMSG_CHANNEL_END", "---------------End of Info--------------"}, { "CSMSG_PEEK_INFO", "$bStatus of %s$b" }, @@ -2388,8 +2397,20 @@ merge_bans(struct chanData *source, struct chanData *target) static void merge_data(struct chanData *source, struct chanData *target) { + /* Use more recent visited and owner-transfer time; use older + * registered time. Bitwise or may_opchan. Use higher max. + * Do not touch last_refresh, ban count or user counts. + */ if(source->visited > target->visited) target->visited = source->visited; + if(source->registered < target->registered) + target->registered = source->registered; + if(source->ownerTransfer > target->ownerTransfer) + target->ownerTransfer = source->ownerTransfer; + if(source->may_opchan) + target->may_opchan = 1; + if(source->max > target->max) + target->max = source->max; } static void @@ -3002,7 +3023,7 @@ static CHANSERV_FUNC(cmd_devoice) } static int -bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims) +bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims) { unsigned int ii; @@ -4170,13 +4191,18 @@ static CHANSERV_FUNC(cmd_topic) { struct chanData *cData; char *topic; + int p10 = 0; + +#ifdef WITH_PROTOCOL_P10 + p10 = 1; +#endif cData = channel->channel_info; if(argc < 2) { if(cData->topic) { - SetChannelTopic(channel, chanserv, cData->topic, 1); + SetChannelTopic(channel, chanserv, p10 ? user : chanserv, cData->topic, 1); reply("CSMSG_TOPIC_SET", cData->topic); return 1; } @@ -4212,10 +4238,10 @@ static CHANSERV_FUNC(cmd_topic) reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN); return 0; } - SetChannelTopic(channel, chanserv, new_topic, 1); + SetChannelTopic(channel, chanserv, p10 ? user : chanserv, new_topic, 1); } else /* No mask set, just set the topic */ - SetChannelTopic(channel, chanserv, topic, 1); + SetChannelTopic(channel, chanserv, p10 ? user : chanserv, topic, 1); } if(check_user_level(channel, user, lvlTopicSnarf, 1, 0)) @@ -4378,6 +4404,31 @@ 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]; @@ -4419,9 +4470,10 @@ static CHANSERV_FUNC(cmd_info) reply("CSMSG_CHANNEL_USERS", cData->userCount); reply("CSMSG_CHANNEL_LAMERS", cData->banCount); reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info)); - reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info)); privileged = IsStaff(user); + if(privileged) + reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info)); if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar) reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar); @@ -4440,6 +4492,13 @@ 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); + } reply("CSMSG_CHANNEL_END"); return 1; } @@ -5379,7 +5438,7 @@ static MODCMD_FUNC(chan_opt_defaulttopic) && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask)) reply("CSMSG_TOPIC_MISMATCH", channel->name); } - SetChannelTopic(channel, chanserv, topic ? topic : "", 1); + SetChannelTopic(channel, chanserv, chanserv, topic ? topic : "", 1); } if(channel->channel_info->topic) @@ -6049,13 +6108,22 @@ static CHANSERV_FUNC(cmd_giveownership) struct userData *new_owner, *curr_user; struct chanData *cData = channel->channel_info; struct do_not_register *dnr; - 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; @@ -6113,6 +6181,8 @@ static CHANSERV_FUNC(cmd_giveownership) chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle); return 0; } + + new_owner_old_access = new_owner->access; if(new_owner->access >= UL_COOWNER) co_access = new_owner->access; else @@ -6121,18 +6191,47 @@ 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); return 1; } +static void +chanserv_expire_user_suspension(void *data) +{ + struct userData *target = data; + + target->expires = 0; + target->flags &= ~USER_SUSPENDED; +} + static CHANSERV_FUNC(cmd_suspend) { struct handle_info *hi; struct userData *self, *target; + time_t expiry; - REQUIRE_PARAMS(2); + REQUIRE_PARAMS(3); if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0; self = GetChannelUser(channel->channel_info, user->handle_info); if(!(target = GetTrueChannelAccess(channel->channel_info, hi))) @@ -6155,6 +6254,24 @@ static CHANSERV_FUNC(cmd_suspend) target->present = 0; target->seen = now; } + if(!strcmp(argv[2], "0")) + expiry = 0; + else + { + unsigned int duration; + if(!(duration = ParseInterval(argv[2]))) + { + reply("MSG_INVALID_DURATION", argv[2]); + return 0; + } + expiry = now + duration; + } + + target->expires = expiry; + + if(target->expires) + timeq_add(target->expires, chanserv_expire_user_suspension, target); + target->flags |= USER_SUSPENDED; reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name); return 1; @@ -6185,6 +6302,7 @@ static CHANSERV_FUNC(cmd_unsuspend) } target->flags &= ~USER_SUSPENDED; scan_user_presence(target, NULL); + timeq_del(target->expires, chanserv_expire_user_suspension, target, 0); reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name); return 1; } @@ -6239,7 +6357,7 @@ chanserv_refresh_topics(UNUSED_ARG(void *data)) if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1'))) continue; if(cData->topic) - SetChannelTopic(cData->channel, chanserv, cData->topic, 1); + SetChannelTopic(cData->channel, chanserv, chanserv, cData->topic, 1); cData->last_refresh = refresh_num; } timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL); @@ -6597,7 +6715,7 @@ handle_new_channel(struct chanNode *channel) mod_chanmode_announce(chanserv, cData->channel, &cData->modes); if(self->uplink && !self->uplink->burst && channel->channel_info->topic) - SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1); + SetChannelTopic(channel, chanserv, chanserv, channel->channel_info->topic, 1); } /* Welcome to my worst nightmare. Warning: Read (or modify) @@ -6927,7 +7045,7 @@ handle_topic(struct userNode *user, struct chanNode *channel, const char *old_to if(bad_topic(channel, user, channel->topic)) { /* User doesnt have privs to set topics. Undo it */ send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name); - SetChannelTopic(channel, chanserv, old_topic, 1); + SetChannelTopic(channel, chanserv, chanserv, old_topic, 1); return 1; } /* If there is a topic mask set, and the new topic doesnt match, @@ -6938,12 +7056,12 @@ handle_topic(struct userNode *user, struct chanNode *channel, const char *old_to conform_topic(cData->topic_mask, channel->topic, new_topic); if(*new_topic) { - SetChannelTopic(channel, chanserv, new_topic, 1); + SetChannelTopic(channel, chanserv, chanserv, new_topic, 1); /* and fall through to topicsnarf code below.. */ } else /* Topic couldnt fit into mask, was too long */ { - SetChannelTopic(channel, chanserv, old_topic, 1); + SetChannelTopic(channel, chanserv, chanserv, old_topic, 1); send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask); send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN); return 1; @@ -7335,7 +7453,7 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan) { struct handle_info *handle; struct userData *uData; - char *seen, *inf, *flags; + char *seen, *inf, *flags, *expires; time_t last_seen; unsigned short access; @@ -7356,6 +7474,7 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan) seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING); last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now; flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING); + expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING); handle = get_handle_info(key); if(!handle) { @@ -7365,6 +7484,15 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan) uData = add_channel_user(chan, handle, access, last_seen, inf); uData->flags = flags ? strtoul(flags, NULL, 0) : 0; + uData->expires = expires ? strtoul(expires, NULL, 0) : 0; + + if((uData->flags & USER_SUSPENDED) && uData->expires) + { + if(uData->expires > now) + timeq_add(uData->expires, chanserv_expire_user_suspension, uData); + else + uData->flags &= ~USER_SUSPENDED; + } /* Upgrade: set autoop to the inverse of noautoop */ if(chanserv_read_version < 2) @@ -7436,10 +7564,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; @@ -7565,6 +7719,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); @@ -7726,6 +7886,8 @@ chanserv_write_users(struct saxdb_context *ctx, struct userData *uData) saxdb_write_int(ctx, KEY_SEEN, uData->seen); if(uData->flags) saxdb_write_int(ctx, KEY_FLAGS, uData->flags); + if(uData->expires) + saxdb_write_int(ctx, KEY_EXPIRES, uData->expires); if(uData->info) saxdb_write_string(ctx, KEY_INFO, uData->info); saxdb_end_record(ctx); @@ -7774,6 +7936,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) { @@ -7798,6 +7981,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); saxdb_start_record(ctx, KEY_OPTIONS, 0); saxdb_write_int(ctx, KEY_FLAGS, channel->flags);