Author: Carlo Wood <run@alinoe.com>
authorPerry Lorier <isomer@undernet.org>
Fri, 8 Mar 2002 23:19:33 +0000 (23:19 +0000)
committerPerry Lorier <isomer@undernet.org>
Fri, 8 Mar 2002 23:19:33 +0000 (23:19 +0000)
Log message:

Implementation of network synchronization code.                                 Includes the implementation of the previously                                   unused DESTRUCT message.  Correction on ms_join                                 to copy older timestamps and now allowing                                       BURST message to occur outside the net-burst.                                   See my last two posts to coder-com for detailed                                 information on how I got to this implementation                                 and for a C++ simulation program that wrote to                                  test it.                                                                                                                                                        Note that I changed the copyright notice in                                     m_destruct - Jarko didn't add that function, I did.                             Not that it is a big deal to me.

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

ChangeLog
include/channel.h
ircd/m_burst.c
ircd/m_destruct.c
ircd/m_join.c
ircd/m_part.c

index 65e4ebe1faca3f15dbbb41a3b4c196062e1e867b..d3b33e789e6d35a13c2272595084bf6cb7213929 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
-2002-02-27 Reed Loden <reed@redmagnet.com>
+2002-03-08  Carlo Wood  <run@alinoe.com>
+       * include/channel.h: Added CHFL_BURST_ALREADY_OPPED
+       and CHFL_BURST_ALREADY_VOICED.
+
+       * ircd/m_burst.c: Allow BURST outside net-burst
+       and take into account that users are already joined
+       to the channel in that case.
 
+       * ircd/m_destruct.c: Implementation of DESTRUCT
+       handling code.
+
+       * ircd/m_join.c: Set the channel creationtime to
+       the timestamp of a message when that timestamp is
+       smaller.
+2002-02-27 Reed Loden <reed@redmagnet.com>
        * tools/crypter: Updated some variables, added another notice,
        added CVS Id tag, and updated Perl location.
 
index 2825da70a64866754189aa7055f7cd9e85a29fe6..131e11d84dc5c0b52f302ebdb1af2369c8ac2c87 100644 (file)
@@ -67,6 +67,8 @@ struct Client;
 #define CHFL_BANVALID           0x0800  /* CHFL_BANNED bit is valid */
 #define CHFL_BANNED             0x1000  /* Channel member is banned */
 #define CHFL_SILENCE_IPMASK     0x2000  /* silence mask is an IP-number mask */
+#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_OVERLAP         (CHFL_CHANOP | CHFL_VOICE)
 #define CHFL_BANVALIDMASK    (CHFL_BANVALID | CHFL_BANNED)
index 0f1e8bfcdad2e172e01138c7a514744ea4575ac9..e253acb57170dbe146e3708cc77b5ade704e6350 100644 (file)
  * the TS in the BURST message, then we cancel all existing modes.
  * If its is smaller then the received BURST message is ignored.
  * If it's equal, then the received modes are just added.
+ *
+ * BURST is also accepted outside a netburst now because it
+ * is sent upstream as reaction to a DESTRUCT message.  For
+ * these BURST messages it is possible that the listed channel
+ * members are already joined.
  */
 int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
 {
@@ -182,10 +187,6 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   if (parc < 3)
     return protocol_violation(sptr,"Too few parameters for BURST");
 
-  if (!IsBurst(sptr)) /* don't allow BURST outside of burst */
-    return exit_client_msg(cptr, cptr, &me, "HACK: BURST message outside "
-                          "net.burst from %s", cli_name(sptr));
-
   if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
     return 0; /* can't create the channel? */
 
@@ -193,7 +194,7 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
 
   /* turn off burst joined flag */
   for (member = chptr->members; member; member = member->next_member)
-    member->status &= ~CHFL_BURST_JOINED;
+    member->status &= ~(CHFL_BURST_JOINED|CHFL_BURST_ALREADY_OPPED|CHFL_BURST_ALREADY_VOICED);
 
   if (!chptr->creationtime) /* mark channel as created during BURST */
     chptr->mode.mode |= MODE_BURSTADDED;
@@ -304,6 +305,7 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
        int last_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
        int oplevel = -1;       /* Mark first field with digits: means the same as 'o' (but with level). */
        int last_oplevel = 0;
+       struct Membership* member;
 
        for (nick = ircd_strtok(&p, nicklist, ","); nick;
             nick = ircd_strtok(&p, 0, ",")) {
@@ -381,14 +383,29 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
             last_oplevel = oplevel;
          }
 
-         add_user_to_channel(chptr, acptr, current_mode, oplevel);
-         sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, "%H", chptr);
+         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);
+         }
+         else
+         {
+           /* The member was already joined (either by CREATE or JOIN).
+              Remember the current mode. */
+           if (member->status & CHFL_CHANOP)
+             member->status |= CHFL_BURST_ALREADY_OPPED;
+           if (member->status & CHFL_VOICE)
+             member->status |= CHFL_BURST_ALREADY_VOICED;
+           /* Synchronize with the burst. */
+           member->status |= CHFL_BURST_JOINED | (current_mode & (CHFL_CHANOP|CHFL_VOICE));
+           member->oplevel = oplevel;
+         }
        }
       }
       param++;
       break;
