ircu2/ircd/m_destruct.c

201 lines
7.4 KiB
C

/*
* IRC - Internet Relay Chat, ircd/m_destruct.c
* Copyright (C) 1997, 2005 Carlo Wood.
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: m_destruct.c 1600 2006-01-03 01:25:50Z entrope $
*/
#include "config.h"
#include "client.h"
#include "hash.h"
#include "ircd.h"
#include "ircd_log.h"
#include "ircd_reply.h"
#include "ircd_string.h"
#include "msg.h"
#include "numeric.h"
#include "numnicks.h"
#include "send.h"
#include "channel.h"
#include "destruct_event.h"
/* #include <assert.h> -- Now using assert in ircd_log.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 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 ourselves: 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 overridden 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);
assert(IsServer(cptr));
if (parc < 3 || EmptyString(parv[2]))
return need_more_params(sptr,"DESTRUCT");
chanTS = atoi(parv[2]);
/* Ignore DESTRUCT messages for non-existing channels. */
if (!(chptr = FindChannel(parv[1])))
return 0;
/* 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) {
#if 0 /* Once all servers are 2.10.12, this can be used too.
Until then we have to use CREATE and MODE to
get the message accross, because older server do
not accept a BURST outside the net.burst. */
send_channel_modes(cptr, chptr);
#else
/* This happens when a JOIN and DESTRUCT crossed, ie:
server1 ----------------- server2
DESTRUCT--> <-- JOIN,MODE
Where the JOIN and MODE are the result of joining
the zannel before it expired on server2, or in the
case of simulateous expiration, a DESTRUCT crossing
with another DESTRUCT (that will be ignored) and
a CREATE of a user joining right after that:
server1 ----------------- server2
DESTRUCT--> <-- DESTRUCT <-- CREATE
in both cases, when the DESTRUCT arrives on
server2 we need to send synchronizing messages
upstream (to server1). Since sending two CREATEs
or JOINs for the same user after another is a
protocol violation, we first have to send PARTs
(we can't send a DESTRUCT because 2.10.11 ignores
DESTRUCT messages (just passes them on) and has
a bug that causes two JOIN's for the same user to
result in that user being on the channel twice). */
struct Membership *member;
struct ModeBuf mbuf;
struct Ban *link;
/* Next, send all PARTs upstream. */
for (member = chptr->members; member; member = member->next_member)
sendcmdto_one(member->user, CMD_PART, cptr, "%H", chptr);
/* Next, send JOINs for all members. */
for (member = chptr->members; member; member = member->next_member)
sendcmdto_one(member->user, CMD_JOIN, cptr, "%H", chptr);
/* Build MODE strings. We use MODEBUF_DEST_BOUNCE with MODE_DEL to assure
that the resulting MODEs are only sent upstream. */
modebuf_init(&mbuf, sptr, cptr, chptr, MODEBUF_DEST_SERVER | MODEBUF_DEST_BOUNCE);
/* Op/voice the users as appropriate. We use MODE_DEL because we fake a bounce. */
for (member = chptr->members; member; member = member->next_member)
{
if (IsChanOp(member))
modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, member->user, OpLevel(member));
if (HasVoice(member))
modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, member->user, MAXOPLEVEL + 1);
}
/* Send other MODEs. */
modebuf_mode(&mbuf, MODE_DEL | chptr->mode.mode);
if (*chptr->mode.key)
modebuf_mode_string(&mbuf, MODE_DEL | MODE_KEY, chptr->mode.key, 0);
if (chptr->mode.limit)
modebuf_mode_uint(&mbuf, MODE_DEL | MODE_LIMIT, chptr->mode.limit);
if (*chptr->mode.upass)
modebuf_mode_string(&mbuf, MODE_DEL | MODE_UPASS, chptr->mode.upass, 0);
if (*chptr->mode.apass)
modebuf_mode_string(&mbuf, MODE_DEL | MODE_APASS, chptr->mode.apass, 0);
for (link = chptr->banlist; link; link = link->next)
modebuf_mode_string(&mbuf, MODE_DEL | MODE_BAN, link->banstr, 0);
for(link = chptr->exceptlist; link; link = link->next)
modebuf_mode_string(&mbuf, MODE_DEL | MODE_EXCEPT, link->banstr, 0);
modebuf_flush(&mbuf);
#endif
return 0;
}
/* Pass on DESTRUCT message and ALSO bounce it back! */
sendcmdto_serv_butone(&me, CMD_DESTRUCT, 0, "%s %Tu", parv[1], chanTS);
/* Remove the empty channel. */
if (chptr->destruct_event)
remove_destruct_event(chptr);
destruct_channel(chptr);
return 0;
}