Update copyright year.
[umurmur.git] / src / channel.c
1 /* Copyright (C) 2009-2012, Martin Johansson <martin@fatbob.nu>
2    Copyright (C) 2005-2012, 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                         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                         if (chdesc.password) {
165                                 Log_info("Setting password on channel '%s'", ch->name); 
166                                 ch->password = strdup(chdesc.password);
167                         }
168                         if (strcmp(defaultChannelName, chdesc.name) == 0) {
169                                 Log_info("Setting default channel '%s'", ch->name); 
170                                 defaultChan = ch;
171                         }
172                         
173                         do {
174                                 Chan_iterate(&ch_itr);
175                         } while (ch_itr != NULL && strcmp(ch_itr->name, chdesc.parent) != 0);
176                         
177                         if (ch_itr == NULL)
178                                 Log_fatal("Error in channel configuration: parent '%s' not found", chdesc.parent);
179                         else {
180                                 Chan_addChannel(ch_itr, ch);
181                                 Log_info("Adding channel '%s' parent '%s'", ch->name, chdesc.parent);
182                         }
183                 }
184         }
185         if (defaultChan == NULL)
186                 defaultChan = rootChan;
187         
188         if (defaultChan->noenter)
189                 Log_fatal("Error in channel configuration: default channel is marked as noenter");
190         if (defaultChan->password)
191                 Log_fatal("Error in channel configuration: default channel has a password");
192
193         /* Channel links */
194         for (i = 0; ; i++) {
195                 channel_t *ch_src, *ch_dst, *ch_itr = NULL;
196                 if (Conf_getNextChannelLink(&chlink, i) < 0) {
197                         if (i == 0)
198                                 Log_info("No channel links found in configuration file.");
199                         break;
200                 }
201                 ch_itr = NULL;
202                 do {
203                         Chan_iterate(&ch_itr);
204                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.source) != 0);
205                 if (ch_itr == NULL)
206                         Log_fatal("Error in channel link configuration: source channel '%s' not found.",
207                                           chlink.source);
208                 else
209                         ch_src = ch_itr;
210                 
211                 ch_itr = NULL;          
212                 do {
213                         Chan_iterate(&ch_itr);
214                 } while (ch_itr != NULL && strcmp(ch_itr->name, chlink.destination) != 0);
215                 if (ch_itr == NULL)
216                         Log_fatal("Error in channel link configuration: destination channel '%s' not found",
217                                           chlink.destination);
218                 else
219                         ch_dst = ch_itr;
220                 
221                 list_add_tail(&ch_dst->link_node, &ch_src->channel_links);
222                 ch_src->linkcount++;
223                 Log_info("Adding channel link '%s' -> '%s'", ch_src->name, ch_dst->name);
224         }
225 }
226
227 void Chan_free()
228 {
229         struct dlist *itr, *save;
230         channel_t *ch;
231         
232         list_iterate_safe(itr, save, &channels) {
233                 ch = list_get_entry(itr, channel_t, flatlist_node);
234                 Log_debug("Free channel '%s'", ch->name);
235                 free(ch->name);
236                 if (ch->desc)
237                         free(ch->desc);
238                 if (ch->password)
239                         free(ch->password);
240                 free(ch);
241         }
242 }
243
244 channel_t *Chan_createChannel(const char *name, const char *desc)
245 {
246         int id = findFreeId();
247         if (id < 0)
248                 Log_fatal("No free channel ID found");
249         return createChannel(id, name, desc);
250 }
251
252 void Chan_freeChannel(channel_t *ch)
253 {
254         list_del(&ch->node);
255         list_del(&ch->flatlist_node);
256         free(ch);
257 }
258
259 void Chan_addChannel(channel_t *parent, channel_t *ch)
260 {
261         list_add_tail(&ch->node, &parent->subs);
262         ch->parent = parent;
263         list_add_tail(&ch->flatlist_node, &channels);
264 }
265
266
267 int Chan_userLeave(client_t *client)
268 {
269         channel_t *leaving = NULL;
270         int leaving_id = -1;
271         
272         if (client->channel) {
273                 list_del(&client->chan_node);
274                 leaving = (channel_t *)client->channel;
275                 if (leaving->temporary && list_empty(&leaving->clients)) {
276                         leaving_id = leaving->id;
277                         Chan_freeChannel(leaving);
278                 }
279         }
280         return leaving_id;
281 }
282
283 int Chan_userJoin(channel_t *ch, client_t *client)
284 {
285         int leaving_id;
286
287         /* Do nothing if user already is in this channel */
288         if ((channel_t *)client->channel == ch)
289                 return 0;
290         
291         Log_debug("Add user %s to channel %s", client->username, ch->name); 
292         /* Only allowed in one channel at a time */
293         leaving_id = Chan_userLeave(client);
294         list_add_tail(&client->chan_node, &ch->clients);
295         client->channel = (void *)ch;
296         return leaving_id;
297 }
298
299 int Chan_userJoin_id(int channelid, client_t *client)
300 {
301         channel_t *ch_itr = NULL;
302         do {
303                 Chan_iterate(&ch_itr);
304         } while (ch_itr != NULL && ch_itr->id != channelid);
305         if (ch_itr == NULL) {
306                 Log_warn("Channel id %d not found - ignoring.", channelid);
307                 return -1;
308         }
309         else
310                 return Chan_userJoin(ch_itr, client);   
311 }
312
313 channelJoinResult_t Chan_userJoin_id_test(int channelid, client_t *client)
314 {
315         channel_t *ch_itr = NULL;
316         do {
317                 Chan_iterate(&ch_itr);
318         } while (ch_itr != NULL && ch_itr->id != channelid);
319         if (ch_itr == NULL) {
320                 Log_warn("Channel id %d not found - ignoring.", channelid);
321                 return CHJOIN_NOTFOUND;
322         }
323         else if (ch_itr->noenter)
324                 return CHJOIN_NOENTER;
325         else if (ch_itr->password && !Client_token_match(client, ch_itr->password))
326                 return CHJOIN_WRONGPW;
327         else return CHJOIN_OK;
328 }
329
330 #if 0
331 void Chan_addChannel_id(int parentId, channel_t *ch)
332 {
333         channel_t *ch_itr = NULL;
334         do {
335                 Chan_iterate(&ch_itr);
336         } while (ch_itr != NULL && ch_itr->id != parentId);
337         if (ch_itr == NULL)
338                 Log_warn("Chan_addChannel_id: Channel id %d not found - ignoring.", parentId);
339         else
340                 list_add_tail(&ch->node, &ch_itr->subs);
341 }
342 #endif
343
344 channel_t *Chan_fromId(int channelid)
345 {
346         channel_t *ch_itr = NULL;
347         do {
348                 Chan_iterate(&ch_itr);
349         } while (ch_itr != NULL && ch_itr->id != channelid);
350         if (ch_itr == NULL)
351                 Log_warn("Chan_fromId: Channel id %d not found.", channelid);
352         return ch_itr;
353 }
354
355 void Chan_removeChannel(channel_t *ch)
356 {
357         list_del(&ch->node);
358 }
359
360 void Chan_buildTreeList(channel_t *ch, struct dlist *head)
361 {
362         channellist_t *chl;
363         struct dlist *itr;
364         channel_t *sub;
365         
366         chl = malloc(sizeof(channellist_t));
367         chl->chan = ch;
368         init_list_entry(&chl->node);
369         list_add_tail(&chl->node, head);
370
371         list_iterate(itr, &ch->subs) {
372                 sub = list_get_entry(itr, channel_t, node);
373                 Chan_buildTreeList(sub, head);
374         }
375 }
376
377 void Chan_freeTreeList(struct dlist *head)
378 {
379         struct dlist *itr, *save;
380         list_iterate_safe(itr, save, head) {
381                 free(list_get_entry(itr, channellist_t, node));
382         }
383 }