-    } /* switch (*parv[param]) */
-  } /* while (param < parc) */
+    } /* switch (*parv[param]) */
+  } /* while (param < parc) */
 
   nickstr[nickpos] = '\0';
   banstr[banpos] = '\0';
@@ -409,9 +426,9 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     /* first deal with channel members */
     for (member = chptr->members; member; member = member->next_member) {
       if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
-       if (member->status & CHFL_CHANOP)
+       if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED))
          modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user);
-       if (member->status & CHFL_VOICE)
+       if ((member->status & CHFL_VOICE) && !(member->status & CHFL_BURST_ALREADY_VOICED))
          modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user);
       } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
        if (member->status & CHFL_CHANOP)
index fc4e77d2b7f593a38355e9186f2915f34dd3c884..a81c6d8c677ecea0a6ddfceec5d8b54dac8895bc 100644 (file)
@@ -1,7 +1,6 @@
 /*
  * IRC - Internet Relay Chat, ircd/m_destruct.c
- * Copyright (C) 1990 Jarkko Oikarinen and
- *                    University of Oulu, Computing Center
+ * Copyright (C) 1997 Carlo Wood.
  *
  * See file AUTHORS in IRC package for additional names of
  * the programmers.
  * $Id$
  */
 
-/*
- * m_functions execute protocol messages on this server:
- *
- *    cptr    is always NON-NULL, pointing to a *LOCAL* client
- *            structure (with an open socket connected!). This
- *            identifies the physical socket where the message
- *            originated (or which caused the m_function to be
- *            executed--some m_functions may call others...).
- *
- *    sptr    is the source of the message, defined by the
- *            prefix part of the message if present. If not
- *            or prefix not found, then sptr==cptr.
- *
- *            (!IsServer(cptr)) => (cptr == sptr), because
- *            prefixes are taken *only* from servers...
- *
- *            (IsServer(cptr))
- *                    (sptr == cptr) => the message didn't
- *                    have the prefix.
- *
- *                    (sptr != cptr && IsServer(sptr) means
- *                    the prefix specified servername. (?)
- *
- *                    (sptr != cptr && !IsServer(sptr) means
- *                    that message originated from a remote
- *                    user (not local).
- *
- *            combining
- *
- *            (!IsServer(sptr)) means that, sptr can safely
- *            taken as defining the target structure of the
- *            message in this server.
- *
- *    *Always* true (if 'parse' and others are working correct):
- *
- *    1)      sptr->from == cptr  (note: cptr->from == cptr)
- *
- *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
- *            *cannot* be a local connection, unless it's
- *            actually cptr!). [MyConnect(x) should probably
- *            be defined as (x == x->from) --msa ]
- *
- *    parc    number of variable parameter strings (if zero,
- *            parv is allowed to be NULL)
- *
- *    parv    a NULL terminated list of parameter pointers,
- *
- *                    parv[0], sender (prefix string), if not present
- *                            this points to an empty string.
- *                    parv[1]...parv[parc-1]
- *                            pointers to additional parameters
- *                    parv[parc] == NULL, *always*
- *
- *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
- *                    non-NULL pointers.
- */
 #include "config.h"
 
 #include "client.h"
@@ -90,6 +33,8 @@
 #include "numeric.h"
 #include "numnicks.h"
 #include "send.h"
