Fix silent channel not working.
[umurmur.git] / src / channel.c
1 /* Copyright (C) 2009-2013, Martin Johansson <martin@fatbob.nu>
2    Copyright (C) 2005-2013, 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         channel_t *ch_itr = NULL;
319         do {
320                 Chan_iterate(&ch_itr);
321         } while (ch_itr != NULL && ch_itr->id != channelid);
322         if (ch_itr == NULL) {
323                 Log_warn("Channel id %d not found - ignoring.", channelid);
324                 return CHJOIN_NOTFOUND;
325         }
326         else if (ch_itr->noenter)
327                 return CHJOIN_NOENTER;
328         else if (ch_itr->password && !Client_token_match(client, ch_itr->password))
329                 return CHJOIN_WRONGPW;
330         else return CHJOIN_OK;
331 }
332
333 #if 0
334 void Chan_addChannel_id(int parentId, channel_t *ch)
335 {
336         channel_t *ch_itr = NULL;
337         do {
338                 Chan_iterate(&ch_itr);
339         } while (ch_itr != NULL && ch_itr->id != parentId);
340         if (ch_itr == NULL)
341                 Log_warn("Chan_addChannel_id: Channel id %d not found - ignoring.", parentId);
342         else
343                 list_add_tail(&ch->node, &ch_itr->subs);
344 }
345 #endif
346
347 channel_t *Chan_fromId(int channelid)
348 {
349         channel_t *ch_itr = NULL;
350         do {
351                 Chan_iterate(&ch_itr);
352         } while (ch_itr != NULL && ch_itr->id != channelid);
353         if (ch_itr == NULL)
354                 Log_warn("Chan_fromId: Channel id %d not found.", channelid);
355         return ch_itr;
356 }
357
358 void Chan_removeChannel(channel_t *ch)
359 {
360         list_del(&ch->node);
361 }
362
363 void Chan_buildTreeList(channel_t *ch, struct dlist *head)
364 {
365         channellist_t *chl;
366         struct dlist *itr;
367         channel_t *sub;
368         
369         chl = malloc(sizeof(channellist_t));
370         chl->chan = ch;
371         init_list_entry(&chl->node);
372         list_add_tail(&chl->node, head);
373
374         list_iterate(itr, &ch->subs) {
375                 sub = list_get_entry(itr, channel_t, node);
376                 Chan_buildTreeList(sub, head);
377         }
378 }
379
380 void Chan_freeTreeList(struct dlist *head)
381 {
382         struct dlist *itr, *save;
383         list_iterate_safe(itr, save, head) {
384                 free(list_get_entry(itr, channellist_t, node));
385         }
386 }