1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2002-2013 Wilmer van der Gaast and others *
5 \********************************************************************/
6
7 /* The IRC-based UI - Representing (virtual) channels. */
8
9 /*
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License with
21 the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22 if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23 Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26 #include "bitlbee.h"
27
28 static char *set_eval_channel_type(set_t *set, char *value);
29 static gint irc_channel_user_cmp(gconstpointer a_, gconstpointer b_);
30 static const struct irc_channel_funcs control_channel_funcs;
31
32 extern const struct irc_channel_funcs irc_channel_im_chat_funcs;
33
irc_channel_new(irc_t * irc,const char * name)34 irc_channel_t *irc_channel_new(irc_t *irc, const char *name)
35 {
36 irc_channel_t *ic;
37 set_t *s;
38
39 if (!irc_channel_name_ok(name) || irc_channel_by_name(irc, name)) {
40 return NULL;
41 }
42
43 ic = g_new0(irc_channel_t, 1);
44 ic->irc = irc;
45 ic->name = g_strdup(name);
46 strcpy(ic->mode, CMODE);
47
48 irc_channel_add_user(ic, irc->root);
49
50 irc->channels = g_slist_append(irc->channels, ic);
51
52 set_add(&ic->set, "auto_join", "false", set_eval_bool, ic);
53
54 s = set_add(&ic->set, "type", "control", set_eval_channel_type, ic);
55 s->flags |= SET_NOSAVE; /* Layer violation (XML format detail) */
56
57 if (name[0] == '&') {
58 set_setstr(&ic->set, "type", "control");
59 } else { /* if( name[0] == '#' ) */
60 set_setstr(&ic->set, "type", "chat");
61 }
62
63 return ic;
64 }
65
irc_channel_by_name(irc_t * irc,const char * name)66 irc_channel_t *irc_channel_by_name(irc_t *irc, const char *name)
67 {
68 GSList *l;
69
70 for (l = irc->channels; l; l = l->next) {
71 irc_channel_t *ic = l->data;
72
73 if (irc_channel_name_cmp(name, ic->name) == 0) {
74 return ic;
75 }
76 }
77
78 return NULL;
79 }
80
irc_channel_get(irc_t * irc,char * id)81 irc_channel_t *irc_channel_get(irc_t *irc, char *id)
82 {
83 irc_channel_t *ic, *ret = NULL;
84 GSList *l;
85 int nr;
86
87 if (sscanf(id, "%d", &nr) == 1 && nr < 1000) {
88 for (l = irc->channels; l; l = l->next) {
89 ic = l->data;
90 if ((nr--) == 0) {
91 return ic;
92 }
93 }
94
95 return NULL;
96 }
97
98 /* Exact match first: Partial match only sucks if there's a channel
99 #aa and #aabb */
100 if ((ret = irc_channel_by_name(irc, id))) {
101 return ret;
102 }
103
104 for (l = irc->channels; l; l = l->next) {
105 ic = l->data;
106
107 if (strstr(ic->name, id)) {
108 /* Make sure it's a unique match. */
109 if (!ret) {
110 ret = ic;
111 } else {
112 return NULL;
113 }
114 }
115 }
116
117 return ret;
118 }
119
irc_channel_free(irc_channel_t * ic)120 int irc_channel_free(irc_channel_t *ic)
121 {
122 irc_t *irc;
123 GHashTableIter iter;
124 gpointer itervalue;
125
126 if (ic == NULL) {
127 return 0;
128 }
129 irc = ic->irc;
130
131 if (ic->flags & IRC_CHANNEL_JOINED) {
132 irc_channel_del_user(ic, irc->user, IRC_CDU_KICK, "Cleaning up channel");
133 }
134
135 if (ic->f->_free) {
136 ic->f->_free(ic);
137 }
138
139 while (ic->set) {
140 set_del(&ic->set, ic->set->key);
141 }
142
143 irc->channels = g_slist_remove(irc->channels, ic);
144 while (ic->users) {
145 g_free(ic->users->data);
146 ic->users = g_slist_remove(ic->users, ic->users->data);
147 }
148
149 g_hash_table_iter_init(&iter, irc->nick_user_hash);
150
151 while (g_hash_table_iter_next(&iter, NULL, &itervalue)) {
152 irc_user_t *iu = itervalue;
153
154 if (iu->last_channel == ic) {
155 iu->last_channel = irc->default_channel;
156 }
157 }
158
159 if (ic->pastebuf_timer) {
160 b_event_remove(ic->pastebuf_timer);
161 }
162
163 g_free(ic->name);
164 g_free(ic->topic);
165 g_free(ic->topic_who);
166 g_free(ic);
167
168 return 1;
169 }
170
171 struct irc_channel_free_data {
172 irc_t *irc;
173 irc_channel_t *ic;
174 char *name;
175 };
176
irc_channel_free_callback(gpointer data,gint fd,b_input_condition cond)177 static gboolean irc_channel_free_callback(gpointer data, gint fd, b_input_condition cond)
178 {
179 struct irc_channel_free_data *d = data;
180
181 if (g_slist_find(irc_connection_list, d->irc) &&
182 irc_channel_by_name(d->irc, d->name) == d->ic &&
183 !(d->ic->flags & IRC_CHANNEL_JOINED)) {
184 irc_channel_free(d->ic);
185 }
186
187 g_free(d->name);
188 g_free(d);
189 return FALSE;
190 }
191
192 /* Free the channel, but via the event loop, so after finishing whatever event
193 we're currently handling. */
irc_channel_free_soon(irc_channel_t * ic)194 void irc_channel_free_soon(irc_channel_t *ic)
195 {
196 struct irc_channel_free_data *d = g_new0(struct irc_channel_free_data, 1);
197
198 d->irc = ic->irc;
199 d->ic = ic;
200 d->name = g_strdup(ic->name);
201
202 b_timeout_add(0, irc_channel_free_callback, d);
203 }
204
set_eval_channel_type(set_t * set,char * value)205 static char *set_eval_channel_type(set_t *set, char *value)
206 {
207 struct irc_channel *ic = set->data;
208 const struct irc_channel_funcs *new;
209
210 if (strcmp(value, "control") == 0) {
211 new = &control_channel_funcs;
212 } else if (ic != ic->irc->default_channel && strcmp(value, "chat") == 0) {
213 new = &irc_channel_im_chat_funcs;
214 } else {
215 return SET_INVALID;
216 }
217
218 /* Skip the free/init if nothing is being changed */
219 if (ic->f == new) {
220 return value;
221 }
222
223 /* TODO: Return values. */
224 if (ic->f && ic->f->_free) {
225 ic->f->_free(ic);
226 }
227
228 ic->f = new;
229
230 if (ic->f && ic->f->_init) {
231 ic->f->_init(ic);
232 }
233
234 return value;
235 }
236
irc_channel_add_user(irc_channel_t * ic,irc_user_t * iu)237 int irc_channel_add_user(irc_channel_t *ic, irc_user_t *iu)
238 {
239 irc_channel_user_t *icu;
240
241 if (irc_channel_has_user(ic, iu)) {
242 return 0;
243 }
244
245 icu = g_new0(irc_channel_user_t, 1);
246 icu->iu = iu;
247
248 ic->users = g_slist_insert_sorted(ic->users, icu, irc_channel_user_cmp);
249
250 if (iu == ic->irc->user || iu == ic->irc->root) {
251 irc_channel_update_ops(ic, set_getstr(&ic->irc->b->set, "ops"));
252 }
253
254 if (iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED) {
255 ic->flags |= IRC_CHANNEL_JOINED;
256 irc_send_join(ic, iu);
257 }
258
259 return 1;
260 }
261
irc_channel_del_user(irc_channel_t * ic,irc_user_t * iu,irc_channel_del_user_type_t type,const char * msg)262 int irc_channel_del_user(irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg)
263 {
264 irc_channel_user_t *icu;
265
266 if (!(icu = irc_channel_has_user(ic, iu))) {
267 if (iu == ic->irc->user && type == IRC_CDU_KICK) {
268 /* an error happened before joining, inform the client with a numeric */
269 irc_send_num(ic->irc, 403, "%s :Error joining channel (check control channel?)", ic->name);
270 }
271 return 0;
272 }
273
274 ic->users = g_slist_remove(ic->users, icu);
275 g_free(icu);
276
277 if (!(ic->flags & IRC_CHANNEL_JOINED) || type == IRC_CDU_SILENT) {
278 }
279 /* Do nothing. The caller should promise it won't screw
280 up state of the IRC client. :-) */
281 else if (type == IRC_CDU_PART) {
282 irc_send_part(ic, iu, msg);
283 } else if (type == IRC_CDU_KICK) {
284 irc_send_kick(ic, iu, ic->irc->root, msg);
285 }
286
287 if (iu == ic->irc->user) {
288 ic->flags &= ~IRC_CHANNEL_JOINED;
289
290 if (ic->irc->status & USTATUS_SHUTDOWN) {
291 /* Don't do anything fancy when we're shutting down anyway. */
292 } else if (ic->flags & IRC_CHANNEL_TEMP) {
293 irc_channel_free_soon(ic);
294 } else {
295 /* Flush userlist now. The user won't see it anyway. */
296 while (ic->users) {
297 g_free(ic->users->data);
298 ic->users = g_slist_remove(ic->users, ic->users->data);
299 }
300 irc_channel_add_user(ic, ic->irc->root);
301 }
302 }
303
304 return 1;
305 }
306
irc_channel_has_user(irc_channel_t * ic,irc_user_t * iu)307 irc_channel_user_t *irc_channel_has_user(irc_channel_t *ic, irc_user_t *iu)
308 {
309 GSList *l;
310
311 for (l = ic->users; l; l = l->next) {
312 irc_channel_user_t *icu = l->data;
313
314 if (icu->iu == iu) {
315 return icu;
316 }
317 }
318
319 return NULL;
320 }
321
322 /* Find a channel we're currently in, that currently has iu in it. */
irc_channel_with_user(irc_t * irc,irc_user_t * iu)323 struct irc_channel *irc_channel_with_user(irc_t *irc, irc_user_t *iu)
324 {
325 GSList *l;
326
327 for (l = irc->channels; l; l = l->next) {
328 irc_channel_t *ic = l->data;
329
330 if (strcmp(set_getstr(&ic->set, "type"), "control") != 0) {
331 continue;
332 }
333
334 if ((ic->flags & IRC_CHANNEL_JOINED) &&
335 irc_channel_has_user(ic, iu)) {
336 return ic;
337 }
338 }
339
340 /* If there was no match, try once more but just see if the user
341 *would* be in the channel, i.e. if s/he were online. */
342 if (iu->bu == NULL) {
343 return NULL;
344 }
345
346 for (l = irc->channels; l; l = l->next) {
347 irc_channel_t *ic = l->data;
348
349 if (strcmp(set_getstr(&ic->set, "type"), "control") != 0) {
350 continue;
351 }
352
353 if ((ic->flags & IRC_CHANNEL_JOINED) &&
354 irc_channel_wants_user(ic, iu)) {
355 return ic;
356 }
357 }
358
359 return NULL;
360 }
361
irc_channel_set_topic(irc_channel_t * ic,const char * topic,const irc_user_t * iu)362 int irc_channel_set_topic(irc_channel_t *ic, const char *topic, const irc_user_t *iu)
363 {
364 g_free(ic->topic);
365 ic->topic = g_strdup(topic);
366
367 g_free(ic->topic_who);
368 if (iu) {
369 ic->topic_who = g_strdup_printf("%s!%s@%s", iu->nick, iu->user, iu->host);
370 } else {
371 ic->topic_who = NULL;
372 }
373
374 ic->topic_time = time(NULL);
375
376 if (ic->flags & IRC_CHANNEL_JOINED) {
377 irc_send_topic(ic, TRUE);
378 }
379
380 return 1;
381 }
382
irc_channel_user_set_mode(irc_channel_t * ic,irc_user_t * iu,irc_channel_user_flags_t flags)383 void irc_channel_user_set_mode(irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags)
384 {
385 irc_channel_user_t *icu = irc_channel_has_user(ic, iu);
386
387 if (!icu || icu->flags == flags) {
388 return;
389 }
390
391 if (ic->flags & IRC_CHANNEL_JOINED) {
392 irc_send_channel_user_mode_diff(ic, iu, icu->flags, flags);
393 }
394
395 icu->flags = flags;
396 }
397
irc_channel_set_mode(irc_channel_t * ic,const char * s)398 void irc_channel_set_mode(irc_channel_t *ic, const char *s)
399 {
400 irc_t *irc = ic->irc;
401 char m[128], st = 1;
402 const char *t;
403 int i;
404 char changes[512], *p, st2 = 2;
405
406 memset(m, 0, sizeof(m));
407
408 for (t = ic->mode; *t; t++) {
409 if (*t < sizeof(m)) {
410 m[(int) *t] = 1;
411 }
412 }
413
414 p = changes;
415 for (t = s; *t; t++) {
416 if (*t == '+' || *t == '-') {
417 st = *t == '+';
418 } else if (strchr(CMODES, *t)) {
419 if (m[(int) *t] != st) {
420 if (st != st2) {
421 st2 = st, *p++ = st ? '+' : '-';
422 }
423 *p++ = *t;
424 }
425 m[(int) *t] = st;
426 }
427 }
428 *p = '\0';
429
430 memset(ic->mode, 0, sizeof(ic->mode));
431
432 for (i = 'A'; i <= 'z' && strlen(ic->mode) < (sizeof(ic->mode) - 1); i++) {
433 if (m[i]) {
434 ic->mode[strlen(ic->mode)] = i;
435 }
436 }
437
438 if (*changes && (ic->flags & IRC_CHANNEL_JOINED)) {
439 irc_write(irc, ":%s!%s@%s MODE %s :%s", irc->root->nick,
440 irc->root->user, irc->root->host, ic->name,
441 changes);
442 }
443 }
444
irc_channel_user_get_prefix(irc_channel_user_t * icu)445 char irc_channel_user_get_prefix(irc_channel_user_t *icu)
446 {
447 if (icu->flags & IRC_CHANNEL_USER_OP) {
448 return '@';
449 } else if (icu->flags & IRC_CHANNEL_USER_HALFOP) {
450 return '%';
451 } else if (icu->flags & IRC_CHANNEL_USER_VOICE) {
452 return '+';
453 }
454 return 0;
455 }
456
irc_channel_auto_joins(irc_t * irc,account_t * acc)457 void irc_channel_auto_joins(irc_t *irc, account_t *acc)
458 {
459 GSList *l;
460
461 for (l = irc->channels; l; l = l->next) {
462 irc_channel_t *ic = l->data;
463 gboolean aj = set_getbool(&ic->set, "auto_join");
464 char *type;
465
466 if (acc &&
467 (type = set_getstr(&ic->set, "chat_type")) &&
468 strcmp(type, "room") == 0) {
469 /* Bit of an ugly special case: Handle chatrooms here, we
470 can only auto-join them if their account is online. */
471 char *acc_s;
472
473 if (!aj && !(ic->flags & IRC_CHANNEL_JOINED)) {
474 /* Only proceed if this one's marked as auto_join
475 or if we're in it already. (Very likely the IRC
476 client auto-(re)joining at reconnect time.) */
477 continue;
478 } else if (!(acc_s = set_getstr(&ic->set, "account"))) {
479 continue;
480 } else if (account_get(irc->b, acc_s) != acc) {
481 continue;
482 } else if (acc->ic == NULL || !(acc->ic->flags & OPT_LOGGED_IN)) {
483 continue;
484 } else {
485 ic->f->join(ic);
486 }
487 } else if (aj) {
488 irc_channel_add_user(ic, irc->user);
489 }
490 }
491 }
492
irc_channel_printf(irc_channel_t * ic,char * format,...)493 void irc_channel_printf(irc_channel_t *ic, char *format, ...)
494 {
495 va_list params;
496 char *text;
497
498 va_start(params, format);
499 text = g_strdup_vprintf(format, params);
500 va_end(params);
501
502 irc_send_msg(ic->irc->root, "PRIVMSG", ic->name, text, NULL);
503 g_free(text);
504 }
505
irc_channel_name_ok(const char * name_)506 gboolean irc_channel_name_ok(const char *name_)
507 {
508 const unsigned char *name = (unsigned char *) name_;
509 int i;
510
511 if (name_[0] == '\0') {
512 return FALSE;
513 }
514
515 /* Check if the first character is in CTYPES (#&) */
516 if (strchr(CTYPES, name_[0]) == NULL) {
517 return FALSE;
518 }
519
520 /* RFC 1459 keeps amazing me: While only a "few" chars are allowed
521 in nicknames, channel names can be pretty much anything as long
522 as they start with # or &. I'll be a little bit more strict and
523 disallow all non-printable characters. */
524 for (i = 1; name[i]; i++) {
525 if (name[i] <= ' ' || name[i] == ',') {
526 return FALSE;
527 }
528 }
529
530 return TRUE;
531 }
532
irc_channel_name_strip(char * name)533 void irc_channel_name_strip(char *name)
534 {
535 int i, j;
536
537 for (i = j = 0; name[i]; i++) {
538 if (name[i] > ' ' && name[i] != ',') {
539 name[j++] = name[i];
540 }
541 }
542
543 name[j] = '\0';
544 }
545
irc_channel_name_cmp(const char * a_,const char * b_)546 int irc_channel_name_cmp(const char *a_, const char *b_)
547 {
548 static unsigned char case_map[256];
549 const unsigned char *a = (unsigned char *) a_, *b = (unsigned char *) b_;
550 int i;
551
552 if (case_map['A'] == '\0') {
553 for (i = 33; i < 256; i++) {
554 if (i != ',') {
555 case_map[i] = i;
556 }
557 }
558
559 for (i = 0; i < 26; i++) {
560 case_map['A' + i] = 'a' + i;
561 }
562
563 case_map['['] = '{';
564 case_map[']'] = '}';
565 case_map['~'] = '`';
566 case_map['\\'] = '|';
567 }
568
569 if (!irc_channel_name_ok(a_) || !irc_channel_name_ok(b_)) {
570 return -1;
571 }
572
573 for (i = 0; a[i] && b[i] && case_map[a[i]] && case_map[b[i]]; i++) {
574 if (case_map[a[i]] == case_map[b[i]]) {
575 continue;
576 } else {
577 return case_map[a[i]] - case_map[b[i]];
578 }
579 }
580
581 return case_map[a[i]] - case_map[b[i]];
582 }
583
irc_channel_is_unused(irc_t * irc,char * name)584 gboolean irc_channel_is_unused(irc_t *irc, char *name)
585 {
586 char *type, *chat_type;
587 irc_channel_t *oic;
588
589 if (!irc_channel_name_ok(name)) {
590 return FALSE;
591 }
592
593 if (!(oic = irc_channel_by_name(irc, name))) {
594 return TRUE;
595 }
596
597 type = set_getstr(&oic->set, "type");
598 chat_type = set_getstr(&oic->set, "chat_type");
599
600 if (type && chat_type && oic->data == FALSE &&
601 strcmp(type, "chat") == 0 &&
602 strcmp(chat_type, "groupchat") == 0) {
603 /* There's a channel with this name already, but it looks
604 like it's not in use yet. Most likely the IRC client
605 rejoined the channel after a reconnect. Remove it so
606 we can reuse its name. */
607 irc_channel_free(oic);
608 return TRUE;
609 }
610
611 return FALSE;
612 }
613
irc_channel_name_gen(irc_t * irc,const char * hint)614 char *irc_channel_name_gen(irc_t *irc, const char *hint)
615 {
616 char name[MAX_NICK_LENGTH + 1] = { 0 };
617 char *translit_name;
618 gsize bytes_written;
619
620 translit_name = g_convert_with_fallback(hint, -1, "ASCII//TRANSLIT", "UTF-8", "", NULL, &bytes_written, NULL);
621
622 if (!translit_name) {
623 /* Same thing as in nick_gen() in nick.c, try again without //TRANSLIT */
624 translit_name = g_convert_with_fallback(hint, -1, "ASCII", "UTF-8", "", NULL, &bytes_written, NULL);
625 }
626
627 if (!translit_name) {
628 return NULL;
629 }
630
631 if (bytes_written > MAX_NICK_LENGTH) {
632 translit_name[MAX_NICK_LENGTH] = '\0';
633 }
634
635 name[0] = '#';
636 strncpy(name + 1, translit_name, MAX_NICK_LENGTH - 1);
637 name[MAX_NICK_LENGTH] = '\0';
638
639 g_free(translit_name);
640
641 irc_channel_name_strip(name);
642
643 if (set_getbool(&irc->b->set, "nick_lowercase")) {
644 nick_lc(irc, name + 1);
645 }
646
647 while (!irc_channel_is_unused(irc, name)) {
648 underscore_dedupe(name);
649 }
650
651 return g_strdup(name);
652 }
653
irc_channel_name_hint(irc_channel_t * ic,const char * name)654 gboolean irc_channel_name_hint(irc_channel_t *ic, const char *name)
655 {
656 irc_t *irc = ic->irc;
657 char *full_name;
658
659 /* Don't rename a channel if the user's in it already. */
660 if (ic->flags & IRC_CHANNEL_JOINED) {
661 return FALSE;
662 }
663
664 if (!(full_name = irc_channel_name_gen(irc, name))) {
665 return FALSE;
666 }
667
668 g_free(ic->name);
669 ic->name = full_name;
670
671 return TRUE;
672 }
673
irc_channel_user_cmp(gconstpointer a_,gconstpointer b_)674 static gint irc_channel_user_cmp(gconstpointer a_, gconstpointer b_)
675 {
676 const irc_channel_user_t *a = a_, *b = b_;
677
678 return irc_user_cmp(a->iu, b->iu);
679 }
680
irc_channel_update_ops(irc_channel_t * ic,char * value)681 void irc_channel_update_ops(irc_channel_t *ic, char *value)
682 {
683 irc_channel_user_set_mode(ic, ic->irc->root,
684 (strcmp(value, "both") == 0 ||
685 strcmp(value, "root") == 0) ? IRC_CHANNEL_USER_OP : 0);
686 irc_channel_user_set_mode(ic, ic->irc->user,
687 (strcmp(value, "both") == 0 ||
688 strcmp(value, "user") == 0) ? IRC_CHANNEL_USER_OP : 0);
689 }
690
set_eval_irc_channel_ops(set_t * set,char * value)691 char *set_eval_irc_channel_ops(set_t *set, char *value)
692 {
693 irc_t *irc = set->data;
694 GSList *l;
695
696 if (strcmp(value, "both") != 0 && strcmp(value, "none") != 0 &&
697 strcmp(value, "user") != 0 && strcmp(value, "root") != 0) {
698 return SET_INVALID;
699 }
700
701 for (l = irc->channels; l; l = l->next) {
702 irc_channel_update_ops(l->data, value);
703 }
704
705 return value;
706 }
707
708 /* Channel-type dependent functions, for control channels: */
control_channel_privmsg(irc_channel_t * ic,const char * msg)709 static gboolean control_channel_privmsg(irc_channel_t *ic, const char *msg)
710 {
711 irc_t *irc = ic->irc;
712 irc_user_t *iu;
713 const char *s;
714
715 /* Scan for non-whitespace chars followed by a colon: */
716 for (s = msg; *s && !g_ascii_isspace(*s) && *s != ':' && *s != ','; s++) {
717 }
718
719 if (*s == ':' || *s == ',') {
720 char to[s - msg + 1];
721
722 memset(to, 0, sizeof(to));
723 strncpy(to, msg, s - msg);
724 while (*(++s) && g_ascii_isspace(*s)) {
725 }
726 msg = s;
727
728 if (!(iu = irc_user_by_name(irc, to))) {
729 irc_channel_printf(ic, "User does not exist: %s", to);
730 } else {
731 ic->last_target = iu;
732 }
733 } else if (g_strcasecmp(set_getstr(&irc->b->set, "default_target"), "last") == 0 &&
734 ic->last_target && g_hash_table_contains(irc->nick_user_hash, ic->last_target->key)) {
735 iu = ic->last_target;
736 } else {
737 iu = irc->root;
738 }
739
740 if (iu && iu->f->privmsg) {
741 iu->last_channel = ic;
742 iu->f->privmsg(iu, msg);
743 }
744
745 return TRUE;
746 }
747
control_channel_invite(irc_channel_t * ic,irc_user_t * iu)748 static gboolean control_channel_invite(irc_channel_t *ic, irc_user_t *iu)
749 {
750 struct irc_control_channel *icc = ic->data;
751 bee_user_t *bu = iu->bu;
752
753 if (bu == NULL) {
754 return FALSE;
755 }
756
757 if (icc->type != IRC_CC_TYPE_GROUP) {
758 irc_send_num(ic->irc, 482, "%s :Invitations are only possible to fill_by=group channels", ic->name);
759 return FALSE;
760 }
761
762 bu->ic->acc->prpl->add_buddy(bu->ic, bu->handle,
763 icc->group ? icc->group->name : NULL);
764
765 return TRUE;
766 }
767
control_channel_kick(irc_channel_t * ic,irc_user_t * iu,const char * msg)768 static void control_channel_kick(irc_channel_t *ic, irc_user_t *iu, const char *msg)
769 {
770 struct irc_control_channel *icc = ic->data;
771 bee_user_t *bu = iu->bu;
772
773 if (bu == NULL) {
774 return;
775 }
776
777 if (icc->type != IRC_CC_TYPE_GROUP) {
778 irc_send_num(ic->irc, 482, "%s :Kicks are only possible to fill_by=group channels", ic->name);
779 return;
780 }
781
782 bu->ic->acc->prpl->remove_buddy(bu->ic, bu->handle,
783 icc->group ? icc->group->name : NULL);
784 }
785
786 static char *set_eval_by_account(set_t *set, char *value);
787 static char *set_eval_fill_by(set_t *set, char *value);
788 static char *set_eval_by_group(set_t *set, char *value);
789 static char *set_eval_by_protocol(set_t *set, char *value);
790 static char *set_eval_show_users(set_t *set, char *value);
791
control_channel_init(irc_channel_t * ic)792 static gboolean control_channel_init(irc_channel_t *ic)
793 {
794 struct irc_control_channel *icc;
795
796 set_add(&ic->set, "account", NULL, set_eval_by_account, ic);
797 set_add(&ic->set, "fill_by", "all", set_eval_fill_by, ic);
798 set_add(&ic->set, "group", NULL, set_eval_by_group, ic);
799 set_add(&ic->set, "protocol", NULL, set_eval_by_protocol, ic);
800
801 /* When changing the default, also change it below. */
802 set_add(&ic->set, "show_users", "online+,special%,away", set_eval_show_users, ic);
803
804 ic->data = icc = g_new0(struct irc_control_channel, 1);
805 icc->type = IRC_CC_TYPE_DEFAULT;
806
807 /* Have to run the evaluator to initialize icc->modes. */
808 set_setstr(&ic->set, "show_users", "online+,special%,away");
809
810 /* For scripts that care. */
811 irc_channel_set_mode(ic, "+C");
812
813 return TRUE;
814 }
815
control_channel_join(irc_channel_t * ic)816 static gboolean control_channel_join(irc_channel_t *ic)
817 {
818 bee_irc_channel_update(ic->irc, ic, NULL);
819
820 return TRUE;
821 }
822
set_eval_by_account(set_t * set,char * value)823 static char *set_eval_by_account(set_t *set, char *value)
824 {
825 struct irc_channel *ic = set->data;
826 struct irc_control_channel *icc = ic->data;
827 account_t *acc;
828
829 if (!(acc = account_get(ic->irc->b, value))) {
830 return SET_INVALID;
831 }
832
833 icc->account = acc;
834 if ((icc->type & IRC_CC_TYPE_MASK) == IRC_CC_TYPE_ACCOUNT) {
835 bee_irc_channel_update(ic->irc, ic, NULL);
836 }
837
838 return g_strdup(acc->tag);
839 }
840
set_eval_fill_by(set_t * set,char * value)841 static char *set_eval_fill_by(set_t *set, char *value)
842 {
843 struct irc_channel *ic = set->data;
844 struct irc_control_channel *icc = ic->data;
845 char *s;
846
847 icc->type &= ~(IRC_CC_TYPE_MASK | IRC_CC_TYPE_INVERT);
848
849 s = value;
850 if (s[0] == '!') {
851 icc->type |= IRC_CC_TYPE_INVERT;
852 s++;
853 }
854
855 if (strcmp(s, "all") == 0) {
856 icc->type |= IRC_CC_TYPE_DEFAULT;
857 } else if (strcmp(s, "rest") == 0) {
858 icc->type |= IRC_CC_TYPE_REST;
859 } else if (strcmp(s, "group") == 0) {
860 icc->type |= IRC_CC_TYPE_GROUP;
861 } else if (strcmp(s, "account") == 0) {
862 icc->type |= IRC_CC_TYPE_ACCOUNT;
863 } else if (strcmp(s, "protocol") == 0) {
864 icc->type |= IRC_CC_TYPE_PROTOCOL;
865 } else {
866 return SET_INVALID;
867 }
868
869 bee_irc_channel_update(ic->irc, ic, NULL);
870 return value;
871 }
872
set_eval_by_group(set_t * set,char * value)873 static char *set_eval_by_group(set_t *set, char *value)
874 {
875 struct irc_channel *ic = set->data;
876 struct irc_control_channel *icc = ic->data;
877
878 icc->group = bee_group_by_name(ic->irc->b, value, TRUE);
879 if ((icc->type & IRC_CC_TYPE_MASK) == IRC_CC_TYPE_GROUP) {
880 bee_irc_channel_update(ic->irc, ic, NULL);
881 }
882
883 return g_strdup(icc->group->name);
884 }
885
set_eval_by_protocol(set_t * set,char * value)886 static char *set_eval_by_protocol(set_t *set, char *value)
887 {
888 struct irc_channel *ic = set->data;
889 struct irc_control_channel *icc = ic->data;
890 struct prpl *prpl;
891
892 if (!(prpl = find_protocol(value))) {
893 return SET_INVALID;
894 }
895
896 icc->protocol = prpl;
897 if ((icc->type & IRC_CC_TYPE_MASK) == IRC_CC_TYPE_PROTOCOL) {
898 bee_irc_channel_update(ic->irc, ic, NULL);
899 }
900
901 return value;
902 }
903
set_eval_show_users(set_t * set,char * value)904 static char *set_eval_show_users(set_t *set, char *value)
905 {
906 struct irc_channel *ic = set->data;
907 struct irc_control_channel *icc = ic->data;
908 char **parts = g_strsplit(value, ",", 0), **part;
909 char modes[5];
910
911 memset(modes, 0, 5);
912 for (part = parts; *part; part++) {
913 char last, modechar = IRC_CHANNEL_USER_NONE;
914
915 if (**part == '\0') {
916 goto fail;
917 }
918
919 last = (*part)[strlen(*part + 1)];
920 if (last == '+') {
921 modechar = IRC_CHANNEL_USER_VOICE;
922 } else if (last == '%') {
923 modechar = IRC_CHANNEL_USER_HALFOP;
924 } else if (last == '@') {
925 modechar = IRC_CHANNEL_USER_OP;
926 }
927
928 if (strncmp(*part, "offline", 7) == 0) {
929 modes[0] = modechar;
930 } else if (strncmp(*part, "away", 4) == 0) {
931 modes[1] = modechar;
932 } else if (strncmp(*part, "special", 7) == 0) {
933 modes[2] = modechar;
934 } else if (strncmp(*part, "online", 6) == 0) {
935 modes[3] = modechar;
936 } else {
937 goto fail;
938 }
939 }
940 memcpy(icc->modes, modes, 5);
941 bee_irc_channel_update(ic->irc, ic, NULL);
942
943 g_strfreev(parts);
944 return value;
945
946 fail:
947 g_strfreev(parts);
948 return SET_INVALID;
949 }
950
951 /* Figure out if a channel is supposed to have the user, assuming s/he is
952 online or otherwise also selected by the show_users setting. Only works
953 for control channels, but does *not* check if this channel is of that
954 type. Beware! */
irc_channel_wants_user(irc_channel_t * ic,irc_user_t * iu)955 gboolean irc_channel_wants_user(irc_channel_t *ic, irc_user_t *iu)
956 {
957 struct irc_control_channel *icc = ic->data;
958 gboolean ret = FALSE;
959
960 if (iu->bu == NULL) {
961 return FALSE;
962 }
963
964 switch (icc->type & IRC_CC_TYPE_MASK) {
965 case IRC_CC_TYPE_GROUP:
966 ret = iu->bu->group == icc->group;
967 break;
968 case IRC_CC_TYPE_ACCOUNT:
969 ret = iu->bu->ic->acc == icc->account;
970 break;
971 case IRC_CC_TYPE_PROTOCOL:
972 ret = iu->bu->ic->acc->prpl == icc->protocol;
973 break;
974 case IRC_CC_TYPE_DEFAULT:
975 default:
976 ret = TRUE;
977 break;
978 }
979
980 if (icc->type & IRC_CC_TYPE_INVERT) {
981 ret = !ret;
982 }
983
984 return ret;
985 }
986
control_channel_free(irc_channel_t * ic)987 static gboolean control_channel_free(irc_channel_t *ic)
988 {
989 struct irc_control_channel *icc = ic->data;
990
991 set_del(&ic->set, "account");
992 set_del(&ic->set, "fill_by");
993 set_del(&ic->set, "group");
994 set_del(&ic->set, "protocol");
995 set_del(&ic->set, "show_users");
996
997 g_free(icc);
998 ic->data = NULL;
999
1000 /* For scripts that care. */
1001 irc_channel_set_mode(ic, "-C");
1002
1003 return TRUE;
1004 }
1005
1006 static const struct irc_channel_funcs control_channel_funcs = {
1007 control_channel_privmsg,
1008 control_channel_join,
1009 NULL,
1010 NULL,
1011 control_channel_invite,
1012 control_channel_kick,
1013
1014 control_channel_init,
1015 control_channel_free,
1016 };
1017