]> jfr.im git - solanum.git/blobdiff - ircd/s_user.c
Add general::hidden_caps
[solanum.git] / ircd / s_user.c
index b2e3829c19bd693ffe68a4fcbec3b9ab2911b21d..1a9d51f433c80f77ab81005ae956388385ee04e0 100644 (file)
@@ -20,8 +20,6 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
  *  USA
- *
- *  $Id: s_user.c 3586 2007-11-20 11:16:43Z nenolod $
  */
 
 #include "stdinc.h"
@@ -29,7 +27,6 @@
 #include "channel.h"
 #include "class.h"
 #include "client.h"
-#include "common.h"
 #include "hash.h"
 #include "match.h"
 #include "ircd.h"
@@ -51,7 +48,6 @@
 #include "hook.h"
 #include "monitor.h"
 #include "snomask.h"
-#include "blacklist.h"
 #include "substitution.h"
 #include "chmode.h"
 #include "s_assert.h"
@@ -85,7 +81,7 @@ int user_modes[256] = {
        0,                      /* O */
        0,                      /* P */
        UMODE_NOFORWARD,        /* Q */
-       UMODE_REGONLYMSG,       /* R */
+       0,                      /* R */
        UMODE_SERVICE,          /* S */
        0,                      /* T */
        0,                      /* U */
@@ -93,7 +89,7 @@ int user_modes[256] = {
        0,                      /* W */
        0,                      /* X */
        0,                      /* Y */
-       UMODE_SSLCLIENT,        /* Z */
+       UMODE_SECURE,           /* Z */
        /* 0x5B */ 0, 0, 0, 0, 0, 0, /* 0x60 */
        UMODE_ADMIN,            /* a */
        0,                      /* b */
@@ -101,7 +97,7 @@ int user_modes[256] = {
        0,                      /* d */
        0,                      /* e */
        0,                      /* f */
-       UMODE_CALLERID,         /* g */
+       0,                      /* g */
        0,                      /* h */
        UMODE_INVISIBLE,        /* i */
        0,                      /* j */
@@ -140,7 +136,7 @@ int user_modes[256] = {
  * output      -
  * side effects        - display to client user counts etc.
  */
-int
+void
 show_lusers(struct Client *source_p)
 {
        if(rb_dlink_list_length(&lclient_list) > (unsigned long)MaxClientCount)
@@ -190,8 +186,139 @@ show_lusers(struct Client *source_p)
                           form_str(RPL_STATSCONN),
                           MaxConnectionCount, MaxClientCount,
                           Count.totalrestartcount);
+}
 
-       return 0;
+/* check if we should exit a client due to authd decision
+ * inputs      - client server, client connecting
+ * outputs     - true if exited, false if not
+ * side effects        - messages/exits client if authd rejected and not exempt
+ */
+static bool
+authd_check(struct Client *client_p, struct Client *source_p)
+{
+       struct ConfItem *aconf = source_p->localClient->att_conf;
+       rb_dlink_list varlist = { NULL, NULL, 0 };
+       bool reject = false;
+       char *reason;
+
+       if(source_p->preClient->auth.accepted == true)
+               return reject;
+
+       substitution_append_var(&varlist, "nick", source_p->name);
+       substitution_append_var(&varlist, "ip", source_p->sockhost);
+       substitution_append_var(&varlist, "host", source_p->host);
+       substitution_append_var(&varlist, "dnsbl-host", source_p->preClient->auth.data);
+       substitution_append_var(&varlist, "network-name", ServerInfo.network_name);
+       reason = substitution_parse(source_p->preClient->auth.reason, &varlist);
+
+       switch(source_p->preClient->auth.cause)
+       {
+       case 'B':       /* DNSBL */
+               {
+                       struct DNSBLEntryStats *stats;
+                       char *dnsbl_name = source_p->preClient->auth.data;
+
+                       if(dnsbl_stats != NULL)
+                               if((stats = rb_dictionary_retrieve(dnsbl_stats, dnsbl_name)) != NULL)
+                                       stats->hits++;
+
+                       if(IsExemptKline(source_p) || IsConfExemptDNSBL(aconf))
+                       {
+                               sendto_one_notice(source_p, ":*** Your IP address %s is listed in %s, but you are exempt",
+                                               source_p->sockhost, dnsbl_name);
+                               break;
+                       }
+
+                       sendto_realops_snomask(SNO_REJ, L_NETWIDE,
+                               "Listed on DNSBL %s: %s (%s@%s) [%s] [%s]",
+                               dnsbl_name, source_p->name, source_p->username, source_p->host,
+                               IsIPSpoof(source_p) ? "255.255.255.255" : source_p->sockhost,
+                               source_p->info);
+
+                       sendto_one(source_p, form_str(ERR_YOUREBANNEDCREEP),
+                               me.name, source_p->name, reason);
+
+                       sendto_one_notice(source_p, ":*** Your IP address %s is listed in %s",
+                               source_p->sockhost, dnsbl_name);
+                       add_reject(source_p, NULL, NULL, NULL, "Banned (listed in a DNSBL)");
+                       exit_client(client_p, source_p, &me, "Banned (listed in a DNSBL)");
+                       reject = true;
+               }
+               break;
+       case 'O':       /* OPM */
+               {
+                       char *proxy = source_p->preClient->auth.data;
+                       char *port = strrchr(proxy, ':');
+
+                       if(port == NULL)
+                       {
+                               /* This shouldn't happen, better tell the ops... */
+                               ierror("authd sent us a malformed OPM string %s", proxy);
+                               sendto_realops_snomask(SNO_GENERAL, L_ALL,
+                                       "authd sent us a malformed OPM string %s", proxy);
+                               break;
+                       }
+
+                       /* Terminate the proxy type */
+                       *(port++) = '\0';
+
+                       if(IsExemptKline(source_p) || IsConfExemptProxy(aconf))
+                       {
+                               sendto_one_notice(source_p,
+                                       ":*** Your IP address %s has been detected as an open proxy (type %s, port %s), but you are exempt",
+                                       source_p->sockhost, proxy, port);
+                               break;
+                       }
+                       sendto_realops_snomask(SNO_REJ, L_NETWIDE,
+                               "Open proxy %s/%s: %s (%s@%s) [%s] [%s]",
+                               proxy, port,
+                               source_p->name,
+                               source_p->username, source_p->host,
+                               IsIPSpoof(source_p) ? "255.255.255.255" : source_p->sockhost,
+                               source_p->info);
+
+                       sendto_one(source_p, form_str(ERR_YOUREBANNEDCREEP),
+                                       me.name, source_p->name, reason);
+
+                       sendto_one_notice(source_p,
+                               ":*** Your IP address %s has been detected as an open proxy (type %s, port %s)",
+                               source_p->sockhost, proxy, port);
+                       add_reject(source_p, NULL, NULL, NULL, "Banned (Open proxy)");
+                       exit_client(client_p, source_p, &me, "Banned (Open proxy)");
+                       reject = true;
+               }
+               break;
+       default:        /* Unknown, but handle the case properly */
+               if(IsExemptKline(source_p))
+               {
+                       sendto_one_notice(source_p,
+                               ":*** You were rejected, but you are exempt (reason: %s)",
+                               reason);
+                       break;
+               }
+               sendto_realops_snomask(SNO_REJ, L_NETWIDE,
+                       "Rejected by authentication system (reason %s): %s (%s@%s) [%s] [%s]",
+                       reason, source_p->name, source_p->username, source_p->host,
+                       IsIPSpoof(source_p) ? "255.255.255.255" : source_p->sockhost,
+                       source_p->info);
+
+               sendto_one(source_p, form_str(ERR_YOUREBANNEDCREEP),
+                       me.name, source_p->name, reason);
+
+               sendto_one_notice(source_p, ":*** Rejected by authentication system: %s",
+                       reason);
+               add_reject(source_p, NULL, NULL, NULL, "Banned (authentication system)");
+               exit_client(client_p, source_p, &me, "Banned (authentication system)");
+               reject = true;
+               break;
+       }
+
+       if(reject)
+               ServerStats.is_ref++;
+
+       substitution_free(&varlist);
+
+       return reject;
 }
 
 /*
@@ -216,14 +343,12 @@ show_lusers(struct Client *source_p)
 **         would just issue "KILL foobar" to clean out dups. But,
 **         this is not fair. It should actually request another
 **         nick from local user or kill him/her...
-*/
-
+ */
 int
 register_local_user(struct Client *client_p, struct Client *source_p)
 {
        struct ConfItem *aconf, *xconf;
-       struct User *user = source_p->user;
-       char tmpstr2[IRCD_BUFSIZE];
+       char tmpstr2[BUFSIZE];
        char ipaddr[HOSTIPLEN];
        char myusername[USERLEN+1];
        int status;
@@ -241,9 +366,9 @@ register_local_user(struct Client *client_p, struct Client *source_p)
        {
                if(!(source_p->flags & FLAGS_PINGSENT) && source_p->localClient->random_ping == 0)
                {
-                       source_p->localClient->random_ping = (unsigned long) (rand() * rand()) << 1;
-                       sendto_one(source_p, "PING :%08lX",
-                                  (unsigned long) source_p->localClient->random_ping);
+                       source_p->localClient->random_ping = (uint32_t)(((rand() * rand()) << 1) | 1);
+                       sendto_one(source_p, "PING :%08X",
+                                  (unsigned int) source_p->localClient->random_ping);
                        source_p->flags |= FLAGS_PINGSENT;
                        return -1;
                }
@@ -257,11 +382,13 @@ register_local_user(struct Client *client_p, struct Client *source_p)
        if(source_p->flags & FLAGS_CLICAP)
                return -1;
 
-       /* still has DNSbls to validate against */
-       if(rb_dlink_list_length(&source_p->preClient->dnsbl_queries) > 0)
+       /* Waiting on authd */
+       if(source_p->preClient->auth.cid)
                return -1;
 
-       client_p->localClient->last = rb_current_time();
+       /* Set firsttime here so that post_registration_delay works from registration,
+        * rather than initial connection.  */
+       source_p->localClient->firsttime = client_p->localClient->last = rb_current_time();
 
        /* XXX - fixme. we shouldnt have to build a users buffer twice.. */
        if(!IsGotId(source_p) && (strchr(source_p->username, '[') != NULL))
@@ -289,13 +416,13 @@ register_local_user(struct Client *client_p, struct Client *source_p)
        /* Apply nick override */
        if(*source_p->preClient->spoofnick)
        {
-               char note[NICKLEN + 10];
+               char note[NAMELEN + 10];
 
                del_from_client_hash(source_p->name, source_p);
                rb_strlcpy(source_p->name, source_p->preClient->spoofnick, NICKLEN + 1);
                add_to_client_hash(source_p->name, source_p);
 
-               rb_snprintf(note, NICKLEN + 10, "Nick: %s", source_p->name);
+               snprintf(note, sizeof(note), "Nick: %s", source_p->name);
                rb_note(source_p->localClient->F, note);
        }
 
@@ -306,7 +433,6 @@ register_local_user(struct Client *client_p, struct Client *source_p)
                rb_strlcpy(source_p->host, source_p->sockhost, sizeof(source_p->host));
        }
 
-
        aconf = source_p->localClient->att_conf;
 
        if(aconf == NULL)
@@ -315,7 +441,7 @@ register_local_user(struct Client *client_p, struct Client *source_p)
                return (CLIENT_EXITED);
        }
 
-       if(IsConfSSLNeeded(aconf) && !IsSSL(source_p))
+       if(IsConfSSLNeeded(aconf) && !IsSecure(source_p))
        {
                ServerStats.is_ref++;
                sendto_one_notice(source_p, ":*** Notice -- You need to use SSL/TLS to use this server");
@@ -323,6 +449,14 @@ register_local_user(struct Client *client_p, struct Client *source_p)
                return (CLIENT_EXITED);
        }
 
+       if(IsSCTP(source_p) && !IsConfAllowSCTP(aconf))
+       {
+               ServerStats.is_ref++;
+               sendto_one_notice(source_p, ":*** Notice -- You are not allowed to use SCTP on this server");
+               exit_client(client_p, source_p, &me, "SCTP not allowed");
+               return (CLIENT_EXITED);
+       }
+
        if(!IsGotId(source_p))
        {
                const char *p;
@@ -355,7 +489,7 @@ register_local_user(struct Client *client_p, struct Client *source_p)
                }
        }
 
-       if(IsNeedSasl(aconf) && !*user->suser)
+       if(IsNeedSasl(aconf) && !*source_p->user->suser)
        {
                ServerStats.is_ref++;
                sendto_one_notice(source_p, ":*** Notice -- You need to identify via SASL to use this server");
@@ -393,7 +527,7 @@ register_local_user(struct Client *client_p, struct Client *source_p)
                }
        }
 
-       /* report if user has &^>= etc. and set flags as needed in source_p */
+       /* report and set flags (kline exempt etc.) as needed in source_p */
        report_and_set_user_flags(source_p, aconf);
 
        /* Limit clients */
@@ -421,51 +555,14 @@ register_local_user(struct Client *client_p, struct Client *source_p)
           (xconf = find_xline(source_p->info, 1)) != NULL)
        {
                ServerStats.is_ref++;
-               add_reject(source_p, xconf->host, NULL);
+               add_reject(source_p, xconf->host, NULL, NULL, NULL);
                exit_client(client_p, source_p, &me, "Bad user info");
                return CLIENT_EXITED;
        }
 
