X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/aa222db2f6f30aaca4ea75578a8d72a4000f1901..9666b12d7db80e0902efd9b92d95e4c4e94712ef:/src/chanserv.c diff --git a/src/chanserv.c b/src/chanserv.c index b29c0a4..e61cb35 100644 --- a/src/chanserv.c +++ b/src/chanserv.c @@ -56,9 +56,13 @@ #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout" /* ChanServ database */ +#define KEY_VERSION_CONTROL "version_control" #define KEY_CHANNELS "channels" #define KEY_NOTE_TYPES "note_types" +/* version control paramiter */ +#define KEY_VERSION_NUMBER "version_number" + /* Note type parameters */ #define KEY_NOTE_OPSERV_ACCESS "opserv_access" #define KEY_NOTE_CHANNEL_ACCESS "channel_access" @@ -191,6 +195,11 @@ static const struct message_entry msgtab[] = { { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." }, { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." }, { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." }, + { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, i will finish adding them automatically." }, + { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." }, + { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing */ + /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */ + { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here, %s would like to add you to my userlist in channel %s, but you are not auth'd to $b$N$b. Please auth now, and you will be added. If you do not have an accont, type /msg $N help register" }, { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." }, { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." }, @@ -242,9 +251,11 @@ static const struct message_entry msgtab[] = { { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." }, { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." }, { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." }, + /* { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." }, { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." }, { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." }, + */ { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." }, { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." }, { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" }, @@ -257,24 +268,29 @@ static const struct message_entry msgtab[] = { { "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_GIVE_HALFOPS", "$bGiveHalfOps $b %d" }, + */ { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" }, { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" }, { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" }, { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d" }, + /* { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" }, + */ { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" }, { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" }, { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" }, { "CSMSG_SET_SETTERS", "$bSetters $b %d" }, { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" }, + { "CSMSG_SET_VOICE", "$bvoice $b %d - %s" }, { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" }, { "CSMSG_SET_TOYS", "$bToys $b %d - %s" }, { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" }, { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" }, - { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" }, - { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" }, + { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" }, + { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" }, { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" }, { "CSMSG_USET_INFO", "$bInfo $b %s" }, @@ -288,6 +304,9 @@ static const struct message_entry msgtab[] = { { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." }, { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." }, { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." }, + { "CSMSG_VOICE_NONE", "Noone will be auto-voiced" }, + { "CSMSG_VOICE_PEON", "PEONs will be auto-voiced" }, + { "CSMSG_VOICE_ALL", "Everyone will be auto-voiced" }, { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." }, { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." }, { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." }, @@ -488,6 +507,8 @@ dict_t note_types; int off_channel; static dict_t plain_dnrs, mask_dnrs, handle_dnrs; static struct log_type *CS_LOG; +struct adduserPending* adduser_pendings = NULL; +unsigned int adduser_pendings_count = 0; static struct { @@ -616,7 +637,11 @@ static const struct { struct charOptionValues { char value; char *format_name; -} protectValues[] = { +} voiceValues[] = { + { 'n', "CSMSG_VOICE_NONE" }, + { 'p', "CSMSG_VOICE_PEON" }, + { 'a', "CSMSG_VOICE_ALL" } +}, protectValues[] = { { 'a', "CSMSG_PROTECT_ALL" }, { 'e', "CSMSG_PROTECT_EQUAL" }, { 'l', "CSMSG_PROTECT_LOWER" }, @@ -646,8 +671,9 @@ static const struct { unsigned char count; struct charOptionValues *values; } charOptions[] = { - { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues }, - { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues }, + { "CSMSG_SET_VOICE", "voice", 'p', 99, ArrayLength(voiceValues), voiceValues }, + { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues }, + { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues }, { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues }, { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues } }; @@ -656,6 +682,9 @@ struct userData *helperList; struct chanData *channelList; static struct module *chanserv_module; static unsigned int userCount; +unsigned int chanserv_read_version = 0; /* db version control */ + +#define CHANSERV_DB_VERSION 2 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0) #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0) @@ -1163,6 +1192,7 @@ add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned ud->u_next->u_prev = ud; ud->handle->channels = ud; + ud->flags = USER_FLAGS_DEFAULT; return ud; } @@ -1196,6 +1226,110 @@ del_channel_user(struct userData *user, int do_gc) unregister_channel(channel, "lost all users."); } +static struct adduserPending* +add_adduser_pending(struct chanNode *channel, struct userNode *user, int level) +{ + struct adduserPending *ap; + ap = calloc(1,sizeof(struct adduserPending)); + ap->channel = channel; + ap->user = user; + ap->level = level; + ap->created = time(NULL); + + /* ap->prev defaults to NULL already.. */ + ap->next = adduser_pendings; + if(adduser_pendings) + adduser_pendings->prev = ap; + adduser_pendings = ap; + adduser_pendings_count++; + return(ap); +} + +static void +del_adduser_pending(struct adduserPending *ap) +{ + if(ap->prev) + ap->prev->next = ap->next; + else + adduser_pendings = ap->next; + + if(ap->next) + ap->next->prev = ap->prev; + free(ap); +} + +static void expire_adduser_pending(); + +/* find_adduser_pending(channel, user) will find an arbitrary record + * from user, channel, or user and channel. + * if user or channel are NULL, they will match any records. + */ +static struct adduserPending* +find_adduser_pending(struct chanNode *channel, struct userNode *user) +{ + struct adduserPending *ap; + + expire_adduser_pending(); /* why not here.. */ + + if(!channel && !user) /* 2 nulls matches all */ + return(adduser_pendings); + for(ap = adduser_pendings;ap;ap = ap->next) + { + if((channel == ap->channel && (user == NULL || user == ap->user)) || (user==ap->user && channel==NULL)) + return ap; + } + return NULL; +} + + +/* Remove all pendings for a user or channel + * + * called in nickserv.c DelUser() and proto-* unregister_channel() + */ +void +wipe_adduser_pending(struct chanNode *channel, struct userNode *user) +{ + struct adduserPending *ap; + + /* So this is a bit wastefull, i hate dealing with linked lists. + * if its a problem we'll rewrite it right */ + while((ap = find_adduser_pending(channel, user))) { + del_adduser_pending(ap); + } +} + +/* Called from nickserv.c cmd_auth after someone auths */ +void +process_adduser_pending(struct userNode *user) +{ + struct adduserPending *ap; + while((ap = find_adduser_pending(NULL, user))) + { + struct userData *actee; + actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL); + scan_user_presence(actee, NULL); + del_adduser_pending(ap); + } +} + +static void +expire_adduser_pending() +{ + struct adduserPending *ap, *ap_next; + ap = adduser_pendings; + while(ap) + { + if((ap->created + ADDUSER_PENDING_EXPIRE) < time(NULL)) + { /* expire it */ + ap_next = ap->next; /* save next */ + del_adduser_pending(ap); /* free and relink */ + ap = ap_next; /* advance */ + } + else + ap = ap->next; + } +} + static void expire_ban(void *data); static struct banData* @@ -1313,6 +1447,7 @@ unregister_channel(struct chanData *channel, const char *reason) - Channel users. - Channel bans. - Channel suspension data. + - adduser_pending data. - Timeq entries. (Except timed bans, which are handled elsewhere.) */ @@ -1328,6 +1463,8 @@ unregister_channel(struct chanData *channel, const char *reason) mod_chanmode_announce(chanserv, channel->channel, &change); } + wipe_adduser_pending(channel->channel, NULL); + while(channel->users) del_channel_user(channel->users, 0); @@ -1421,7 +1558,7 @@ protect_user(const struct userNode *victim, const struct userNode *aggressor, st /* Don't protect if no one is to be protected, someone is attacking himself, or if the aggressor is an IRC Operator. */ - if(protect == 'n' || victim == aggressor || IsOper(aggressor)) + if(protect == 'n' || victim == aggressor /* Opers dont get special treatment :/ || IsOper(aggressor) */) return 0; /* Don't protect if the victim isn't authenticated (because they @@ -1464,7 +1601,7 @@ validate_op(struct userNode *user, struct chanNode *channel, struct userNode *vi struct userData *cs_victim; if((!(cs_victim = GetChannelUser(cData, victim->handle_info)) - || (cs_victim->access < cData->lvlOpts[lvlGiveOps])) + || (cs_victim->access < UL_OP /* cData->lvlOpts[lvlGiveOps]*/)) && !check_user_level(channel, user, lvlEnfOps, 0, 0)) { send_message(user, chanserv, "CSMSG_OPBY_LOCKED"); @@ -1481,7 +1618,7 @@ validate_halfop(struct userNode *user, struct chanNode *channel, struct userNode struct userData *cs_victim; if((!(cs_victim = GetChannelUser(cData, victim->handle_info)) - || (cs_victim->access < cData->lvlOpts[lvlGiveHalfOps])) + || (cs_victim->access < UL_HALFOP /* cData->lvlOpts[lvlGiveHalfOps] */)) && !check_user_level(channel, user, lvlEnfHalfOps, 0, 0)) { send_message(user, chanserv, "CSMSG_HOPBY_LOCKED"); @@ -2298,7 +2435,30 @@ static CHANSERV_FUNC(cmd_adduser) } if(!(handle = modcmd_get_handle_info(user, argv[1]))) + { + // 'kevin must first authenticate with AuthServ.' is sent to user + struct userNode *unode; + unode = GetUserH(argv[1]); /* find user struct by nick */ + if(unode) + { + if(find_adduser_pending(channel, unode)) { + reply("CSMSG_ADDUSER_PENDING_ALREADY", channel->name); + } + else { + if(IsInChannel(channel, unode)) { + reply("CSMSG_ADDUSER_PENDING"); + add_adduser_pending(channel, unode, access); + send_message_type(1,unode, chanserv, "CSMSG_ADDUSER_PENDING_TARGET", user->nick, channel->name); + } + /* this results in user must auth AND not in chan errors. too confusing.. + else { + reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name); + } + */ + } + } return 0; + } if((actee = GetTrueChannelAccess(channel->channel_info, handle))) { @@ -2620,17 +2780,17 @@ static CHANSERV_FUNC(cmd_up) reply("CSMSG_GODMODE_UP", argv[0]); return 0; } - else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps]) + else if(uData->access >= UL_OP /*channel->channel_info->lvlOpts[lvlGiveOps]*/) { change.args[0].mode = MODE_CHANOP; errmsg = "CSMSG_ALREADY_OPPED"; } - else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveHalfOps]) + else if(uData->access >= UL_HALFOP /*channel->channel_info->lvlOpts[lvlGiveHalfOps]*/) { change.args[0].mode = MODE_HALFOP; errmsg = "CSMSG_ALREADY_HALFOPPED"; } - else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice]) + else if(uData->access >= UL_PEON && (channel->channel_info->chOpts[chVoice] == 'p' || channel->channel_info->chOpts[chVoice] == 'a')) { change.args[0].mode = MODE_VOICE; errmsg = "CSMSG_ALREADY_VOICED"; @@ -3336,17 +3496,17 @@ static CHANSERV_FUNC(cmd_myaccess) continue; sbuf.used = 0; string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access); - if(uData->flags != USER_AUTO_OP) + if(uData->flags == USER_AUTO_OP) string_buffer_append(&sbuf, ','); if(IsUserSuspended(uData)) string_buffer_append(&sbuf, 's'); if(IsUserAutoOp(uData)) { - if(uData->access >= cData->lvlOpts[lvlGiveOps]) + if(uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/) string_buffer_append(&sbuf, 'o'); - else if(uData->access >= cData->lvlOpts[lvlGiveHalfOps]) + else if(uData->access >= UL_HALFOP /*cData->lvlOpts[lvlGiveHalfOps]*/) string_buffer_append(&sbuf, 'h'); - else if(uData->access >= cData->lvlOpts[lvlGiveVoice]) + else if(uData->access >= UL_PEON /*cData->lvlOpts[lvlGiveVoice]*/) string_buffer_append(&sbuf, 'v'); } if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe])) @@ -3626,8 +3786,6 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg lData.table.contents[matches] = ary; /* ary[0] = strtab(uData->access);*/ ary[0] = user_level_name_from_level(uData->access); - /* TODO: replace above with func that returns static string - * of userlevel for that level. eg OP/MANAGER etc. -rubin */ ary[1] = strtab(uData->access); ary[2] = uData->handle->handle; if(uData->present) @@ -3655,6 +3813,14 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg return 1; } +static CHANSERV_FUNC(cmd_pending) +{ + struct adduserPending *ap; + for(ap = adduser_pendings;ap;ap = ap->next) + reply("CSMSG_ADDUSER_PENDING_LIST", ap->channel->name, ap->user->nick); + return 1; +} + static CHANSERV_FUNC(cmd_users) { return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER); @@ -3783,20 +3949,71 @@ static CHANSERV_FUNC(cmd_bans) return 1; } +/* bad_topic + * + * return + if the user does NOT have the right to set the topic, and + * the topic is changed. + */ static int bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic) { struct chanData *cData = channel->channel_info; if(check_user_level(channel, user, lvlEnfTopic, 1, 0)) return 0; - if(cData->topic_mask) - return !match_ircglob(new_topic, cData->topic_mask); else if(cData->topic) return irccasecmp(new_topic, cData->topic); else return 0; } +/* conform_topic + * + * Makes a givin topic fit into a givin topic mask and returns + * the results. + * + * topic_mask - the mask to conform to + * topic - the topic to make conform + * new_topic - the pre-allocated char* to put the new topic into + * + * modifies: new_topic + */ +void +conform_topic(char* topic_mask, char* topic, char *new_topic) +{ + //char *topic_mask = cData->topic_mask; + char tchar; + int pos=0, starpos=-1, dpos=0, len; + + while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN)) + { + switch(tchar) + { + case '*': + if(starpos != -1) + { + strcpy(new_topic, ""); + return; + } + len = strlen(topic); + if((dpos + len) > TOPICLEN) + len = TOPICLEN + 1 - dpos; + memcpy(new_topic+dpos, topic, len); + dpos += len; + starpos = pos; + break; + case '\\': tchar = topic_mask[pos++]; /* and fall through */ + default: new_topic[dpos++] = tchar; break; + } + } + if((dpos > TOPICLEN) || tchar) + { + strcpy(new_topic, ""); + return; + } + new_topic[dpos] = 0; + return; +} + static CHANSERV_FUNC(cmd_topic) { struct chanData *cData; @@ -3805,12 +4022,12 @@ static CHANSERV_FUNC(cmd_topic) cData = channel->channel_info; if(argc < 2) { - if(cData->topic) - { - SetChannelTopic(channel, chanserv, cData->topic, 1); - reply("CSMSG_TOPIC_SET", cData->topic); + if(cData->topic) + { + SetChannelTopic(channel, chanserv, cData->topic, 1); + reply("CSMSG_TOPIC_SET", cData->topic); return 1; - } + } reply("CSMSG_NO_TOPIC", channel->name); return 0; @@ -3820,48 +4037,34 @@ static CHANSERV_FUNC(cmd_topic) /* If they say "!topic *", use an empty topic. */ if((topic[0] == '*') && (topic[1] == 0)) topic[0] = 0; + if(bad_topic(channel, user, topic)) { - char *topic_mask = cData->topic_mask; - if(topic_mask) + reply("CSMSG_TOPIC_LOCKED", channel->name); + return 0; + } + else + { + /* If there is a topicmask set, and the new topic doesnt match, make it */ + if(cData->topic_mask && !match_ircglob(topic, cData->topic_mask)) { - char new_topic[TOPICLEN+1], tchar; - int pos=0, starpos=-1, dpos=0, len; + char *topic_mask = cData->topic_mask; + char new_topic[TOPICLEN+1]; - while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN)) + /* make a new topic fitting mask */ + conform_topic(topic_mask, topic, new_topic); + if(!*new_topic) { - switch(tchar) - { - case '*': - if(starpos != -1) - goto bad_mask; - len = strlen(topic); - if((dpos + len) > TOPICLEN) - len = TOPICLEN + 1 - dpos; - memcpy(new_topic+dpos, topic, len); - dpos += len; - starpos = pos; - break; - case '\\': tchar = topic_mask[pos++]; /* and fall through */ - default: new_topic[dpos++] = tchar; break; - } - } - if((dpos > TOPICLEN) || tchar) - { - bad_mask: + /* Topic couldnt fit into mask, was too long */ reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask); reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN); return 0; } - new_topic[dpos] = 0; SetChannelTopic(channel, chanserv, new_topic, 1); - } else { - reply("CSMSG_TOPIC_LOCKED", channel->name); - return 0; } + else /* No mask set, just set the topic */ + SetChannelTopic(channel, chanserv, topic, 1); } - else - SetChannelTopic(channel, chanserv, topic, 1); if(check_user_level(channel, user, lvlTopicSnarf, 1, 0)) { @@ -4038,7 +4241,7 @@ static CHANSERV_FUNC(cmd_info) reply("CSMSG_BAR"); uData = GetChannelUser(cData, user->handle_info); - if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps])) + if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/)) { mod_chanmode_format(&cData->modes, modes); reply("CSMSG_CHANNEL_TOPIC", cData->topic); @@ -4243,8 +4446,7 @@ static CHANSERV_FUNC(cmd_resync) continue; uData = GetChannelAccess(cData, mn->user->handle_info); - if(!cData->lvlOpts[lvlGiveOps] - || (uData && uData->access >= cData->lvlOpts[lvlGiveOps])) + if(uData && uData->access >= UL_OP /* cData->lvlOpts[lvlGiveOps]*/) { if(!(mn->modes & MODE_CHANOP)) { @@ -4252,8 +4454,7 @@ static CHANSERV_FUNC(cmd_resync) changes->args[used++].u.member = mn; } } - else if(!cData->lvlOpts[lvlGiveHalfOps] - || (uData && uData->access >= cData->lvlOpts[lvlGiveHalfOps])) + else if(uData && uData->access >= UL_HALFOP /*cData->lvlOpts[lvlGiveHalfOps]*/) { if(!(mn->modes & MODE_HALFOP)) { @@ -4271,8 +4472,7 @@ static CHANSERV_FUNC(cmd_resync) changes->args[used++].u.member = mn; } } - else if(!cData->lvlOpts[lvlGiveVoice] - || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice])) + else if(uData && uData->access >= UL_PEON /* cData->lvlOpts[lvlGiveVoice]*/) { if(mn->modes & MODE_CHANOP) { @@ -5232,6 +5432,7 @@ channel_level_option(enum levelOption option, struct userNode *user, struct chan } switch(option) { + /* removing these level sets.. case lvlGiveVoice: if(value > cData->lvlOpts[lvlGiveOps]) { @@ -5253,6 +5454,7 @@ channel_level_option(enum levelOption option, struct userNode *user, struct chan return 0; } break; + */ case lvlSetters: /* This test only applies to owners, since non-owners * trying to set an option to above their level get caught @@ -5282,7 +5484,7 @@ static MODCMD_FUNC(chan_opt_enfhalfops) { return channel_level_option(lvlEnfHalfOps, CSFUNC_ARGS); } - +/* static MODCMD_FUNC(chan_opt_giveops) { return channel_level_option(lvlGiveOps, CSFUNC_ARGS); @@ -5292,7 +5494,7 @@ static MODCMD_FUNC(chan_opt_givehalfops) { return channel_level_option(lvlGiveHalfOps, CSFUNC_ARGS); } - +*/ static MODCMD_FUNC(chan_opt_enfmodes) { return channel_level_option(lvlEnfModes, CSFUNC_ARGS); @@ -5323,10 +5525,12 @@ static MODCMD_FUNC(chan_opt_userinfo) return channel_level_option(lvlUserInfo, CSFUNC_ARGS); } +/* static MODCMD_FUNC(chan_opt_givevoice) { return channel_level_option(lvlGiveVoice, CSFUNC_ARGS); } +*/ static MODCMD_FUNC(chan_opt_topicsnarf) { @@ -5378,6 +5582,11 @@ channel_multiple_option(enum charOption option, struct userNode *user, struct ch return 1; } +static MODCMD_FUNC(chan_opt_voice) +{ + return channel_multiple_option(chVoice, CSFUNC_ARGS); +} + static MODCMD_FUNC(chan_opt_protect) { return channel_multiple_option(chProtect, CSFUNC_ARGS); @@ -5499,7 +5708,7 @@ user_binary_option(char *name, unsigned long mask, struct userNode *user, struct return 1; } -static MODCMD_FUNC(user_opt_noautoop) +static MODCMD_FUNC(user_opt_autoop) { struct userData *uData; @@ -5509,10 +5718,11 @@ static MODCMD_FUNC(user_opt_noautoop) reply("CSMSG_NOT_USER", channel->name); return 0; } - if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps]) - return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS); + if(uData->access < UL_OP /*channel->channel_info->lvlOpts[lvlGiveOps]*/) + return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS); else - return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS); + return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP, CSFUNC_ARGS); + /* TODO: add halfops error message? or is the op one generic enough? */ } static MODCMD_FUNC(user_opt_autoinvite) @@ -5578,7 +5788,7 @@ static CHANSERV_FUNC(cmd_uset) { char *options[] = { - "NoAutoOp", "AutoInvite", "Info" + "AutoOp", "AutoInvite", "Info" }; if(!uset_shows_list.size) @@ -6123,13 +6333,10 @@ handle_join(struct modeNode *mNode) if(channel->join_flooded) { - /* don't automatically give ops or voice during a join flood */ + /* don't automatically give non users ops or voice during a join flood */ } - else if(cData->lvlOpts[lvlGiveOps] == 0) - modes |= MODE_CHANOP; - else if(cData->lvlOpts[lvlGiveHalfOps] == 0) - modes |= MODE_HALFOP; - else if(cData->lvlOpts[lvlGiveVoice] == 0) + /* EVERYONE is to get voice */ + else if(cData->chOpts[chVoice] == 'a') modes |= MODE_VOICE; greeting = cData->greeting; @@ -6153,14 +6360,14 @@ handle_join(struct modeNode *mNode) uData = GetTrueChannelAccess(cData, handle); if(uData && !IsUserSuspended(uData)) { - /* Ops and above were handled by the above case. */ + /* non users getting voice are handled above. */ if(IsUserAutoOp(uData)) { - if(uData->access >= cData->lvlOpts[lvlGiveOps]) + if(uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/) modes |= MODE_CHANOP; - if(uData->access >= cData->lvlOpts[lvlGiveHalfOps]) + if(uData->access >= UL_HALFOP /*cData->lvlOpts[lvlGiveHalfOps]*/) modes |= MODE_HALFOP; - else if(uData->access >= cData->lvlOpts[lvlGiveVoice]) + else if(uData->access >= UL_PEON && cData->chOpts[chVoice] == 'p') modes |= MODE_VOICE; } if(uData->access >= UL_PRESENT) @@ -6234,11 +6441,11 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle)) if(IsUserAutoOp(channel)) { - if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps]) + if(channel->access >= UL_OP /* cn->channel_info->lvlOpts[lvlGiveOps] */) change.args[0].mode = MODE_CHANOP; - else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveHalfOps]) + else if(channel->access >= UL_HALFOP /* cn->channel_info->lvlOpts[lvlGiveHalfOps]*/) change.args[0].mode = MODE_HALFOP; - else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice]) + else if(channel->access >= UL_PEON /* cn->channel_info->lvlOpts[lvlGiveVoice]*/) change.args[0].mode = MODE_VOICE; else change.args[0].mode = 0; @@ -6366,14 +6573,30 @@ handle_topic(struct userNode *user, struct chanNode *channel, const char *old_to cData = channel->channel_info; 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); - if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask)) - SetChannelTopic(channel, chanserv, old_topic, 1); - else if(cData->topic) - SetChannelTopic(channel, chanserv, cData->topic, 1); + SetChannelTopic(channel, chanserv, old_topic, 1); return 1; } + /* If there is a topic mask set, and the new topic doesnt match, + * set the topic to mask + new_topic */ + if(cData->topic_mask && !match_ircglob(channel->topic, cData->topic_mask)) + { + char new_topic[TOPICLEN+1]; + conform_topic(cData->topic_mask, channel->topic, new_topic); + if(*new_topic) + { + SetChannelTopic(channel, 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); + send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask); + send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN); + return 1; + } + } /* With topicsnarf, grab the topic and save it as the default topic. */ if(check_user_level(channel, user, lvlTopicSnarf, 0, 0)) { @@ -6650,7 +6873,7 @@ chanserv_conf_read(void) /* free form text */ "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes", /* options based on user level */ - "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveHalfOps", "GiveOps", "EnfOps", + "PubCmd", "InviteMe", "UserInfo",/* "GiveVoice", "GiveHalfOps", "GiveOps", */ "EnfOps", "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers", /* multiple choice options */ "CtcpReaction", "Protect", "Toys", "TopicRefresh", @@ -6786,6 +7009,18 @@ 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; + + /* Upgrade: set autoop to the inverse of noautoop */ + if(chanserv_read_version < 2) + { + /* if noautoop is true, set autoop false, and vice versa */ + if(uData->flags & USER_NOAUTO_OP) + uData->flags = uData->flags & ~USER_AUTO_OP; + else + uData->flags = uData->flags | USER_AUTO_OP; + log_module(CS_LOG, LOG_INFO, "UPGRADE: to db version 2 from %u. Changing flag to %d for %s in %s.", chanserv_read_version, uData->flags, key, chan->channel->name); + } + } static void @@ -7086,12 +7321,26 @@ chanserv_dnr_read(const char *key, struct record_data *hir) dnr->set = 0; } +static void +chanserv_version_read(struct dict *section) +{ + /* global var.. */ + char *str; + str = database_get_data(section, KEY_VERSION_NUMBER, RECDB_QSTRING); + if(str) + chanserv_read_version = atoi(str); + log_module(CS_LOG, LOG_DEBUG, "Chanserv db version is %d.", chanserv_read_version); +} + static int chanserv_saxdb_read(struct dict *database) { struct dict *section; dict_iterator_t it; + if((section = database_get_data(database, KEY_VERSION_CONTROL, RECDB_OBJECT))) + chanserv_version_read(section); + if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT))) for(it = dict_first(section); it; it = iter_next(it)) chanserv_note_type_read(iter_key(it), iter_data(it)); @@ -7290,6 +7539,11 @@ chanserv_saxdb_write(struct saxdb_context *ctx) dict_iterator_t it; struct chanData *channel; + /* Version Control*/ + saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1); + saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION); + saxdb_end_record(ctx); + /* Notes */ saxdb_start_record(ctx, KEY_NOTE_TYPES, 1); for(it = dict_first(note_types); it; it = iter_next(it)) @@ -7368,6 +7622,8 @@ init_chanserv(const char *nick) dict_set_free_data(plain_dnrs, free); mask_dnrs = dict_new(); dict_set_free_data(mask_dnrs, free); + //TODO + //adduser_pending reg_svccmd_unbind_func(handle_svccmd_unbind); chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable); @@ -7380,6 +7636,8 @@ init_chanserv(const char *nick) DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL); DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL); + DEFINE_COMMAND(pending, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL); + DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL); DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL); @@ -7479,13 +7737,16 @@ init_chanserv(const char *nick) DEFINE_CHANNEL_OPTION(modes); DEFINE_CHANNEL_OPTION(enfops); DEFINE_CHANNEL_OPTION(enfhalfops); - DEFINE_CHANNEL_OPTION(giveops); + /*DEFINE_CHANNEL_OPTION(giveops); DEFINE_CHANNEL_OPTION(givehalfops); + */ + DEFINE_CHANNEL_OPTION(voice); DEFINE_CHANNEL_OPTION(protect); DEFINE_CHANNEL_OPTION(enfmodes); DEFINE_CHANNEL_OPTION(enftopic); DEFINE_CHANNEL_OPTION(pubcmd); - DEFINE_CHANNEL_OPTION(givevoice); + /*DEFINE_CHANNEL_OPTION(givevoice); + */ DEFINE_CHANNEL_OPTION(userinfo); DEFINE_CHANNEL_OPTION(dynlimit); DEFINE_CHANNEL_OPTION(topicsnarf); @@ -7504,12 +7765,12 @@ init_chanserv(const char *nick) modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL); /* User options */ - DEFINE_USER_OPTION(noautoop); DEFINE_USER_OPTION(autoinvite); DEFINE_USER_OPTION(info); + DEFINE_USER_OPTION(autoop); /* Alias uset autovoice to uset autoop. */ - modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL); + modcmd_register(chanserv_module, "uset autovoice", user_opt_autoop, 1, 0, NULL); note_types = dict_new(); dict_set_free_data(note_types, chanserv_deref_note_type); @@ -7520,6 +7781,7 @@ init_chanserv(const char *nick) service_register(chanserv)->trigger = '!'; reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check); } + saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write); if(chanserv_conf.channel_expire_frequency)