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