-       /* dnsbl check */
-       if (source_p->preClient->dnsbl_listed != NULL)
-       {
-               if (IsExemptKline(source_p) || IsConfExemptDNSBL(aconf))
-                       sendto_one_notice(source_p, ":*** Your IP address %s is listed in %s, but you are exempt",
-                                       source_p->sockhost, source_p->preClient->dnsbl_listed->host);
-               else
-               {
-                       sendto_realops_snomask(SNO_REJ, L_NETWIDE,
-                               "Listed on DNSBL %s: %s (%s@%s) [%s] [%s]",
-                               source_p->preClient->dnsbl_listed->host,
-                               source_p->name,
-                               source_p->username, source_p->host,
-                               IsIPSpoof(source_p) ? "255.255.255.255" : source_p->sockhost,
-                               source_p->info);
-
-                       rb_dlink_list varlist = { NULL, NULL, 0 };
-
-                       substitution_append_var(&varlist, "nick", source_p->name);
-                       substitution_append_var(&varlist, "ip", source_p->sockhost);
-                       substitution_append_var(&varlist, "host", source_p->host);
-                       substitution_append_var(&varlist, "dnsbl-host", source_p->preClient->dnsbl_listed->host);
-                       substitution_append_var(&varlist, "network-name", ServerInfo.network_name);
-
-                       ServerStats.is_ref++;
-
-                       sendto_one(source_p, form_str(ERR_YOUREBANNEDCREEP),
-                                       me.name, source_p->name,
-                                       substitution_parse(source_p->preClient->dnsbl_listed->reject_reason, &varlist));
-
-                       substitution_free(&varlist);
-
-                       sendto_one_notice(source_p, ":*** Your IP address %s is listed in %s",
-                                       source_p->sockhost, source_p->preClient->dnsbl_listed->host);
-                       source_p->preClient->dnsbl_listed->hits++;
-                       add_reject(source_p, NULL, NULL);
-                       exit_client(client_p, source_p, &me, "*** Banned (DNS blacklist)");
-                       return CLIENT_EXITED;
-               }
-       }
+       /* authd rejection check */
+       if(authd_check(client_p, source_p))
+               return CLIENT_EXITED;
 
        /* valid user name check */
 
