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