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