fixed propagation of user mode changes (user should ALWAYS be notified)
[ircu2.10.12-pk.git] / ircd / s_user.c
index 942c44c96a7b291db160290f3d67164bb82f014d..e5d17b975d1d594ae7a0a066a319702bb7c0aaf8 100644 (file)
@@ -485,7 +485,7 @@ int register_user(struct Client *cptr, struct Client *sptr)
    * account assignment causes a numeric reply during registration.
    */
   if (HasHiddenHost(sptr))
-    hide_hostmask(sptr, FLAG_HIDDENHOST);
+    hide_hostmask(sptr, HIDE_HOSTMASK_FLAG_HIDDENHOST);
   if (IsInvisible(sptr))
     ++UserStats.inv_clients;
   if (IsOper(sptr))
@@ -589,7 +589,9 @@ static const struct UserMode {
   { FLAG_WEBIRC,      'W' },
   { FLAG_SEE_IDLETIME,'t' },
   { FLAG_SECURITY_SERV,'D' },
-  { FLAG_HIDDENHOST,  'x' }
+  { FLAG_KEEPCONN_ENABLED, 'K' },
+  { FLAG_HIDDENHOST,  'x' },
+  { FLAG_NOTCONN,     'Z' }
 };
 
 /** Length of #userModeList. */
@@ -727,7 +729,23 @@ int set_nick_name(struct Client* cptr, struct Client* sptr,
   }
   else {
     /* Local client setting NICK the first time */
-    strcpy(cli_name(sptr), nick);
+    if(!force)
+      strcpy(cli_name(sptr), nick);
+    else {
+      /* use a "temponary" nick here (we'll switch later) */
+      char tmpnick[NICKLEN + 2];
+      int tmpnickend; 
+      strcpy(tmpnick, nick);
+      /* we need at least 10 characters */
+      if (strlen(tmpnick) > IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN)) - 10)
+        tmpnick[IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN))-10] = '\0';
+      tmpnickend = strlen(tmpnick);
+      
+      do { /* get a non-used nick... */
+        sprintf(tmpnick + tmpnickend, "[rz%d]", ircrandom() % 10000);
+      } while(FindClient(tmpnick));
+      strcpy(cli_name(sptr), tmpnick);
+    }
     hAddClient(sptr);
     return auth_set_nick(cli_auth(sptr), nick);
   }
@@ -930,6 +948,8 @@ void send_umode_out(struct Client *cptr, struct Client *sptr,
   }
   if (cptr && MyUser(cptr))
     send_umode(cptr, sptr, old, ALL_UMODES, 0);
+  if (sptr && sptr != cptr && MyUser(sptr))
+    send_umode(sptr, sptr, old, ALL_UMODES, 0);
 }
 
 
@@ -979,35 +999,47 @@ hide_hostmask(struct Client *cptr, unsigned int flag)
   struct Membership *chan;
   char buf[HOSTLEN];
 
-  switch (flag) {
-  case FLAG_HIDDENHOST:
+  if (flag & HIDE_HOSTMASK_FLAG_HIDDENHOST) {
     /* Local users cannot set +x unless FEAT_HOST_HIDING is true. */
     if (MyConnect(cptr) && !feature_bool(FEAT_HOST_HIDING))
       return 0;
-    break;
-  case FLAG_ACCOUNT:
-  case FLAG_FAKEHOST:
-  case FLAG_FAKEIDENT:
+  }
+  if (flag & (HIDE_HOSTMASK_FLAG_ACCOUNT | HIDE_HOSTMASK_FLAG_FAKEHOST | HIDE_HOSTMASK_FLAG_FAKEIDENT)) {
     /* Invalidate all bans against the user so we check them again */
     for (chan = (cli_user(cptr))->channel; chan;
          chan = chan->next_channel)
       ClearBanValid(chan);
-    break;
-  default:
-    /* default: no special handling */
-    break;
   }
 
   /* Set flags and stop if no fakehost has to be applied. */
-  SetFlag(cptr, flag);
+  if(flag & HIDE_HOSTMASK_FLAG_HIDDENHOST)
+    SetFlag(cptr, FLAG_HIDDENHOST);
+  if(flag & HIDE_HOSTMASK_FLAG_ACCOUNT)
+    SetFlag(cptr, FLAG_ACCOUNT);
+  if(flag & HIDE_HOSTMASK_FLAG_FAKEHOST)
+    SetFlag(cptr, FLAG_FAKEHOST);
+  if(flag & HIDE_HOSTMASK_FLAG_FAKEIDENT)
+    SetFlag(cptr, FLAG_FAKEIDENT);
+  
   if(!HasHiddenHost(cptr))
     return 0;
 
   /* Generate new fakehost. */
-  if(IsFakeHost(cptr)) ircd_strncpy(buf, cli_user(cptr)->fakehost, HOSTLEN);
-  else if (IsAccount(cptr)) ircd_snprintf(0, buf, HOSTLEN, "%s.%s", cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST));
-  else return 0;
-  if(strncmp(buf, cli_user(cptr)->host, HOSTLEN) == 0 && (!IsFakeIdent(cptr) || strncmp(cli_user(cptr)->fakeuser, cli_user(cptr)->username, USERLEN) == 0)) return 0;
+  unsigned int reregister = 0;
+  if(IsFakeHost(cptr) || IsAccount(cptr)) {
+    if(IsFakeHost(cptr))
+      ircd_strncpy(buf, cli_user(cptr)->fakehost, HOSTLEN);
+    else
+      ircd_snprintf(0, buf, HOSTLEN, "%s.%s", cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST));
+    if(strncmp(buf, cli_user(cptr)->host, HOSTLEN))
+      reregister |= HIDE_HOSTMASK_FLAG_FAKEHOST;
+  }
+  if(IsFakeIdent(cptr)) {
+    if(strncmp(cli_user(cptr)->fakeuser, cli_user(cptr)->username, USERLEN))
+      reregister |= HIDE_HOSTMASK_FLAG_FAKEIDENT;
+  }
+  
+  if (!reregister) return 0;
 
   /* Remove all "valid" marks on the bans. This forces them to be
    * rechecked if the ban is accessed again.
@@ -1018,20 +1050,17 @@ hide_hostmask(struct Client *cptr, unsigned int flag)
 
   /* Quit user and set the previously generated fakehost. */
   sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Registered");
