1 /* $Id: channels.c 448 2010-08-22 09:20:48Z tsaviran $
2 * -------------------------------------------------------
3 * Copyright (C) 2002-2006 Tommi Saviranta <wnd@iki.fi>
4 * (C) 2002 Lee Hardy <lee@leeh.co.uk>
5 * (C) 1998-2002 Sebastian Kienzl <zap@riot.org>
6 * -------------------------------------------------------
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif /* ifdef HAVE_CONFIG_H */
21
22 #include "channels.h"
23 #include "client.h"
24 #include "common.h"
25 #include "qlog.h"
26 #include "irc.h"
27 #include "miau.h"
28 #include "messages.h"
29 #include "error.h"
30 #include "chanlog.h"
31 #include "automode.h"
32
33 #include <string.h>
34
35
36
37 /*
38 * active_channels
39 * miau is on these channels
40 *
41 * passive_channels
42 * miau will be on these channels.
43 * For example, if cfg.leave == cfg.rejoin == true, active_channels are
44 * moved to passive_channels when client detached.
45 *
46 * old_channels
47 * miau was on these channels - and they still contain some
48 */
49 llist_list active_channels;
50 llist_list passive_channels;
51 llist_list old_channels;
52
53
54
55 /*
56 * Free data in channel_type -structure.
57 */
58 void
channel_free(channel_type * chan)59 channel_free(channel_type *chan)
60 {
61 if (chan == NULL) {
62 #ifdef ENDUSERDEBUG
63 enduserdebug("channel_free(NULL)");
64 #endif /* ifdef ENDUSERDEBUG */
65 return;
66 }
67 if (chan->simple_name != chan->name) {
68 xfree(chan->simple_name);
69 }
70 xfree(chan->name);
71 xfree(chan->topic);
72 xfree(chan->topicwhen);
73 xfree(chan->topicwho);
74 xfree(chan->key);
75 xfree(chan);
76 } /* void channel_free(channel_type *chan) */
77
78
79
80 /*
81 * Adds a channel to miaus internal list.
82 */
83 channel_type *
channel_add(const char * channel,const char * key,const int list)84 channel_add(const char *channel, const char *key, const int list)
85 {
86 llist_list *target;
87 channel_type *chptr;
88
89 /* See if channel is already on active list. */
90 if (channel_find(channel, LIST_ACTIVE) != NULL) {
91 #ifdef ENDUSERDEBUG
92 if (list == LIST_PASSIVE) {
93 enduserdebug("add chan to pass when already on active");
94 }
95 #endif /* ENDUSERDEBUG */
96 return NULL;
97 }
98
99 /*
100 * See if channel is already on passive list. If it is, it isn't
101 * a bad thing, tho.
102 */
103 if (list == LIST_PASSIVE) {
104 if (channel_find(channel, LIST_PASSIVE) != NULL) {
105 return NULL;
106 }
107 }
108
109 /*
110 * We must do this to keep old GCC (and probably some other compilers
111 * too) happy.
112 *
113 * if (list == LIST_ACTIVE) {
114 * either {
115 * target = foo;
116 * } or {
117 * chptr == NULL;
118 * }
119 * }
120 * if (chptr == NULL) {
121 * target = bar;
122 * }
123 *
124 * Some compilers just don't get it!
125 */
126 target = NULL;
127
128 /*
129 * See if we could simply move channels from passive/old_channels to
130 * active_channels. If we can do this, it's all we need to do.
131 */
132 if (list == LIST_ACTIVE) {
133 llist_list *source;
134
135 /* find a list to remove from */
136 source = NULL;
137 chptr = channel_find(channel, LIST_PASSIVE);
138 if (chptr != NULL) {
139 source = &passive_channels;
140 } else {
141 chptr = channel_find(channel, LIST_OLD);
142 if (chptr != NULL) {
143 source = &old_channels;
144 }
145 }
146
147 if (source != NULL) {
148 llist_node *ptr;
149 ptr = llist_find((void *) chptr, source);
150 if (ptr != NULL) {
151 llist_delete(ptr, source);
152 }
153 #ifdef ENDUSERDEBUG
154 else {
155 enduserdebug("first the channel found, now it's not. what's going on!?");
156 }
157 #endif /* ifdef ENDUSERDEBUG */
158 target = &active_channels;
159 }
160 } else {
161 chptr = NULL;
162 }
163
164 /*
165 * Undernet doesn't echo channel password when password protected
166 * channel is joined. We would have to record the pass when used
167 * requests the channel. At the moment we don't do that, and
168 * automatically rejoining password protected channel will fail unless
169 * channel key was set while being on the channel and unless key was
170 * defined in "channels" option in miaurc.
171 */
172
173 /* Perhaps channel was not on passive/old_channels, after all. */
174 if (chptr == NULL) {
175 /* Create new node to channel list. */
176 chptr = (channel_type *) xcalloc(1, sizeof(channel_type));
177 chptr->name = xstrdup(channel);
178 chptr->simple_name = chptr->name; /* assume to be the same... */
179 chptr->simple_set = 0; /* ...but mark as not confirmed */
180 chptr->name_set = 0; /* real name isn't known either */
181 /*
182 * We don't need to touch other variabels - calloc did the
183 * job. This is neat, since a few values are set to 0 by
184 * default.
185 */
186 chptr->key = xstrdup(key == NULL ? "-" : key);
187 chptr->jointries = JOINTRIES_UNSET;
188 #ifdef AUTOMODE
189 chptr->oper = -1; /* Don't know our status. */
190 #endif /* AUTOMODE */
191
192 /* Get list on which to add this channel to. */
193 if (list == LIST_ACTIVE) {
194 target = &active_channels;
195 } else if (list == LIST_PASSIVE) {
196 target = &passive_channels;
197 } else {
198 target = &old_channels;
199 }
200 }
201
202 llist_add_tail(llist_create(chptr), target);
203
204 /*
205 * From now on, we know two things:
206 * - channel is only on one list
207 * - chptr is non-NULL
208 */
209
210 if (list == LIST_ACTIVE) {
211 /* adding to ACTIVE -> we know real name of the channel */
212 if (chptr->name_set == 0) {
213 /* real name wasn't known before */
214 xfree(chptr->name);
215 chptr->name = xstrdup(channel);
216 chptr->name_set = 1;
217 if (chptr->simple_set == 0) {
218 chptr->simple_name = chptr->name;
219 }
220 }
221 if (chptr->simple_set == 0) {
222 chptr->simple_name = channel_simplify_name(chptr->name);
223 chptr->simple_set = 1;
224 }
225 #ifdef CHANLOG
226 /* active channels may need log-structures. */
227 chanlog_open(chptr);
228 #endif /* CHANLOG */
229 }
230
231 return chptr;
232 } /* channel_type *channel_add(const char *channel, const char *key,
233 const int list) */
234
235
236
237 /*
238 * Rmoves a channel from miaus internal list.
239 *
240 * "list" defines list hannel is to be removed from.
241 */
242 void
channel_rem(channel_type * chptr,const int list)243 channel_rem(channel_type *chptr, const int list)
244 {
245 llist_list *source;
246 llist_node *node;
247
248 if (list == LIST_ACTIVE) {
249 source = &active_channels;
250 #ifdef QUICKLOG
251 /* Need to know which active channels have qlog. */
252 qlog_check(cfg.qloglength * 60);
253 #endif /* QUICKLOG */
254 } else {
255 source = (list == LIST_PASSIVE) ?
256 &passive_channels : &old_channels;
257 }
258
259 /* Only remove existing channels. :-) */
260 if (chptr == NULL) {
261 #ifdef ENDUSERDEBUG
262 enduserdebug("channel_rem(NULL, %d) called", list);
263 #endif /* ENDUSERDEBUG */
264 return;
265 }
266
267 #ifdef AUTOMODE
268 automode_drop_channel(chptr, NULL, ANY_MODE);
269 #endif /* AUTOMODE */
270
271 #ifdef CHANLOG
272 /* Close the logfile if we have one. */
273 if (chptr->log != NULL) {
274 chanlog_close(chptr);
275 }
276 #endif /* CHANLOG */
277
278 node = llist_find(chptr, source);
279 if (node == NULL) {
280 #ifdef ENDUSERDEBUG
281 if (list == LIST_ACTIVE) {
282 enduserdebug("removing non-existing channel");
283 }
284 #endif /* ENDUSERDEBUG */
285 return;
286 }
287
288 #ifdef QUICKLOG
289 /* See if we need to move this channel to list of old channels. */
290 if (chptr->hasqlog && list == LIST_ACTIVE) {
291 /* Yep, we'll just move it. */
292 llist_add_tail(llist_create(chptr), &old_channels);
293 } else {
294 /* No moving, just freeing the resources. */
295 channel_free(chptr);
296 }
297 #else /* QUICKLOG */
298 /* Free resources. */
299 channel_free(chptr);
300 #endif /* QUICKLOG */
301
302 /* And finally, remove channel from the list. */
303 llist_delete(node, source);
304 } /* void channel_rem(channel_type *, const int) */
305
306
307
308 /*
309 * Searches for a channel, returning it if found, else NULL.
310 *
311 * "list" declares which list to search.
312 */
313 channel_type *
channel_find(const char * name,int list)314 channel_find(const char *name, int list)
315 {
316 llist_node *node;
317 channel_type *chan;
318 channel_type *bmatch;
319 char *sname;
320
321 /* this means we won't work with new channel modes */
322 if (channel_is_name(name) == 0) {
323 return NULL;
324 }
325
326 if (list == LIST_ACTIVE) {
327 node = active_channels.head;
328 } else if (list == LIST_PASSIVE) {
329 node = passive_channels.head;
330 } else {
331 node = old_channels.head;
332 }
333
334 sname = channel_simplify_name(name);
335
336 for (bmatch = NULL; node != NULL; node = node->next) {
337 chan = (channel_type *) node->data;
338
339 if (xstrcasecmp(chan->name, name) == 0) {
340 /* exact match */
341 xfree(sname);
342 return chan;
343 } else if (xstrcasecmp(chan->simple_name, sname) == 0) {
344 bmatch = chan;
345 }
346 }
347
348 xfree(sname);
349 if (bmatch != NULL) {
350 return bmatch;
351 }
352
353 /* no match found */
354 return NULL;
355 } /* channel_type *channel_find(char *name, int list) */
356
357
358
359 /*
360 * Stores a topic for a channel we're in.
361 */
362 void
channel_topic(channel_type * chan,const char * topic)363 channel_topic(channel_type *chan, const char *topic)
364 {
365 xfree(chan->topic);
366 xfree(chan->topicwho);
367 xfree(chan->topicwhen);
368 chan->topic = NULL;
369 chan->topicwho = NULL;
370 chan->topicwhen = NULL;
371
372 if (topic != NULL && topic[0] != '\0') {
373 chan->topic = xstrdup(topic);
374 }
375 } /* void channel_topic(channel_type *chan, const char *topic) */
376
377
378
379 /*
380 * Stores who set the topic for a channel we're in.
381 */
382 void
channel_when(channel_type * chan,const char * who,const char * when)383 channel_when(channel_type *chan, const char *who, const char *when)
384 {
385 if (chan->topic != NULL) {
386 xfree(chan->topicwho);
387 xfree(chan->topicwhen);
388
389 chan->topicwho = xstrdup(who);
390 chan->topicwhen = xstrdup(when);
391 }
392 } /* void channel_when(channel_type *chan, const char *who, char *when) */
393
394
395
396 void
channel_join_list(const int list,const int rejoin,connection_type * client)397 channel_join_list(const int list, const int rejoin, connection_type *client)
398 {
399 llist_node *first;
400 char *chans, *keys;
401 size_t csize, ksize;
402 size_t tclen, tklen;
403 int try_joining;
404
405 if (list == LIST_PASSIVE) {
406 first = passive_channels.head;
407 } else {
408 first = active_channels.head;
409 }
410 try_joining = 0;
411
412 /* Join old_channels if client was defined. */
413 if (client != NULL) {
414 LLIST_WALK_H(old_channels.head, channel_type *);
415 #ifdef QUICKLOG
416 if (data->hasqlog) {
417 irc_write(client, ":%s!%s JOIN :%s",
418 status.nickname,
419 status.idhostname,
420 data->name);
421 } else {
422 channel_rem(data, LIST_OLD);
423 }
424 #else /* ifdef QUICKLOG */
425 channel_rem(data, LIST_OLD);
426 #endif /* ifdef else QUICKLOG */
427 LLIST_WALK_F;
428 }
429
430 if (first == NULL) {
431 return;
432 }
433
434 csize = ksize = 256;
435 chans = (char *) xcalloc(csize, 1);
436 keys = (char *) xcalloc(ksize, 1);
437 tclen = tklen = 1;
438
439 LLIST_WALK_H(first, channel_type *);
440 if (list == LIST_PASSIVE) {
441 if ((rejoin == 1 || (cfg.rejoin == 1 && cfg.leave == 0))
442 && data->jointries == JOINTRIES_UNSET) {
443 /*
444 * Set data->jointries to 1 even if
445 * cfg.jointries = 0. This way we can try to
446 * join channels in miaurc. If the channel
447 * is a safe channel, try joining only once.
448 */
449 if (cfg.jointries == 0) {
450 data->jointries = 1;
451 } else {
452 if (data->name[0] == '!') {
453 data->jointries = 1;
454 } else {
455 data->jointries = cfg.jointries;
456 }
457 }
458 }
459
460 if (data->jointries > 0) {
461 size_t clen, klen;
462 try_joining = 1;
463 data->jointries--;
464 /* Add channel and key in queue. */
465 /*
466 * name and key guaranteed
467 * terminated and valid
468 *
469 * strncat == paranoid
470 */
471 /*
472 * join with "simple" name, "real" name
473 * of a safe channel won't do here
474 */
475 clen = strlen(data->name);
476 klen = strlen(data->key);
477 tclen += clen + 1;
478 tklen += klen + 1;
479 if (tclen > csize) {
480 csize = tclen;
481 chans = (char *) xrealloc(chans, csize);
482 }
483 if (tklen > ksize) {
484 ksize = tklen;
485 keys = (char *) xrealloc(keys, ksize);
486 }
487 strcat(chans, ",");
488 strncat(chans, data->name, clen);
489 strcat(keys, ",");
490 strncat(keys, data->key, klen);
491 } else if (data->jointries == 0) {
492 channel_type *chan;
493 /*
494 * If cfg.jointries is 0, remove channel from
495 * list after unsuccesfull attempt to join it.
496 */
497 chan = channel_find(data->name, LIST_PASSIVE);
498 channel_rem(chan, LIST_PASSIVE);
499 }
500 } else {
501 /* Tell client to join this channel. */
502 irc_write(client, ":%s!%s JOIN :%s",
503 status.nickname,
504 status.idhostname,
505 data->name);
506
507 /* Also tell client about this channel. */
508 irc_write(client, ":%s %d %s %s %s",
509 i_server.realname,
510 (data->topic != NULL ?
511 RPL_TOPIC : RPL_NOTOPIC),
512 status.nickname,
513 data->name,
514 (data->topic != NULL ?
515 data->topic :
516 ":No topic is set"));
517 if (data->topicwho != NULL && data->topicwhen != NULL) {
518 irc_write(client, ":%s %d %s %s %s %s",
519 i_server.realname,
520 RPL_TOPICWHO,
521 status.nickname,
522 data->name,
523 data->topicwho,
524 data->topicwhen);
525 }
526 #ifdef CNANLOG
527 if (chanlog_has_log((channel_type *) data, LOG_MIAU)) {
528 log_write_entry(data, LOGM_MIAU,
529 get_short_localtime(), "");
530 }
531 #endif /* CHANLOG */
532 irc_write(&c_server, "NAMES %s", data->name);
533 }
534 LLIST_WALK_F;
535
536 if (try_joining == 1) {
537 report(rejoin ? MIAU_REINTRODUCE : MIAU_JOINING, chans + 1);
538 irc_write(&c_server, "JOIN %s %s", chans + 1, keys + 1);
539 }
540
541 xfree(chans);
542 xfree(keys);
543 } /* void channel_join_list(const int, const int, connection_type *) */
544
545
546
547 char *
channel_simplify_name(const char * chan)548 channel_simplify_name(const char *chan)
549 {
550 if (chan[0] != '!') {
551 /* not safe-channel */
552 return xstrdup(chan);
553 } else {
554 size_t len;
555 char *name;
556
557 len = strlen(chan);
558 if (len < 6) {
559 #ifdef ENDUSERDEBUG
560 enduserdebug("weird channel: '%s'", chan);
561 #endif /* ifdef ENDUSERDEBUG */
562 return xstrdup(chan); /* some weird channel */
563 }
564
565 /* safe channel */
566 len -= 4; /* original length - 5 + terminator */
567 name = (char *) xmalloc(len);
568 snprintf(name, len, "!%s", chan + 6);
569 name[len - 1] = '\0';
570 return name;
571 }
572 } /* char *channel_simplify_name(const char *chan) */
573
574
575
576 /*
577 * Check if name could be a channel name.
578 *
579 * Return non-zero if name could be a channel name.
580 */
581 int
channel_is_name(const char * name)582 channel_is_name(const char *name)
583 {
584 if (name == NULL) {
585 return 0;
586 }
587
588 switch (name[0]) {
589 case '#': /* ordinary channel (rfc 1459) */
590 case '&': /* local channel (rfc 1459) */
591 case '!': /* safe channel (rfc 2811) */
592 case '+': /* modeless channel (rfc 2811) */
593 case '.': /* programmable channel (kineircd) */
594 case '~': /* global channel (what is that? kineircd) */
595 return (int) name[0];
596 break; /* dummy */
597
598 default:
599 return 0;
600 break; /* dummy */
601 }
602 } /* int channel_is_name(const char *name) */
603
604
605
606 #ifdef OBSOLETE /* Never defined - ignore this. */
607 static void
set_simple_name(channel_type * chan,const char * name)608 set_simple_name(channel_type *chan, const char *name)
609 {
610 if (chan->name[0] != '!') {
611 /* not safe-channel */
612 chan->simple_set = 1;
613 } else {
614 if (strlen(chan->name) < 2 || strlen(name) < 6) {
615 /* channel name too short -- not safe channel */
616 return;
617 }
618 if (xstrcasecmp(chan->name + 2, name + 6) == 0) {
619 chan->simplename = xstrdup(name);
620 chan->simple_set = 1;
621 }
622 }
623 } /* static void set_simple_name(channel_type *chan, const char *name) */
624
625
626
627 /*
628 * Creates a hash value based on the first 25 chars of a channel name.
629 */
630 unsigned int
channel_hash(char * p)631 channel_hash(char *p)
632 {
633 int i = 25;
634 unsigned int hash = 0;
635
636 while (*p && --i) {
637 hash = (hash << 4) - (hash + (unsigned char) tolower(*p++));
638 }
639
640 return (hash & (MAX_CHANNELS - 1));
641 } /* unsigned int channel_hash(char *) */
642 #endif /* OBSOLETE */
643