Forward port of delayed-join.patch from Quakenet's "Asuka" patch set
authorMichael Poole <mdpoole@troilus.org>
Tue, 11 May 2004 02:10:30 +0000 (02:10 +0000)
committerMichael Poole <mdpoole@troilus.org>
Tue, 11 May 2004 02:10:30 +0000 (02:10 +0000)
(which was a port of code I wrote for the other ircu).

git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@1040 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

22 files changed:
ChangeLog
include/channel.h
include/numeric.h
include/s_user.h
include/supported.h
ircd/channel.c
ircd/ircd_relay.c
ircd/m_burst.c
ircd/m_clearmode.c
ircd/m_join.c
ircd/m_kick.c
ircd/m_names.c
ircd/m_part.c
ircd/m_quit.c
ircd/m_topic.c
ircd/m_wallchops.c
ircd/m_wallvoices.c
ircd/m_who.c
ircd/m_whois.c
ircd/s_err.c
ircd/s_user.c
ircd/send.c

index 8ee50c806a1825dca1778685e90977e231e5a7bd..bf0cc26d2eb19a6c5854d285071afda99f98a23d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,83 @@
+2004-05-10  Michael Poole <mdpoole@troilus.org>
+
+       Forward port of delayed-join.patch from Quakenet's "Asuka" patch
+       set (which was a port of code I wrote for the other ircu).
+
+       * include/channel.h (CHFL_DELAYED): New membership flag.
+       (MODE_DELJOINS, MODE_WASDELJOINS): New channel modes.
+       (infochanmodes): Add +D to list of supported channel modes.
+       (IsDelayedJoin, SetDelayedJoin, ClearDelayedJoin): New macros.
+       (member_can_send_to_channel, client_can_send_to_channel): Add
+       "reveal" parameter to indicate whether a request should cause
+       a join-delayed user to become visible.
+       (RevealDelayedJoin, CheckDelayedJoins): New functions.
+
+       * include/numeric.h (RPL_DELNAMREPLY): New numeric.
+       
+       * include/s_user.h (NAMES_DEL): New flag for do_names().
+
+       * include/supported.h (FEATURESVALUES2): Add +D to list of
+       supported channel modes.
+
+       * ircd/channel.c (remove_member_from_channel,
+       member_can_send_to_channel, client_can_send_to_channel,
+       joinbuf_join): Handle join-delayed users.
+       (channel_modes, modebuf_flush_int, modebuf_mode, modebuf_flush,
+       modebuf_extract, mode_process_clients, mode_parse_mode,
+       mode_parse): Handle delayed-join channels.
+       (RevealDelayedJoin, CheckDelayedJoins): New functions.
+
+       * ircd/ircd_relay.c (relay_channel_message, relay_channel_notice,
+       server_relay_channel_message, server_relay_channel_notice): Add
+       argument for "reveal" parameter to client_can_send_to_channel().
+
+       * ircd/m_burst.c (ms_burst): Support MODE_DELJOINS channels.
+
+       * ircd/m_clearmode.c (do_clearmode): Support clearing mode +D.
+
+       * ircd/m_join.c (join0): Pass the CHFL_DELAYED flag when parting a
+       channel with JOIN 0.
+
+       * ircd/m_kick.c (m_kick): For join-delayed members, only send the
+       KICK to the kicker and kickee.  Then check whether +d can be
+       removed.
+
+       * ircd/m_names.c (do_names): Show join-delayed users if and only
+       if the NAMES_DEL flag is given.  If NAMES_DEL is given, also use
+       RPL_DELNAMREPLY instead of RPL_NAMREPLY.
+       (m_names): If NAMES -D, pass NAMES_DEL to do_names().
+
+       * ircd/m_part.c (m_part, ms_part): Add "reveal" argument for
+       member_can_send_to_channel().  Set CHFL_DELAYED join in joinbuf if
+       the user is join-delayed.
+
+       * ircd/m_quit.c (m_quit): Handle join-delayed users and new
+       argument for member_can_send_to_channel().
+
+       * ircd/m_topic.c (do_settopic): If a join-delayed channel member
+       changes the topic, reveal the member.
+
+       * ircd/m_wallchops.c (m_wallchops, ms_wallchops): Add argument for
+       "reveal" parameter to client_can_send_to_channel().
+
+       * ircd/m_wallvoices.c (m_wallvoices, ms_wallvoices): Likewise.
+
+       * ircd/m_who.c (m_who): Skip join-delayed members where we skip
+       zombies.
+
+       * ircd/m_whois.c (do_whois): Use '<' as a prefix for join-delayed
+       users.  Use slightly more efficient macros rather than function
+       calls to test for ops and voice.
+
+       * ircd/s_err.c (RPL_DELNAMREPLY): New numeric response string.
+
+       * ircd/s_user.c (hide_hostmask): For users with no modes in a
+       join-delayed channel, do not send JOIN to other members after the
+       QUIT :Registered.
+
+       * ircd/send.c (sendcmdto_common_channels_butone): Skip
+       join-delayed users where we skip zombies.
+       
 2004-05-10  Michael Poole <mdpoole@troilus.org>
 
        * ircd/ircd_events.c: Actually reference and try to use the epoll
