Remove casts made unneeded by the previous changeset.
[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 "memory.h"
36 #include "list.h"
37 #include "client.h"
38 #include "channel.h"
39 #include "conf.h"
40
41
42 static channel_t *rootChan;
43 channel_t *defaultChan;
44 declare_list(channels); /* A flat list of the channels */
45
46 static channel_t *createChannel(int id, const char *name, const char *desc)
47 {
48         channel_t *ch;
49
50         ch = Memory_safeCalloc(1, sizeof(channel_t));
51         ch->id = id;
52         ch->name = strdup(name);
53         if (desc)
54                 ch->desc = strdup(desc);
55         init_list_entry(&ch->subs);
56         init_list_entry(&ch->node);
57         init_list_entry(&ch->clients);
58         init_list_entry(&ch->flatlist_node);
59         init_list_entry(&ch->channel_links);
60         return ch;
61 }
62
63 static int findFreeId()
64 {
65         int id = 0;
66         channel_t *ch_itr = NULL;
67         for (id = 0; id < INT_MAX; id++) {
68                 ch_itr = NULL;
69                 while ((ch_itr = Chan_iterate(&ch_itr)) != NULL) {
70                         if (ch_itr->id == id)
71                                 break;
72                 }
73                 if (ch_itr == NULL) /* Found free id */
74                         return id;
75         }
76         return -1;
77 }
78
79 #if 0
80 /* Might be used when tree traversal becomes neccessary */
81 static channel_t *first_subchannel(channel_t *ch)
82 {
83         if (list_empty(&ch->subs))
84                 return NULL;
85         else
86                 return list_get_entry(list_get_first(&ch->subs), channel_t, node);
87 }
88
89 static channel_t *next_channel(channel_t *ch)
90 {
91         if (list_get_next(&ch->node) == &list_get_entry(&ch->node, channel_t, node)->parent->subs)
92                 return NULL;
93         else
94                 return list_get_entry(list_get_next(&ch->node), channel_t, node);
95 }
96 #endif
97
98 channel_t *Chan_iterate(channel_t **channelpptr)
99 {
100         channel_t *ch = *channelpptr;
101
102         if (!list_empty(&channels)) {
103                 if (ch == NULL)
104                         ch = list_get_entry(list_get_first(&channels), channel_t, flatlist_node);
105                 else {
106                         if (list_get_next(&ch->flatlist_node) == &channels)
107                                 ch = NULL;
108                         else
109                                 ch = list_get_entry(list_get_next(&ch->flatlist_node), channel_t, flatlist_node);
110                 }
111         }
112
113         *channelpptr = ch;
114         return ch;
115 }
116
117 channel_t *Chan_iterate_siblings(channel_t *parent, channel_t **channelpptr)
118 {
119         channel_t *ch = *channelpptr;
120
121         if (!list_empty(&parent->subs)) {
122                 if (ch == NULL)
123                         ch = list_get_entry(list_get_first(&parent->subs), channel_t, node);
124                 else {
125                         if (list_get_next(&ch->node) == &parent->subs)
126                                 ch = NULL;
127                         else
128                                 ch = list_get_entry(list_get_next(&ch->node), channel_t, node);
129                 }
130         }
131
132         *channelpptr = ch;
133         return ch;
134 }
135
136 void Chan_init()
137 {
138         int i;
139         conf_channel_t chdesc;
140         conf_channel_link_t chlink;
141         const char *defaultChannelName;
142
143         defaultChannelName = getStrConf(DEFAULT_CHANNEL);
144
145         for (i = 0; ; i++) {
146                 if (Conf_getNextChannel(&chdesc, i) < 0) {
147                         if (i == 0)
148                                 Log_fatal("No valid channels found in configuration file. Exiting.");
149                         break;
150                 }
151                 if (i == 0) {
152                         rootChan = createChannel(0, chdesc.name, chdesc.description);
153                         rootChan->noenter = chdesc.noenter;
154                         rootChan->silent = chdesc.silent;
155                         list_add_tail(&rootChan->flatlist_node, &channels);
156                         if (strcmp(defaultChannelName, chdesc.name) == 0)
157                                 defaultChan = rootChan;
158                 }
159                 else {
160                         channel_t *ch, *ch_itr = NULL;
161                         ch = Chan_createChannel(chdesc.name, chdesc.description);
162                         ch->noenter = chdesc.noenter;
163                         ch->position = chdesc.position;
164                         ch->silent = chdesc.silent;
165                         if (chdesc.password) {
166                                 Log_info("Setting password on channel '%s'", ch->name);
167                                 ch->password = strdup(chdesc.password);
168                         }
169                         if (strcmp(defaultChannelName, chdesc.name) == 0) {
170                                 Log_info("Setting default channel '%s'", ch->name);
171                                 defaultChan = ch;
172                         }
173
174                         do {
175                                 Chan_iterate(&ch_itr);
176                         } while (ch_itr != NULL && strcmp(ch_itr->name, chdesc.parent) != 0);
177
178                         if (ch_itr == NULL)
179                                 Log_fatal("Error in channel configuration: parent '%s' not found", chdesc.parent);
180                         else {
181                                 Chan_addChannel(ch_itr, ch);
182                                 Log_info("Adding channel '%s' parent '%s'", ch->name, chdesc.parent);
183                         }
184                 }
185         }
186         if (defaultChan == NULL)
187                 defaultChan = rootChan;
188
189         if (defaultChan->noenter)
190                 Log_fatal("Error in channel configuration: default channel is marked as noenter");
191         if (defaultChan->password)
192                 Log_fatal("Error in channel configuration: default channel has a password");
193
194         /* Channel links */
195         for (i = 0; ; i++) {
196                 channel_t *ch_src, *ch_dst, *ch_itr = NULL;
197                 channellist_t *chl;
198                 if (Conf_getNextChannelLink(&chlink, i) < 0) {
199                         if (i == 0)
200                                 Log_info("No channel links found in configuration file.");
201                         break;
202                 }
203                 ch_itr = NULL;
204                 do {
205                         Chan_iterate(&ch_itr);
206                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.source) != 0);
207                 if (ch_itr == NULL)
208                         Log_fatal("Error in channel link configuration: source channel '%s' not found.",
209                                           chlink.source);
210                 else
211                         ch_src = ch_itr;
212
213                 ch_itr = NULL;
214                 do {
215                         Chan_iterate(&ch_itr);
216                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.destination) != 0);
217                 if (ch_itr == NULL)
218                         Log_fatal("Error in channel link configuration: destination channel '%s' not found",
219                                           chlink.destination);
220                 else
221                         ch_dst = ch_itr;
222
223                 chl = Memory_safeMalloc(1, sizeof(channellist_t));
224                 chl->chan = ch_dst;
225                 init_list_entry(&chl->node);
226                 list_add_tail(&chl->node, &ch_src->channel_links);
227                 ch_src->linkcount++;
228                 Log_info("Adding channel link '%s' -> '%s'", ch_src->name, ch_dst->name);
229         }
230 }
231
232 void Chan_free()
233 {
234         struct dlist *itr, *save;
235         struct dlist *linkitr, *linksave;
236         channel_t *ch;
237
238         list_iterate_safe(itr, save, &channels) {
239                 ch = list_get_entry(itr, channel_t, flatlist_node);
240                 Log_debug("Free channel '%s'", ch->name);
241                 free(ch->name);
242                 if (ch->desc)
243                         free(ch->desc);
244                 if (ch->password)
245                         free(ch->password);
246                 list_iterate_safe(linkitr, linksave, &ch->channel_links) {
247                         channellist_t *chl;
248                         chl = list_get_entry(linkitr, channellist_t, node);
249                         free(chl);
250                 }
251                 free(ch);
252         }
253 }
254
255 channel_t *Chan_createChannel(const char *name, const char *desc)
256 {
257         int id = findFreeId();
258         if (id < 0)
259                 Log_fatal("No free channel ID found");
260         return createChannel(id, name, desc);
261 }
262
263 void Chan_freeChannel(channel_t *ch)
264 {
265         list_del(&ch->node);
266         list_del(&ch->flatlist_node);
267         free(ch);
268 }
269
270 void Chan_addChannel(channel_t *parent, channel_t *ch)
271 {
272         list_add_tail(&ch->node, &parent->subs);
273         ch->parent = parent;
274         list_add_tail(&ch->flatlist_node, &channels);
275 }
276
277
278 int Chan_userLeave(client_t *client)
279 {
280         channel_t *leaving = NULL;
281         int leaving_id = -1;
282
283         if (client->channel) {
284                 list_del(&client->chan_node);
285                 leaving = client->channel;
286                 if (leaving->temporary && list_empty(&leaving->clients)) {
287                         leaving_id = leaving->id;
288                         Chan_freeChannel(leaving);
289                 }
290         }
291         return leaving_id;
292 }
293
294 int Chan_userJoin(channel_t *ch, client_t *client)
295 {
296         int leaving_id;
297
298         /* Do nothing if user already is in this channel */
299         if (client->channel == ch)
300                 return 0;
301
302         Log_debug("Add user %s to channel %s", client->username, ch->name);
303         /* Only allowed in one channel at a time */
304         leaving_id = Chan_userLeave(client);
305         list_add_tail(&client->chan_node, &ch->clients);
306         client->channel = ch;
307         return leaving_id;
308 }
309
310 int Chan_userJoin_id(int channelid, client_t *client)
311 {
312         channel_t *ch_itr = NULL;
313         do {
314                 Chan_iterate(&ch_itr);
315         } while (ch_itr != NULL && ch_itr->id != channelid);
316         if (ch_itr == NULL) {
317                 Log_warn("Channel id %d not found - ignoring.", channelid);
318                 return -1;
319         }
320         else
321                 return Chan_userJoin(ch_itr, client);
322 }
323
324 channelJoinResult_t Chan_userJoin_id_test(int channelid, client_t *client)
325 {
326         channelJoinResult_t result;
327         channel_t *ch_itr = NULL;
328         do {
329                 Chan_iterate(&ch_itr);
330         } while (ch_itr != NULL && ch_itr->id != channelid);
331         if (ch_itr == NULL) {
332                 Log_warn("Channel id %d not found - ignoring.", channelid);
333                 result.CHJOIN_NOTFOUND = true;
334         }
335         else
336                 result.CHJOIN_NOTFOUND = false;
337
338         result.CHJOIN_NOENTER = ch_itr->noenter;
339         result.CHJOIN_WRONGPW = ch_itr->password && !Client_token_match(client, ch_itr->password) && !client->isAdmin;
340         result.CHJOIN_SILENT = ch_itr->silent;
341
342         return result;
343 }
344
345 #if 0
346 void Chan_addChannel_id(int parentId, channel_t *ch)
347 {
348         channel_t *ch_itr = NULL;
349         do {
350                 Chan_iterate(&ch_itr);
351         } while (ch_itr != NULL && ch_itr->id != parentId);
352         if (ch_itr == NULL)
353                 Log_warn("Chan_addChannel_id: Channel id %d not found - ignoring.", parentId);
354         else
355                 list_add_tail(&ch->node, &ch_itr->subs);
356 }
357 #endif
358
359 channel_t *Chan_fromId(int channelid)
360 {
361         channel_t *ch_itr = NULL;
362         do {
363                 Chan_iterate(&ch_itr);
364         } while (ch_itr != NULL && ch_itr->id != channelid);
365         if (ch_itr == NULL)
366                 Log_warn("Chan_fromId: Channel id %d not found.", channelid);
367         return ch_itr;
368 }
369
370 void Chan_removeChannel(channel_t *ch)
371 {
372         list_del(&ch->node);
373 }
374
375 void Chan_buildTreeList(channel_t *ch, struct dlist *head)
376 {
377         channellist_t *chl;
378         struct dlist *itr;
379         channel_t *sub;
380
381         chl = Memory_safeMalloc(1, sizeof(channellist_t));
382         chl->chan = ch;
383         init_list_entry(&chl->node);
384         list_add_tail(&chl->node, head);
385
386         list_iterate(itr, &ch->subs) {
387                 sub = list_get_entry(itr, channel_t, node);
388                 Chan_buildTreeList(sub, head);
389         }
390 }
391
392 void Chan_freeTreeList(struct dlist *head)
393 {
394         struct dlist *itr, *save;
395         list_iterate_safe(itr, save, head) {
396                 free(list_get_entry(itr, channellist_t, node));
397         }
398 }