54b57a27a21421da214b04d53c8c3d7b05c28af3
[umurmur.git] / src / channel.c
1 /* Copyright (C) 2009-2014, Martin Johansson <martin@fatbob.nu>
2    Copyright (C) 2005-2014, Thorvald Natvig <thorvald@natvig.com>
3
4    All rights reserved.
5
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9
10    - Redistributions of source code must retain the above copyright notice,
11      this list of conditions and the following disclaimer.
12    - Redistributions in binary form must reproduce the above copyright notice,
13      this list of conditions and the following disclaimer in the documentation
14      and/or other materials provided with the distribution.
15    - Neither the name of the Developers nor the names of its contributors may
16      be used to endorse or promote products derived from this software without
17      specific prior written permission.
18
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <limits.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include "log.h"
35 #include "list.h"
36 #include "client.h"
37 #include "channel.h"
38 #include "conf.h"
39
40
41 static channel_t *rootChan;
42 channel_t *defaultChan;
43 declare_list(channels); /* A flat list of the channels */
44
45 static channel_t *createChannel(int id, const char *name, const char *desc)
46 {
47         channel_t *ch;
48
49         ch = malloc(sizeof(channel_t));
50         if (ch == NULL)
51                 Log_fatal("out of memory");
52         memset(ch, 0, sizeof(channel_t));
53         ch->id = id;
54         ch->name = strdup(name);
55         if (desc)
56                 ch->desc = strdup(desc);
57         init_list_entry(&ch->subs);
58         init_list_entry(&ch->node);
59         init_list_entry(&ch->clients);
60         init_list_entry(&ch->flatlist_node);
61         init_list_entry(&ch->channel_links);
62         return ch;
63 }
64
65 static int findFreeId()
66 {
67         int id = 0;
68         channel_t *ch_itr = NULL;
69         for (id = 0; id < INT_MAX; id++) {
70                 ch_itr = NULL;
71                 while ((ch_itr = Chan_iterate(&ch_itr)) != NULL) {
72                         if (ch_itr->id == id)
73                                 break;
74                 }
75                 if (ch_itr == NULL) /* Found free id */
76                         return id;
77         }
78         return -1;
79 }
80
81 #if 0
82 /* Might be used when tree traversal becomes neccessary */
83 static channel_t *first_subchannel(channel_t *ch)
84 {
85         if (list_empty(&ch->subs))
86                 return NULL;
87         else
88                 return list_get_entry(list_get_first(&ch->subs), channel_t, node);
89 }
90
91 static channel_t *next_channel(channel_t *ch)
92 {
93         if (list_get_next(&ch->node) == &list_get_entry(&ch->node, channel_t, node)->parent->subs)
94                 return NULL;
95         else
96                 return list_get_entry(list_get_next(&ch->node), channel_t, node);
97 }
98 #endif
99
100 channel_t *Chan_iterate(channel_t **channelpptr)
101 {
102         channel_t *ch = *channelpptr;
103
104         if (!list_empty(&channels)) {
105                 if (ch == NULL)
106                         ch = list_get_entry(list_get_first(&channels), channel_t, flatlist_node);
107                 else {
108                         if (list_get_next(&ch->flatlist_node) == &channels)
109                                 ch = NULL;
110                         else
111                                 ch = list_get_entry(list_get_next(&ch->flatlist_node), channel_t, flatlist_node);
112                 }
113         }
114
115         *channelpptr = ch;
116         return ch;
117 }
118
119 channel_t *Chan_iterate_siblings(channel_t *parent, channel_t **channelpptr)
120 {
121         channel_t *ch = *channelpptr;
122
123         if (!list_empty(&parent->subs)) {
124                 if (ch == NULL)
125                         ch = list_get_entry(list_get_first(&parent->subs), channel_t, node);
126                 else {
127                         if (list_get_next(&ch->node) == &parent->subs)
128                                 ch = NULL;
129                         else
130                                 ch = list_get_entry(list_get_next(&ch->node), channel_t, node);
131                 }
132         }
133
134         *channelpptr = ch;
135         return ch;
136 }
137
138 void Chan_init()
139 {
140         int i;
141         conf_channel_t chdesc;
142         conf_channel_link_t chlink;
143         const char *defaultChannelName;
144
145         defaultChannelName = getStrConf(DEFAULT_CHANNEL);
146
147         for (i = 0; ; i++) {
148                 if (Conf_getNextChannel(&chdesc, i) < 0) {
149                         if (i == 0)
150                                 Log_fatal("No valid channels found in configuration file. Exiting.");
151                         break;
152                 }
153                 if (i == 0) {
154                         rootChan = createChannel(0, chdesc.name, chdesc.description);
155                         rootChan->noenter = chdesc.noenter;
156                         rootChan->silent = chdesc.silent;
157                         list_add_tail(&rootChan->flatlist_node, &channels);
158                         if (strcmp(defaultChannelName, chdesc.name) == 0)
159                                 defaultChan = rootChan;
160                 }
161                 else {
162                         channel_t *ch, *ch_itr = NULL;
163                         ch = Chan_createChannel(chdesc.name, chdesc.description);
164                         ch->noenter = chdesc.noenter;
165                         ch->position = chdesc.position;
166                         ch->silent = chdesc.silent;
167                         if (chdesc.password) {
168                                 Log_info("Setting password on channel '%s'", ch->name);
169                                 ch->password = strdup(chdesc.password);
170                         }
171                         if (strcmp(defaultChannelName, chdesc.name) == 0) {
172                                 Log_info("Setting default channel '%s'", ch->name);
173                                 defaultChan = ch;
174                         }
175
176                         do {
177                                 Chan_iterate(&ch_itr);
178                         } while (ch_itr != NULL && strcmp(ch_itr->name, chdesc.parent) != 0);
179
180                         if (ch_itr == NULL)
181                                 Log_fatal("Error in channel configuration: parent '%s' not found", chdesc.parent);
182                         else {
183                                 Chan_addChannel(ch_itr, ch);
184                                 Log_info("Adding channel '%s' parent '%s'", ch->name, chdesc.parent);
185                         }
186                 }
187         }
188         if (defaultChan == NULL)
189                 defaultChan = rootChan;
190
191         if (defaultChan->noenter)
192                 Log_fatal("Error in channel configuration: default channel is marked as noenter");
193         if (defaultChan->password)
194                 Log_fatal("Error in channel configuration: default channel has a password");
195
196         /* Channel links */
197         for (i = 0; ; i++) {
198                 channel_t *ch_src, *ch_dst, *ch_itr = NULL;
199                 if (Conf_getNextChannelLink(&chlink, i) < 0) {
200                         if (i == 0)
201                                 Log_info("No channel links found in configuration file.");
202                         break;
203                 }
204                 ch_itr = NULL;
205                 do {
206                         Chan_iterate(&ch_itr);
207                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.source) != 0);
208                 if (ch_itr == NULL)
209                         Log_fatal("Error in channel link configuration: source channel '%s' not found.",
210                                           chlink.source);
211                 else
212                         ch_src = ch_itr;
213
214                 ch_itr = NULL;
215                 do {
216                         Chan_iterate(&ch_itr);
217                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.destination) != 0);
218                 if (ch_itr == NULL)
219                         Log_fatal("Error in channel link configuration: destination channel '%s' not found",
220                                           chlink.destination);
221                 else
222                         ch_dst = ch_itr;
223
224                 list_add_tail(&ch_dst->link_node, &ch_src->channel_links);
225                 ch_src->linkcount++;
226                 Log_info("Adding channel link '%s' -> '%s'", ch_src->name, ch_dst->name);
227         }
228 }
229
230 void Chan_free()
231 {
232         struct dlist *itr, *save;
233         channel_t *ch;
234
235         list_iterate_safe(itr, save, &channels) {
236                 ch = list_get_entry(itr, channel_t, flatlist_node);
237                 Log_debug("Free channel '%s'", ch->name);
238                 free(ch->name);
239                 if (ch->desc)
240                         free(ch->desc);
241                 if (ch->password)
242                         free(ch->password);
243                 free(ch);
244         }
245 }
246
247 channel_t *Chan_createChannel(const char *name, const char *desc)
248 {
249         int id = findFreeId();
250         if (id < 0)
251                 Log_fatal("No free channel ID found");
252         return createChannel(id, name, desc);
253 }
254
255 void Chan_freeChannel(channel_t *ch)
256 {
257         list_del(&ch->node);
258         list_del(&ch->flatlist_node);
259         free(ch);
260 }
261
262 void Chan_addChannel(channel_t *parent, channel_t *ch)
263 {
264         list_add_tail(&ch->node, &parent->subs);
265         ch->parent = parent;
266         list_add_tail(&ch->flatlist_node, &channels);
267 }
268
269
270 int Chan_userLeave(client_t *client)
271 {
272         channel_t *leaving = NULL;
273         int leaving_id = -1;
274
275         if (client->channel) {
276                 list_del(&client->chan_node);
277                 leaving = (channel_t *)client->channel;
278                 if (leaving->temporary && list_empty(&leaving->clients)) {
279                         leaving_id = leaving->id;
280                         Chan_freeChannel(leaving);
281                 }
282         }
283         return leaving_id;
284 }
285
286 int Chan_userJoin(channel_t *ch, client_t *client)
287 {
288         int leaving_id;
289
290         /* Do nothing if user already is in this channel */
291         if ((channel_t *)client->channel == ch)
292                 return 0;
293
294         Log_debug("Add user %s to channel %s", client->username, ch->name);
295         /* Only allowed in one channel at a time */
296         leaving_id = Chan_userLeave(client);
297         list_add_tail(&client->chan_node, &ch->clients);
298         client->channel = (void *)ch;
299         return leaving_id;
300 }
301
302 int Chan_userJoin_id(int channelid, client_t *client)
303 {
304         channel_t *ch_itr = NULL;
305         do {
306                 Chan_iterate(&ch_itr);
307         } while (ch_itr != NULL && ch_itr->id != channelid);
308         if (ch_itr == NULL) {
309                 Log_warn("Channel id %d not found - ignoring.", channelid);
310                 return -1;
311         }
312         else
313                 return Chan_userJoin(ch_itr, client);
314 }
315
316 channelJoinResult_t Chan_userJoin_id_test(int channelid, client_t *client)
317 {
318         channelJoinResult_t result;
319         channel_t *ch_itr = NULL;
320         do {
321                 Chan_iterate(&ch_itr);
322         } while (ch_itr != NULL && ch_itr->id != channelid);
323         if (ch_itr == NULL) {
324                 Log_warn("Channel id %d not found - ignoring.", channelid);
325                 result.CHJOIN_NOTFOUND = true;
326         }
327         else
328                 result.CHJOIN_NOTFOUND = false;
329
330         result.CHJOIN_NOENTER = ch_itr->noenter;
331         result.CHJOIN_WRONGPW = ch_itr->password && !Client_token_match(client, ch_itr->password) && !client->isAdmin;
332         result.CHJOIN_SILENT = ch_itr->silent;
333
334         return result;
335 }
336
337 #if 0
338 void Chan_addChannel_id(int parentId, channel_t *ch)
339 {
340         channel_t *ch_itr = NULL;
341         do {
342                 Chan_iterate(&ch_itr);
343         } while (ch_itr != NULL && ch_itr->id != parentId);
344         if (ch_itr == NULL)
345                 Log_warn("Chan_addChannel_id: Channel id %d not found - ignoring.", parentId);
346         else
347                 list_add_tail(&ch->node, &ch_itr->subs);
348 }
349 #endif
350
351 channel_t *Chan_fromId(int channelid)
352 {
353         channel_t *ch_itr = NULL;
354         do {
355                 Chan_iterate(&ch_itr);
356         } while (ch_itr != NULL && ch_itr->id != channelid);
357         if (ch_itr == NULL)
358                 Log_warn("Chan_fromId: Channel id %d not found.", channelid);
359         return ch_itr;
360 }
361
362 void Chan_removeChannel(channel_t *ch)
363 {
364         list_del(&ch->node);
365 }
366
367 void Chan_buildTreeList(channel_t *ch, struct dlist *head)
368 {
369         channellist_t *chl;
370         struct dlist *itr;
371         channel_t *sub;
372
373         chl = malloc(sizeof(channellist_t));
374         chl->chan = ch;
375         init_list_entry(&chl->node);
376         list_add_tail(&chl->node, head);
377
378         list_iterate(itr, &ch->subs) {
379                 sub = list_get_entry(itr, channel_t, node);
380                 Chan_buildTreeList(sub, head);
381         }
382 }
383
384 void Chan_freeTreeList(struct dlist *head)
385 {
386         struct dlist *itr, *save;
387         list_iterate_safe(itr, save, head) {
388                 free(list_get_entry(itr, channellist_t, node));
389         }
390 }