index 0fc12e9ccd234ef22e1bdd07b2bcc3c7a9f3d182..18d6d9fec7994dd0842ee894b9482e4fda53b0ea 100644 (file)
@@ -70,7 +70,8 @@ struct Client;
 #define CHFL_BURST_ALREADY_OPPED       0x04000  /* In oob BURST, but was already joined and opped */
 #define CHFL_BURST_ALREADY_VOICED      0x08000  /* In oob BURST, but was already joined and voiced */
 #define CHFL_CHANNEL_MANAGER   0x10000 /* Set when creating channel or using Apass */
-#define CHFL_USER_PARTING       0x20000  /* User is already parting that channel */
+#define CHFL_USER_PARTING       0x20000 /* User is already parting that channel */
+#define CHFL_DELAYED            0x40000 /* User's join message is delayed */
 
 #define CHFL_OVERLAP         (CHFL_CHANOP | CHFL_VOICE)
 #define CHFL_BANVALIDMASK    (CHFL_BANVALID | CHFL_BANNED)
@@ -90,18 +91,20 @@ struct Client;
 #define MODE_BAN        0x0200
 #define MODE_LIMIT      0x0400
 #define MODE_REGONLY    0x0800  /* Only +r users may join */
+#define MODE_DELJOINS   0x1000  /* New join messages are delayed */
 #define MODE_LISTED     0x10000
 #define MODE_SAVE      0x20000 /* save this mode-with-arg 'til later */
 #define MODE_FREE      0x40000 /* string needs to be passed to MyFree() */
 #define MODE_BURSTADDED        0x80000 /* channel was created by a BURST */
 #define MODE_UPASS     0x100000
 #define MODE_APASS     0x200000
+#define MODE_WASDELJOINS 0x400000 /* Not DELJOINS, but some joins pending */
 /*
  * mode flags which take another parameter (With PARAmeterS)
  */
 #define MODE_WPARAS     (MODE_CHANOP|MODE_VOICE|MODE_BAN|MODE_KEY|MODE_LIMIT|MODE_APASS|MODE_UPASS)
 
-#define infochanmodes feature_bool(FEAT_OPLEVELS) ? "Abiklmnopstuvr" : "biklmnopstvr"
+#define infochanmodes feature_bool(FEAT_OPLEVELS) ? "AbiklmnopstuvrD" : "biklmnopstvrD"
 #define infochanmodeswithparams feature_bool(FEAT_OPLEVELS) ? "Abklouv" : "bklov"
 
 #define HoldChannel(x)          (!(x))
