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