X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fs_user.c;h=1f0e0d92ddd990ff543c0445af741d7d72464555;hb=c3e49ba3f999d28a480f8f66cc633d9ea968099f;hp=141c0f8a8854ed57cd33b9ec798f9f85c44e8a62;hpb=71719a0aca6a5adcf075025ef0f298ced6f3fe9e;p=ircu2.10.12-pk.git diff --git a/ircd/s_user.c b/ircd/s_user.c index 141c0f8..1f0e0d9 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -34,7 +34,6 @@ #include "hash.h" #include "ircd.h" #include "ircd_alloc.h" -#include "ircd_auth.h" #include "ircd_chattr.h" #include "ircd_features.h" #include "ircd_log.h" @@ -51,6 +50,7 @@ #include "parse.h" #include "querycmds.h" #include "random.h" +#include "s_auth.h" #include "s_bsd.h" #include "s_conf.h" #include "s_debug.h" @@ -118,6 +118,7 @@ void free_user(struct User* user) assert(0 == user->channel); MyFree(user); + assert(userCount>0); --userCount; } } @@ -304,40 +305,6 @@ int hunt_server_prio_cmd(struct Client *from, const char *cmd, const char *tok, } -/** Copy a cleaned-up version of a username. - * Replace all instances of '~' and "invalid" username characters - * (!isIrcUi()) with underscores, truncating at USERLEN or the first - * control character. \a dest and \a source may be the same buffer. - * @param[out] dest Destination buffer. - * @param[in] source Source buffer. - * @param[in] tilde If non-zero, prepend a '~' to \a dest. - */ -static char *clean_user_id(char *dest, char *source, int tilde) -{ - char ch; - char *d = dest; - char *s = source; - int rlen = USERLEN; - - ch = *s++; /* Store first character to copy: */ - if (tilde) - { - *d++ = '~'; /* If `dest' == `source', then this overwrites `ch' */ - --rlen; - } - while (ch && !IsCntrl(ch) && rlen--) - { - char nch = *s++; /* Store next character to copy */ - *d++ = IsUserChar(ch) ? ch : '_'; /* This possibly overwrites it */ - if (nch == '~') - ch = '_'; - else - ch = nch; - } - *d = 0; - return dest; -} - /* * register_user * @@ -369,29 +336,13 @@ static char *clean_user_id(char *dest, char *source, int tilde) * * @param[in] cptr Client who introduced the user. * @param[in,out] sptr Client who has been fully introduced. - * @param[in] nick Client's new nickname. - * @param[in] username Client's username. * @return Zero or CPTR_KILLED. */ -int register_user(struct Client *cptr, struct Client *sptr, - const char *nick, char *username) +int register_user(struct Client *cptr, struct Client *sptr) { - struct ConfItem* aconf; char* parv[4]; char* tmpstr; - char* tmpstr2; - char c = 0; /* not alphanum */ - char d = 'a'; /* not a digit */ - short upper = 0; - short lower = 0; - short pos = 0; - short leadcaps = 0; - short other = 0; - short digits = 0; - short badid = 0; - short digitgroups = 0; struct User* user = cli_user(sptr); - int killreason; char ip_base64[25]; user->last = CurrentTime; @@ -400,177 +351,29 @@ int register_user(struct Client *cptr, struct Client *sptr, if (MyConnect(sptr)) { - static time_t last_too_many1; - static time_t last_too_many2; - assert(cptr == sptr); - assert(cli_unreg(sptr) == 0); - if (!IsIAuthed(sptr)) { - if (iauth_active) - return iauth_start_client(iauth_active, sptr); - else - SetIAuthed(sptr); - } - switch (conf_check_client(sptr)) - { - case ACR_OK: - break; - case ACR_NO_AUTHORIZATION: - sendto_opmask_butone(0, SNO_UNAUTH, "Unauthorized connection from %s.", - get_client_name(sptr, HIDE_IP)); - ++ServerStats->is_ref; - return exit_client(cptr, sptr, &me, - "No Authorization - use another server"); - case ACR_TOO_MANY_IN_CLASS: - if (CurrentTime - last_too_many1 >= (time_t) 60) - { - last_too_many1 = CurrentTime; - sendto_opmask_butone(0, SNO_TOOMANY, "Too many connections in " - "class %s for %s.", get_client_class(sptr), - get_client_name(sptr, SHOW_IP)); - } - ++ServerStats->is_ref; - IPcheck_connect_fail(sptr); - return exit_client(cptr, sptr, &me, - "Sorry, your connection class is full - try " - "again later or try another server"); - case ACR_TOO_MANY_FROM_IP: - if (CurrentTime - last_too_many2 >= (time_t) 60) - { - last_too_many2 = CurrentTime; - sendto_opmask_butone(0, SNO_TOOMANY, "Too many connections from " - "same IP for %s.", - get_client_name(sptr, SHOW_IP)); - } - ++ServerStats->is_ref; - return exit_client(cptr, sptr, &me, - "Too many connections from your host"); - case ACR_ALREADY_AUTHORIZED: - /* Can this ever happen? */ - case ACR_BAD_SOCKET: - ++ServerStats->is_ref; - IPcheck_connect_fail(sptr); - return exit_client(cptr, sptr, &me, "Unknown error -- Try again"); - } - ircd_strncpy(user->host, cli_sockhost(sptr), HOSTLEN); - ircd_strncpy(user->realhost, cli_sockhost(sptr), HOSTLEN); - aconf = cli_confs(sptr)->value.aconf; - clean_user_id(user->username, - HasFlag(sptr, FLAG_GOTID) ? cli_username(sptr) : username, - HasFlag(sptr, FLAG_DOID) && !HasFlag(sptr, FLAG_GOTID)); - - if ((user->username[0] == '\0') - || ((user->username[0] == '~') && (user->username[1] == '\000'))) - return exit_client(cptr, sptr, &me, "USER: Bogus userid."); + Count_unknownbecomesclient(sptr, UserStats); - if (!EmptyString(aconf->passwd) - && strcmp(cli_passwd(sptr), aconf->passwd)) - { - ServerStats->is_ref++; - send_reply(sptr, ERR_PASSWDMISMATCH); - return exit_client(cptr, sptr, &me, "Bad Password"); - } - memset(cli_passwd(sptr), 0, sizeof(cli_passwd(sptr))); - /* - * following block for the benefit of time-dependent K:-lines - */ - killreason = find_kill(sptr); - if (killreason) { - ServerStats->is_ref++; - return exit_client(cptr, sptr, &me, - (killreason == -1 ? "K-lined" : "G-lined")); - } /* - * Check for mixed case usernames, meaning probably hacked. Jon2 3-94 - * Summary of rules now implemented in this patch: Ensor 11-94 - * In a mixed-case name, if first char is upper, one more upper may - * appear anywhere. (A mixed-case name *must* have an upper first - * char, and may have one other upper.) - * A third upper may appear if all 3 appear at the beginning of the - * name, separated only by "others" (-/_/.). - * A single group of digits is allowed anywhere. - * Two groups of digits are allowed if at least one of the groups is - * at the beginning or the end. - * Only one '-', '_', or '.' is allowed (or two, if not consecutive). - * But not as the first or last char. - * No other special characters are allowed. - * Name must contain at least one letter. + * Set user's initial modes */ - tmpstr2 = tmpstr = (username[0] == '~' ? &username[1] : username); - while (*tmpstr && !badid) - { - pos++; - c = *tmpstr; - tmpstr++; - if (IsLower(c)) - { - lower++; - } - else if (IsUpper(c)) - { - upper++; - if ((leadcaps || pos == 1) && !lower && !digits) - leadcaps++; - } - else if (IsDigit(c)) - { - digits++; - if (pos == 1 || !IsDigit(d)) - { - digitgroups++; - if (digitgroups > 2) - badid = 1; - } - } - else if (c == '-' || c == '_' || c == '.') - { - other++; - if (pos == 1) - badid = 1; - else if (d == '-' || d == '_' || d == '.' || other > 2) - badid = 1; - } - else - badid = 1; - d = c; - } - if (!badid) - { - if (lower && upper && (!leadcaps || leadcaps > 3 || - (upper > 2 && upper > leadcaps))) - badid = 1; - else if (digitgroups == 2 && !(IsDigit(tmpstr2[0]) || IsDigit(c))) - badid = 1; - else if ((!lower && !upper) || !IsAlnum(c)) - badid = 1; - } - if (badid && (!HasFlag(sptr, FLAG_GOTID) || - strcmp(cli_username(sptr), username) != 0)) - { - ServerStats->is_ref++; - - send_reply(cptr, SND_EXPLICIT | ERR_INVALIDUSERNAME, - ":Your username is invalid."); - send_reply(cptr, SND_EXPLICIT | ERR_INVALIDUSERNAME, - ":Connect with your real username, in lowercase."); - send_reply(cptr, SND_EXPLICIT | ERR_INVALIDUSERNAME, - ":If your mail address were foo@bar.com, your username " - "would be foo."); - return exit_client(cptr, sptr, &me, "USER: Bad username"); + tmpstr = (char*)client_get_default_umode(sptr); + if (tmpstr) { + char *umodev[] = { NULL, NULL, NULL, NULL }; + umodev[2] = tmpstr; + set_user_mode(cptr, sptr, 3, umodev, ALLOWMODES_ANY); } - Count_unknownbecomesclient(sptr, UserStats); SetUser(sptr); cli_handler(sptr) = CLIENT_HANDLER; - release_dns_reply(sptr); SetLocalNumNick(sptr); send_reply(sptr, RPL_WELCOME, feature_str(FEAT_NETWORK), feature_str(FEAT_PROVIDER) ? " via " : "", feature_str(FEAT_PROVIDER) ? feature_str(FEAT_PROVIDER) : "", - nick); + cli_name(sptr)); /* * This is a duplicate of the NOTICE but see below... */ @@ -592,42 +395,10 @@ int register_user(struct Client *cptr, struct Client *sptr, cli_info(sptr), NumNick(cptr) /* two %s's */); IPcheck_connect_succeeded(sptr); - /* - * Set user's initial modes - */ - tmpstr = (char*)client_get_default_umode(sptr); - if (tmpstr) for (; *tmpstr; ++tmpstr) { - switch (*tmpstr) { - case 's': - if (!feature_bool(FEAT_HIS_SNOTICES_OPER_ONLY)) { - SetServNotice(sptr); - set_snomask(sptr, SNO_DEFAULT, SNO_SET); - } - break; - case 'w': - if (!feature_bool(FEAT_WALLOPS_OPER_ONLY)) - SetWallops(sptr); - break; - case 'i': - SetInvisible(sptr); - break; - case 'd': - SetDeaf(sptr); - break; - case 'g': - if (!feature_bool(FEAT_HIS_DEBUG_OPER_ONLY)) - SetDebug(sptr); - break; - } - } } else { - struct Client *acptr; - - ircd_strncpy(user->username, username, USERLEN); - Count_newremoteclient(UserStats, user->server); + struct Client *acptr = user->server; - acptr = user->server; if (cli_from(acptr) != cli_from(sptr)) { sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s (%s != %s[%s])", @@ -662,6 +433,12 @@ int register_user(struct Client *cptr, struct Client *sptr, SetUser(sptr); } + /* If they get both +x and an account during registration, hide + * their hostmask here. Calling hide_hostmask() from IAuth's + * account assignment causes a numeric reply during registration. + */ + if (HasHiddenHost(sptr)) + hide_hostmask(sptr, FLAG_HIDDENHOST); if (IsInvisible(sptr)) ++UserStats.inv_clients; if (IsOper(sptr)) @@ -672,7 +449,8 @@ int register_user(struct Client *cptr, struct Client *sptr, sendcmdto_flag_serv_butone(user->server, CMD_NICK, cptr, FLAG_IPV6, FLAG_LAST_FLAG, "%s %d %Tu %s %s %s%s%s%s %s%s :%s", - nick, cli_hopcount(sptr) + 1, cli_lastnick(sptr), + cli_name(sptr), cli_hopcount(sptr) + 1, + cli_lastnick(sptr), user->username, user->realhost, *tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "", iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 1), @@ -681,7 +459,8 @@ int register_user(struct Client *cptr, struct Client *sptr, sendcmdto_flag_serv_butone(user->server, CMD_NICK, cptr, FLAG_LAST_FLAG, FLAG_IPV6, "%s %d %Tu %s %s %s%s%s%s %s%s :%s", - nick, cli_hopcount(sptr) + 1, cli_lastnick(sptr), + cli_name(sptr), cli_hopcount(sptr) + 1, + cli_lastnick(sptr), user->username, user->realhost, *tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "", iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 0), @@ -691,6 +470,14 @@ int register_user(struct Client *cptr, struct Client *sptr, if (MyUser(sptr)) { static struct Flags flags; /* automatically initialized to zeros */ + /* To avoid sending +r to the client due to auth-on-connect, set + * the "old" FLAG_ACCOUNT bit to match the client's value. + */ + if (IsAccount(cptr)) + FlagSet(&flags, FLAG_ACCOUNT); + else + FlagClr(&flags, FLAG_ACCOUNT); + client_set_privs(sptr, NULL); send_umode(cptr, sptr, &flags, ALL_UMODES); if ((cli_snomask(sptr) != SNO_DEFAULT) && HasFlag(sptr, FLAG_SERVNOTICE)) send_reply(sptr, RPL_SNOMASK, cli_snomask(sptr), cli_snomask(sptr)); @@ -737,9 +524,6 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, const char* nick, int parc, char* parv[]) { if (IsServer(sptr)) { - int i; - const char* account = 0; - const char* p; /* * A server introducing a new client, change source @@ -749,23 +533,7 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, cli_hopcount(new_client) = atoi(parv[2]); cli_lastnick(new_client) = atoi(parv[3]); - if (Protocol(cptr) > 9 && parc > 7 && *parv[6] == '+') - { - for (p = parv[6] + 1; *p; p++) - { - for (i = 0; i < USERMODELIST_SIZE; ++i) - { - if (userModeList[i].c == *p) - { - SetFlag(new_client, userModeList[i].flag); - if (userModeList[i].flag == FLAG_ACCOUNT) - account = parv[7]; - break; - } - } - } - } - client_set_privs(new_client, NULL); /* set privs on user */ + /* * Set new nick name. */ @@ -783,25 +551,19 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, cli_serv(sptr)->ghost = 0; /* :server NICK means end of net.burst */ ircd_strncpy(cli_username(new_client), parv[4], USERLEN); + ircd_strncpy(cli_user(new_client)->username, parv[4], USERLEN); ircd_strncpy(cli_user(new_client)->host, parv[5], HOSTLEN); ircd_strncpy(cli_user(new_client)->realhost, parv[5], HOSTLEN); ircd_strncpy(cli_info(new_client), parv[parc - 1], REALLEN); - if (account) { - int len = ACCOUNTLEN; - if ((p = strchr(account, ':'))) { - len = (p++) - account; - cli_user(new_client)->acc_create = atoi(p); - Debug((DEBUG_DEBUG, "Received timestamped account in user mode; " - "account \"%s\", timestamp %Tu", account, - cli_user(new_client)->acc_create)); - } - ircd_strncpy(cli_user(new_client)->account, account, len); + + Count_newremoteclient(UserStats, sptr); + + if (parc > 7 && *parv[6] == '+') { + /* (parc-4) -3 for the ip, numeric nick, realname */ + set_user_mode(cptr, new_client, parc-7, parv+4, ALLOWMODES_ANY); } - if (HasHiddenHost(new_client)) - ircd_snprintf(0, cli_user(new_client)->host, HOSTLEN, "%s.%s", - account, feature_str(FEAT_HIDDEN_HOST)); - return register_user(cptr, new_client, cli_name(new_client), parv[4]); + return register_user(cptr, new_client); } else if ((cli_name(sptr))[0]) { /* @@ -874,36 +636,9 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, } else { /* Local client setting NICK the first time */ - strcpy(cli_name(sptr), nick); - if (!cli_user(sptr)) { - cli_user(sptr) = make_user(sptr); - cli_user(sptr)->server = &me; - } hAddClient(sptr); - - cli_unreg(sptr) &= ~CLIREG_NICK; /* nickname now set */ - - /* - * If the client hasn't gotten a cookie-ping yet, - * choose a cookie and send it. -record!jegelhof@cloud9.net - */ - if (!cli_cookie(sptr)) { - do { - cli_cookie(sptr) = (ircrandom() & 0x7fffffff); - } while (!cli_cookie(sptr)); - sendrawto_one(cptr, MSG_PING " :%u", cli_cookie(sptr)); - } - else if (!cli_unreg(sptr)) { - /* - * USER and PONG already received, now we have NICK. - * register_user may reject the client and call exit_client - * for it - must test this and exit m_nick too ! - */ - cli_lastnick(sptr) = TStime(); /* Always local client */ - if (register_user(cptr, sptr, nick, cli_user(sptr)->username) == CPTR_KILLED) - return CPTR_KILLED; - } + return auth_set_nick(cli_auth(sptr), nick); } return 0; } @@ -967,10 +702,6 @@ int check_target_limit(struct Client *sptr, void *target, const char *name, assert(cli_local(sptr)); targets = cli_targets(sptr); - /* If user is invited to channel, give him/her a free target */ - if (IsChannelName(name) && IsInvited(sptr, target)) - return 0; - /* * Same target as last time? */ @@ -988,6 +719,10 @@ int check_target_limit(struct Client *sptr, void *target, const char *name, */ if (!created) { if (CurrentTime < cli_nexttarget(sptr)) { + /* If user is invited to channel, give him/her a free target */ + if (IsChannelName(name) && IsInvited(sptr, target)) + return 0; + if (cli_nexttarget(sptr) - CurrentTime < TARGET_DELAY + 8) { /* * No server flooding @@ -1065,12 +800,14 @@ int whisper(struct Client* source, const char* nick, const char* channel, if (is_silenced(source, dest)) return 0; - if (cli_user(dest)->away) - send_reply(source, RPL_AWAY, cli_name(dest), cli_user(dest)->away); if (is_notice) sendcmdto_one(source, CMD_NOTICE, dest, "%C :%s", dest, text); else + { + if (cli_user(dest)->away) + send_reply(source, RPL_AWAY, cli_name(dest), cli_user(dest)->away); sendcmdto_one(source, CMD_PRIVATE, dest, "%C :%s", dest, text); + } return 0; } @@ -1185,11 +922,11 @@ hide_hostmask(struct Client *cptr, unsigned int flag) sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr, 0, "%H", chan->channel); if (IsChanOp(chan) && HasVoice(chan)) - sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr, 0, + sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, cptr, 0, "%H +ov %C %C", chan->channel, cptr, cptr); else if (IsChanOp(chan) || HasVoice(chan)) - sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr, 0, + sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, cptr, 0, "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', cptr); } return 0; @@ -1204,13 +941,15 @@ hide_hostmask(struct Client *cptr, unsigned int flag) * @param[in] sptr Client who sent the mode change message. * @param[in] parc Number of parameters in \a parv. * @param[in] parv Parameters to MODE. + * @param[in] allow_modes ALLOWMODES_ANY for any mode, ALLOWMODES_DEFAULT for + * only permitting legitimate default user modes. * @return Zero. */ -int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) +int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, + char *parv[], int allow_modes) { char** p; char* m; - struct Client *acptr; int what; int i; struct Flags setflags; @@ -1219,30 +958,10 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv char buf[BUFSIZE]; int prop = 0; int do_host_hiding = 0; + char* account = NULL; what = MODE_ADD; - if (parc < 2) - return need_more_params(sptr, "MODE"); - - if (!(acptr = FindUser(parv[1]))) - { - if (MyConnect(sptr)) - send_reply(sptr, ERR_NOSUCHCHANNEL, parv[1]); - return 0; - } - - if (IsServer(sptr) || sptr != acptr) - { - if (IsServer(cptr)) - sendwallto_group_butone(&me, WALL_WALLOPS, 0, - "MODE for User %s from %s!%s", parv[1], - cli_name(cptr), cli_name(sptr)); - else - send_reply(sptr, ERR_USERSDONTMATCH); - return 0; - } - if (parc < 3) { m = buf; @@ -1274,7 +993,7 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv /* * parse mode change string(s) */ - for (p = &parv[2]; *p; p++) { /* p is changed in loop too */ + for (p = &parv[2]; *p && p<&parv[parc]; p++) { /* p is changed in loop too */ for (m = *p; *m; m++) { switch (*m) { case '+': @@ -1358,6 +1077,13 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv if (what == MODE_ADD) do_host_hiding = 1; break; + case 'r': + if (*(p + 1) && (what == MODE_ADD)) { + account = *(++p); + SetAccount(sptr); + } + /* There is no -r */ + break; default: send_reply(sptr, ERR_UMODEUNKNOWNFLAG, *m); break; @@ -1374,6 +1100,8 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv ClearOper(sptr); if (!FlagHas(&setflags, FLAG_LOCOP) && IsLocOp(sptr)) ClearLocOp(sptr); + if (!FlagHas(&setflags, FLAG_ACCOUNT) && IsAccount(sptr)) + ClrFlag(sptr, FLAG_ACCOUNT); /* * new umode; servers can set it, local users cannot; * prevents users from /kick'ing or /mode -o'ing @@ -1400,7 +1128,10 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv { if ((FlagHas(&setflags, FLAG_OPER) || FlagHas(&setflags, FLAG_LOCOP)) && !IsAnOper(sptr)) + { det_confs_butmask(sptr, CONF_CLIENT & ~CONF_OPERATOR); + client_set_privs(sptr, NULL); + } if (SendServNotice(sptr)) { @@ -1416,28 +1147,48 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv * Compare new flags with old flags and send string which * will cause servers to update correctly. */ - if (!FlagHas(&setflags, FLAG_OPER) && IsOper(sptr)) - { - /* user now oper */ - ++UserStats.opers; - client_set_privs(sptr, NULL); /* may set propagate privilege */ - } - /* remember propagate privilege setting */ - if (HasPriv(sptr, PRIV_PROPAGATE)) - prop = 1; - if (FlagHas(&setflags, FLAG_OPER) && !IsOper(sptr)) - { - /* user no longer oper */ - --UserStats.opers; - client_set_privs(sptr, NULL); /* will clear propagate privilege */ + if (!FlagHas(&setflags, FLAG_ACCOUNT) && IsAccount(sptr)) { + int len = ACCOUNTLEN; + char *ts; + if ((ts = strchr(account, ':'))) { + len = (ts++) - account; + cli_user(sptr)->acc_create = atoi(ts); + Debug((DEBUG_DEBUG, "Received timestamped account in user mode; " + "account \"%s\", timestamp %Tu", account, + cli_user(sptr)->acc_create)); + } + ircd_strncpy(cli_user(sptr)->account, account, len); } - if (FlagHas(&setflags, FLAG_INVISIBLE) && !IsInvisible(sptr)) - --UserStats.inv_clients; - if (!FlagHas(&setflags, FLAG_INVISIBLE) && IsInvisible(sptr)) - ++UserStats.inv_clients; - if (!FlagHas(&setflags, FLAG_HIDDENHOST) && do_host_hiding) + if (!FlagHas(&setflags, FLAG_HIDDENHOST) && do_host_hiding && allow_modes != ALLOWMODES_DEFAULT) hide_hostmask(sptr, FLAG_HIDDENHOST); - send_umode_out(cptr, sptr, &setflags, prop); + + if (IsRegistered(sptr)) { + if (!FlagHas(&setflags, FLAG_OPER) && IsOper(sptr)) { + /* user now oper */ + ++UserStats.opers; + client_set_privs(sptr, NULL); /* may set propagate privilege */ + } + /* remember propagate privilege setting */ + if (HasPriv(sptr, PRIV_PROPAGATE)) { + prop = 1; + } + if (FlagHas(&setflags, FLAG_OPER) && !IsOper(sptr)) { + /* user no longer oper */ + assert(UserStats.opers > 0); + --UserStats.opers; + client_set_privs(sptr, NULL); /* will clear propagate privilege */ + } + if (FlagHas(&setflags, FLAG_INVISIBLE) && !IsInvisible(sptr)) { + assert(UserStats.inv_clients > 0); + --UserStats.inv_clients; + } + if (!FlagHas(&setflags, FLAG_INVISIBLE) && IsInvisible(sptr)) { + ++UserStats.inv_clients; + } + assert(UserStats.opers <= UserStats.clients + UserStats.unknowns); + assert(UserStats.inv_clients <= UserStats.clients + UserStats.unknowns); + send_umode_out(cptr, sptr, &setflags, prop); + } return 0; } @@ -1746,3 +1497,6 @@ send_supported(struct Client *cptr) return 0; /* convenience return, if it's ever needed */ } + +/* vim: shiftwidth=2 + */