added ssl cipher notice on connect
[ircu2.10.12-pk.git] / ircd / s_user.c
index 5a7afd6ce89221d9f3968de592d8db71687182b5..9853ed237b74e818dfa53267eaaf08767595e065 100644 (file)
@@ -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"
 #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"
 #include "s_misc.h"
 #include "s_serv.h" /* max_client_count */
 #include "send.h"
+#include "ssl.h"
 #include "struct.h"
 #include "supported.h"
 #include "sys.h"
@@ -91,9 +92,7 @@ struct User *make_user(struct Client *cptr)
 
     /* All variables are 0 by default */
     memset(cli_user(cptr), 0, sizeof(struct User));
-#ifdef  DEBUGMODE
     ++userCount;
-#endif
     cli_user(cptr)->refcnt = 1;
   }
   return cli_user(cptr);
@@ -120,9 +119,8 @@ void free_user(struct User* user)
     assert(0 == user->channel);
 
     MyFree(user);
-#ifdef  DEBUGMODE
+    assert(userCount>0);
     --userCount;
-#endif
   }
 }
 
@@ -308,40 +306,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
  *
@@ -373,29 +337,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;
@@ -404,200 +352,40 @@ 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);
-    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 %i for %s.", get_client_class(sptr),
-                               get_client_name(sptr, SHOW_IP));
-        }
-        ++ServerStats->is_ref;
-        IPcheck_connect_fail(&cli_ip(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(&cli_ip(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)
-        && !(IsDigit(*aconf->passwd) && !aconf->passwd[1])
-        && 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
+     * Set user's initial modes
      */
-    killreason = find_kill(sptr);
-    if (killreason) {
-      ServerStats->is_ref++;
-      return exit_client(cptr, sptr, &me,
-                         (killreason == -1 ? "K-lined" : "G-lined"));
+    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);
     }
-    /*
-     * 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.
-     */
-    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");
-    }
-    Count_unknownbecomesclient(sptr, UserStats);
-  }
-  else {
-    ircd_strncpy(user->username, username, USERLEN);
-    Count_newremoteclient(UserStats, user->server);
-  }
-  SetUser(sptr);
-
-  if (IsInvisible(sptr))
-    ++UserStats.inv_clients;
-  if (IsOper(sptr))
-    ++UserStats.opers;
 
-  if (MyConnect(sptr)) {
+    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...
      */
     send_reply(sptr, RPL_YOURHOST, cli_name(&me), version);
     send_reply(sptr, RPL_CREATED, creation);
-    send_reply(sptr, RPL_MYINFO, cli_name(&me), infousermodes, infochanmodes,
-               infochanmodeswithparams, version);
+    send_reply(sptr, RPL_MYINFO, cli_name(&me), version, infousermodes,
+               infochanmodes, infochanmodeswithparams);
     send_supported(sptr);
     m_lusers(sptr, sptr, 1, parv);
     update_load();
     motd_signon(sptr);
-/*      nextping = CurrentTime; */
     if (cli_snomask(sptr) & SNO_NOISY)
       set_snomask(sptr, cli_snomask(sptr) & SNO_NOISY, SNO_ADD);
     if (feature_bool(FEAT_CONNEXIT_NOTICES))
@@ -608,22 +396,17 @@ 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
-     */
-    parv[0] = (char*)nick;
-    parv[1] = (char*)nick;
-    parv[2] = (char*)client_get_default_umode(sptr);
-    parv[3] = NULL; /* needed in case of +s */
-    set_user_mode(sptr, sptr, 3, parv);
-    ClearHiddenHost(sptr); /* just in case somebody stuck +x in there */
+    
+    if(cli_connect(sptr)->con_ssl) {
+      SetSSLConn(sptr);
+      
+      const char *cipher = ssl_get_cipher(cli_connect(sptr)->con_ssl);
+      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :You are connected to %s with %s", sptr, cli_name(&me), cipher);
+    }
   }
-  else
-    /* if (IsServer(cptr)) */
-  {
-    struct Client *acptr;
+  else {
+    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])",
@@ -636,12 +419,12 @@ int register_user(struct Client *cptr, struct Client *sptr,
       SetFlag(sptr, FLAG_TS8);
 
     /*
-     * Check to see if this user is being propogated
+     * Check to see if this user is being propagated
      * as part of a net.burst, or is using protocol 9.
-     * FIXME: This can be speeded up - its stupid to check it for
+     * FIXME: This can be sped up - its stupid to check it for
      * every NICK message in a burst again  --Run.
      */
-    for (acptr = user->server; acptr != &me; acptr = cli_serv(acptr)->up)
+    for (; acptr != &me; acptr = cli_serv(acptr)->up)
     {
       if (IsBurst(acptr) || Protocol(acptr) < 10)
         break;
@@ -655,13 +438,27 @@ int register_user(struct Client *cptr, struct Client *sptr,
                     sptr, cli_name(&me));
       return exit_client(cptr, sptr, &me,"Too many connections from your host -- throttled");
     }
+    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))
+    ++UserStats.opers;
+
   tmpstr = umode_str(sptr);
   /* Send full IP address to IPv6-grokking servers. */
   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),