@@ -477,7 +574,7 @@ register_local_user(struct Client *client_p, struct Client *source_p)
                ServerStats.is_ref++;
                sendto_one_notice(source_p, ":*** Your username is invalid. Please make sure that your username contains "
                                            "only alphanumeric characters.");
-               rb_sprintf(tmpstr2, "Invalid username [%s]", source_p->username);
+               sprintf(tmpstr2, "Invalid username [%s]", source_p->username);
                exit_client(client_p, source_p, &me, tmpstr2);
                return (CLIENT_EXITED);
        }
@@ -533,12 +630,12 @@ register_local_user(struct Client *client_p, struct Client *source_p)
         */
        if(!*source_p->id)
        {
-               strcpy(source_p->id, generate_uid());
+               rb_strlcpy(source_p->id, generate_uid(), sizeof(source_p->id));
                add_to_id_hash(source_p->id, source_p);
        }
 
-       if (IsSSL(source_p))
-               source_p->umodes |= UMODE_SSLCLIENT;
+       if (IsSecure(source_p))
+               source_p->umodes |= UMODE_SECURE;
 
        if (source_p->umodes & UMODE_INVISIBLE)
                Count.invisi++;
@@ -577,7 +674,8 @@ register_local_user(struct Client *client_p, struct Client *source_p)
 
        free_pre_client(source_p);
 
