fdf4e82df9acab3a9597b56b5449d13508068b23
[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                 channellist_t *chl;
200                 if (Conf_getNextChannelLink(&chlink, i) < 0) {
201                         if (i == 0)
202                                 Log_info("No channel links found in configuration file.");
203                         break;
204                 }
205                 ch_itr = NULL;
206                 do {
207                         Chan_iterate(&ch_itr);
208                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.source) != 0);
209                 if (ch_itr == NULL)
210                         Log_fatal("Error in channel link configuration: source channel '%s' not found.",
211                                           chlink.source);
212                 else
213                         ch_src = ch_itr;
214
215                 ch_itr = NULL;
216                 do {
217                         Chan_iterate(&ch_itr);
218                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.destination) != 0);
219                 if (ch_itr == NULL)
220                         Log_fatal("Error in channel link configuration: destination channel '%s' not found",
221                                           chlink.destination);
222                 else
223                         ch_dst = ch_itr;
224
225                 chl = malloc(sizeof(channellist_t));
226                 if(!chl)
227                         Log_fatal("Out of memory");
228                 chl->chan = ch_dst;
229                 init_list_entry(&chl->node);
230                 list_add_tail(&chl->node, &ch_src->channel_links);
231                 ch_src->linkcount++;
232                 Log_info("Adding channel link '%s' -> '%s'", ch_src->name, ch_dst->name);
233         }
234 }
235
236 void Chan_free()
237 {
238         struct dlist *itr, *save;
239         struct dlist *linkitr, *linksave;
240         channel_t *ch;
241
242         list_iterate_safe(itr, save, &channels) {
243                 ch = list_get_entry(itr, channel_t, flatlist_node);
244                 Log_debug("Free channel '%s'", ch->name);
245                 free(ch->name);
246                 if (ch->desc)
247                         free(ch->desc);
248                 if (ch->password)
249                         free(ch->password);
250                 list_iterate_safe(linkitr, linksave, &ch->channel_links) {
251                         channellist_t *chl;
252                         chl = list_get_entry(linkitr, channellist_t, node);
253                         free(chl);
254                 }
255                 free(ch);
256         }
257 }
258
259 channel_t *Chan_createChannel(const char *name, const char *desc)
260 {
261         int id = findFreeId();
262         if (id < 0)
263                 Log_fatal("No free channel ID found");
264         return createChannel(id, name, desc);
265 }
266
267 void Chan_freeChannel(channel_t *ch)
268 {
269         list_del(&ch->node);
270         list_del(&ch->flatlist_node);
271         free(ch);
272 }
273
274 void Chan_addChannel(channel_t *parent, channel_t *ch)
275 {
276         list_add_tail(&ch->node, &parent->subs);
277         ch->parent = parent;
278         list_add_tail(&ch->flatlist_node, &channels);
279 }
280
281
282 int Chan_userLeave(client_t *client)
283 {
284         channel_t *leaving = NULL;
285         int leaving_id = -1;
286
287         if (client->channel) {
288                 list_del(&client->chan_node);
289                 leaving = (channel_t *)client->channel;
290                 if (leaving->temporary && list_empty(&leaving->clients)) {
291                         leaving_id = leaving->id;
292                         Chan_freeChannel(leaving);
293                 }
294         }
295         return leaving_id;
296 }
297
298 int Chan_userJoin(channel_t *ch, client_t *client)
299 {
300         int leaving_id;
301
302         /* Do nothing if user already is in this channel */
303         if ((channel_t *)client->channel == ch)
304                 return 0;
305
306         Log_debug("Add user %s to channel %s", client->username, ch->name);
307         /* Only allowed in one channel at a time */
308         leaving_id = Chan_userLeave(client);
309         list_add_tail(&client->chan_node, &ch->clients);
310         client->channel = (void *)ch;
311         return leaving_id;
312 }
313
314 int Chan_userJoin_id(int channelid, client_t *client)
315 {
316         channel_t *ch_itr = NULL;
317         do {
318                 Chan_iterate(&ch_itr);
319         } while (ch_itr != NULL && ch_itr->id != channelid);
320         if (ch_itr == NULL) {
321                 Log_warn("Channel id %d not found - ignoring.", channelid);
322                 return -1;
323         }
324         else
325                 return Chan_userJoin(ch_itr, client);
326 }
327
328 channelJoinResult_t Chan_userJoin_id_test(int channelid, client_t *client)
329 {
330         channelJoinResult_t result;
331         channel_t *ch_itr = NULL;
332         do {
333                 Chan_iterate(&ch_itr);
334         } while (ch_itr != NULL && ch_itr->id != channelid);
335         if (ch_itr == NULL) {
336                 Log_warn("Channel id %d not found - ignoring.", channelid);
337                 result.CHJOIN_NOTFOUND = true;
338         }
339         else
340                 result.CHJOIN_NOTFOUND = false;
341
342         result.CHJOIN_NOENTER = ch_itr->noenter;
343         result.CHJOIN_WRONGPW = ch_itr->password && !Client_token_match(client, ch_itr->password) && !client->isAdmin;
344         result.CHJOIN_SILENT = ch_itr->silent;
345
346         return result;
347 }
348
349 #if 0
350 void Chan_addChannel_id(int parentId, channel_t *ch)
351 {
352         channel_t *ch_itr = NULL;
353         do {
354                 Chan_iterate(&ch_itr);
355         } while (ch_itr != NULL && ch_itr->id != parentId);
356         if (ch_itr == NULL)
357                 Log_warn("Chan_addChannel_id: Channel id %d not found - ignoring.", parentId);
358         else
359                 list_add_tail(&ch->node, &ch_itr->subs);
360 }
361 #endif
362
363 channel_t *Chan_fromId(int channelid)
364 {
365         channel_t *ch_itr = NULL;
366         do {
367                 Chan_iterate(&ch_itr);
368         } while (ch_itr != NULL && ch_itr->id != channelid);
369         if (ch_itr == NULL)
370                 Log_warn("Chan_fromId: Channel id %d not found.", channelid);
371         return ch_itr;
372 }
373
374 void Chan_removeChannel(channel_t *ch)
375 {
376         list_del(&ch->node);
377 }
378
379 void Chan_buildTreeList(channel_t *ch, struct dlist *head)
380 {
381         channellist_t *chl;
382         struct dlist *itr;
383         channel_t *sub;
384
385         chl = malloc(sizeof(channellist_t));
386         if(!chl)
387                 Log_fatal("Out of memory");
388         chl->chan = ch;
389         init_list_entry(&chl->node);
390         list_add_tail(&chl->node, head);
391
392         list_iterate(itr, &ch->subs) {
393                 sub = list_get_entry(itr, channel_t, node);
394                 Chan_buildTreeList(sub, head);
395         }
396 }
397
398 void Chan_freeTreeList(struct dlist *head)
399 {
400         struct dlist *itr, *save;
401         list_iterate_safe(itr, save, head) {
402                 free(list_get_entry(itr, channellist_t, node));
403         }
404 }