cafbb6e3ba052970b84d948004c4156bc8f77d68
[ircu2.10.12-pk.git] / ircd / m_destruct.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_destruct.c
3  * Copyright (C) 1997, 2005 Carlo Wood.
4  *
5  * See file AUTHORS in IRC package for additional names of
6  * the programmers.
7  *
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)
11  * any later version.
12  *
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.
17  *
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.
21  *
22  * $Id$
23  */
24
25 #include "config.h"
26
27 #include "client.h"
28 #include "hash.h"
29 #include "ircd.h"
30 #include "ircd_log.h"
31 #include "ircd_reply.h"
32 #include "ircd_string.h"
33 #include "msg.h"
34 #include "numeric.h"
35 #include "numnicks.h"
36 #include "send.h"
37 #include "channel.h"
38 #include "destruct_event.h"
39
40 /* #include <assert.h> -- Now using assert in ircd_log.h */
41 #include <stdlib.h>
42
43 /*
44  * ms_destruct - server message handler
45  *
46  * Added 1997 by Run, actually coded and used since 2002.
47  *
48  * parv[0] = sender prefix
49  * parv[1] = channel channelname
50  * parv[2] = channel time stamp
51  *
52  * This message is intended to destruct _empty_ channels.
53  *
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'.
89  */
90 int ms_destruct(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
91 {
92   time_t chanTS;                /* Creation time of the channel */
93   struct Channel* chptr;
94
95   assert(0 != cptr);
96   assert(0 != sptr);
97   assert(IsServer(cptr));
98
99   if (parc < 3 || EmptyString(parv[2]))
100     return need_more_params(sptr,"DESTRUCT");
101
102   chanTS = atoi(parv[2]);
103
104   /* Ignore DESTRUCT messages for non-existing channels. */
105   if (!(chptr = FindChannel(parv[1])))
106     return 0;
107
108   /* Ignore DESTRUCT when the channel is older than the
109      timestamp on the message. */
110   if (chanTS > chptr->creationtime)
111     return 0;
112
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);
121 #else
122   /* This happens when a JOIN and DESTRUCT crossed, ie:
123
124      server1 ----------------- server2
125         DESTRUCT-->   <-- JOIN,MODE
126
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:
132
133      server1 ----------------- server2
134         DESTRUCT-->   <-- DESTRUCT <-- CREATE
135      
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). */
145
146     struct Membership *chanop;
147     struct Membership *member;
148     int is_real_chanop;
149     struct ModeBuf mbuf;
150     struct Ban *link;
151
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;
156
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);
160
161     /* Next, send a CREATE. If we don't have a chanop, just use the first member. */
162     if (!chanop)
163       chanop = chptr->members;
164     sendcmdto_one(chanop->user, CMD_CREATE, cptr, "%H %Tu", chptr, chanTS);
165
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);
170
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);
174
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)
177     {
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);
182     }
183
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);
197
198     /* When chanop wasn't really a chanop, let him deop himself. */
199     if (!is_real_chanop)
200       sendcmdto_one(chanop->user, CMD_MODE, cptr, "%H -o %C", chptr, chanop->user);
201 #endif
202
203     return 0;
204   }
205
206   /* Pass on DESTRUCT message and ALSO bounce it back! */
207   sendcmdto_serv_butone(&me, CMD_DESTRUCT, 0, "%s %Tu", parv[1], chanTS);
208
209   /* Remove the empty channel. */
210   remove_destruct_event(chptr);
211   destruct_channel(chptr);
212
213   return 0;
214 }