source: irc_channel.c @ af09afd

Last change on this file since af09afd was 9767d03, checked in by dequis <dx@…>, at 2018-07-31T04:41:55Z

Modify server-time implementation to not change API

The new functions with the different paramters are flagged as internal,
so it doesn't even add new symbols.

abi-compliance checker says it's 100% compatible, yay.

  • Property mode set to 100644
File size: 23.1 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;
[5c90890]123        GHashTableIter iter;
124        gpointer itervalue;
[5ebff60]125
126        if (ic == NULL) {
[7d4ffc2]127                return 0;
[5ebff60]128        }
[7d4ffc2]129        irc = ic->irc;
[5ebff60]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
[5c90890]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;
[5ebff60]153
154                if (iu->last_channel == ic) {
[2fe5eb9]155                        iu->last_channel = irc->default_channel;
[5ebff60]156                }
157        }
158
159        if (ic->pastebuf_timer) {
160                b_event_remove(ic->pastebuf_timer);
[2fe5eb9]161        }
[5ebff60]162
163        g_free(ic->name);
164        g_free(ic->topic);
165        g_free(ic->topic_who);
166        g_free(ic);
167
[63a520b]168        return 1;
169}
170
[5ebff60]171struct irc_channel_free_data {
[ab6006c]172        irc_t *irc;
173        irc_channel_t *ic;
174        char *name;
175};
176
[5ebff60]177static gboolean irc_channel_free_callback(gpointer data, gint fd, b_input_condition cond)
[ab6006c]178{
179        struct irc_channel_free_data *d = data;
[5ebff60]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);
[ab6006c]189        return FALSE;
190}
191
192/* Free the channel, but via the event loop, so after finishing whatever event
193   we're currently handling. */
[5ebff60]194void irc_channel_free_soon(irc_channel_t *ic)
[ab6006c]195{
[5ebff60]196        struct irc_channel_free_data *d = g_new0(struct irc_channel_free_data, 1);
197
[ab6006c]198        d->irc = ic->irc;
199        d->ic = ic;
[5ebff60]200        d->name = g_strdup(ic->name);
201
202        b_timeout_add(0, irc_channel_free_callback, d);
[ab6006c]203}
204
[5ebff60]205static char *set_eval_channel_type(set_t *set, char *value)
[2b8473c]206{
207        struct irc_channel *ic = set->data;
208        const struct irc_channel_funcs *new;
[5ebff60]209
210        if (strcmp(value, "control") == 0) {
[2b8473c]211                new = &control_channel_funcs;
[5ebff60]212        } else if (ic != ic->irc->default_channel && strcmp(value, "chat") == 0) {
[5a75d15]213                new = &irc_channel_im_chat_funcs;
[5ebff60]214        } else {
[2b8473c]215                return SET_INVALID;
[5ebff60]216        }
217
[25f6151]218        /* Skip the free/init if nothing is being changed */
219        if (ic->f == new) {
220                return value;
221        }
222
[2b8473c]223        /* TODO: Return values. */
[5ebff60]224        if (ic->f && ic->f->_free) {
225                ic->f->_free(ic);
226        }
227
[2b8473c]228        ic->f = new;
[5ebff60]229
230        if (ic->f && ic->f->_init) {
231                ic->f->_init(ic);
232        }
233
[2b8473c]234        return value;
235}
236
[5ebff60]237int irc_channel_add_user(irc_channel_t *ic, irc_user_t *iu)
[4be8239]238{
[e54112f]239        irc_channel_user_t *icu;
[5ebff60]240
241        if (irc_channel_has_user(ic, iu)) {
[4be8239]242                return 0;
[5ebff60]243        }
244
245        icu = g_new0(irc_channel_user_t, 1);
[e54112f]246        icu->iu = iu;
[5ebff60]247
248        ic->users = g_slist_insert_sorted(ic->users, icu, irc_channel_user_cmp);
249
[3c3b916]250        if (iu == ic->irc->user || iu == ic->irc->root) {
251                irc_channel_update_ops(ic, set_getstr(&ic->irc->b->set, "ops"));
252        }
[5ebff60]253
254        if (iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED) {
[4be8239]255                ic->flags |= IRC_CHANNEL_JOINED;
[5ebff60]256                irc_send_join(ic, iu);
[4be8239]257        }
[5ebff60]258
[4be8239]259        return 1;
260}
261
[5ebff60]262int irc_channel_del_user(irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg)
[4be8239]263{
[e54112f]264        irc_channel_user_t *icu;
[5ebff60]265
266        if (!(icu = irc_channel_has_user(ic, iu))) {
[d088ee8]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                }
[4be8239]271                return 0;
[5ebff60]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) {
[4be8239]288                ic->flags &= ~IRC_CHANNEL_JOINED;
[5ebff60]289
290                if (ic->irc->status & USTATUS_SHUTDOWN) {
[06f9548]291                        /* Don't do anything fancy when we're shutting down anyway. */
[5ebff60]292                } else if (ic->flags & IRC_CHANNEL_TEMP) {
293                        irc_channel_free_soon(ic);
294                } else {
[9052bc1]295                        /* Flush userlist now. The user won't see it anyway. */
[5ebff60]296                        while (ic->users) {
297                                g_free(ic->users->data);
298                                ic->users = g_slist_remove(ic->users, ic->users->data);
[9052bc1]299                        }
[5ebff60]300                        irc_channel_add_user(ic, ic->irc->root);
[9052bc1]301                }
[1c40aa7]302        }
[5ebff60]303
[4be8239]304        return 1;
305}
306
[5ebff60]307irc_channel_user_t *irc_channel_has_user(irc_channel_t *ic, irc_user_t *iu)
[0b5cc72]308{
[e54112f]309        GSList *l;
[5ebff60]310
311        for (l = ic->users; l; l = l->next) {
[e54112f]312                irc_channel_user_t *icu = l->data;
[5ebff60]313
314                if (icu->iu == iu) {
[e54112f]315                        return icu;
[5ebff60]316                }
[e54112f]317        }
[5ebff60]318
[e54112f]319        return NULL;
[0b5cc72]320}
321
[bb151f7]322/* Find a channel we're currently in, that currently has iu in it. */
[5ebff60]323struct irc_channel *irc_channel_with_user(irc_t *irc, irc_user_t *iu)
[bb151f7]324{
325        GSList *l;
[5ebff60]326
327        for (l = irc->channels; l; l = l->next) {
[bb151f7]328                irc_channel_t *ic = l->data;
[5ebff60]329
330                if (strcmp(set_getstr(&ic->set, "type"), "control") != 0) {
[bb151f7]331                        continue;
[5ebff60]332                }
333
334                if ((ic->flags & IRC_CHANNEL_JOINED) &&
335                    irc_channel_has_user(ic, iu)) {
[bb151f7]336                        return ic;
[5ebff60]337                }
[bb151f7]338        }
[5ebff60]339
[bb151f7]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. */
[5ebff60]342        if (iu->bu == NULL) {
[bb151f7]343                return NULL;
[5ebff60]344        }
345
346        for (l = irc->channels; l; l = l->next) {
[bb151f7]347                irc_channel_t *ic = l->data;
[5ebff60]348
349                if (strcmp(set_getstr(&ic->set, "type"), "control") != 0) {
[bb151f7]350                        continue;
[5ebff60]351                }
352
353                if ((ic->flags & IRC_CHANNEL_JOINED) &&
354                    irc_channel_wants_user(ic, iu)) {
[bb151f7]355                        return ic;
[5ebff60]356                }
[bb151f7]357        }
[5ebff60]358
[bb151f7]359        return NULL;
360}
361
[5ebff60]362int irc_channel_set_topic(irc_channel_t *ic, const char *topic, const irc_user_t *iu)
[4be8239]363{
[5ebff60]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 {
[83e92bf]371                ic->topic_who = NULL;
[5ebff60]372        }
373
374        ic->topic_time = time(NULL);
375
376        if (ic->flags & IRC_CHANNEL_JOINED) {
377                irc_send_topic(ic, TRUE);
378        }
379
[4be8239]380        return 1;
381}
[b919363]382
[5ebff60]383void irc_channel_user_set_mode(irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags)
[6a9d068]384{
[5ebff60]385        irc_channel_user_t *icu = irc_channel_has_user(ic, iu);
386
387        if (!icu || icu->flags == flags) {
[6a9d068]388                return;
[5ebff60]389        }
390
391        if (ic->flags & IRC_CHANNEL_JOINED) {
392                irc_send_channel_user_mode_diff(ic, iu, icu->flags, flags);
393        }
394
[6a9d068]395        icu->flags = flags;
396}
397
[5ebff60]398void irc_channel_set_mode(irc_channel_t *ic, const char *s)
[65016a6]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;
[5ebff60]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
[65016a6]414        p = changes;
[5ebff60]415        for (t = s; *t; t++) {
416                if (*t == '+' || *t == '-') {
[65016a6]417                        st = *t == '+';
[5ebff60]418                } else if (strchr(CMODES, *t)) {
419                        if (m[(int) *t] != st) {
420                                if (st != st2) {
[65016a6]421                                        st2 = st, *p++ = st ? '+' : '-';
[5ebff60]422                                }
[65016a6]423                                *p++ = *t;
424                        }
[5ebff60]425                        m[(int) *t] = st;
[65016a6]426                }
427        }
428        *p = '\0';
[5ebff60]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]) {
[65016a6]434                        ic->mode[strlen(ic->mode)] = i;
[5ebff60]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        }
[65016a6]443}
444
[687ec88]445char 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
[5ebff60]457void irc_channel_auto_joins(irc_t *irc, account_t *acc)
[c8eeadd]458{
459        GSList *l;
[5ebff60]460
461        for (l = irc->channels; l; l = l->next) {
[c8eeadd]462                irc_channel_t *ic = l->data;
[5ebff60]463                gboolean aj = set_getbool(&ic->set, "auto_join");
[c8eeadd]464                char *type;
[5ebff60]465
466                if (acc &&
467                    (type = set_getstr(&ic->set, "chat_type")) &&
468                    strcmp(type, "room") == 0) {
[c8eeadd]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;
[5ebff60]472
[24de9fa]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.) */
[c8eeadd]477                                continue;
[5ebff60]478                        } else if (!(acc_s = set_getstr(&ic->set, "account"))) {
[c8eeadd]479                                continue;
[5ebff60]480                        } else if (account_get(irc->b, acc_s) != acc) {
[c8eeadd]481                                continue;
[5ebff60]482                        } else if (acc->ic == NULL || !(acc->ic->flags & OPT_LOGGED_IN)) {
[c8eeadd]483                                continue;
[5ebff60]484                        } else {
485                                ic->f->join(ic);
486                        }
487                } else if (aj) {
488                        irc_channel_add_user(ic, irc->user);
[c8eeadd]489                }
490        }
491}
492
[5ebff60]493void irc_channel_printf(irc_channel_t *ic, char *format, ...)
[9893da3]494{
495        va_list params;
496        char *text;
[5ebff60]497
498        va_start(params, format);
499        text = g_strdup_vprintf(format, params);
500        va_end(params);
501
[9767d03]502        irc_send_msg(ic->irc->root, "PRIVMSG", ic->name, text, NULL);
[5ebff60]503        g_free(text);
[9893da3]504}
505
[5ebff60]506gboolean irc_channel_name_ok(const char *name_)
[b919363]507{
[5ebff60]508        const unsigned char *name = (unsigned char *) name_;
[6b90431]509        int i;
[5ebff60]510
511        if (name_[0] == '\0') {
[a670aeb]512                return FALSE;
[5ebff60]513        }
514
[4c17d19]515        /* Check if the first character is in CTYPES (#&) */
[5ebff60]516        if (strchr(CTYPES, name_[0]) == NULL) {
[4c17d19]517                return FALSE;
[5ebff60]518        }
519
[6b90431]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. */
[5ebff60]524        for (i = 1; name[i]; i++) {
525                if (name[i] <= ' ' || name[i] == ',') {
[6b90431]526                        return FALSE;
[5ebff60]527                }
528        }
529
[6b90431]530        return TRUE;
531}
532
[5ebff60]533void irc_channel_name_strip(char *name)
[134a02c]534{
535        int i, j;
[5ebff60]536
537        for (i = j = 0; name[i]; i++) {
538                if (name[i] > ' ' && name[i] != ',') {
[134a02c]539                        name[j++] = name[i];
[5ebff60]540                }
541        }
542
[134a02c]543        name[j] = '\0';
544}
545
[5ebff60]546int irc_channel_name_cmp(const char *a_, const char *b_)
[6b90431]547{
548        static unsigned char case_map[256];
[5ebff60]549        const unsigned char *a = (unsigned char *) a_, *b = (unsigned char *) b_;
[6b90431]550        int i;
[5ebff60]551
552        if (case_map['A'] == '\0') {
553                for (i = 33; i < 256; i++) {
554                        if (i != ',') {
[6b90431]555                                case_map[i] = i;
[5ebff60]556                        }
557                }
558
559                for (i = 0; i < 26; i++) {
560                        case_map['A' + i] = 'a' + i;
561                }
562
[6b90431]563                case_map['['] = '{';
564                case_map[']'] = '}';
565                case_map['~'] = '`';
566                case_map['\\'] = '|';
567        }
[5ebff60]568
569        if (!irc_channel_name_ok(a_) || !irc_channel_name_ok(b_)) {
[6b90431]570                return -1;
[5ebff60]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]]) {
[6b90431]575                        continue;
[5ebff60]576                } else {
[6b90431]577                        return case_map[a[i]] - case_map[b[i]];
[5ebff60]578                }
[6b90431]579        }
[5ebff60]580
[6b90431]581        return case_map[a[i]] - case_map[b[i]];
[b919363]582}
[280c56a]583
[a9b1e0e]584gboolean irc_channel_is_unused(irc_t *irc, char *name)
[e3e2059]585{
586        char *type, *chat_type;
587        irc_channel_t *oic;
588
589        if (!irc_channel_name_ok(name)) {
590                return FALSE;
591        }
592
[a9b1e0e]593        if (!(oic = irc_channel_by_name(irc, name))) {
[e3e2059]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
[a9b1e0e]614char *irc_channel_name_gen(irc_t *irc, const char *hint)
[e3e2059]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);
[b6a3fbf]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
[e3e2059]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
[90254d0]643        if (set_getbool(&irc->b->set, "nick_lowercase")) {
[a9b1e0e]644                nick_lc(irc, name + 1);
[e3e2059]645        }
646
[a9b1e0e]647        while (!irc_channel_is_unused(irc, name)) {
[e3e2059]648                underscore_dedupe(name);
649        }
650
651        return g_strdup(name);
652}
653
654gboolean 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
[a9b1e0e]664        if (!(full_name = irc_channel_name_gen(irc, name))) {
[e3e2059]665                return FALSE;
666        }
667
668        g_free(ic->name);
669        ic->name = full_name;
670
671        return TRUE;
672}
673
[5ebff60]674static gint irc_channel_user_cmp(gconstpointer a_, gconstpointer b_)
[e54112f]675{
676        const irc_channel_user_t *a = a_, *b = b_;
[5ebff60]677
678        return irc_user_cmp(a->iu, b->iu);
[e54112f]679}
680
[5ebff60]681void irc_channel_update_ops(irc_channel_t *ic, char *value)
[c5aefa4]682{
[5ebff60]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);
[c5aefa4]689}
690
[5ebff60]691char *set_eval_irc_channel_ops(set_t *set, char *value)
[c5aefa4]692{
693        irc_t *irc = set->data;
694        GSList *l;
[5ebff60]695
696        if (strcmp(value, "both") != 0 && strcmp(value, "none") != 0 &&
697            strcmp(value, "user") != 0 && strcmp(value, "root") != 0) {
[c5aefa4]698                return SET_INVALID;
[5ebff60]699        }
700
701        for (l = irc->channels; l; l = l->next) {
702                irc_channel_update_ops(l->data, value);
703        }
704
[c5aefa4]705        return value;
706}
707
[280c56a]708/* Channel-type dependent functions, for control channels: */
[5ebff60]709static gboolean control_channel_privmsg(irc_channel_t *ic, const char *msg)
[280c56a]710{
[bce78c8]711        irc_t *irc = ic->irc;
[f7ca587]712        irc_user_t *iu;
[bce78c8]713        const char *s;
[5ebff60]714
[bce78c8]715        /* Scan for non-whitespace chars followed by a colon: */
[5ebff60]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                }
[f7ca587]726                msg = s;
[5ebff60]727
728                if (!(iu = irc_user_by_name(irc, to))) {
729                        irc_channel_printf(ic, "User does not exist: %s", to);
730                } else {
[f7ca587]731                        ic->last_target = iu;
[5ebff60]732                }
733        } else if (g_strcasecmp(set_getstr(&irc->b->set, "default_target"), "last") == 0 &&
[5c90890]734                   ic->last_target && g_hash_table_contains(irc->nick_user_hash, ic->last_target->key)) {
[f7ca587]735                iu = ic->last_target;
[5ebff60]736        } else {
[f7ca587]737                iu = irc->root;
[5ebff60]738        }
739
740        if (iu && iu->f->privmsg) {
[f7ca587]741                iu->last_channel = ic;
[5ebff60]742                iu->f->privmsg(iu, msg);
[bce78c8]743        }
[5ebff60]744
[280c56a]745        return TRUE;
746}
747
[5ebff60]748static gboolean control_channel_invite(irc_channel_t *ic, irc_user_t *iu)
[46d215d]749{
750        struct irc_control_channel *icc = ic->data;
751        bee_user_t *bu = iu->bu;
[5ebff60]752
753        if (bu == NULL) {
[46d215d]754                return FALSE;
[5ebff60]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);
[46d215d]759                return FALSE;
760        }
[5ebff60]761
762        bu->ic->acc->prpl->add_buddy(bu->ic, bu->handle,
763                                     icc->group ? icc->group->name : NULL);
764
[46d215d]765        return TRUE;
766}
767
[5ebff60]768static void control_channel_kick(irc_channel_t *ic, irc_user_t *iu, const char *msg)
[7821ee8]769{
770        struct irc_control_channel *icc = ic->data;
771        bee_user_t *bu = iu->bu;
[5ebff60]772
773        if (bu == NULL) {
[7821ee8]774                return;
[5ebff60]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);
[7821ee8]779                return;
780        }
[5ebff60]781
782        bu->ic->acc->prpl->remove_buddy(bu->ic, bu->handle,
783                                        icc->group ? icc->group->name : NULL);
[7821ee8]784}
785
[5ebff60]786static char *set_eval_by_account(set_t *set, char *value);
787static char *set_eval_fill_by(set_t *set, char *value);
788static char *set_eval_by_group(set_t *set, char *value);
789static char *set_eval_by_protocol(set_t *set, char *value);
790static char *set_eval_show_users(set_t *set, char *value);
[2b8473c]791
[5ebff60]792static gboolean control_channel_init(irc_channel_t *ic)
[9ac3ed1]793{
794        struct irc_control_channel *icc;
[5ebff60]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
[5a61bf59]801        /* When changing the default, also change it below. */
[5ebff60]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);
[9ac3ed1]805        icc->type = IRC_CC_TYPE_DEFAULT;
[5ebff60]806
[94d5da9c]807        /* Have to run the evaluator to initialize icc->modes. */
[5ebff60]808        set_setstr(&ic->set, "show_users", "online+,special%,away");
809
[65016a6]810        /* For scripts that care. */
[5ebff60]811        irc_channel_set_mode(ic, "+C");
812
[2b8473c]813        return TRUE;
814}
815
[5ebff60]816static gboolean control_channel_join(irc_channel_t *ic)
[c8eeadd]817{
[5ebff60]818        bee_irc_channel_update(ic->irc, ic, NULL);
819
[c8eeadd]820        return TRUE;
821}
822
[5ebff60]823static char *set_eval_by_account(set_t *set, char *value)
[2b8473c]824{
825        struct irc_channel *ic = set->data;
826        struct irc_control_channel *icc = ic->data;
827        account_t *acc;
[5ebff60]828
829        if (!(acc = account_get(ic->irc->b, value))) {
[2b8473c]830                return SET_INVALID;
[5ebff60]831        }
832
[2b8473c]833        icc->account = acc;
[5ebff60]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);
[2b8473c]839}
840
[5ebff60]841static char *set_eval_fill_by(set_t *set, char *value)
[2b8473c]842{
843        struct irc_channel *ic = set->data;
844        struct irc_control_channel *icc = ic->data;
[a5c6ebd]845        char *s;
[5ebff60]846
847        icc->type &= ~(IRC_CC_TYPE_MASK | IRC_CC_TYPE_INVERT);
848
[a5c6ebd]849        s = value;
[5ebff60]850        if (s[0] == '!') {
[a5c6ebd]851                icc->type |= IRC_CC_TYPE_INVERT;
[5ebff60]852                s++;
[a5c6ebd]853        }
[5ebff60]854
855        if (strcmp(s, "all") == 0) {
[a5c6ebd]856                icc->type |= IRC_CC_TYPE_DEFAULT;
[5ebff60]857        } else if (strcmp(s, "rest") == 0) {
[a5c6ebd]858                icc->type |= IRC_CC_TYPE_REST;
[5ebff60]859        } else if (strcmp(s, "group") == 0) {
[a5c6ebd]860                icc->type |= IRC_CC_TYPE_GROUP;
[5ebff60]861        } else if (strcmp(s, "account") == 0) {
[a5c6ebd]862                icc->type |= IRC_CC_TYPE_ACCOUNT;
[5ebff60]863        } else if (strcmp(s, "protocol") == 0) {
[a5c6ebd]864                icc->type |= IRC_CC_TYPE_PROTOCOL;
[5ebff60]865        } else {
[2b8473c]866                return SET_INVALID;
[5ebff60]867        }
868
869        bee_irc_channel_update(ic->irc, ic, NULL);
[2b8473c]870        return value;
871}
872
[5ebff60]873static char *set_eval_by_group(set_t *set, char *value)
[2b8473c]874{
875        struct irc_channel *ic = set->data;
876        struct irc_control_channel *icc = ic->data;
[5ebff60]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);
[2b8473c]884}
885
[5ebff60]886static char *set_eval_by_protocol(set_t *set, char *value)
[7a6ba50]887{
888        struct irc_channel *ic = set->data;
889        struct irc_control_channel *icc = ic->data;
890        struct prpl *prpl;
[5ebff60]891
892        if (!(prpl = find_protocol(value))) {
[7a6ba50]893                return SET_INVALID;
[5ebff60]894        }
895
[7a6ba50]896        icc->protocol = prpl;
[5ebff60]897        if ((icc->type & IRC_CC_TYPE_MASK) == IRC_CC_TYPE_PROTOCOL) {
898                bee_irc_channel_update(ic->irc, ic, NULL);
899        }
900
[7a6ba50]901        return value;
902}
903
[5ebff60]904static char *set_eval_show_users(set_t *set, char *value)
[94d5da9c]905{
906        struct irc_channel *ic = set->data;
907        struct irc_control_channel *icc = ic->data;
[5ebff60]908        char **parts = g_strsplit(value, ",", 0), **part;
[7b8238d]909        char modes[5];
[5ebff60]910
911        memset(modes, 0, 5);
912        for (part = parts; *part; part++) {
[94d5da9c]913                char last, modechar = IRC_CHANNEL_USER_NONE;
[5ebff60]914
915                if (**part == '\0') {
[94d5da9c]916                        goto fail;
[5ebff60]917                }
918
919                last = (*part)[strlen(*part + 1)];
920                if (last == '+') {
[94d5da9c]921                        modechar = IRC_CHANNEL_USER_VOICE;
[5ebff60]922                } else if (last == '%') {
[94d5da9c]923                        modechar = IRC_CHANNEL_USER_HALFOP;
[5ebff60]924                } else if (last == '@') {
[94d5da9c]925                        modechar = IRC_CHANNEL_USER_OP;
[5ebff60]926                }
927
928                if (strncmp(*part, "offline", 7) == 0) {
[94d5da9c]929                        modes[0] = modechar;
[5ebff60]930                } else if (strncmp(*part, "away", 4) == 0) {
[94d5da9c]931                        modes[1] = modechar;
[5ebff60]932                } else if (strncmp(*part, "special", 7) == 0) {
[94d5da9c]933                        modes[2] = modechar;
[5ebff60]934                } else if (strncmp(*part, "online", 6) == 0) {
[7b8238d]935                        modes[3] = modechar;
[5ebff60]936                } else {
[94d5da9c]937                        goto fail;
[5ebff60]938                }
[94d5da9c]939        }
[5ebff60]940        memcpy(icc->modes, modes, 5);
941        bee_irc_channel_update(ic->irc, ic, NULL);
942
943        g_strfreev(parts);
[94d5da9c]944        return value;
[5ebff60]945
[94d5da9c]946fail:
[5ebff60]947        g_strfreev(parts);
948        return SET_INVALID;
[94d5da9c]949}
950
[ac2717b]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! */
[5ebff60]955gboolean irc_channel_wants_user(irc_channel_t *ic, irc_user_t *iu)
[ac2717b]956{
957        struct irc_control_channel *icc = ic->data;
[a5c6ebd]958        gboolean ret = FALSE;
[5ebff60]959
960        if (iu->bu == NULL) {
[ac2717b]961                return FALSE;
[5ebff60]962        }
963
964        switch (icc->type & IRC_CC_TYPE_MASK) {
[ac2717b]965        case IRC_CC_TYPE_GROUP:
[a5c6ebd]966                ret = iu->bu->group == icc->group;
967                break;
[ac2717b]968        case IRC_CC_TYPE_ACCOUNT:
[a5c6ebd]969                ret = iu->bu->ic->acc == icc->account;
970                break;
[ac2717b]971        case IRC_CC_TYPE_PROTOCOL:
[a5c6ebd]972                ret = iu->bu->ic->acc->prpl == icc->protocol;
973                break;
[ac2717b]974        case IRC_CC_TYPE_DEFAULT:
975        default:
[a5c6ebd]976                ret = TRUE;
977                break;
[ac2717b]978        }
[5ebff60]979
980        if (icc->type & IRC_CC_TYPE_INVERT) {
[a5c6ebd]981                ret = !ret;
[5ebff60]982        }
983
[a5c6ebd]984        return ret;
[ac2717b]985}
986
[5ebff60]987static gboolean control_channel_free(irc_channel_t *ic)
[2b8473c]988{
989        struct irc_control_channel *icc = ic->data;
[5ebff60]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);
[2b8473c]998        ic->data = NULL;
[5ebff60]999
[65016a6]1000        /* For scripts that care. */
[5ebff60]1001        irc_channel_set_mode(ic, "-C");
1002
[9ac3ed1]1003        return TRUE;
1004}
1005
[280c56a]1006static const struct irc_channel_funcs control_channel_funcs = {
1007        control_channel_privmsg,
[c8eeadd]1008        control_channel_join,
[9ac3ed1]1009        NULL,
1010        NULL,
[46d215d]1011        control_channel_invite,
[7821ee8]1012        control_channel_kick,
[5ebff60]1013
[9ac3ed1]1014        control_channel_init,
[2b8473c]1015        control_channel_free,
[280c56a]1016};
Note: See TracBrowser for help on using the repository browser.