-  ircd_strncpy(cli_user(cptr)->host, buf, HOSTLEN);
-
-  /* spoof also the username if username is passed */
-  if(IsFakeIdent(cptr)) {
-    if(strncmp(cli_user(cptr)->username, cli_user(cptr)->fakeuser, USERLEN) != 0) {
-      ircd_strncpy(cli_username(cptr), cli_user(cptr)->fakeuser, USERLEN);
-      ircd_strncpy(cli_user(cptr)->username, cli_user(cptr)->fakeuser, USERLEN);
-    }
-    /* ok, the client is now fully hidden, so let them know -- hikari */
-    if (MyConnect(cptr))
+  if(reregister & HIDE_HOSTMASK_FLAG_FAKEHOST)
+    ircd_strncpy(cli_user(cptr)->host, buf, HOSTLEN);
+  if(reregister & HIDE_HOSTMASK_FLAG_FAKEIDENT)
+    ircd_strncpy(cli_user(cptr)->username, cli_user(cptr)->fakeuser, USERLEN);
+  
+
+  /* ok, the client is now fully hidden, so let them know -- hikari */
+  if(MyConnect(cptr)) {
+    if (IsFakeIdent(cptr))
       send_reply(cptr, RPL_HOSTUSERHIDDEN, cli_user(cptr)->username, cli_user(cptr)->host);
-  } else {
-    /* ok, the client is now fully hidden, so let them know -- hikari */
-    if (MyConnect(cptr))
+    else
       send_reply(cptr, RPL_HOSTHIDDEN, cli_user(cptr)->host);
   }
 
@@ -1060,6 +1089,25 @@ hide_hostmask(struct Client *cptr, unsigned int flag)
     else {
         SetDelayedJoin(chan);
     }
+    
+    /*
+    * Check if the client is actually overriding a ban with the
+    * mask change, if so, kick him out of the channel.
+    * We have to proceed that way to ensure data consistency (join + kick)
+    */
+    if (find_ban(cptr, chan->channel->banlist)) {
+      /* Silentely kick in case of delayed join */
+      if (chan->channel->mode.mode & MODE_DELJOINS) {
+        sendcmdto_one(&his, CMD_KICK, cptr, "%H %C :Ban override", chan->channel, cptr);
+        CheckDelayedJoins(chan->channel);
+        
+      } else {
+        /* Otherwise publicly kick */
+        sendcmdto_serv_butone(&me, CMD_KICK, NULL, "%H %C :Ban override", chan->channel, cptr);
+        sendcmdto_channel_butserv_butone(&his, CMD_KICK, chan->channel, NULL, 0, "%H %C :Ban override", chan->channel, cptr);
+        make_zombie(chan, cptr, &me, &me, chan->channel);
+      }
+    }
   }
   return 0;
 }
@@ -1090,7 +1138,7 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc,
   char buf[BUFSIZE];
   int prop = 0;
   int do_host_hiding = 0;