@@ -193,6 +196,7 @@ struct Membership {
 #define IsVoicedOrOpped(x)  ((x)->status & CHFL_VOICED_OR_OPPED)
 #define IsChannelManager(x) ((x)->status & CHFL_CHANNEL_MANAGER)
 #define IsUserParting(x)    ((x)->status & CHFL_USER_PARTING)
+#define IsDelayedJoin(x)    ((x)->status & CHFL_DELAYED)
 
 #define SetBanned(x)        ((x)->status |= CHFL_BANNED)
 #define SetBanValid(x)      ((x)->status |= CHFL_BANVALID)
@@ -203,12 +207,14 @@ struct Membership {
 #define SetChannelManager(x) ((x)->status |= CHFL_CHANNEL_MANAGER)
 #define SetOpLevel(x, v)    (void)((x)->oplevel = (v))
 #define SetUserParting(x)   ((x)->status |= CHFL_USER_PARTING)
+#define SetDelayedJoin(x)   ((x)->status |= CHFL_DELAYED)
 
 #define ClearBanned(x)      ((x)->status &= ~CHFL_BANNED)
 #define ClearBanValid(x)    ((x)->status &= ~CHFL_BANVALID)
 #define ClearDeopped(x)     ((x)->status &= ~CHFL_DEOPPED)
 #define ClearServOpOk(x)    ((x)->status &= ~CHFL_SERVOPOK)
 #define ClearBurstJoined(x) ((x)->status &= ~CHFL_BURST_JOINED)
+#define ClearDelayedJoin(x) ((x)->status &= ~CHFL_DELAYED)
 
 
 struct Mode {
@@ -345,8 +351,8 @@ int number_of_zombies(struct Channel *chptr);
 extern const char* find_no_nickchange_channel(struct Client* cptr);
 extern struct Membership* IsMember(struct Client *cptr, struct Channel *chptr);
 extern struct Membership* find_channel_member(struct Client* cptr, struct Channel* chptr);
-extern int member_can_send_to_channel(struct Membership* member);
-extern int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr);
+extern int member_can_send_to_channel(struct Membership* member, int reveal);
+extern int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr, int reveal);
 
 extern void remove_user_from_channel(struct Client *sptr, struct Channel *chptr);
 extern void remove_user_from_all_channels(struct Client* cptr);
@@ -366,6 +372,9 @@ extern void del_invite(struct Client *cptr, struct Channel *chptr);
 extern void list_next_channels(struct Client *cptr, int nr);
 extern void list_set_default(void); /* this belongs elsewhere! */
 
+extern void RevealDelayedJoin(struct Membership *member);
+extern void CheckDelayedJoins(struct Channel *chan);
+
 extern void modebuf_init(struct ModeBuf *mbuf, struct Client *source,
                         struct Client *connect, struct Channel *chan,
                         unsigned int dest);
index 05f62f5fd82b299c44a1c50721b6f47392fc4b9d..334f7b0ba70d41755d3cd4d2d19003467404bc7f 100644 (file)
@@ -267,6 +267,7 @@ extern const struct Numeric* get_error_numeric(int err);
 #define RPL_NAMREPLY         353        /* See also RPL_ENDOFNAMES */
 #define RPL_WHOSPCRPL        354        /* Undernet extension,
                                            See also RPL_ENDOFWHO */
+#define RPL_DELNAMREPLY      355        /* QuakeNet extension */
 
 #define RPL_KILLDONE         361       /* not used */
 #define RPL_CLOSING          362
index 587b599d5e1933be89daea882dcd84e237835f02..aebbbcd47559b3ae7bf8db421c01091e1ce650d7 100644 (file)
@@ -108,6 +108,7 @@ extern int send_supported(struct Client *cptr);
 #define NAMES_ALL 1 /* List all users in channel */
 #define NAMES_VIS 2 /* List only visible users in non-secret channels */
 #define NAMES_EON 4 /* Add an 'End Of Names' reply to the end */
+#define NAMES_DEL 8 /* Show delayed joined users only */
 
 void do_names(struct Client* sptr, struct Channel* chptr, int filter);
 
index 650cfdae442e9ed4534f0d1bc53441796b36cce0..d875641bf3fc1694a0b6a7f36f8c07879236faa1 100644 (file)
@@ -58,7 +58,7 @@
                        feature_int(FEAT_MAXBANS), NICKLEN, TOPICLEN, \
                        AWAYLEN, TOPICLEN
 
-#define FEATURESVALUES2 feature_bool(FEAT_LOCAL_CHANNELS) ? "#&" : "#", "(ov)@+", "b,k,l,imnpstr", "rfc1459", \
+#define FEATURESVALUES2 feature_bool(FEAT_LOCAL_CHANNELS) ? "#&" : "#", "(ov)@+", "b,k,l,imnpstrD", "rfc1459", \
                         feature_str(FEAT_NETWORK)
 
 #endif /* INCLUDED_supported_h */
index 0aa8da8650b4f73dc2e7318fb4301d4da07af950..00e4fd9eefd7d22b06ef71de561deaa21793b985 100644 (file)
@@ -564,7 +564,13 @@ static int remove_member_from_channel(struct Membership* member)
     member->prev_member->next_member = member->next_member;
   else
     member->channel->members = member->next_member; 
-      
+
+  /*
+   * If this is the last delayed-join user, may have to clear WASDELJOINS.
+   */
+  if (IsDelayedJoin(member))
+    CheckDelayedJoins(chptr);
+
   /*
    * unlink client channel list
    */
@@ -657,7 +663,7 @@ int has_voice(struct Client* cptr, struct Channel* chptr)
   return 0;
 }
 
-int member_can_send_to_channel(struct Membership* member)
+int member_can_send_to_channel(struct Membership* member, int reveal)
 {
   assert(0 != member);
 
@@ -680,10 +686,14 @@ int member_can_send_to_channel(struct Membership* member)
    */
   if (MyUser(member->user) && is_banned(member->user, member->channel, member))
     return 0;
+
+  if (IsDelayedJoin(member) && reveal)
+    RevealDelayedJoin(member);
+
   return 1;
 }
 
-int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr)
+int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr, int reveal)
 {
   struct Membership *member;
   assert(0 != cptr); 
@@ -706,7 +716,7 @@ int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr)
     else
       return !is_banned(cptr, chptr, NULL);
   }
-  return member_can_send_to_channel(member); 
+  return member_can_send_to_channel(member, reveal);
 }
 
 /*
@@ -758,6 +768,10 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
     *mbuf++ = 'n';
   if (chptr->mode.mode & MODE_REGONLY)
     *mbuf++ = 'r';
+  if (chptr->mode.mode & MODE_DELJOINS)
+    *mbuf++ = 'D';
+  else if (MyUser(cptr) && (chptr->mode.mode & MODE_WASDELJOINS))
+    *mbuf++ = 'd';
   if (chptr->mode.limit) {
     *mbuf++ = 'l';
     ircd_snprintf(0, pbuf, buflen, "%u", chptr->mode.limit);
@@ -1501,6 +1515,8 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
     MODE_INVITEONLY,   'i',
     MODE_NOPRIVMSGS,   'n',
     MODE_REGONLY,      'r',
+    MODE_DELJOINS,      'D',
+    MODE_WASDELJOINS,   'd',
 /*  MODE_KEY,          'k', */
 /*  MODE_BAN,          'b', */
 /*  MODE_LIMIT,                'l', */
@@ -1886,7 +1902,8 @@ modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
 
   mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED |
-          MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY);
+          MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY |
+           MODE_DELJOINS | MODE_WASDELJOINS);
 
   if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
     return;