@@ -670,16 +467,29 @@ 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),
                              NumNick(sptr), cli_info(sptr));
 
-  /* Send server notice mask to client */
-  if (MyUser(sptr) && (cli_snomask(sptr) != SNO_DEFAULT) && HasFlag(sptr, FLAG_SERVNOTICE))
-    send_reply(sptr, RPL_SNOMASK, cli_snomask(sptr), cli_snomask(sptr));
-
+  /* Send user mode to client */
+  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));
+  }
   return 0;
 }
 
@@ -697,7 +507,8 @@ static const struct UserMode {
   { FLAG_CHSERV,      'k' },
   { FLAG_DEBUG,       'g' },
   { FLAG_ACCOUNT,     'r' },
-  { FLAG_HIDDENHOST,  'x' }
+  { FLAG_HIDDENHOST,  'x' },
+  { FLAG_SSLCONN,     'S' }
 };
 
 /** Length of #userModeList. */
@@ -722,9 +533,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
@@ -734,23 +542,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.
      */
@@ -768,25 +560,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]) {
     /*
@@ -859,34 +645,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);
-
-    /*
-     * 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_user(sptr))->host && cli_cookie(sptr) == COOKIE_VERIFIED) {
-      /*
-       * 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;
 }
@@ -950,10 +711,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?
    */
@@ -971,6 +728,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
@@ -1048,12 +809,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;
 }
 
@@ -1138,6 +901,7 @@ hide_hostmask(struct Client *cptr, unsigned int flag)
     for (chan = (cli_user(cptr))->channel; chan;
          chan = chan->next_channel)
       ClearBanValid(chan);
+    break;
   default:
     return 0;
   }
@@ -1167,11 +931,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;
@@ -1186,13 +950,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;
@@ -1201,30 +967,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;
@@ -1256,7 +1002,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 '+':
@@ -1339,6 +1085,19 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv
       case 'x':
         if (what == MODE_ADD)
          do_host_hiding = 1;
+      case 'S':
+        if (what == MODE_ADD)
+          SetSSLConn(sptr);
+        else
+          ClearSSLConn(sptr);
+        break;
+       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);
@@ -1356,6 +1115,12 @@ 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);
+    if (!FlagHas(&setflags, FLAG_SSLCONN) && IsSSLConn(sptr))
+      ClrFlag(sptr, FLAG_SSLCONN);
+    else if (FlagHas(&setflags, FLAG_SSLCONN) && !IsSSLConn(sptr))
+      SetFlag(sptr, FLAG_SSLCONN);
     /*
      * new umode; servers can set it, local users cannot;
      * prevents users from /kick'ing or /mode -o'ing
@@ -1382,7 +1147,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))
     {
@@ -1398,28 +1166,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;
 }
@@ -1435,9 +1223,7 @@ char *umode_str(struct Client *cptr)
   int i;
   struct Flags c_flags = cli_flags(cptr);
 
-  if (HasPriv(cptr, PRIV_PROPAGATE))
-    FlagSet(&c_flags, FLAG_OPER);
-  else
+  if (!HasPriv(cptr, PRIV_PROPAGATE))
     FlagClr(&c_flags, FLAG_OPER);
 
   for (i = 0; i < USERMODELIST_SIZE; ++i)
@@ -1667,7 +1453,7 @@ void set_snomask(struct Client *cptr, unsigned int newmask, int what)
 /** Check whether \a sptr is allowed to send a message to \a acptr.
  * If \a sptr is a remote user, it means some server has an outdated
  * SILENCE list for \a acptr, so send the missing SILENCE mask(s) back
- * in the direction of \a sptr.
+ * in the direction of \a sptr.  Skip the check if \a sptr is a server.
  * @param[in] sptr Client trying to send a message.
  * @param[in] acptr Destination of message.
  * @return Non-zero if \a sptr is SILENCEd by \a acptr, zero if not.
@@ -1679,7 +1465,7 @@ int is_silenced(struct Client *sptr, struct Client *acptr)
   size_t buf_used, slen;
   char buf[BUFSIZE];
 
-  if (!(user = cli_user(acptr))
+  if (IsServer(sptr) || !(user = cli_user(acptr))
       || !(found = find_ban(sptr, user->silence)))
     return 0;
   assert(!(found->flags & BAN_EXCEPTION));
@@ -1730,3 +1516,6 @@ send_supported(struct Client *cptr)
 
   return 0; /* convenience return, if it's ever needed */
 }
+
+/* vim: shiftwidth=2 
+ */