2 * IRC - Internet Relay Chat, ircd/m_destruct.c
3 * Copyright (C) 1997, 2005 Carlo Wood.
5 * See file AUTHORS in IRC package for additional names of
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 1, or (at your option)
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 #include "ircd_reply.h"
32 #include "ircd_string.h"
38 #include "destruct_event.h"
40 /* #include <assert.h> -- Now using assert in ircd_log.h */
44 * ms_destruct - server message handler
46 * Added 1997 by Run, actually coded and used since 2002.
48 * parv[0] = sender prefix
49 * parv[1] = channel channelname
50 * parv[2] = channel time stamp
52 * This message is intended to destruct _empty_ channels.
54 * The reason it is needed is to somehow add the notion
55 * "I destructed information" to the networks state
56 * (also messages that are still propagating are part
57 * of the global state). Without it the network could
58 * easily be desynced as a result of destructing a channel
59 * on only a part of the network while keeping the modes
60 * and creation time on others.
61 * There are three possible ways a DESTRUCT message is
62 * handled by remote servers:
63 * 1) The channel is empty and has the same timestamp
64 * as on the message. Conclusion: The channel has
65 * not been destructed and recreated in the meantime,
66 * this means that the normal synchronization rules
67 * account and we react as if we decided to destruct
68 * the channel ourselves: we destruct the channel and
69 * send a DESTRUCT in all directions.
70 * 2) The channel is not empty. In case we cannot remove
71 * it and do not propagate the DESTRUCT message. Instead
72 * a resynchronizing BURST message is sent upstream
73 * in order to restore the channel on that side (which
74 * will have a TS younger than the current channel if
75 * it was recreated and will thus be fully synced, just
76 * like in the case of a real net-junction).
77 * 3) The channel is empty, but the creation time of the
78 * channel is older than the timestamp on the message.
79 * This can happen when there is more than one minute
80 * lag and remotely a channel was created slightly
81 * after we created the channel, being abandoned again
82 * and staying empty for a minute without that our
83 * CREATE reached that remote server. The remote server
84 * then could have generated the DESTRUCT. In the meantime
85 * our user also left the channel. We can ignore the
86 * destruct because it comes from an 'area' that will
87 * be overridden by our own CREATE: the state that generated
88 * this DESTRUCT is 'history'.
90 int ms_destruct(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
92 time_t chanTS; /* Creation time of the channel */
93 struct Channel* chptr;
97 assert(IsServer(cptr));
99 if (parc < 3 || EmptyString(parv[2]))
100 return need_more_params(sptr,"DESTRUCT");
102 chanTS = atoi(parv[2]);
104 /* Ignore DESTRUCT messages for non-existing channels. */
105 if (!(chptr = FindChannel(parv[1])))
108 /* Ignore DESTRUCT when the channel is older than the
109 timestamp on the message. */
110 if (chanTS > chptr->creationtime)
113 /* Don't pass on DESTRUCT messages for channels that
114 are not empty, but instead send a BURST msg upstream. */
115 if (chptr->users > 0) {
116 #if 0 /* Once all servers are 2.10.12, this can be used too.
117 Until then we have to use CREATE and MODE to
118 get the message accross, because older server do
119 not accept a BURST outside the net.burst. */
120 send_channel_modes(cptr, chptr);
122 /* This happens when a JOIN and DESTRUCT crossed, ie:
124 server1 ----------------- server2
125 DESTRUCT--> <-- JOIN,MODE
127 Where the JOIN and MODE are the result of joining
128 the zannel before it expired on server2, or in the
129 case of simulateous expiration, a DESTRUCT crossing
130 with another DESTRUCT (that will be ignored) and
131 a CREATE of a user joining right after that:
133 server1 ----------------- server2
134 DESTRUCT--> <-- DESTRUCT <-- CREATE
136 in both cases, when the DESTRUCT arrives on
137 server2 we need to send synchronizing messages
138 upstream (to server1). Since sending two CREATEs
139 or JOINs for the same user after another is a
140 protocol violation, we first have to send PARTs
141 (we can't send a DESTRUCT because 2.10.11 ignores
142 DESTRUCT messages (just passes them on) and has
143 a bug that causes two JOIN's for the same user to
144 result in that user being on the channel twice). */
146 struct Membership *chanop;
147 struct Membership *member;
152 /* First find a channel op, if any. */
153 for (chanop = chptr->members; chanop && !IsChanOp(chanop); chanop = chanop->next_member);
154 /* Now chanop is either a channel op, or NULL. */
155 is_real_chanop = chanop ? 1 : 0;
157 /* Next, send all PARTs upstream. */
158 for (member = chptr->members; member; member = member->next_member)
159 sendcmdto_one(member->user, CMD_PART, cptr, "%H", chptr);
161 /* Next, send a CREATE. If we don't have a chanop, just use the first member. */
163 chanop = chptr->members;
164 sendcmdto_one(chanop->user, CMD_CREATE, cptr, "%H %Tu", chptr, chanTS);
166 /* Next, send JOINs for possible other members. */
167 for (member = chptr->members; member; member = member->next_member)
168 if (member != chanop)
169 sendcmdto_one(member->user, CMD_JOIN, cptr, "%H", chptr);
171 /* Build MODE strings. We use MODEBUF_DEST_BOUNCE with MODE_DEL to assure
172 that the resulting MODEs are only sent upstream. */
173 modebuf_init(&mbuf, sptr, cptr, chptr, MODEBUF_DEST_SERVER | MODEBUF_DEST_BOUNCE);
175 /* Op/voice the users as appropriate. We use MODE_DEL because we fake a bounce. */
176 for (member = chptr->members; member; member = member->next_member)
178 if (IsChanOp(member) && member != chanop)
179 modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, member->user, OpLevel(member));
180 if (HasVoice(member))
181 modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, member->user, MAXOPLEVEL + 1);
184 /* Send other MODEs. */
185 modebuf_mode(&mbuf, MODE_DEL | chptr->mode.mode);
186 if (*chptr->mode.key)
187 modebuf_mode_string(&mbuf, MODE_DEL | MODE_KEY, chptr->mode.key, 0);
188 if (chptr->mode.limit)
189 modebuf_mode_uint(&mbuf, MODE_DEL | MODE_LIMIT, chptr->mode.limit);
190 if (*chptr->mode.upass)
191 modebuf_mode_string(&mbuf, MODE_DEL | MODE_UPASS, chptr->mode.upass, 0);
192 if (*chptr->mode.apass)
193 modebuf_mode_string(&mbuf, MODE_DEL | MODE_APASS, chptr->mode.apass, 0);
194 for (link = chptr->banlist; link; link = link->next)
195 modebuf_mode_string(&mbuf, MODE_DEL | MODE_BAN, link->banstr, 0);
196 modebuf_flush(&mbuf);
198 /* When chanop wasn't really a chanop, let him deop himself. */
200 sendcmdto_one(chanop->user, CMD_MODE, cptr, "%H -o %C", chptr, chanop->user);
206 /* Pass on DESTRUCT message and ALSO bounce it back! */
207 sendcmdto_serv_butone(&me, CMD_DESTRUCT, 0, "%s %Tu", parv[1], chanTS);
209 /* Remove the empty channel. */
210 remove_destruct_event(chptr);
211 destruct_channel(chptr);