@@ -1968,6 +1985,21 @@ modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode,
 int
 modebuf_flush(struct ModeBuf *mbuf)
 {
+  struct Membership *memb;
+
+  /* Check if MODE_WASDELJOINS should be set */
+  if (!(mbuf->mb_channel->mode.mode & (MODE_DELJOINS | MODE_WASDELJOINS))
+      && (mbuf->mb_rem & MODE_DELJOINS)) {
+    for (memb = mbuf->mb_channel->members; memb; memb = memb->next_member) {
+      if (IsDelayedJoin(memb)) {
+          mbuf->mb_channel->mode.mode |= MODE_WASDELJOINS;
+          mbuf->mb_add |= MODE_WASDELJOINS;
+          mbuf->mb_rem &= ~MODE_WASDELJOINS;
+          break;
+      }
+    }
+  }
+
   return modebuf_flush_int(mbuf, 1);
 }
 
@@ -1992,6 +2024,7 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
 /*  MODE_BAN,          'b', */
     MODE_LIMIT,                'l',
     MODE_REGONLY,      'r',
+    MODE_DELJOINS,      'D',
     0x0, 0x0
   };
   unsigned int add;
@@ -2832,13 +2865,11 @@ mode_process_clients(struct ParseState *state)
       SetOpLevel(member, old_level == MAXOPLEVEL ? MAXOPLEVEL : (old_level + level_increment));
     }
 
-    /* accumulate the change */
-    modebuf_mode_client(state->mbuf, state->cli_change[i].flag,
-                       state->cli_change[i].client);
-
     /* actually effect the change */
     if (state->flags & MODE_PARSE_SET) {
       if (state->cli_change[i].flag & MODE_ADD) {
+        if (IsDelayedJoin(member))
+          RevealDelayedJoin(member);
        member->status |= (state->cli_change[i].flag &
                           (MODE_CHANOP | MODE_VOICE));
        if (state->cli_change[i].flag & MODE_CHANOP)
@@ -2847,6 +2878,10 @@ mode_process_clients(struct ParseState *state)
        member->status &= ~(state->cli_change[i].flag &
                            (MODE_CHANOP | MODE_VOICE));
     }
+
+    /* accumulate the change */
+    modebuf_mode_client(state->mbuf, state->cli_change[i].flag,
+                       state->cli_change[i].client);
   } /* for (i = 0; state->cli_change[i].flags; i++) */
 }
 
@@ -2876,6 +2911,10 @@ mode_parse_mode(struct ParseState *state, int *flag_p)
       state->add &= ~MODE_SECRET;
       state->del |= MODE_SECRET;
     }
+    if (flag_p[0] & MODE_DELJOINS) {
+      state->add &= ~MODE_WASDELJOINS;
+      state->del |= MODE_WASDELJOINS;
+    }
   } else {
     state->add &= ~flag_p[0];
     state->del |= flag_p[0];
@@ -2911,6 +2950,7 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
     MODE_BAN,          'b',
     MODE_LIMIT,                'l',
     MODE_REGONLY,      'r',
+    MODE_DELJOINS,      'D',
     MODE_ADD,          '+',
     MODE_DEL,          '-',
     0x0, 0x0
@@ -3147,7 +3187,7 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
     SetUserParting(member);
 
     /* Send notification to channel */
-    if (!(flags & CHFL_ZOMBIE))
+    if (!(flags & (CHFL_ZOMBIE | CHFL_DELAYED)))
       sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL,
                                (flags & CHFL_BANNED || !jbuf->jb_comment) ?
                                ":%H" : "%H :%s", chan, jbuf->jb_comment);
@@ -3167,20 +3207,26 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
       remove_user_from_channel(jbuf->jb_source, chan);
   } else {
     /* Add user to channel */
-    add_user_to_channel(chan, jbuf->jb_source, flags, 0);
+    if ((chan->mode.mode & MODE_DELJOINS) && !(flags & CHFL_VOICED_OR_OPPED))
+      add_user_to_channel(chan, jbuf->jb_source, flags | CHFL_DELAYED, 0);
+    else
+      add_user_to_channel(chan, jbuf->jb_source, flags, 0);
 
     /* send notification to all servers */
     if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !is_local)
       sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
                            "%H %Tu", chan, chan->creationtime);
 
-    /* Send the notification to the channel */
-    sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, ":%H", chan);
+    if (!((chan->mode.mode & MODE_DELJOINS) && !(flags & CHFL_VOICED_OR_OPPED))) {
+      /* Send the notification to the channel */
+      sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, "%H", chan);
 
-    /* send an op, too, if needed */
-    if (!MyUser(jbuf->jb_source) && jbuf->jb_type == JOINBUF_TYPE_CREATE)
-      sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_MODE, chan, NULL, "%H +o %C",
-                               chan, jbuf->jb_source);
+      /* send an op, too, if needed */
+      if (!MyUser(jbuf->jb_source) && jbuf->jb_type == JOINBUF_TYPE_CREATE)
+       sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_MODE, chan, NULL, "%H +o %C",
+                                        chan, jbuf->jb_source);
+    } else if (MyUser(jbuf->jb_source))
+      sendcmdto_one(jbuf->jb_source, CMD_JOIN, jbuf->jb_source, ":%H", chan);
   }
 
   if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
@@ -3258,3 +3304,31 @@ int IsInvited(struct Client* cptr, const void* chptr)
       return 1;
   return 0;
 }
