source: irc_channel.c @ 7a9d968

Last change on this file since 7a9d968 was 7a9d968, checked in by Wilmer van der Gaast <wilmer@…>, at 2018-03-10T11:30:39Z

Merge branch 'master' into HEAD

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