-       return (introduce_client(client_p, source_p, user, source_p->name, 1));
+       introduce_client(client_p, source_p, source_p->user, source_p->name, 1);
+       return 0;
 }
 
 /*
@@ -589,7 +687,7 @@ register_local_user(struct Client *client_p, struct Client *source_p)
  *               of the net, either from a local client connect or
  *               from a remote connect.
  */
-int
+void
 introduce_client(struct Client *client_p, struct Client *source_p, struct User *user, const char *nick, int use_euid)
 {
        char ubuf[BUFSIZE];
@@ -700,21 +798,19 @@ introduce_client(struct Client *client_p, struct Client *source_p, struct User *
        hdata2.client = client_p;
        hdata2.target = source_p;
        call_hook(h_introduce_client, &hdata2);
-
-       return 0;
 }
 
 /*
  * valid_hostname - check hostname for validity
  *
  * Inputs       - pointer to user
- * Output       - YES if valid, NO if not
+ * Output       - true if valid, false if not
  * Side effects - NONE
  *
  * NOTE: this doesn't allow a hostname to begin with a dot and
  * will not allow more dots than chars.
  */
-int
+bool
 valid_hostname(const char *hostname)
 {
        const char *p = hostname, *last_slash = 0;
@@ -723,18 +819,18 @@ valid_hostname(const char *hostname)
        s_assert(NULL != p);
 
        if(hostname == NULL)
-               return NO;
+               return false;
 
        if(!strcmp(hostname, "localhost"))
-               return YES;
+               return true;
 
        if('.' == *p || ':' == *p || '/' == *p)
-               return NO;
+               return false;
 
        while (*p)
        {
                if(!IsHostChar(*p))
-                       return NO;
+                       return false;
                 if(*p == '.' || *p == ':')
                        found_sep++;
                else if(*p == '/')
@@ -746,19 +842,19 @@ valid_hostname(const char *hostname)
        }
 
        if(found_sep == 0)
-               return NO;
+               return false;
 
        if(last_slash && IsDigit(last_slash[1]))
-               return NO;
+               return false;
 
-       return YES;
+       return true;
 }
 
 /*
  * valid_username - check username for validity
  *
  * Inputs       - pointer to user
- * Output       - YES if valid, NO if not
+ * Output       - true if valid, false if not
  * Side effects - NONE
  *
  * Absolutely always reject any '*' '!' '?' '@' in an user name
@@ -766,7 +862,7 @@ valid_hostname(const char *hostname)
  * Allow '.' in username to allow for "first.last"
  * style of username
  */
-int
+bool
 valid_username(const char *username)
 {
        int dots = 0;
@@ -775,7 +871,7 @@ valid_username(const char *username)
        s_assert(NULL != p);
 
        if(username == NULL)
-               return NO;
+               return false;
 
        if('~' == *p)
                ++p;
@@ -785,7 +881,7 @@ valid_username(const char *username)
         * or "-hi-@somehost", "h-----@somehost" would still be accepted.
         */
        if(!IsAlNum(*p))
-               return NO;
+               return false;
 
        while (*++p)
        {
@@ -793,14 +889,14 @@ valid_username(const char *username)
                {
                        dots++;
                        if(dots > ConfigFileEntry.dots_in_ident)
-                               return NO;
+                               return false;
                        if(!IsUserChar(p[1]))
-                               return NO;
+                               return false;
                }
                else if(!IsUserChar(*p))
-                       return NO;
+                       return false;
        }
-       return YES;
+       return true;
 }
 
 /* report_and_set_user_flags
@@ -831,7 +927,7 @@ report_and_set_user_flags(struct Client *source_p, struct ConfItem *aconf)
        if(IsConfExemptDNSBL(aconf))
                /* kline exempt implies this, don't send both */
                if(!IsConfExemptKline(aconf))
-                       sendto_one_notice(source_p, ":*** You are exempt from DNS blacklists");
+                       sendto_one_notice(source_p, ":*** You are exempt from DNSBL listings");
 
        /* If this user is exempt from user limits set it F lined" */
        if(IsConfExemptLimits(aconf))
@@ -916,8 +1012,8 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char
        const char *pm;
        struct Client *target_p;
        int what, setflags;
-       int badflag = NO;       /* Only send one bad flag notice */
-       int showsnomask = NO;
+       bool badflag = false;   /* Only send one bad flag notice */
+       bool showsnomask = false;
        unsigned int setsnomask;
        char buf[BUFSIZE];
        hook_data_umode_changed hdata;
@@ -951,7 +1047,7 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char
 
        if(source_p != target_p)
        {
-               if (MyOper(source_p) && parc < 3)
+               if (HasPrivilege(source_p, "auspex:umodes") && parc < 3)
                        show_other_user_mode(source_p, target_p);
                else
                        sendto_one(source_p, form_str(ERR_USERSDONTMATCH), me.name, source_p->name);
@@ -1020,19 +1116,21 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char
                                if(MyConnect(source_p))
                                {
                                        source_p->umodes &= ~ConfigFileEntry.oper_only_umodes;
-                                       if (!(source_p->umodes & UMODE_SERVNOTICE) && source_p->snomask != 0)
-                                       {
-                                               source_p->snomask = 0;
-                                               showsnomask = YES;
-                                       }
-                                       source_p->flags2 &= ~OPER_FLAGS;
-
-                                       rb_free(source_p->localClient->opername);
-                                       source_p->localClient->opername = NULL;
+                                       source_p->flags &= ~OPER_FLAGS;
 
                                        rb_dlinkFindDestroy(source_p, &local_oper_list);
-                                       privilegeset_unref(source_p->localClient->privset);
-                                       source_p->localClient->privset = NULL;
+                               }
+
+                               if(source_p->user->opername != NULL)
+                               {
+                                       rb_free(source_p->user->opername);
+                                       source_p->user->opername = NULL;
+                               }
+
+                               if(source_p->user->privset != NULL)
+                               {
+                                       privilegeset_unref(source_p->user->privset);
+                                       source_p->user->privset = NULL;
                                }
 
                                rb_dlinkFindDestroy(source_p, &oper_list);
@@ -1055,14 +1153,14 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char
                case 's':
                        if (MyConnect(source_p))
                        {
-                               if(!IsOper(source_p)
-                                               && (ConfigFileEntry.oper_only_umodes & UMODE_SERVNOTICE))
+                               if((ConfigFileEntry.oper_only_umodes & UMODE_SERVNOTICE) &&
+                                               (!IsOper(source_p) || !HasPrivilege(source_p, "usermode:servnotice")))
                                {
                                        if (what == MODE_ADD || source_p->umodes & UMODE_SERVNOTICE)
-                                               badflag = YES;
+                                               badflag = true;
                                        continue;
                                }
-                               showsnomask = YES;
+                               showsnomask = true;
                                if(what == MODE_ADD)
                                {
                                        if (parc > 3)
@@ -1082,7 +1180,7 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char
                default:
                        if (MyConnect(source_p) && *pm == 'Q' && !ConfigChannel.use_forward)
                        {
-                               badflag = YES;
+                               badflag = true;
                                break;
                        }
 
@@ -1094,7 +1192,7 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char
                                                || (orphaned_umodes & flag)))
                                {
                                        if (what == MODE_ADD || source_p->umodes & flag)
-                                               badflag = YES;
+                                               badflag = true;
                                }
                                else
                                {
@@ -1107,7 +1205,7 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char
                        else
                        {
                                if(MyConnect(source_p))
-                                       badflag = YES;
+                                       badflag = true;
                        }
                        break;
                }
@@ -1115,6 +1213,18 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char
        if(badflag)
                sendto_one(source_p, form_str(ERR_UMODEUNKNOWNFLAG), me.name, source_p->name);
 
+       if(MyClient(source_p))
+       {
+               if ((ConfigFileEntry.oper_only_umodes & UMODE_SERVNOTICE) &&
+                               !HasPrivilege(source_p, "usermode:servnotice"))
+                       source_p->umodes &= ~UMODE_SERVNOTICE;
+               if (!(source_p->umodes & UMODE_SERVNOTICE) && source_p->snomask != 0)
+               {
+                       source_p->snomask = 0;
+                       showsnomask = true;
+               }
+       }
+
        if(MyClient(source_p) && (source_p->snomask & SNO_NCHANGE) && !IsOperN(source_p))
        {
                sendto_one_notice(source_p, ":*** You need oper and nick_changes flag for +s +n");
@@ -1134,6 +1244,9 @@ user_mode(struct Client *client_p, struct Client *source_p, int parc, const char
                source_p->umodes &= ~UMODE_ADMIN;
        }
 
+       if(MyClient(source_p))
+               source_p->handler = IsOperGeneral(source_p) ? OPER_HANDLER : CLIENT_HANDLER;
+
        /* let modules providing usermodes know that we've changed our usermode --nenolod */
        hdata.client = source_p;
        hdata.oldumodes = setflags;
@@ -1284,7 +1397,7 @@ user_welcome(struct Client *source_p)
  * output      - none
  * side effects        - opers up source_p using aconf for reference
  */
-int
+void
 oper_up(struct Client *source_p, struct oper_conf *oper_p)
 {
        unsigned int old = source_p->umodes, oldsnomask = source_p->snomask;
@@ -1318,9 +1431,9 @@ oper_up(struct Client *source_p, struct oper_conf *oper_p)
        SetExtendChans(source_p);
        SetExemptKline(source_p);
 
-       source_p->flags2 |= oper_p->flags;
-       source_p->localClient->opername = rb_strdup(oper_p->name);
-       source_p->localClient->privset = privilegeset_ref(oper_p->privset);
+       source_p->flags |= oper_p->flags;
+       source_p->user->opername = rb_strdup(oper_p->name);
+       source_p->user->privset = privilegeset_ref(oper_p->privset);
 
        rb_dlinkAddAlloc(source_p, &local_oper_list);
        rb_dlinkAddAlloc(source_p, &oper_list);
@@ -1331,14 +1444,24 @@ oper_up(struct Client *source_p, struct oper_conf *oper_p)
                source_p->snomask &= ~SNO_NCHANGE;
        if(!IsOperOperwall(source_p))
                source_p->umodes &= ~UMODE_OPERWALL;
+       if((ConfigFileEntry.oper_only_umodes & UMODE_SERVNOTICE) &&
+                       !HasPrivilege(source_p, "usermode:servnotice"))
+       {
+               source_p->umodes &= ~UMODE_SERVNOTICE;
+               source_p->snomask = 0;
+       }
        hdata.client = source_p;
        hdata.oldumodes = old;
        hdata.oldsnomask = oldsnomask;
        call_hook(h_umode_changed, &hdata);
 
+       source_p->handler = IsOperGeneral(source_p) ? OPER_HANDLER : CLIENT_HANDLER;
+
        sendto_realops_snomask(SNO_GENERAL, L_ALL,
                             "%s (%s!%s@%s) is now an operator", oper_p->name, source_p->name,
                             source_p->username, source_p->host);
+       sendto_server(NULL, NULL, CAP_TS6, NOCAPS, ":%s OPER %s %s",
+                       use_id(source_p), oper_p->name, oper_p->privset->name);
        if(!(old & UMODE_INVISIBLE) && IsInvisible(source_p))
                ++Count.invisi;
        if((old & UMODE_INVISIBLE) && !IsInvisible(source_p))
@@ -1350,8 +1473,6 @@ oper_up(struct Client *source_p, struct oper_conf *oper_p)
        sendto_one_notice(source_p, ":*** Oper privilege set is %s", oper_p->privset->name);
        sendto_one_notice(source_p, ":*** Oper privs are %s", oper_p->privset->privs);
        send_oper_motd(source_p);
-
-       return (1);
 }
 
 /*
@@ -1471,10 +1592,8 @@ change_nick_user_host(struct Client *target_p,   const char *nick, const char *use
                                                                    target_p->info);
 
                        if(*mode)
-                               sendto_channel_local_butone(target_p, ALL_MEMBERS, chptr,
-                                               ":%s MODE %s +%s %s",
-                                               target_p->servptr->name,
-                                               chptr->chname, mode, modeval);
+                               sendto_channel_local_with_capability_butone(target_p, ALL_MEMBERS, NOCAPS, CLICAP_CHGHOST, chptr,
+                                               ":%s MODE %s +%s %s", target_p->servptr->name, chptr->chname, mode, modeval);
 
                        *modeval = '\0';
                }