source: irc_channel.c @ 552c8a5

Last change on this file since 552c8a5 was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

  • Property mode set to 100644
File size: 20.4 KB
Line 
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
28static char *set_eval_channel_type(set_t *set, char *value);
29static gint irc_channel_user_cmp(gconstpointer a_, gconstpointer b_);
30static const struct irc_channel_funcs control_channel_funcs;
31
32extern const struct irc_channel_funcs irc_channel_im_chat_funcs;
33
34irc_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
66irc_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
81irc_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
120int irc_channel_free(irc_channel_t *ic)
121{
122        irc_t *irc;
123        GSList *l;
124
125        if (ic == NULL) {
126                return 0;
127        }
128        irc = ic->irc;
129
130        if (ic->flags & IRC_CHANNEL_JOINED) {
131                irc_channel_del_user(ic, irc->user, IRC_CDU_KICK, "Cleaning up channel");
132        }
133
134        if (ic->f->_free) {
135                ic->f->_free(ic);
136        }
137
138        while (ic->set) {
139                set_del(&ic->set, ic->set->key);
140        }
141
142        irc->channels = g_slist_remove(irc->channels, ic);
143        while (ic->users) {
144                g_free(ic->users->data);
145                ic->users = g_slist_remove(ic->users, ic->users->data);
146        }
147
148        for (l = irc->users; l; l = l->next) {
149                irc_user_t *iu = l->data;
150
151                if (iu->last_channel == ic) {
152                        iu->last_channel = irc->default_channel;
153                }
154        }
155
156        if (ic->pastebuf_timer) {
157                b_event_remove(ic->pastebuf_timer);
158        }
159
160        g_free(ic->name);
161        g_free(ic->topic);
162        g_free(ic->topic_who);
163        g_free(ic);
164
165        return 1;
166}
167
168struct irc_channel_free_data {
169        irc_t *irc;
170        irc_channel_t *ic;
171        char *name;
172};
173
174static gboolean irc_channel_free_callback(gpointer data, gint fd, b_input_condition cond)
175{
176        struct irc_channel_free_data *d = data;
177
178        if (g_slist_find(irc_connection_list, d->irc) &&
179            irc_channel_by_name(d->irc, d->name) == d->ic &&
180            !(d->ic->flags & IRC_CHANNEL_JOINED)) {
181                irc_channel_free(d->ic);
182        }
183
184        g_free(d->name);
185        g_free(d);
186        return FALSE;
187}
188
189/* Free the channel, but via the event loop, so after finishing whatever event
190   we're currently handling. */
191void irc_channel_free_soon(irc_channel_t *ic)
192{
193        struct irc_channel_free_data *d = g_new0(struct irc_channel_free_data, 1);
194
195        d->irc = ic->irc;
196        d->ic = ic;
197        d->name = g_strdup(ic->name);
198
199        b_timeout_add(0, irc_channel_free_callback, d);
200}
201
202static char *set_eval_channel_type(set_t *set, char *value)
203{
204        struct irc_channel *ic = set->data;
205        const struct irc_channel_funcs *new;
206
207        if (strcmp(value, "control") == 0) {
208                new = &control_channel_funcs;
209        } else if (ic != ic->irc->default_channel && strcmp(value, "chat") == 0) {
210                new = &irc_channel_im_chat_funcs;
211        } else {
212                return SET_INVALID;
213        }
214
215        /* TODO: Return values. */
216        if (ic->f && ic->f->_free) {
217                ic->f->_free(ic);
218        }
219
220        ic->f = new;
221
222        if (ic->f && ic->f->_init) {
223                ic->f->_init(ic);
224        }
225
226        return value;
227}
228
229int irc_channel_add_user(irc_channel_t *ic, irc_user_t *iu)
230{
231        irc_channel_user_t *icu;
232
233        if (irc_channel_has_user(ic, iu)) {
234                return 0;
235        }
236
237        icu = g_new0(irc_channel_user_t, 1);
238        icu->iu = iu;
239
240        ic->users = g_slist_insert_sorted(ic->users, icu, irc_channel_user_cmp);
241
242        irc_channel_update_ops(ic, set_getstr(&ic->irc->b->set, "ops"));
243
244        if (iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED) {
245                ic->flags |= IRC_CHANNEL_JOINED;
246                irc_send_join(ic, iu);
247        }
248
249        return 1;
250}
251
252int irc_channel_del_user(irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg)
253{
254        irc_channel_user_t *icu;
255
256        if (!(icu = irc_channel_has_user(ic, iu))) {
257                return 0;
258        }
259
260        ic->users = g_slist_remove(ic->users, icu);
261        g_free(icu);
262
263        if (!(ic->flags & IRC_CHANNEL_JOINED) || type == IRC_CDU_SILENT) {
264        }
265        /* Do nothing. The caller should promise it won't screw
266           up state of the IRC client. :-) */
267        else if (type == IRC_CDU_PART) {
268                irc_send_part(ic, iu, msg);
269        } else if (type == IRC_CDU_KICK) {
270                irc_send_kick(ic, iu, ic->irc->root, msg);
271        }
272
273        if (iu == ic->irc->user) {
274                ic->flags &= ~IRC_CHANNEL_JOINED;
275
276                if (ic->irc->status & USTATUS_SHUTDOWN) {
277                        /* Don't do anything fancy when we're shutting down anyway. */
278                } else if (ic->flags & IRC_CHANNEL_TEMP) {
279                        irc_channel_free_soon(ic);
280                } else {
281                        /* Flush userlist now. The user won't see it anyway. */
282                        while (ic->users) {
283                                g_free(ic->users->data);
284                                ic->users = g_slist_remove(ic->users, ic->users->data);
285                        }
286                        irc_channel_add_user(ic, ic->irc->root);
287                }
288        }
289
290        return 1;
291}
292
293irc_channel_user_t *irc_channel_has_user(irc_channel_t *ic, irc_user_t *iu)
294{
295        GSList *l;
296
297        for (l = ic->users; l; l = l->next) {
298                irc_channel_user_t *icu = l->data;
299
300                if (icu->iu == iu) {
301                        return icu;
302                }
303        }
304
305        return NULL;
306}
307
308/* Find a channel we're currently in, that currently has iu in it. */
309struct irc_channel *irc_channel_with_user(irc_t *irc, irc_user_t *iu)
310{
311        GSList *l;
312
313        for (l = irc->channels; l; l = l->next) {
314                irc_channel_t *ic = l->data;
315
316                if (strcmp(set_getstr(&ic->set, "type"), "control") != 0) {
317                        continue;
318                }
319
320                if ((ic->flags & IRC_CHANNEL_JOINED) &&
321                    irc_channel_has_user(ic, iu)) {
322                        return ic;
323                }
324        }
325
326        /* If there was no match, try once more but just see if the user
327           *would* be in the channel, i.e. if s/he were online. */
328        if (iu->bu == NULL) {
329                return NULL;
330        }
331
332        for (l = irc->channels; l; l = l->next) {
333                irc_channel_t *ic = l->data;
334
335                if (strcmp(set_getstr(&ic->set, "type"), "control") != 0) {
336                        continue;
337                }
338
339                if ((ic->flags & IRC_CHANNEL_JOINED) &&
340                    irc_channel_wants_user(ic, iu)) {
341                        return ic;
342                }
343        }
344
345        return NULL;
346}
347
348int irc_channel_set_topic(irc_channel_t *ic, const char *topic, const irc_user_t *iu)
349{
350        g_free(ic->topic);
351        ic->topic = g_strdup(topic);
352
353        g_free(ic->topic_who);
354        if (iu) {
355                ic->topic_who = g_strdup_printf("%s!%s@%s", iu->nick, iu->user, iu->host);
356        } else {
357                ic->topic_who = NULL;
358        }
359
360        ic->topic_time = time(NULL);
361
362        if (ic->flags & IRC_CHANNEL_JOINED) {
363                irc_send_topic(ic, TRUE);
364        }
365
366        return 1;
367}
368
369void irc_channel_user_set_mode(irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags)
370{
371        irc_channel_user_t *icu = irc_channel_has_user(ic, iu);
372
373        if (!icu || icu->flags == flags) {
374                return;
375        }
376
377        if (ic->flags & IRC_CHANNEL_JOINED) {
378                irc_send_channel_user_mode_diff(ic, iu, icu->flags, flags);
379        }
380
381        icu->flags = flags;
382}
383
384void irc_channel_set_mode(irc_channel_t *ic, const char *s)
385{
386        irc_t *irc = ic->irc;
387        char m[128], st = 1;
388        const char *t;
389        int i;
390        char changes[512], *p, st2 = 2;
391
392        memset(m, 0, sizeof(m));
393
394        for (t = ic->mode; *t; t++) {
395                if (*t < sizeof(m)) {
396                        m[(int) *t] = 1;
397                }
398        }
399
400        p = changes;
401        for (t = s; *t; t++) {
402                if (*t == '+' || *t == '-') {
403                        st = *t == '+';
404                } else if (strchr(CMODES, *t)) {
405                        if (m[(int) *t] != st) {
406                                if (st != st2) {
407                                        st2 = st, *p++ = st ? '+' : '-';
408                                }
409                                *p++ = *t;
410                        }
411                        m[(int) *t] = st;
412                }
413        }
414        *p = '\0';
415
416        memset(ic->mode, 0, sizeof(ic->mode));
417
418        for (i = 'A'; i <= 'z' && strlen(ic->mode) < (sizeof(ic->mode) - 1); i++) {
419                if (m[i]) {
420                        ic->mode[strlen(ic->mode)] = i;
421                }
422        }
423
424        if (*changes && (ic->flags & IRC_CHANNEL_JOINED)) {
425                irc_write(irc, ":%s!%s@%s MODE %s :%s", irc->root->nick,
426                          irc->root->user, irc->root->host, ic->name,
427                          changes);
428        }
429}
430
431void irc_channel_auto_joins(irc_t *irc, account_t *acc)
432{
433        GSList *l;
434
435        for (l = irc->channels; l; l = l->next) {
436                irc_channel_t *ic = l->data;
437                gboolean aj = set_getbool(&ic->set, "auto_join");
438                char *type;
439
440                if (acc &&
441                    (type = set_getstr(&ic->set, "chat_type")) &&
442                    strcmp(type, "room") == 0) {
443                        /* Bit of an ugly special case: Handle chatrooms here, we
444                           can only auto-join them if their account is online. */
445                        char *acc_s;
446
447                        if (!aj || (ic->flags & IRC_CHANNEL_JOINED)) {
448                                /* Only continue if this one's marked as auto_join
449                                   or if we're in it already. (Possible if the
450                                   client auto-rejoined it before identyfing.) */
451                                continue;
452                        } else if (!(acc_s = set_getstr(&ic->set, "account"))) {
453                                continue;
454                        } else if (account_get(irc->b, acc_s) != acc) {
455                                continue;
456                        } else if (acc->ic == NULL || !(acc->ic->flags & OPT_LOGGED_IN)) {
457                                continue;
458                        } else {
459                                ic->f->join(ic);
460                        }
461                } else if (aj) {
462                        irc_channel_add_user(ic, irc->user);
463                }
464        }
465}
466
467void irc_channel_printf(irc_channel_t *ic, char *format, ...)
468{
469        va_list params;
470        char *text;
471
472        va_start(params, format);
473        text = g_strdup_vprintf(format, params);
474        va_end(params);
475
476        irc_send_msg(ic->irc->root, "PRIVMSG", ic->name, text, NULL);
477        g_free(text);
478}
479
480gboolean irc_channel_name_ok(const char *name_)
481{
482        const unsigned char *name = (unsigned char *) name_;
483        int i;
484
485        if (name_[0] == '\0') {
486                return FALSE;
487        }
488
489        /* Check if the first character is in CTYPES (#&) */
490        if (strchr(CTYPES, name_[0]) == NULL) {
491                return FALSE;
492        }
493
494        /* RFC 1459 keeps amazing me: While only a "few" chars are allowed
495           in nicknames, channel names can be pretty much anything as long
496           as they start with # or &. I'll be a little bit more strict and
497           disallow all non-printable characters. */
498        for (i = 1; name[i]; i++) {
499                if (name[i] <= ' ' || name[i] == ',') {
500                        return FALSE;
501                }
502        }
503
504        return TRUE;
505}
506
507void irc_channel_name_strip(char *name)
508{
509        int i, j;
510
511        for (i = j = 0; name[i]; i++) {
512                if (name[i] > ' ' && name[i] != ',') {
513                        name[j++] = name[i];
514                }
515        }
516
517        name[j] = '\0';
518}
519
520int irc_channel_name_cmp(const char *a_, const char *b_)
521{
522        static unsigned char case_map[256];
523        const unsigned char *a = (unsigned char *) a_, *b = (unsigned char *) b_;
524        int i;
525
526        if (case_map['A'] == '\0') {
527                for (i = 33; i < 256; i++) {
528                        if (i != ',') {
529                                case_map[i] = i;
530                        }
531                }
532
533                for (i = 0; i < 26; i++) {
534                        case_map['A' + i] = 'a' + i;
535                }
536
537                case_map['['] = '{';
538                case_map[']'] = '}';
539                case_map['~'] = '`';
540                case_map['\\'] = '|';
541        }
542
543        if (!irc_channel_name_ok(a_) || !irc_channel_name_ok(b_)) {
544                return -1;
545        }
546
547        for (i = 0; a[i] && b[i] && case_map[a[i]] && case_map[b[i]]; i++) {
548                if (case_map[a[i]] == case_map[b[i]]) {
549                        continue;
550                } else {
551                        return case_map[a[i]] - case_map[b[i]];
552                }
553        }
554
555        return case_map[a[i]] - case_map[b[i]];
556}
557
558static gint irc_channel_user_cmp(gconstpointer a_, gconstpointer b_)
559{
560        const irc_channel_user_t *a = a_, *b = b_;
561
562        return irc_user_cmp(a->iu, b->iu);
563}
564
565void irc_channel_update_ops(irc_channel_t *ic, char *value)
566{
567        irc_channel_user_set_mode(ic, ic->irc->root,
568                                  (strcmp(value, "both") == 0 ||
569                                   strcmp(value, "root") == 0) ? IRC_CHANNEL_USER_OP : 0);
570        irc_channel_user_set_mode(ic, ic->irc->user,
571                                  (strcmp(value, "both") == 0 ||
572                                   strcmp(value, "user") == 0) ? IRC_CHANNEL_USER_OP : 0);
573}
574
575char *set_eval_irc_channel_ops(set_t *set, char *value)
576{
577        irc_t *irc = set->data;
578        GSList *l;
579
580        if (strcmp(value, "both") != 0 && strcmp(value, "none") != 0 &&
581            strcmp(value, "user") != 0 && strcmp(value, "root") != 0) {
582                return SET_INVALID;
583        }
584
585        for (l = irc->channels; l; l = l->next) {
586                irc_channel_update_ops(l->data, value);
587        }
588
589        return value;
590}
591
592/* Channel-type dependent functions, for control channels: */
593static gboolean control_channel_privmsg(irc_channel_t *ic, const char *msg)
594{
595        irc_t *irc = ic->irc;
596        irc_user_t *iu;
597        const char *s;
598
599        /* Scan for non-whitespace chars followed by a colon: */
600        for (s = msg; *s && !g_ascii_isspace(*s) && *s != ':' && *s != ','; s++) {
601        }
602
603        if (*s == ':' || *s == ',') {
604                char to[s - msg + 1];
605
606                memset(to, 0, sizeof(to));
607                strncpy(to, msg, s - msg);
608                while (*(++s) && g_ascii_isspace(*s)) {
609                }
610                msg = s;
611
612                if (!(iu = irc_user_by_name(irc, to))) {
613                        irc_channel_printf(ic, "User does not exist: %s", to);
614                } else {
615                        ic->last_target = iu;
616                }
617        } else if (g_strcasecmp(set_getstr(&irc->b->set, "default_target"), "last") == 0 &&
618                   ic->last_target && g_slist_find(irc->users, ic->last_target)) {
619                iu = ic->last_target;
620        } else {
621                iu = irc->root;
622        }
623
624        if (iu && iu->f->privmsg) {
625                iu->last_channel = ic;
626                iu->f->privmsg(iu, msg);
627        }
628
629        return TRUE;
630}
631
632static gboolean control_channel_invite(irc_channel_t *ic, irc_user_t *iu)
633{
634        struct irc_control_channel *icc = ic->data;
635        bee_user_t *bu = iu->bu;
636
637        if (bu == NULL) {
638                return FALSE;
639        }
640
641        if (icc->type != IRC_CC_TYPE_GROUP) {
642                irc_send_num(ic->irc, 482, "%s :Invitations are only possible to fill_by=group channels", ic->name);
643                return FALSE;
644        }
645
646        bu->ic->acc->prpl->add_buddy(bu->ic, bu->handle,
647                                     icc->group ? icc->group->name : NULL);
648
649        return TRUE;
650}
651
652static void control_channel_kick(irc_channel_t *ic, irc_user_t *iu, const char *msg)
653{
654        struct irc_control_channel *icc = ic->data;
655        bee_user_t *bu = iu->bu;
656
657        if (bu == NULL) {
658                return;
659        }
660
661        if (icc->type != IRC_CC_TYPE_GROUP) {
662                irc_send_num(ic->irc, 482, "%s :Kicks are only possible to fill_by=group channels", ic->name);
663                return;
664        }
665
666        bu->ic->acc->prpl->remove_buddy(bu->ic, bu->handle,
667                                        icc->group ? icc->group->name : NULL);
668}
669
670static char *set_eval_by_account(set_t *set, char *value);
671static char *set_eval_fill_by(set_t *set, char *value);
672static char *set_eval_by_group(set_t *set, char *value);
673static char *set_eval_by_protocol(set_t *set, char *value);
674static char *set_eval_show_users(set_t *set, char *value);
675
676static gboolean control_channel_init(irc_channel_t *ic)
677{
678        struct irc_control_channel *icc;
679
680        set_add(&ic->set, "account", NULL, set_eval_by_account, ic);
681        set_add(&ic->set, "fill_by", "all", set_eval_fill_by, ic);
682        set_add(&ic->set, "group", NULL, set_eval_by_group, ic);
683        set_add(&ic->set, "protocol", NULL, set_eval_by_protocol, ic);
684
685        /* When changing the default, also change it below. */
686        set_add(&ic->set, "show_users", "online+,special%,away", set_eval_show_users, ic);
687
688        ic->data = icc = g_new0(struct irc_control_channel, 1);
689        icc->type = IRC_CC_TYPE_DEFAULT;
690
691        /* Have to run the evaluator to initialize icc->modes. */
692        set_setstr(&ic->set, "show_users", "online+,special%,away");
693
694        /* For scripts that care. */
695        irc_channel_set_mode(ic, "+C");
696
697        return TRUE;
698}
699
700static gboolean control_channel_join(irc_channel_t *ic)
701{
702        bee_irc_channel_update(ic->irc, ic, NULL);
703
704        return TRUE;
705}
706
707static char *set_eval_by_account(set_t *set, char *value)
708{
709        struct irc_channel *ic = set->data;
710        struct irc_control_channel *icc = ic->data;
711        account_t *acc;
712
713        if (!(acc = account_get(ic->irc->b, value))) {
714                return SET_INVALID;
715        }
716
717        icc->account = acc;
718        if ((icc->type & IRC_CC_TYPE_MASK) == IRC_CC_TYPE_ACCOUNT) {
719                bee_irc_channel_update(ic->irc, ic, NULL);
720        }
721
722        return g_strdup(acc->tag);
723}
724
725static char *set_eval_fill_by(set_t *set, char *value)
726{
727        struct irc_channel *ic = set->data;
728        struct irc_control_channel *icc = ic->data;
729        char *s;
730
731        icc->type &= ~(IRC_CC_TYPE_MASK | IRC_CC_TYPE_INVERT);
732
733        s = value;
734        if (s[0] == '!') {
735                icc->type |= IRC_CC_TYPE_INVERT;
736                s++;
737        }
738
739        if (strcmp(s, "all") == 0) {
740                icc->type |= IRC_CC_TYPE_DEFAULT;
741        } else if (strcmp(s, "rest") == 0) {
742                icc->type |= IRC_CC_TYPE_REST;
743        } else if (strcmp(s, "group") == 0) {
744                icc->type |= IRC_CC_TYPE_GROUP;
745        } else if (strcmp(s, "account") == 0) {
746                icc->type |= IRC_CC_TYPE_ACCOUNT;
747        } else if (strcmp(s, "protocol") == 0) {
748                icc->type |= IRC_CC_TYPE_PROTOCOL;
749        } else {
750                return SET_INVALID;
751        }
752
753        bee_irc_channel_update(ic->irc, ic, NULL);
754        return value;
755}
756
757static char *set_eval_by_group(set_t *set, char *value)
758{
759        struct irc_channel *ic = set->data;
760        struct irc_control_channel *icc = ic->data;
761
762        icc->group = bee_group_by_name(ic->irc->b, value, TRUE);
763        if ((icc->type & IRC_CC_TYPE_MASK) == IRC_CC_TYPE_GROUP) {
764                bee_irc_channel_update(ic->irc, ic, NULL);
765        }
766
767        return g_strdup(icc->group->name);
768}
769
770static char *set_eval_by_protocol(set_t *set, char *value)
771{
772        struct irc_channel *ic = set->data;
773        struct irc_control_channel *icc = ic->data;
774        struct prpl *prpl;
775
776        if (!(prpl = find_protocol(value))) {
777                return SET_INVALID;
778        }
779
780        icc->protocol = prpl;
781        if ((icc->type & IRC_CC_TYPE_MASK) == IRC_CC_TYPE_PROTOCOL) {
782                bee_irc_channel_update(ic->irc, ic, NULL);
783        }
784
785        return value;
786}
787
788static char *set_eval_show_users(set_t *set, char *value)
789{
790        struct irc_channel *ic = set->data;
791        struct irc_control_channel *icc = ic->data;
792        char **parts = g_strsplit(value, ",", 0), **part;
793        char modes[5];
794
795        memset(modes, 0, 5);
796        for (part = parts; *part; part++) {
797                char last, modechar = IRC_CHANNEL_USER_NONE;
798
799                if (**part == '\0') {
800                        goto fail;
801                }
802
803                last = (*part)[strlen(*part + 1)];
804                if (last == '+') {
805                        modechar = IRC_CHANNEL_USER_VOICE;
806                } else if (last == '%') {
807                        modechar = IRC_CHANNEL_USER_HALFOP;
808                } else if (last == '@') {
809                        modechar = IRC_CHANNEL_USER_OP;
810                }
811
812                if (strncmp(*part, "offline", 7) == 0) {
813                        modes[0] = modechar;
814                } else if (strncmp(*part, "away", 4) == 0) {
815                        modes[1] = modechar;
816                } else if (strncmp(*part, "special", 7) == 0) {
817                        modes[2] = modechar;
818                } else if (strncmp(*part, "online", 6) == 0) {
819                        modes[3] = modechar;
820                } else {
821                        goto fail;
822                }
823        }
824        memcpy(icc->modes, modes, 5);
825        bee_irc_channel_update(ic->irc, ic, NULL);
826
827        g_strfreev(parts);
828        return value;
829
830fail:
831        g_strfreev(parts);
832        return SET_INVALID;
833}
834
835/* Figure out if a channel is supposed to have the user, assuming s/he is
836   online or otherwise also selected by the show_users setting. Only works
837   for control channels, but does *not* check if this channel is of that
838   type. Beware! */
839gboolean irc_channel_wants_user(irc_channel_t *ic, irc_user_t *iu)
840{
841        struct irc_control_channel *icc = ic->data;
842        gboolean ret = FALSE;
843
844        if (iu->bu == NULL) {
845                return FALSE;
846        }
847
848        switch (icc->type & IRC_CC_TYPE_MASK) {
849        case IRC_CC_TYPE_GROUP:
850                ret = iu->bu->group == icc->group;
851                break;
852        case IRC_CC_TYPE_ACCOUNT:
853                ret = iu->bu->ic->acc == icc->account;
854                break;
855        case IRC_CC_TYPE_PROTOCOL:
856                ret = iu->bu->ic->acc->prpl == icc->protocol;
857                break;
858        case IRC_CC_TYPE_DEFAULT:
859        default:
860                ret = TRUE;
861                break;
862        }
863
864        if (icc->type & IRC_CC_TYPE_INVERT) {
865                ret = !ret;
866        }
867
868        return ret;
869}
870
871static gboolean control_channel_free(irc_channel_t *ic)
872{
873        struct irc_control_channel *icc = ic->data;
874
875        set_del(&ic->set, "account");
876        set_del(&ic->set, "fill_by");
877        set_del(&ic->set, "group");
878        set_del(&ic->set, "protocol");
879        set_del(&ic->set, "show_users");
880
881        g_free(icc);
882        ic->data = NULL;
883
884        /* For scripts that care. */
885        irc_channel_set_mode(ic, "-C");
886
887        return TRUE;
888}
889
890static const struct irc_channel_funcs control_channel_funcs = {
891        control_channel_privmsg,
892        control_channel_join,
893        NULL,
894        NULL,
895        control_channel_invite,
896        control_channel_kick,
897
898        control_channel_init,
899        control_channel_free,
900};
Note: See TracBrowser for help on using the repository browser.