-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.
#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)
* 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[])
{
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? */
/* 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;
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, ",")) {
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';
/* 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)
/*
* 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"
#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);
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;
}
}
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;
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);
struct Channel *chptr;
struct Membership *member;
struct JoinBuf parts;
+ unsigned int flags = 0;
char *p = 0;
char *name;
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 */
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);