+
+/* RevealDelayedJoin: sends a join for a hidden user */
+
+void RevealDelayedJoin(struct Membership *member) {
+  ClearDelayedJoin(member);
+  sendcmdto_channel_butserv_butone(member->user, CMD_JOIN, member->channel, member->user, ":%H",
+                                   member->channel);
+  CheckDelayedJoins(member->channel);
+}
+
+/* CheckDelayedJoins: checks and clear +d if necessary */
+
+void CheckDelayedJoins(struct Channel *chan) {
+  struct Membership *memb2;
+  
+  if (chan->mode.mode & MODE_WASDELJOINS) {
+    for (memb2=chan->members;memb2;memb2=memb2->next_member)
+      if (IsDelayedJoin(memb2))
+        break;
+    
+    if (!memb2) {
+      /* clear +d */
+      chan->mode.mode &= ~MODE_WASDELJOINS;
+      sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan, NULL, 
+                                       "%H -d", chan);
+    }
+  }
+}
index f8cd9d4894699136d656459a76ddcfaf94f2e95a..3ac0850e3a9ef1bf8a645a8a7431d898a06c612a 100644 (file)
@@ -68,7 +68,7 @@ void relay_channel_message(struct Client* sptr, const char* name, const char* te
   /*
    * This first: Almost never a server/service
    */
-  if (!client_can_send_to_channel(sptr, chptr)) {
+  if (!client_can_send_to_channel(sptr, chptr, 1)) {
     send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
     return;
   }
@@ -92,7 +92,7 @@ void relay_channel_notice(struct Client* sptr, const char* name, const char* tex
   /*
    * This first: Almost never a server/service
    */
-  if (!client_can_send_to_channel(sptr, chptr))
+  if (!client_can_send_to_channel(sptr, chptr, 1))
     return;
 
   if ((chptr->mode.mode & MODE_NOPRIVMSGS) &&
@@ -121,7 +121,7 @@ void server_relay_channel_message(struct Client* sptr, const char* name, const c
    * This first: Almost never a server/service
    * Servers may have channel services, need to check for it here
    */
-  if (client_can_send_to_channel(sptr, chptr) || IsChannelService(sptr)) {
+  if (client_can_send_to_channel(sptr, chptr, 1) || IsChannelService(sptr)) {
     sendcmdto_channel_butone(sptr, CMD_PRIVATE, chptr, cli_from(sptr),
                             SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text);
   }
@@ -142,7 +142,7 @@ void server_relay_channel_notice(struct Client* sptr, const char* name, const ch
    * This first: Almost never a server/service
    * Servers may have channel services, need to check for it here
    */
-  if (client_can_send_to_channel(sptr, chptr) || IsChannelService(sptr)) {
+  if (client_can_send_to_channel(sptr, chptr, 1) || IsChannelService(sptr)) {
     sendcmdto_channel_butone(sptr, CMD_NOTICE, chptr, cli_from(sptr),
                             SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text);
   }
index 289c93efb390414a3b05bb033bb8c9648ec470ca..3354a0461d3eb54207853131beb2d5fef260edcd 100644 (file)
@@ -238,7 +238,7 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     modebuf_mode(mbuf, MODE_DEL | chptr->mode.mode); /* wipeout modes */
     chptr->mode.mode &= ~(MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET |
                          MODE_MODERATED | MODE_TOPICLIMIT | MODE_INVITEONLY |
-                         MODE_NOPRIVMSGS);
+                         MODE_NOPRIVMSGS | MODE_DELJOINS);
 
     parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */
 
@@ -340,12 +340,16 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
       {
        struct Client *acptr;
        char *nicklist = parv[param], *p = 0, *nick, *ptr;
-       int current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
-       int last_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
+       int current_mode, last_mode, base_mode;
        int oplevel = -1;       /* Mark first field with digits: means the same as 'o' (but with level). */
        int last_oplevel = 0;
        struct Membership* member;
 
+        base_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
+        if (chptr->mode.mode & MODE_DELJOINS)
+            base_mode |= CHFL_DELAYED;
+        current_mode = last_mode = base_mode;
+
        for (nick = ircd_strtok(&p, nicklist, ","); nick;
             nick = ircd_strtok(&p, 0, ",")) {
 
@@ -363,29 +367,29 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
                   */
                  oplevel = MAXOPLEVEL;
                  if (current_mode_needs_reset) {
-                   current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
+                   current_mode = base_mode;
                    current_mode_needs_reset = 0;
                  }
-                 current_mode = (current_mode & ~CHFL_DEOPPED) | CHFL_CHANOP;
+                 current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
                }
                else if (*ptr == 'v') { /* has voice status */
                  if (current_mode_needs_reset) {
-                   current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
+                    current_mode = base_mode;
                    current_mode_needs_reset = 0;
                  }
-                 current_mode |= CHFL_VOICE;
+                 current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE;
                  oplevel = -1; /* subsequential digits are an absolute op-level value. */
                 }
                else if (isdigit(*ptr)) {
                  int level_increment = 0;
                  if (oplevel == -1) { /* op-level is absolute value? */
                    if (current_mode_needs_reset) {
-                     current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
+                     current_mode = base_mode;
                      current_mode_needs_reset = 0;
                    }
                    oplevel = 0;
                  }
-                 current_mode = (current_mode & ~CHFL_DEOPPED) | CHFL_CHANOP;
+                 current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
                  do {
                    level_increment = 10 * level_increment + *ptr++ - '0';
                  } while(isdigit(*ptr));
@@ -425,7 +429,8 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
          if (IsBurst(sptr) || !(member = find_member_link(chptr, acptr)))
          {
            add_user_to_channel(chptr, acptr, current_mode, oplevel);
-           sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, "%H", chptr);
+            if (!(current_mode & CHFL_DELAYED))
+              sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, "%H", chptr);
          }
          else
          {
index 2ac76521712d9ad48cbba7d0b80008449e04cd55..d2f99b158a76b15f999ee9432d38810ac57efa68 100644 (file)
@@ -124,6 +124,7 @@ do_clearmode(struct Client *cptr, struct Client *sptr, struct Channel *chptr,
     MODE_BAN,          'b',
     MODE_LIMIT,                'l',
     MODE_REGONLY,      'r',
+    MODE_DELJOINS,      'D',
     0x0, 0x0
   };
   int *flag_p;
index 143a09081f32d6464668710a87d072c97f56ba5a..76eb13867d65b8b581e510cf01700b3a78af243d 100644 (file)
@@ -152,7 +152,9 @@ join0(struct JoinBuf *join, struct Client *cptr, struct Client *sptr,
 
   while ((member = cli_user(sptr)->channel))
     joinbuf_join(&part, member->channel,
-                IsZombie(member) ? CHFL_ZOMBIE : 0);
+                IsZombie(member) ? CHFL_ZOMBIE :
+                 IsDelayedJoin(member) ? CHFL_DELAYED :
+                 0);
 
   joinbuf_flush(&part);
 
index 97b59e4d1b18f77d76555d5164b454175dbc28db..2096bf5f04274ab8bc9b5a6347161a25b57d0465 100644 (file)
@@ -154,8 +154,16 @@ int m_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     sendcmdto_serv_butone(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who,
                          comment);
 
-  sendcmdto_channel_butserv_butone(sptr, CMD_KICK, chptr, NULL, "%H %C :%s", chptr, who,
-                           comment);
+  if (IsDelayedJoin(member)) {
+    /* If it's a delayed join, only send the KICK to the person doing
+     * the kicking and the victim */
+    if (MyUser(who))
+      sendcmdto_one(sptr, CMD_KICK, who, "%H %C :%s", chptr, who, comment);
+    sendcmdto_one(sptr, CMD_KICK, sptr, "%H %C :%s", chptr, who, comment);
+    CheckDelayedJoins(chptr);
+  } else
+    sendcmdto_channel_butserv_butone(sptr, CMD_KICK, chptr, NULL, "%H %C :%s", chptr, who,
+                                     comment);
 
   make_zombie(member, who, cptr, sptr, chptr);
 
index 02e80d2282789716c0268f46a7d0ff7d419b5687..2ee0c65b1f67ea2f3a0cc52165131164682574d7 100644 (file)
@@ -155,6 +155,12 @@ void do_names(struct Client* sptr, struct Channel* chptr, int filter)
     if (IsZombie(member) && member->user != sptr)
       continue;
 
+    if (IsDelayedJoin(member) && (member->user != sptr) && !(filter & NAMES_DEL))
+        continue;
+
+    if ((!IsDelayedJoin(member) || (member->user == sptr)) && (filter & NAMES_DEL))
+        continue;
+
     if (needs_space) {
        strcat(buf, " ");
       idx++;
@@ -181,7 +187,7 @@ void do_names(struct Client* sptr, struct Channel* chptr, int filter)
     if (mlen + idx + NICKLEN + 5 > BUFSIZE)
       /* space, modifier, nick, \r \n \0 */
     { 
-      send_reply(sptr, RPL_NAMREPLY, buf);
+      send_reply(sptr, (filter & NAMES_DEL) ? RPL_DELNAMREPLY : RPL_NAMREPLY, buf);
       strcpy(buf, "* ");
       ircd_strncpy(buf + 2, chptr->chname, len + 1);
       buf[len + 2] = 0;
@@ -196,7 +202,7 @@ void do_names(struct Client* sptr, struct Channel* chptr, int filter)
     }
   }
   if (flag)
-    send_reply(sptr, RPL_NAMREPLY, buf); 
+    send_reply(sptr, (filter & NAMES_DEL) ? RPL_DELNAMREPLY : RPL_NAMREPLY, buf); 
   if (filter&NAMES_EON)
     send_reply(sptr, RPL_ENDOFNAMES, chptr->chname);
 }
@@ -216,8 +222,14 @@ int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   struct Membership* member; 
   char* s;
   char* para = parc > 1 ? parv[1] : 0; 
+  int showingdelayed = 0;
 
-  if (parc > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2, parc, parv))
+  if (parc > 1 && !ircd_strcmp(parv[1], "-D")) {
+      para = (parc > 2) ? parv[2] : 0;
+      showingdelayed = 1;
+  }
+
+  if ((parc - showingdelayed) > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2+showingdelayed, parc, parv))
     return 0; 
 
   if (EmptyString(para)) {
@@ -229,7 +241,7 @@ int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   
   s = strchr(para, ','); /* Recursively call m_names for each comma-seperated channel. Eww. */
   if (s) {
-    parv[1] = ++s;
+    parv[1+showingdelayed] = ++s;
     m_names(cptr, sptr, parc, parv);
   }
  
@@ -256,9 +268,9 @@ int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 
       if (find_channel_member(sptr, ch2ptr))
       {
-        do_names(sptr, ch2ptr, NAMES_ALL); /* Full list if we're in this chan. */
+        do_names(sptr, ch2ptr, (showingdelayed?NAMES_DEL:0)|NAMES_ALL); /* Full list if we're in this chan. */
       } else { 
-        do_names(sptr, ch2ptr, NAMES_VIS);
+        do_names(sptr, ch2ptr, (showingdelayed?NAMES_DEL:0)|NAMES_VIS);
       }
     } 
 
@@ -321,7 +333,7 @@ int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
     member = find_member_link(chptr, sptr);
     if (member)
     { 
-      do_names(sptr, chptr, NAMES_ALL);
+      do_names(sptr, chptr, (showingdelayed?NAMES_DEL:0)|NAMES_ALL);
       if (!EmptyString(para))
       {
         send_reply(sptr, RPL_ENDOFNAMES, chptr ? chptr->chname : para);
@@ -334,7 +346,7 @@ int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
        *  Special Case 3: User isn't on this channel, show all visible users, in 
        *  non secret channels.
        */ 
-      do_names(sptr, chptr, NAMES_VIS);
+      do_names(sptr, chptr, (showingdelayed?NAMES_DEL:0)|NAMES_VIS);
       send_reply(sptr, RPL_ENDOFNAMES, para);
     } 
   } else { /* Channel doesn't exist. */ 
index 492469202f0e9a0e0987a95c58447e3c5827abb6..00da1ef64f5d07fbb9d763e92ff2ccda2e385420 100644 (file)
@@ -139,13 +139,16 @@ int m_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 
     assert(!IsZombie(member)); /* Local users should never zombie */
 
-    if (!member_can_send_to_channel(member))
+    if (!member_can_send_to_channel(member, 0))
     {
       flags |= CHFL_BANNED;
       /* Remote clients don't want to see a comment either. */
       parts.jb_comment = 0;
     }
 
+    if (IsDelayedJoin(member))
+      flags |= CHFL_DELAYED;
+
     joinbuf_join(&parts, chptr, flags); /* part client from channel */
   }
 
@@ -194,12 +197,15 @@ int ms_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
     if (IsZombie(member)) /* figure out special flags... */
       flags |= CHFL_ZOMBIE;
 
+    if (IsDelayedJoin(member))
+      flags |= CHFL_DELAYED;
+
     /*
      * XXX BUG: If a client /part's with a part notice, on channels where
      * he's banned, local clients will not see the part notice, but remote
      * clients will.
      */
-    if (!member_can_send_to_channel(member))
+    if (!member_can_send_to_channel(member, 0))
       flags |= CHFL_BANNED;
 
     /* part user from channel */
index 9ca6786325ede6f5147beaccb3f1317579cd80c4..e09dc3c66e05ee20f792922a1236f200f425bc29 100644 (file)
@@ -107,7 +107,7 @@ int m_quit(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   if (cli_user(sptr)) {
     struct Membership* chan;
     for (chan = cli_user(sptr)->channel; chan; chan = chan->next_channel) {
-      if (!IsZombie(chan) && !member_can_send_to_channel(chan))
+        if (!IsZombie(chan) && !IsDelayedJoin(chan) && !member_can_send_to_channel(chan, 0))
         return exit_client(cptr, sptr, sptr, "Signed off");
     }
   }
index a41745c0fca6941bab5ad56e7c2dac68a4a5337d..83e89f22a122f43e8e77f6e677ee4c64e16fa067 100644 (file)
 static void do_settopic(struct Client *sptr, struct Client *cptr, 
                        struct Channel *chptr,char *topic)
 {
+   struct Membership *member;
    int newtopic;
+   member = find_channel_member(sptr, chptr);
    /* if +n and not @'d, return an error and ignore the topic */
-   if ((chptr->mode.mode & MODE_TOPICLIMIT) != 0 && !is_chan_op(sptr, chptr)) 
+   if ((chptr->mode.mode & MODE_TOPICLIMIT) != 0 && (!member || !IsChanOp(member)))
    {
       send_reply(sptr, ERR_CHANOPRIVSNEEDED, chptr->chname);
       return;
    }
+   if (member && IsDelayedJoin(member))
+     RevealDelayedJoin(member);
    /* Note if this is just a refresh of an old topic, and don't
     * send it to all the clients to save bandwidth.  We still send
     * it to other servers as they may have split and lost the topic.
index f05769bdb8a99e06fc474ec8b72821dc75604ff1..b569c83892631e26ff1251d0fa2f03d36e82af71 100644 (file)
@@ -114,7 +114,7 @@ int m_wallchops(struct Client* cptr, struct Client* sptr, int parc, char* parv[]
     return send_reply(sptr, ERR_NOTEXTTOSEND);
 
   if (IsChannelName(parv[1]) && (chptr = FindChannel(parv[1]))) {
-    if (client_can_send_to_channel(sptr, chptr)) {
+    if (client_can_send_to_channel(sptr, chptr, 0)) {
       if ((chptr->mode.mode & MODE_NOPRIVMSGS) &&
           check_target_limit(sptr, chptr, chptr->chname, 0))
         return 0;
@@ -144,7 +144,7 @@ int ms_wallchops(struct Client* cptr, struct Client* sptr, int parc, char* parv[
     return 0;
 
   if ((chptr = FindChannel(parv[1]))) {
-    if (client_can_send_to_channel(sptr, chptr)) {
+    if (client_can_send_to_channel(sptr, chptr, 0)) {
       sendcmdto_channel_butone(sptr, CMD_WALLCHOPS, chptr, cptr,
                               SKIP_DEAF | SKIP_BURST | SKIP_NONOPS,
                               "%H :%s", chptr, parv[parc - 1]);
index b228f1775b16f61cb7b8fbf440e0ef68dd6c0f9d..a92267478b91e865d50042f30e98ba023a620096 100644 (file)
@@ -113,7 +113,7 @@ int m_wallvoices(struct Client* cptr, struct Client* sptr, int parc, char* parv[
     return send_reply(sptr, ERR_NOTEXTTOSEND);
 
   if (IsChannelName(parv[1]) && (chptr = FindChannel(parv[1]))) {
-    if (client_can_send_to_channel(sptr, chptr)) {
+    if (client_can_send_to_channel(sptr, chptr, 0)) {
       if ((chptr->mode.mode & MODE_NOPRIVMSGS) &&
           check_target_limit(sptr, chptr, chptr->chname, 0))
         return 0;
@@ -143,7 +143,7 @@ int ms_wallvoices(struct Client* cptr, struct Client* sptr, int parc, char* parv
     return 0;
 
   if ((chptr = FindChannel(parv[1]))) {
-    if (client_can_send_to_channel(sptr, chptr)) {
+    if (client_can_send_to_channel(sptr, chptr, 0)) {
       sendcmdto_channel_butone(sptr, CMD_WALLVOICES, chptr, cptr,
                               SKIP_DEAF | SKIP_BURST | SKIP_NONVOICES, 
                               "%H :%s", chptr, parv[parc - 1]);
index 473509e861117cccef746770d77352da7f010912..797b5a87e581be327f8c113c3b7c3af810e8193e 100644 (file)
@@ -323,7 +323,7 @@ int m_who(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
             acptr = member->user;
             if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr))
               continue;
-            if ((acptr != sptr) && (member->status & CHFL_ZOMBIE))
+            if ((acptr != sptr) && (member->status & (CHFL_ZOMBIE | CHFL_DELAYED)))
               continue;
             if (!(isthere || (SEE_USER(sptr, acptr, bitsel))))
               continue;
index 319ea1dc0f7f79d3980c0521a1a0b57bdb455e36..ed0213ad2ee8d7a18d099d1e8ce45f06f8d4f6df 100644 (file)
@@ -166,9 +166,11 @@ static void do_whois(struct Client* sptr, struct Client *acptr, int parc)
        }
        if (IsDeaf(acptr))
          *(buf + len++) = '-';
-       if (is_chan_op(acptr, chptr))
+       if (IsDelayedJoin(chan) && (sptr != acptr))
+         *(buf + len++) = '<';
+       else if (IsChanOp(chan))
          *(buf + len++) = '@';
-       else if (has_voice(acptr, chptr))
+       else if (HasVoice(chan))
          *(buf + len++) = '+';
        else if (IsZombie(chan))
          *(buf + len++) = '!';
index 0c8868129557c14ea6c9189b808805c86201b1fa..6145a1cb80ea2ccc109569138b6954dcd29ebe37 100644 (file)
@@ -742,7 +742,7 @@ static Numeric replyTable[] = {
 /* 354 */
   { RPL_WHOSPCRPL, "%s", "354" },
 /* 355 */
-  { 0 },
+  { RPL_DELNAMREPLY, "%s", "355" },
 /* 356 */
   { 0 },
 /* 357 */
index 9b561c9a947d8e953db473231857ab43cfbebd05..8986e108c640a23ccfd9a56827ee5dbcb22d0f70 100644 (file)
@@ -1082,8 +1082,14 @@ hide_hostmask(struct Client *cptr, unsigned int flag)
    */
   for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel)
   {
-    sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr,
-                                     "%H", chan->channel);
+    /* For a user with no modes in a join-delayed channel, do not show
+     * the rejoin. */
+    if (!IsChanOp(chan) && !HasVoice(chan)
+        && (chan->channel->mode.mode & MODE_DELJOINS))
+      SetDelayedJoin(chan);
+    else
+      sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr,
+                                         "%H", chan->channel);
     if (IsChanOp(chan) && HasVoice(chan))
       sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr,
                                        "%H +ov %C %C", chan->channel, cptr,
index 4d562b715c9984e24f0e30f0ed10a7109755d00a..33c8faa9f3f479e8d31f1f863b4f22582e09ad0f 100644 (file)
@@ -403,7 +403,7 @@ void sendcmdto_common_channels_butone(struct Client *from, const char *cmd,
    * loop through from's channels, and the members on their channels
    */
   for (chan = cli_user(from)->channel; chan; chan = chan->next_channel) {
-    if (IsZombie(chan))
+    if (IsZombie(chan) || IsDelayedJoin(chan))
       continue;
     for (member = chan->channel->members; member;
         member = member->next_member)