+#include "channel.h"
+#include "destruct_event.h"
 
 #include <assert.h>
 #include <stdlib.h>
 /*
  * ms_destruct - server message handler
  *
+ * Added 1997 by Run, actually coded and used since 2002.
+ *
  * parv[0] = sender prefix
  * parv[1] = channel channelname
  * parv[2] = channel time stamp
  *
- * This function does nothing, it does passes DESTRUCT to the other servers.
- * In the future we will start to use this message.
- *
+ * This message is intended to destruct _empty_ channels.
+ *
+ * The reason it is needed is to somehow add the notion
+ * "I destructed information" to the networks state
+ * (also messages that are still propagating are part
+ *  of the global state).  Without it the network could
+ * easily be desynced as a result of destructing a channel
+ * on only a part of the network while keeping the modes
+ * and creation time on others.
+ * There are three possible ways a DESTRUCT message is
+ * handled by remote servers:
+ * 1) The channel is empty and has the same timestamp
+ *    as on the message.  Conclusion: The channel has
+ *    not been destructed and recreated in the meantime,
+ *    this means that the normal synchronization rules
+ *    account and we react as if we decided to destruct
+ *    the channel ourselfs: we destruct the channel and
+ *    send a DESTRUCT in all directions.
+ * 2) The channel is not empty.  In case we cannot remove
+ *    it and do not propagate the DESTRUCT message. Instead
+ *    a resynchronizing BURST message is sent upstream
+ *    in order to restore the channel on that side (which
+ *    will have a TS younger than the current channel if
+ *    it was recreated and will thus be fully synced, just
+ *    like in the case of a real net-junction).
+ * 3) The channel is empty, but the creation time of the
+ *    channel is older than the timestamp on the message.
+ *    This can happen when there is more than one minute
+ *    lag and remotely a channel was created slightly
+ *    after we created the channel, being abandoned again
+ *    and staying empty for a minute without that our
+ *    CREATE reached that remote server.  The remote server
+ *    then could have generated the DESTRUCT.  In the meantime
+ *    our user also left the channel.  We can ignore the
+ *    destruct because it comes from an 'area' that will
+ *    be overriden by our own CREATE: the state that generated
+ *    this DESTRUCT is 'history'.
  */
 int ms_destruct(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 {
   time_t chanTS;                /* Creation time of the channel */
+  struct Channel* chptr;
 
   assert(0 != cptr);
   assert(0 != sptr);
@@ -116,14 +98,30 @@ int ms_destruct(struct Client* cptr, struct Client* sptr, int parc, char* parv[]
   if (parc < 3 || EmptyString(parv[2]))
     return need_more_params(sptr,"DESTRUCT");
 
-  /* Don't pass on DESTRUCT messages for channels that exist */
-  if (FindChannel(parv[1]))
+  chanTS = atoi(parv[2]);
+
+  /* Ignore DESTRUCT messages for non-existing channels. */
+  if (!(chptr = FindChannel(parv[1])))
     return 0;
 
-  chanTS = atoi(parv[2]);
+  /* Ignore DESTRUCT when the channel is older than the
+     timestamp on the message. */
+  if (chanTS > chptr->creationtime)
+    return 0;
+
+  /* Don't pass on DESTRUCT messages for channels that
+     are not empty, but instead send a BURST msg upstream. */
+  if (chptr->users > 0) {
+    send_channel_modes(cptr, chptr);
+    return 0;
+  }
+
+  /* Pass on DESTRUCT message and ALSO bounce it back! */
+  sendcmdto_serv_butone(sptr, CMD_DESTRUCT, 0, "%s %Tu", parv[1], chanTS);
 
-  /* Pass on DESTRUCT message */
-  sendcmdto_serv_butone(sptr, CMD_DESTRUCT, cptr, "%s %Tu", parv[1], chanTS);
+  /* Remove the empty channel. */
+  remove_destruct_event(chptr);
+  destruct_channel(chptr);
 
   return 0;
 }
index e8f9bebe7172bdc1aecaa038752e28b0a2969f9e..50ddfc761f39a59faf296639a2e9efdf5147a780 100644 (file)
@@ -332,6 +332,7 @@ int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     }
     else { /* We have a valid channel? */
       if ((member = find_member_link(chptr, sptr))) {
+       /* It is impossible to get here --Run */
        if (!IsZombie(member)) /* already on channel */
          continue;
 
@@ -340,6 +341,10 @@ int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
        chptr = FindChannel(name);
       } else
        flags = CHFL_DEOPPED | ((cli_flags(sptr) & FLAGS_TS8) ? CHFL_SERVOPOK : 0);
+      /* Always copy the timestamp when it is older, that is the only way to
+         ensure network-wide synchronization of creation times. */
+      if (creation && creation < chptr->creationtime)
+       chptr->creationtime = creation;
     } 
 
     joinbuf_join(&join, chptr, flags);
index 34e6b642a01d804ab7442096c007dba6f225958a..020b97a0bf0ba32db759a711c44b2bd1692a0412 100644 (file)
@@ -106,6 +106,7 @@ int m_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   struct Channel *chptr;
   struct Membership *member;
   struct JoinBuf parts;
+  unsigned int flags = 0;
   char *p = 0;
   char *name;
 
@@ -138,8 +139,14 @@ int m_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 
     assert(!IsZombie(member)); /* Local users should never zombie */
 
-    joinbuf_join(&parts, chptr, /* part client from channel */
-                member_can_send_to_channel(member) ? 0 : CHFL_BANNED);
+    if (!member_can_send_to_channel(member))
+    {
+      flags |= CHFL_BANNED;
+      /* Remote clients don't want to see a comment either. */
+      parts.jb_comment = 0;
+    }
+
+    joinbuf_join(&parts, chptr, flags); /* part client from channel */
   }
 
   return joinbuf_flush(&parts); /* flush channel parts */
@@ -186,13 +193,6 @@ int ms_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 
     if (IsZombie(member)) /* figure out special flags... */
       flags |= CHFL_ZOMBIE;
-    /*
-     * 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))
-      flags |= CHFL_BANNED;
 
     /* part user from channel */
     joinbuf_join(&parts, chptr, flags);