-  char* account = NULL, *fakehost = NULL;
+  char* account = NULL, *fakehost = NULL, *keepconn = NULL;
   struct Membership *chan;
 
   what = MODE_ADD;
@@ -1262,6 +1310,16 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc,
         else
           ClearWebIRC(sptr);
         break;
+      case 'K':
+        if (what == MODE_ADD) {
+          if(*(p + 1) == 0)
+            break;
+          keepconn = *(++p);
+          SetKeepConnEnabled(sptr);
+        } else {
+          ClearKeepConnEnabled(sptr);
+        }
+        break;
       case 'r':
         if (*(p + 1) && (what == MODE_ADD)) {
           account = *(++p);
@@ -1290,6 +1348,12 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc,
       case 'z': /* Formerly SSL mode; we ignore it. */
         break;
 #endif
+      case 'Z':
+        if (what == MODE_ADD)
+          SetNotConn(sptr);
+        else
+          ClearNotConn(sptr);
+        break;
       default:
         send_reply(sptr, ERR_UMODEUNKNOWNFLAG, *m);
         break;
@@ -1312,6 +1376,8 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc,
       ClrFlag(sptr, FLAG_FAKEHOST);
     if (!FlagHas(&setflags, FLAG_SEE_IDLETIME) && IsSeeIdletime(sptr))
       ClrFlag(sptr, FLAG_SEE_IDLETIME);
+    if (!FlagHas(&setflags, FLAG_NOTCONN) && IsNotConn(sptr))
+      ClrFlag(sptr, FLAG_NOTCONN);
     /*
      * new umode; servers and privileged opers can set it, local users cannot;
      * prevents users from /kick'ing or /mode -o'ing
@@ -1330,6 +1396,10 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc,
       ClearHiddenOper(sptr);
     if (!FlagHas(&setflags, FLAG_OVERRIDECC) && IsOverrideCC(sptr) && !HasPriv(sptr, PRIV_UMODE_OVERRIDECC))
       ClearOverrideCC(sptr);
+    if (!FlagHas(&setflags, FLAG_KEEPCONN_ENABLED) && IsKeepConnEnabled(sptr) && !HasPriv(sptr, PRIV_SET_KEEPCONN))
+      ClearKeepConnEnabled(sptr);
+    if(keepconn && !HasPriv(sptr, PRIV_SET_KEEPCONN))
+      keepconn = NULL;
     /* Opers are able to fake the webirc usermode only if FEAT_FAKE_WEBIRC is true. */
     if (!FlagHas(&setflags, FLAG_WEBIRC) && IsWebIRC(sptr) && !(feature_bool(FEAT_FAKE_WEBIRC) && IsOper(sptr)))
       ClearWebIRC(sptr);
@@ -1390,6 +1460,12 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc,
   if (!FlagHas(&setflags, FLAG_FAKEHOST) && IsFakeHost(sptr)) {
     ircd_strncpy(cli_user(sptr)->fakehost, fakehost, HOSTLEN);
   }
+  if (!FlagHas(&setflags, FLAG_KEEPCONN_ENABLED) && IsKeepConnEnabled(sptr)) {
+    sptr->keepconn = atoi(keepconn);
+  } else if(keepconn && sptr->keepconn != atoi(keepconn)) {
+    FlagClr(&setflags, FLAG_KEEPCONN_ENABLED);
+    sptr->keepconn = atoi(keepconn);
+  }
 
   if (IsRegistered(sptr)) {
     if(do_host_hiding)
@@ -1517,7 +1593,7 @@ int send_umode(struct Client *cptr, struct Client *sptr, struct Flags *old,
   int flag;
   char *m;
   int what = MODE_NULL;
-  int add_fakehost = 0, add_account = 0;
+  int add_fakehost = 0, add_account = 0, add_keepconn = 0;
 
   /*
    * Build a string in umodeBuf to represent the change in the user's
@@ -1544,6 +1620,10 @@ int send_umode(struct Client *cptr, struct Client *sptr, struct Flags *old,
       if(!serv_modes || FlagHas(old, flag)) continue;
       add_fakehost = 1;
     }
+    
+    if(flag == FLAG_KEEPCONN_ENABLED) {
+      add_keepconn = 1;
+    }
 
     switch (sendset)
     {
@@ -1609,6 +1689,11 @@ int send_umode(struct Client *cptr, struct Client *sptr, struct Flags *old,
     while((*m++ = *t++)) ; /* Empty loop */
     --m; /* back up over previous nul-termination */
   }
+  
+  if(add_keepconn) {
+    *m++ = ' ';
+    m += sprintf(m, "%u", sptr->keepconn);
+  }
 
   *m = '\0';
   if (*umodeBuf && cptr)