X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/639bdb1d287235b147d36acb79a0b608e5fca0af..0f3e9cfc4b5d0f13085c975e10c0e6c4c8e5fbc3:/src/chanserv.c diff --git a/src/chanserv.c b/src/chanserv.c index 148c092..1410843 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" @@ -349,8 +354,14 @@ static const struct message_entry msgtab[] = { { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." }, /* Channel userlist */ - { "CSMSG_ACCESS_ALL_HEADER", "$b%s Users From Level %s To %s$b" }, - { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" }, + { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" }, + { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" }, + /* uncomment if needed to adujust styles (and change code below) + { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" }, + { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" }, + { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" }, + { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" }, + */ { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." }, { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." }, { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" }, @@ -427,7 +438,12 @@ 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_CHANNEL_END_CLEAN", "End of Info"}, { "CSMSG_PEEK_INFO", "$bStatus of %s$b" }, { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" }, @@ -633,14 +649,14 @@ static const struct { unsigned int old_flag; unsigned short flag_value; } levelOptions[] = { - { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 }, - { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 }, - { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 }, - { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 }, - { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 }, - { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 }, - { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 }, - { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 }, + { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 }, + { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 }, + { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 }, + { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 }, + { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 }, + { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 }, + { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 }, + { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 }, { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 } }; @@ -1798,7 +1814,8 @@ static CHANSERV_FUNC(cmd_noregister) dict_iterator_t it; reply("CSMSG_DNR_SEARCH_RESULTS"); - reply("CSMSG_BAR"); + if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN) + reply("CSMSG_BAR"); matches = 0; for(it = dict_first(handle_dnrs); it; it = iter_next(it)) { @@ -1857,7 +1874,8 @@ static CHANSERV_FUNC(cmd_noregister) } reply("CSMSG_DNR_SEARCH_RESULTS"); - reply("CSMSG_BAR"); + if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN) + reply("CSMSG_BAR"); if(*target == '*') matches = chanserv_show_dnrs(user, cmd, NULL, target + 1); else @@ -2388,8 +2406,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 +3032,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; @@ -3104,18 +3134,18 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c reply("CSMSG_MASK_PROTECTED", argv[1]); return 0; } - /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/ - if(cmd) - victimCount = 0; /* Dont deop etc ppl who match this */ - -#ifdef entropy_lameness - if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user)) +/* If i want to ban *.nl and theres 5 of them, what is it to the bot?!? +// if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user)) + And, ^^^^^^^^^ BAH! + We use x2 style over-mask detection instead because it doesnt stop channel owners from doing + reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with + some creativity, but its not x3's job to be the ban censor anyway. */ + if(is_overmask(argv[1])) { if(cmd) reply("CSMSG_LAME_MASK", argv[1]); return 0; } -#endif if((action == ACTION_KICK) && (victimCount == 0)) { @@ -3841,13 +3871,31 @@ zoot_list(struct listData *list) */ static void -def_list(struct listData *list) +normal_list(struct listData *list) +{ + const char *msg; + if(list->search) + send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search); + else + send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest)); + if(list->table.length == 1) + { + msg = user_find_message(list->user, "MSG_NONE"); + send_message_type(4, list->user, list->bot, " %s", msg); + } + else + table_send(list->bot, list->user->nick, 0, NULL, list->table); +} + +/* if these need changed, uncomment and customize +static void +clean_list(struct listData *list) { const char *msg; if(list->search) - send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search); + send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search); else - send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest)); + send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest)); if(list->table.length == 1) { msg = user_find_message(list->user, "MSG_NONE"); @@ -3857,6 +3905,25 @@ def_list(struct listData *list) table_send(list->bot, list->user->nick, 0, NULL, list->table); } +static void +advanced_list(struct listData *list) +{ + const char *msg; + if(list->search) + send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search); + else + send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest)); + if(list->table.length == 1) + { + msg = user_find_message(list->user, "MSG_NONE"); + send_message_type(4, list->user, list->bot, " %s", msg); + } + else + table_send(list->bot, list->user->nick, 0, NULL, list->table); +} + +*/ + static int userData_access_comp(const void *arg_a, const void *arg_b) { @@ -3878,6 +3945,8 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg struct listData lData; unsigned int matches; const char **ary; + int i = 0; + int seen_index; lData.user = user; lData.bot = cmd->parent->bot; @@ -3885,20 +3954,29 @@ 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 = def_list; + send_list = normal_list; /* What does the following line do exactly?? */ /*(void)zoot_list; ** since it doesn't show user levels */ - /* this does nothing!! -rubin + /* 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 = def_list; break; - } + switch(user->handle_info->userlist_style) + { + case HI_STYLE_CLEAN: + send_list = clean_list; + break; + case HI_STYLE_ADVANCED: + send_list = advanced_list; + break; + case HI_STYLE_NORMAL: + default: + send_list = normal_list; + break; + } } */ + send_list = normal_list; lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *)); matches = 0; @@ -3913,45 +3991,54 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp); lData.table.length = matches+1; - lData.table.width = 5; lData.table.flags = TABLE_NO_FREE; lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents)); + + if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED) + lData.table.width = 5; /* with level = 5 */ + else + lData.table.width = 4; /* without = 4 */ ary = malloc(lData.table.width*sizeof(**lData.table.contents)); lData.table.contents[0] = ary; - ary[0] = "Access"; - ary[1] = "Level"; - ary[2] = "Account"; - ary[3] = "Last Seen"; - ary[4] = "Status"; + ary[i++] = "Access"; + if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED) + ary[i++] = "Level"; /* Only on advanced view */ + ary[i++] = "Account"; + ary[i] = "Last Seen"; + seen_index = i++; + ary[i++] = "Status"; for(matches = 1; matches < lData.table.length; ++matches) { struct userData *uData = lData.users[matches-1]; char seen[INTERVALLEN]; + i = 0; ary = malloc(lData.table.width*sizeof(**lData.table.contents)); lData.table.contents[matches] = ary; - /* ary[0] = strtab(uData->access);*/ - ary[0] = user_level_name_from_level(uData->access); - ary[1] = strtab(uData->access); - ary[2] = uData->handle->handle; + ary[i++] = user_level_name_from_level(uData->access); + if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED) + ary[i++] = strtab(uData->access); + ary[i++] = uData->handle->handle; if(uData->present) - ary[3] = "Here"; + ary[i] = "Here"; else if(!uData->seen) - ary[3] = "Never"; + ary[i] = "Never"; else - ary[3] = intervalString(seen, now - uData->seen, user->handle_info); - ary[3] = strdup(ary[3]); + ary[i] = intervalString(seen, now - uData->seen, user->handle_info); + ary[i] = strdup(ary[i]); + i++; if(IsUserSuspended(uData)) - ary[4] = "Suspended"; + ary[i++] = "Suspended"; else if(HANDLE_FLAGGED(uData->handle, FROZEN)) - ary[4] = "Vacation"; + ary[i++] = "Vacation"; else - ary[4] = "Normal"; + ary[i++] = "Normal"; } send_list(&lData); for(matches = 1; matches < lData.table.length; ++matches) { - free((char*)lData.table.contents[matches][3]); + /* Free strdup above */ + free((char*)lData.table.contents[matches][seen_index]); free(lData.table.contents[matches]); } free(lData.table.contents[0]); @@ -3965,7 +4052,8 @@ static CHANSERV_FUNC(cmd_pending) { struct adduserPending *ap; reply("CSMSG_ADDUSER_PENDING_HEADER"); - reply("CSMSG_BAR"); + if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN) + reply("CSMSG_BAR"); for(ap = adduser_pendings;ap;ap = ap->next) reply("CSMSG_ADDUSER_PENDING_LIST", ap->channel->name, ap->user->nick); reply("CSMSG_ADDUSER_PENDING_FOOTER"); @@ -4170,13 +4258,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 +4305,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 +4471,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]; @@ -4390,7 +4508,8 @@ static CHANSERV_FUNC(cmd_info) cData = channel->channel_info; reply("CSMSG_CHANNEL_INFO", channel->name); - reply("CSMSG_BAR"); + if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN) + reply("CSMSG_BAR"); uData = GetChannelUser(cData, user->handle_info); if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/)) @@ -4419,9 +4538,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,7 +4560,17 @@ static CHANSERV_FUNC(cmd_info) reply("CSMSG_CHANNEL_SUSPENDED", channel->name); show_suspension_info(cmd, user, cData->suspended); } - reply("CSMSG_CHANNEL_END"); + 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); + } + if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN) + reply("CSMSG_CHANNEL_END"); + else + reply("CSMSG_CHANNEL_END_CLEAN"); return 1; } @@ -4527,7 +4657,8 @@ static CHANSERV_FUNC(cmd_peek) irc_make_chanmode(channel, modes); reply("CSMSG_PEEK_INFO", channel->name); - reply("CSMSG_BAR"); + if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN) + reply("CSMSG_BAR"); reply("CSMSG_PEEK_TOPIC", channel->topic); reply("CSMSG_PEEK_MODES", modes); reply("CSMSG_PEEK_USERS", channel->members.used); @@ -4588,7 +4719,8 @@ static CHANSERV_FUNC(cmd_resync) struct chanData *cData = channel->channel_info; unsigned int ii, used; - changes = mod_chanmode_alloc(channel->members.used * 2); + /* 6 = worst case -ovh+ovh on everyone */ + changes = mod_chanmode_alloc(channel->members.used * 6); for(ii = used = 0; ii < channel->members.used; ++ii) { struct modeNode *mn = channel->members.list[ii]; @@ -4597,53 +4729,128 @@ static CHANSERV_FUNC(cmd_resync) if(IsService(mn->user)) continue; + uData = GetChannelAccess(cData, mn->user->handle_info); - if(uData && uData->access >= UL_OP ) - { - if(!(mn->modes & MODE_CHANOP)) - { - changes->args[used].mode = MODE_CHANOP; - changes->args[used++].u.member = mn; - } - } - else if(uData && uData->access >= UL_HALFOP) + + /* If the channel is in no-mode mode, de-mode EVERYONE */ + if(cData->chOpts[chAutomode] == 'n') { - if(mn->modes & MODE_CHANOP) - { - changes->args[used].mode = MODE_REMOVE | MODE_CHANOP; - changes->args[used++].u.member = mn; - } - if(!(mn->modes & MODE_HALFOP)) - { - changes->args[used].mode = MODE_HALFOP; - changes->args[used++].u.member = mn; - } + if(mn->modes) + { + changes->args[used].mode = MODE_REMOVE | mn->modes; + changes->args[used++].u.member = mn; + } } - else if(uData && uData->access >= UL_PEON ) + else /* Give various userlevels their modes.. */ { - if(mn->modes & MODE_CHANOP) + if(uData && uData->access >= UL_OP ) { - changes->args[used].mode = MODE_REMOVE | MODE_CHANOP; - changes->args[used++].u.member = mn; + if(!(mn->modes & MODE_CHANOP)) + { + changes->args[used].mode = MODE_CHANOP; + changes->args[used++].u.member = mn; + } } - if(mn->modes & MODE_HALFOP) + else if(uData && uData->access >= UL_HALFOP) { - changes->args[used].mode = MODE_REMOVE | MODE_HALFOP; - changes->args[used++].u.member = mn; + if(mn->modes & MODE_CHANOP) + { + changes->args[used].mode = MODE_REMOVE | MODE_CHANOP; + changes->args[used++].u.member = mn; + } + if(!(mn->modes & MODE_HALFOP)) + { + changes->args[used].mode = MODE_HALFOP; + changes->args[used++].u.member = mn; + } } - /* Don't voice peons if were in mode m */ - if(!(mn->modes & MODE_VOICE) && cData->chOpts[chAutomode] != 'm') + else if(uData && uData->access >= UL_PEON ) { - changes->args[used].mode = MODE_VOICE; - changes->args[used++].u.member = mn; + if(mn->modes & MODE_CHANOP) + { + changes->args[used].mode = MODE_REMOVE | MODE_CHANOP; + changes->args[used++].u.member = mn; + } + if(mn->modes & MODE_HALFOP) + { + changes->args[used].mode = MODE_REMOVE | MODE_HALFOP; + changes->args[used++].u.member = mn; + } + /* Don't voice peons if were in mode m */ + if( cData->chOpts[chAutomode] == 'm') + { + if(mn->modes & MODE_VOICE) + { + changes->args[used].mode = MODE_REMOVE | MODE_VOICE; + changes->args[used++].u.member = mn; + } + } + /* otherwise, make user they do have voice */ + else if(!(mn->modes & MODE_VOICE)) + { + changes->args[used].mode = MODE_VOICE; + changes->args[used++].u.member = mn; + } } - } - else - { - if(mn->modes) + else /* They arnt on the userlist.. */ { - changes->args[used].mode = MODE_REMOVE | mn->modes; - changes->args[used++].u.member = mn; + /* If we voice everyone, but they dont.. */ + if(cData->chOpts[chAutomode] == 'v') + { + /* Remove anything except v */ + if(mn->modes & ~MODE_VOICE) + { + changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE); + changes->args[used++].u.member = mn; + } + /* Add v */ + if(!(mn->modes & MODE_VOICE)) + { + changes->args[used].mode = MODE_VOICE; + changes->args[used++].u.member = mn; + } + } + /* If we hop everyone, but they dont.. */ + else if(cData->chOpts[chAutomode] == 'h') + { + /* Remove anything except h */ + if(mn->modes & ~MODE_HALFOP) + { + changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_HALFOP); + changes->args[used++].u.member = mn; + } + /* Add h */ + if(!(mn->modes & MODE_HALFOP)) + { + changes->args[used].mode = MODE_HALFOP; + changes->args[used++].u.member = mn; + } + } + /* If we op everyone, but they dont.. */ + else if(cData->chOpts[chAutomode] == 'o') + { + /* Remove anything except h */ + if(mn->modes & ~MODE_CHANOP) + { + changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_CHANOP); + changes->args[used++].u.member = mn; + } + /* Add h */ + if(!(mn->modes & MODE_CHANOP)) + { + changes->args[used].mode = MODE_CHANOP; + changes->args[used++].u.member = mn; + } + } + /* they have no excuse for having modes, de-everything them */ + else + { + if(mn->modes) + { + changes->args[used].mode = MODE_REMOVE | mn->modes; + changes->args[used++].u.member = mn; + } + } } } } @@ -4741,16 +4948,16 @@ note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, st switch(ntype->set_access_type) { - case NOTE_SET_CHANNEL_ACCESS: - if(!user->handle_info) - return 0; - if(!(uData = GetChannelUser(channel->channel_info, user->handle_info))) - return 0; - return uData->access >= ntype->set_access.min_ulevel; - case NOTE_SET_CHANNEL_SETTER: - return check_user_level(channel, user, lvlSetters, 1, 0); - case NOTE_SET_PRIVILEGED: default: - return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv); + case NOTE_SET_CHANNEL_ACCESS: + if(!user->handle_info) + return 0; + if(!(uData = GetChannelUser(channel->channel_info, user->handle_info))) + return 0; + return uData->access >= ntype->set_access.min_ulevel; + case NOTE_SET_CHANNEL_SETTER: + return check_user_level(channel, user, lvlSetters, 1, 0); + case NOTE_SET_PRIVILEGED: default: + return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv); } } @@ -4896,7 +5103,8 @@ static CHANSERV_FUNC(cmd_events) report.reporter = chanserv; report.user = user; reply("CSMSG_EVENT_SEARCH_RESULTS", channel->name); - reply("CSMSG_BAR"); + if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN) + reply("CSMSG_BAR"); matches = log_entry_search(&discrim, log_report_entry, &report); if(matches) reply("MSG_MATCH_COUNT", matches); @@ -5232,8 +5440,9 @@ static CHANSERV_FUNC(cmd_search) if(action == search_print) { - reply("CSMSG_CHANNEL_SEARCH_RESULTS"); - reply("CSMSG_BAR"); + reply("CSMSG_CHANNEL_SEARCH_RESULTS"); + if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN) + reply("CSMSG_BAR"); } matches = chanserv_channel_search(search, action, user); @@ -5303,7 +5512,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) @@ -5784,8 +5993,9 @@ static CHANSERV_FUNC(cmd_set) if(argc < 2) { - reply("CSMSG_CHANNEL_OPTIONS", channel->name); - reply("CSMSG_BAR"); + reply("CSMSG_CHANNEL_OPTIONS", channel->name); + if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN) + reply("CSMSG_BAR"); for(ii = 0; ii < set_shows_list.used; ii++) { subcmd = set_shows_list.list[ii]; @@ -5973,13 +6183,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; @@ -6037,6 +6256,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 @@ -6045,18 +6266,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))) @@ -6079,6 +6329,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; @@ -6109,6 +6377,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; } @@ -6163,7 +6432,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); @@ -6298,13 +6567,13 @@ void eightball(char *outcome, int method, unsigned int seed) case 4: strcpy(tmp, "A gross kind of mucky %s."); break; case 5: strcpy(tmp, "Brilliant whiteish %s."); - break; - case 6: case 7: case 8: case 9: strcpy(tmp, "%s."); - break; - case 10: strcpy(tmp, "Solid %s."); - break; - case 11: strcpy(tmp, "Transparent %s."); - break; + break; + case 6: case 7: case 8: case 9: strcpy(tmp, "%s."); + break; + case 10: strcpy(tmp, "Solid %s."); + break; + case 11: strcpy(tmp, "Transparent %s."); + break; default: strcpy(outcome, "An invalid random number was generated."); return; } @@ -6521,7 +6790,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) @@ -6851,7 +7120,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, @@ -6862,12 +7131,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; @@ -7259,7 +7528,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; @@ -7280,6 +7549,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) { @@ -7289,6 +7559,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) @@ -7360,10 +7639,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; @@ -7489,6 +7794,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); @@ -7650,6 +7961,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); @@ -7698,6 +8011,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) { @@ -7722,6 +8056,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); @@ -7774,21 +8110,22 @@ chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype) saxdb_start_record(ctx, ntype->name, 0); switch(ntype->set_access_type) { - case NOTE_SET_CHANNEL_ACCESS: - saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel); - break; - case NOTE_SET_CHANNEL_SETTER: - saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1); - break; - case NOTE_SET_PRIVILEGED: default: - saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv); - break; + case NOTE_SET_CHANNEL_ACCESS: + saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel); + break; + case NOTE_SET_CHANNEL_SETTER: + saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1); + break; + case NOTE_SET_PRIVILEGED: default: + saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv); + break; } switch(ntype->visible_type) { - case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break; - case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break; - case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break; + case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break; + case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break; + case NOTE_VIS_PRIVILEGED: + default: str = KEY_NOTE_VIS_PRIVILEGED; break; } saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str); saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);