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