source: irc_im.c @ 4e0e590

Last change on this file since 4e0e590 was b1634a8, checked in by dequis <dx@…>, at 2016-03-20T03:58:05Z

Don't send 301 RPL_AWAY when talking to contacts through &bitlbee

Fixes trac ticket 865 ("Make bitlbee send "is away" and such status
messages via &bitlbee channel instead of separate query window"),
except that the solution is to not send them at all.

  • Property mode set to 100644
File size: 27.6 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Some glue to put the IRC and the IM stuff together.                  */
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#include "dcc.h"
28
29/* IM->IRC callbacks: Simple IM/buddy-related stuff. */
30
31static const struct irc_user_funcs irc_user_im_funcs;
32
33static void bee_irc_imc_connected(struct im_connection *ic)
34{
35        irc_t *irc = (irc_t *) ic->bee->ui_data;
36
37        irc_channel_auto_joins(irc, ic->acc);
38}
39
40static void bee_irc_imc_disconnected(struct im_connection *ic)
41{
42        /* Maybe try to send /QUITs here instead of later on. */
43}
44
45static gboolean bee_irc_user_new(bee_t *bee, bee_user_t *bu)
46{
47        irc_user_t *iu;
48        irc_t *irc = (irc_t *) bee->ui_data;
49        char nick[MAX_NICK_LENGTH + 1], *s;
50
51        memset(nick, 0, MAX_NICK_LENGTH + 1);
52        strncpy(nick, nick_get(bu), MAX_NICK_LENGTH);
53
54        bu->ui_data = iu = irc_user_new(irc, nick);
55        iu->bu = bu;
56
57        if (set_getbool(&irc->b->set, "private")) {
58                iu->last_channel = NULL;
59        } else {
60                iu->last_channel = irc_channel_with_user(irc, iu);
61        }
62
63        if ((s = strchr(bu->handle, '@'))) {
64                iu->host = g_strdup(s + 1);
65                iu->user = g_strndup(bu->handle, s - bu->handle);
66        } else {
67                iu->user = g_strdup(bu->handle);
68                if (bu->ic->acc->server) {
69                        iu->host = g_strdup(bu->ic->acc->server);
70                } else {
71                        char *s;
72                        for (s = bu->ic->acc->tag; g_ascii_isalnum(*s); s++) {
73                                ;
74                        }
75                        /* Only use the tag if we got to the end of the string.
76                           (So allow alphanumerics only. Hopefully not too
77                           restrictive.) */
78                        if (*s) {
79                                iu->host = g_strdup(bu->ic->acc->prpl->name);
80                        } else {
81                                iu->host = g_strdup(bu->ic->acc->tag);
82                        }
83                }
84        }
85
86        /* Sanitize */
87        str_reject_chars(iu->user, " ", '_');
88        str_reject_chars(iu->host, " ", '_');
89
90        if (bu->flags & BEE_USER_LOCAL) {
91                char *s = set_getstr(&bee->set, "handle_unknown");
92
93                if (strcmp(s, "add_private") == 0) {
94                        iu->last_channel = NULL;
95                } else if (strcmp(s, "add_channel") == 0) {
96                        iu->last_channel = irc->default_channel;
97                }
98        }
99
100        iu->f = &irc_user_im_funcs;
101
102        return TRUE;
103}
104
105static gboolean bee_irc_user_free(bee_t *bee, bee_user_t *bu)
106{
107        return irc_user_free(bee->ui_data, (irc_user_t *) bu->ui_data);
108}
109
110static gboolean bee_irc_user_status(bee_t *bee, bee_user_t *bu, bee_user_t *old)
111{
112        irc_t *irc = bee->ui_data;
113        irc_user_t *iu = bu->ui_data;
114
115        /* Do this outside the if below since away state can change without
116           the online state changing. */
117        iu->flags &= ~IRC_USER_AWAY;
118        if (bu->flags & BEE_USER_AWAY || !(bu->flags & BEE_USER_ONLINE)) {
119                iu->flags |= IRC_USER_AWAY;
120        }
121
122        if ((bu->flags & BEE_USER_ONLINE) != (old->flags & BEE_USER_ONLINE)) {
123                if (bu->flags & BEE_USER_ONLINE) {
124                        if (g_hash_table_lookup(irc->watches, iu->key)) {
125                                irc_send_num(irc, 600, "%s %s %s %d :%s", iu->nick, iu->user,
126                                             iu->host, (int) time(NULL), "logged online");
127                        }
128                } else {
129                        if (g_hash_table_lookup(irc->watches, iu->key)) {
130                                irc_send_num(irc, 601, "%s %s %s %d :%s", iu->nick, iu->user,
131                                             iu->host, (int) time(NULL), "logged offline");
132                        }
133
134                        /* Send a QUIT since those will also show up in any
135                           query windows the user may have, plus it's only
136                           one QUIT instead of possibly many (in case of
137                           multiple control chans). If there's a channel that
138                           shows offline people, a JOIN will follow. */
139                        if (set_getbool(&bee->set, "offline_user_quits")) {
140                                irc_user_quit(iu, "Leaving...");
141                        }
142                }
143        }
144
145        /* Reset this one since the info may have changed. */
146        iu->away_reply_timeout = 0;
147
148        bee_irc_channel_update(irc, NULL, iu);
149
150        if ((irc->caps & CAP_AWAY_NOTIFY) &&
151            ((bu->flags & BEE_USER_AWAY) != (old->flags & BEE_USER_AWAY) ||
152             (bu->flags & BEE_USER_ONLINE) != (old->flags & BEE_USER_ONLINE))) {
153                irc_send_away_notify(iu);
154        }
155
156        return TRUE;
157}
158
159void bee_irc_channel_update(irc_t *irc, irc_channel_t *ic, irc_user_t *iu)
160{
161        GSList *l;
162
163        if (ic == NULL) {
164                for (l = irc->channels; l; l = l->next) {
165                        ic = l->data;
166                        /* TODO: Just add a type flag or so.. */
167                        if (ic->f == irc->default_channel->f &&
168                            (ic->flags & IRC_CHANNEL_JOINED)) {
169                                bee_irc_channel_update(irc, ic, iu);
170                        }
171                }
172                return;
173        }
174        if (iu == NULL) {
175                for (l = irc->users; l; l = l->next) {
176                        iu = l->data;
177                        if (iu->bu) {
178                                bee_irc_channel_update(irc, ic, l->data);
179                        }
180                }
181                return;
182        }
183
184        if (!irc_channel_wants_user(ic, iu)) {
185                irc_channel_del_user(ic, iu, IRC_CDU_PART, NULL);
186        } else {
187                struct irc_control_channel *icc = ic->data;
188                int mode = 0;
189
190                if (!(iu->bu->flags & BEE_USER_ONLINE)) {
191                        mode = icc->modes[0];
192                } else if (iu->bu->flags & BEE_USER_AWAY) {
193                        mode = icc->modes[1];
194                } else if (iu->bu->flags & BEE_USER_SPECIAL) {
195                        mode = icc->modes[2];
196                } else {
197                        mode = icc->modes[3];
198                }
199
200                if (!mode) {
201                        irc_channel_del_user(ic, iu, IRC_CDU_PART, NULL);
202                } else {
203                        irc_channel_add_user(ic, iu);
204                        irc_channel_user_set_mode(ic, iu, mode);
205                }
206        }
207}
208
209static gboolean bee_irc_user_msg(bee_t *bee, bee_user_t *bu, const char *msg_, guint32 flags, time_t sent_at)
210{
211        irc_t *irc = bee->ui_data;
212        irc_user_t *iu = (irc_user_t *) bu->ui_data;
213        irc_user_t *src_iu = iu;
214        irc_user_t *dst_iu = irc->user;
215        const char *dst;
216        char *prefix = NULL;
217        char *wrapped, *ts = NULL;
218        char *msg = g_strdup(msg_);
219        char *message_type = "PRIVMSG";
220        GSList *l;
221
222        if (sent_at > 0 && set_getbool(&irc->b->set, "display_timestamps")) {
223                ts = irc_format_timestamp(irc, sent_at);
224        }
225
226        dst = irc_user_msgdest(iu);
227
228        if (flags & OPT_SELFMESSAGE) {
229                char *setting = set_getstr(&irc->b->set, "self_messages");
230
231                if (is_bool(setting)) {
232                        if (bool2int(setting)) {
233                                /* set to true, send it with src/dst flipped */
234                               
235                                dst_iu = iu;
236                                src_iu = irc->user;
237
238                                if (dst == irc->user->nick) {
239                                        dst = dst_iu->nick;
240                                }
241                        } else {
242                                /* set to false, skip the message completely */
243                                goto cleanup;
244                        }
245                } else if (g_strncasecmp(setting, "prefix", 6) == 0) {
246                        /* third state, prefix, loosely imitates the znc privmsg_prefix module */
247
248                        g_free(msg);
249                        if (g_strncasecmp(msg_, "/me ", 4) == 0) {
250                                msg = g_strdup_printf("/me -> %s", msg_ + 4);
251                        } else {
252                                msg = g_strdup_printf("-> %s", msg_);
253                        }
254
255                        if (g_strcasecmp(setting, "prefix_notice") == 0) {
256                                message_type = "NOTICE";
257                        }
258                }
259
260        }
261
262        if (dst != dst_iu->nick) {
263                /* if not messaging directly (control channel), call user by name */
264                prefix = g_strdup_printf("%s%s%s", dst_iu->nick, set_getstr(&bee->set, "to_char"), ts ? : "");
265        } else {
266                prefix = ts;
267                ts = NULL;      /* don't double-free */
268        }
269
270        for (l = irc_plugins; l; l = l->next) {
271                irc_plugin_t *p = l->data;
272                if (p->filter_msg_in) {
273                        char *s = p->filter_msg_in(iu, msg, 0);
274                        if (s) {
275                                if (s != msg) {
276                                        g_free(msg);
277                                }
278                                msg = s;
279                        } else {
280                                /* Modules can swallow messages. */
281                                goto cleanup;
282                        }
283                }
284        }
285
286        if ((g_strcasecmp(set_getstr(&bee->set, "strip_html"), "always") == 0) ||
287            ((bu->ic->flags & OPT_DOES_HTML) && set_getbool(&bee->set, "strip_html"))) {
288                char *s = g_strdup(msg);
289                strip_html(s);
290                g_free(msg);
291                msg = s;
292        }
293
294        wrapped = word_wrap(msg, 425);
295        irc_send_msg(src_iu, message_type, dst, wrapped, prefix);
296        g_free(wrapped);
297
298cleanup:
299        g_free(prefix);
300        g_free(msg);
301        g_free(ts);
302
303        return TRUE;
304}
305
306static gboolean bee_irc_user_typing(bee_t *bee, bee_user_t *bu, guint32 flags)
307{
308        irc_t *irc = (irc_t *) bee->ui_data;
309
310        if (set_getbool(&bee->set, "typing_notice")) {
311                irc_send_msg_f((irc_user_t *) bu->ui_data, "PRIVMSG", irc->user->nick,
312                               "\001TYPING %d\001", (flags >> 8) & 3);
313        } else {
314                return FALSE;
315        }
316
317        return TRUE;
318}
319
320static gboolean bee_irc_user_action_response(bee_t *bee, bee_user_t *bu, const char *action, char * const args[],
321                                             void *data)
322{
323        irc_t *irc = (irc_t *) bee->ui_data;
324        GString *msg = g_string_new("\001");
325
326        g_string_append(msg, action);
327        while (*args) {
328                if (strchr(*args, ' ')) {
329                        g_string_append_printf(msg, " \"%s\"", *args);
330                } else {
331                        g_string_append_printf(msg, " %s", *args);
332                }
333                args++;
334        }
335        g_string_append_c(msg, '\001');
336
337        irc_send_msg((irc_user_t *) bu->ui_data, "NOTICE", irc->user->nick, msg->str, NULL);
338
339        g_string_free(msg, TRUE);
340
341        return TRUE;
342}
343
344static gboolean bee_irc_user_nick_update(irc_user_t *iu, gboolean offline_only);
345
346static gboolean bee_irc_user_fullname(bee_t *bee, bee_user_t *bu)
347{
348        irc_user_t *iu = (irc_user_t *) bu->ui_data;
349        char *s;
350
351        if (iu->fullname != iu->nick) {
352                g_free(iu->fullname);
353        }
354        iu->fullname = g_strdup(bu->fullname);
355
356        /* Strip newlines (unlikely, but IRC-unfriendly so they must go)
357           TODO(wilmer): Do the same with away msgs again! */
358        for (s = iu->fullname; *s; s++) {
359                if (g_ascii_isspace(*s)) {
360                        *s = ' ';
361                }
362        }
363
364        if ((bu->ic->flags & OPT_LOGGED_IN) && set_getbool(&bee->set, "display_namechanges")) {
365                /* People don't like this /NOTICE. Meh, let's go back to the old one.
366                char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
367                irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
368                */
369                imcb_log(bu->ic, "User `%s' changed name to `%s'", iu->nick, iu->fullname);
370        }
371
372        bee_irc_user_nick_update(iu, TRUE);
373
374        return TRUE;
375}
376
377static gboolean bee_irc_user_nick_hint(bee_t *bee, bee_user_t *bu, const char *hint)
378{
379        bee_irc_user_nick_update((irc_user_t *) bu->ui_data, TRUE);
380
381        return TRUE;
382}
383
384static gboolean bee_irc_user_nick_change(bee_t *bee, bee_user_t *bu, const char *nick)
385{
386        bee_irc_user_nick_update((irc_user_t *) bu->ui_data, FALSE);
387
388        return TRUE;
389}
390
391static gboolean bee_irc_user_group(bee_t *bee, bee_user_t *bu)
392{
393        irc_user_t *iu = (irc_user_t *) bu->ui_data;
394        irc_t *irc = (irc_t *) bee->ui_data;
395
396        bee_irc_channel_update(irc, NULL, iu);
397        bee_irc_user_nick_update(iu, FALSE);
398
399        return TRUE;
400}
401
402static gboolean bee_irc_user_nick_update(irc_user_t *iu, gboolean offline_only)
403{
404        bee_user_t *bu = iu->bu;
405        char *newnick;
406
407        if (offline_only && bu->flags & BEE_USER_ONLINE) {
408                /* Ignore if the user is visible already. */
409                return TRUE;
410        }
411
412        if (nick_saved(bu)) {
413                /* The user already assigned a nickname to this person. */
414                return TRUE;
415        }
416
417        newnick = nick_get(bu);
418
419        if (strcmp(iu->nick, newnick) != 0) {
420                nick_dedupe(bu, newnick);
421                irc_user_set_nick(iu, newnick);
422        }
423
424        return TRUE;
425}
426
427void bee_irc_user_nick_reset(irc_user_t *iu)
428{
429        bee_user_t *bu = iu->bu;
430
431        if (bu == FALSE) {
432                return;
433        }
434
435        nick_del(bu);
436        bee_irc_user_nick_update(iu, FALSE);
437
438}
439
440/* IRC->IM calls */
441
442static gboolean bee_irc_user_privmsg_cb(gpointer data, gint fd, b_input_condition cond);
443
444static gboolean bee_irc_user_privmsg(irc_user_t *iu, const char *msg)
445{
446        const char *away;
447
448        if (iu->bu == NULL) {
449                return FALSE;
450        }
451
452        if (iu->last_channel == NULL &&
453            (away = irc_user_get_away(iu)) &&
454            time(NULL) >= iu->away_reply_timeout) {
455                irc_send_num(iu->irc, 301, "%s :%s", iu->nick, away);
456                iu->away_reply_timeout = time(NULL) +
457                                         set_getint(&iu->irc->b->set, "away_reply_timeout");
458        }
459
460        if (iu->pastebuf == NULL) {
461                iu->pastebuf = g_string_new(msg);
462        } else {
463                b_event_remove(iu->pastebuf_timer);
464                g_string_append_printf(iu->pastebuf, "\n%s", msg);
465        }
466
467        if (set_getbool(&iu->irc->b->set, "paste_buffer")) {
468                int delay;
469
470                if ((delay = set_getint(&iu->irc->b->set, "paste_buffer_delay")) <= 5) {
471                        delay *= 1000;
472                }
473
474                iu->pastebuf_timer = b_timeout_add(delay, bee_irc_user_privmsg_cb, iu);
475
476                return TRUE;
477        } else {
478                bee_irc_user_privmsg_cb(iu, 0, 0);
479
480                return TRUE;
481        }
482}
483
484static gboolean bee_irc_user_privmsg_cb(gpointer data, gint fd, b_input_condition cond)
485{
486        irc_user_t *iu = data;
487        char *msg;
488        GSList *l;
489
490        msg = g_string_free(iu->pastebuf, FALSE);
491        iu->pastebuf = NULL;
492        iu->pastebuf_timer = 0;
493
494        for (l = irc_plugins; l; l = l->next) {
495                irc_plugin_t *p = l->data;
496                if (p->filter_msg_out) {
497                        char *s = p->filter_msg_out(iu, msg, 0);
498                        if (s) {
499                                if (s != msg) {
500                                        g_free(msg);
501                                }
502                                msg = s;
503                        } else {
504                                /* Modules can swallow messages. */
505                                iu->pastebuf = NULL;
506                                g_free(msg);
507                                return FALSE;
508                        }
509                }
510        }
511
512        bee_user_msg(iu->irc->b, iu->bu, msg, 0);
513
514        g_free(msg);
515
516        return FALSE;
517}
518
519static gboolean bee_irc_user_ctcp(irc_user_t *iu, char *const *ctcp)
520{
521        if (ctcp[1] && g_strcasecmp(ctcp[0], "DCC") == 0
522            && g_strcasecmp(ctcp[1], "SEND") == 0) {
523                if (iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request) {
524                        file_transfer_t *ft = dcc_request(iu->bu->ic, ctcp);
525                        if (ft) {
526                                iu->bu->ic->acc->prpl->transfer_request(iu->bu->ic, ft, iu->bu->handle);
527                        }
528
529                        return TRUE;
530                }
531        } else if (g_strcasecmp(ctcp[0], "TYPING") == 0) {
532                if (iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1]) {
533                        int st = ctcp[1][0];
534                        if (st >= '0' && st <= '2') {
535                                st <<= 8;
536                                iu->bu->ic->acc->prpl->send_typing(iu->bu->ic, iu->bu->handle, st);
537                        }
538
539                        return TRUE;
540                }
541        } else if (g_strcasecmp(ctcp[0], "HELP") == 0 && iu->bu) {
542                GString *supp = g_string_new("Supported CTCPs:");
543                GList *l;
544
545                if (iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request) {
546                        g_string_append(supp, " DCC SEND,");
547                }
548                if (iu->bu->ic && iu->bu->ic->acc->prpl->send_typing) {
549                        g_string_append(supp, " TYPING,");
550                }
551                if (iu->bu->ic->acc->prpl->buddy_action_list) {
552                        for (l = iu->bu->ic->acc->prpl->buddy_action_list(iu->bu); l; l = l->next) {
553                                struct buddy_action *ba = l->data;
554                                g_string_append_printf(supp, " %s (%s),",
555                                                       ba->name, ba->description);
556                        }
557                }
558                g_string_truncate(supp, supp->len - 1);
559                irc_send_msg_f(iu, "NOTICE", iu->irc->user->nick, "\001HELP %s\001", supp->str);
560                g_string_free(supp, TRUE);
561        } else if (iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->buddy_action) {
562                iu->bu->ic->acc->prpl->buddy_action(iu->bu, ctcp[0], ctcp + 1, NULL);
563        }
564
565        return FALSE;
566}
567
568static const struct irc_user_funcs irc_user_im_funcs = {
569        bee_irc_user_privmsg,
570        bee_irc_user_ctcp,
571};
572
573
574/* IM->IRC: Groupchats */
575const struct irc_channel_funcs irc_channel_im_chat_funcs;
576
577static gboolean bee_irc_chat_new(bee_t *bee, struct groupchat *c)
578{
579        irc_t *irc = bee->ui_data;
580        irc_channel_t *ic;
581        char *topic;
582        GSList *l;
583        int i;
584
585        /* Try to find a channel that expects to receive a groupchat.
586           This flag is set earlier in our current call trace. */
587        for (l = irc->channels; l; l = l->next) {
588                ic = l->data;
589                if (ic->flags & IRC_CHANNEL_CHAT_PICKME) {
590                        break;
591                }
592        }
593
594        /* If we found none, just generate some stupid name. */
595        if (l == NULL) {
596                for (i = 0; i <= 999; i++) {
597                        char name[16];
598                        sprintf(name, "#chat_%03d", i);
599                        if ((ic = irc_channel_new(irc, name))) {
600                                break;
601                        }
602                }
603        }
604
605        if (ic == NULL) {
606                return FALSE;
607        }
608
609        c->ui_data = ic;
610        ic->data = c;
611
612        topic = g_strdup_printf(
613                "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!",
614                c->title);
615        irc_channel_set_topic(ic, topic, irc->root);
616        g_free(topic);
617
618        return TRUE;
619}
620
621static gboolean bee_irc_chat_free(bee_t *bee, struct groupchat *c)
622{
623        irc_channel_t *ic = c->ui_data;
624
625        if (ic == NULL) {
626                return FALSE;
627        }
628
629        if (ic->flags & IRC_CHANNEL_JOINED) {
630                irc_channel_printf(ic, "Cleaning up channel, bye!");
631        }
632
633        ic->data = NULL;
634        c->ui_data = NULL;
635        irc_channel_del_user(ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server");
636
637        return TRUE;
638}
639
640static gboolean bee_irc_chat_log(bee_t *bee, struct groupchat *c, const char *text)
641{
642        irc_channel_t *ic = c->ui_data;
643
644        if (ic == NULL) {
645                return FALSE;
646        }
647
648        irc_channel_printf(ic, "%s", text);
649
650        return TRUE;
651}
652
653static gboolean bee_irc_chat_msg(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at)
654{
655        irc_t *irc = bee->ui_data;
656        irc_user_t *iu = flags & OPT_SELFMESSAGE ? irc->user : bu->ui_data;
657        irc_channel_t *ic = c->ui_data;
658        char *wrapped, *ts = NULL;
659
660        if (ic == NULL) {
661                return FALSE;
662        }
663
664        if (sent_at > 0 && set_getbool(&bee->set, "display_timestamps")) {
665                ts = irc_format_timestamp(irc, sent_at);
666        }
667
668        wrapped = word_wrap(msg, 425);
669        irc_send_msg(iu, "PRIVMSG", ic->name, wrapped, ts);
670        g_free(ts);
671        g_free(wrapped);
672
673        return TRUE;
674}
675
676static gboolean bee_irc_chat_add_user(bee_t *bee, struct groupchat *c, bee_user_t *bu)
677{
678        irc_t *irc = bee->ui_data;
679        irc_channel_t *ic = c->ui_data;
680
681        if (ic == NULL) {
682                return FALSE;
683        }
684
685        irc_channel_add_user(ic, bu == bee->user ? irc->user : bu->ui_data);
686
687        return TRUE;
688}
689
690static gboolean bee_irc_chat_remove_user(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *reason)
691{
692        irc_t *irc = bee->ui_data;
693        irc_channel_t *ic = c->ui_data;
694
695        if (ic == NULL || bu == NULL) {
696                return FALSE;
697        }
698
699        /* TODO: Possible bug here: If a module removes $user here instead of just
700           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
701           a broken state around here. */
702        irc_channel_del_user(ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, reason);
703
704        return TRUE;
705}
706
707static gboolean bee_irc_chat_topic(bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu)
708{
709        irc_channel_t *ic = c->ui_data;
710        irc_t *irc = bee->ui_data;
711        irc_user_t *iu;
712
713        if (ic == NULL) {
714                return FALSE;
715        }
716
717        if (bu == NULL) {
718                iu = irc->root;
719        } else if (bu == bee->user) {
720                iu = irc->user;
721        } else {
722                iu = bu->ui_data;
723        }
724
725        irc_channel_set_topic(ic, new, iu);
726
727        return TRUE;
728}
729
730static gboolean bee_irc_chat_name_hint(bee_t *bee, struct groupchat *c, const char *name)
731{
732        return irc_channel_name_hint(c->ui_data, name);
733}
734
735static gboolean bee_irc_chat_invite(bee_t *bee, bee_user_t *bu, const char *name, const char *msg)
736{
737        char *channel, *s;
738        irc_t *irc = bee->ui_data;
739        irc_user_t *iu = bu->ui_data;
740        irc_channel_t *chan;
741
742        if (strchr(CTYPES, name[0])) {
743                channel = g_strdup(name);
744        } else {
745                channel = g_strdup_printf("#%s", name);
746        }
747
748        if ((s = strchr(channel, '@'))) {
749                *s = '\0';
750        }
751
752        if (strlen(channel) > MAX_NICK_LENGTH) {
753                /* If the channel name is very long (like those insane GTalk
754                   UUID names), try if we can use the inviter's nick. */
755                s = g_strdup_printf("#%s", iu->nick);
756                if (irc_channel_by_name(irc, s) == NULL) {
757                        g_free(channel);
758                        channel = s;
759                } else {
760                        g_free(s);
761                }
762        }
763
764        if ((chan = irc_channel_new(irc, channel)) &&
765            set_setstr(&chan->set, "type", "chat") &&
766            set_setstr(&chan->set, "chat_type", "room") &&
767            set_setstr(&chan->set, "account", bu->ic->acc->tag) &&
768            set_setstr(&chan->set, "room", (char *) name)) {
769                /* I'm assuming that if the user didn't "chat add" the room
770                   himself but got invited, it's temporary, so make this a
771                   temporary mapping that is removed as soon as we /PART. */
772                chan->flags |= IRC_CHANNEL_TEMP;
773        } else {
774                irc_channel_free(chan);
775                chan = NULL;
776        }
777        g_free(channel);
778
779        irc_send_msg_f(iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name);
780        if (msg) {
781                irc_send_msg(iu, "PRIVMSG", irc->user->nick, msg, NULL);
782        }
783        if (chan) {
784                irc_send_msg_f(iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name);
785                irc_send_invite(iu, chan);
786        }
787
788        return TRUE;
789}
790
791/* IRC->IM */
792static gboolean bee_irc_channel_chat_privmsg_cb(gpointer data, gint fd, b_input_condition cond);
793
794static gboolean bee_irc_channel_chat_privmsg(irc_channel_t *ic, const char *msg)
795{
796        struct groupchat *c = ic->data;
797        char *trans = NULL, *s;
798
799        if (c == NULL) {
800                return FALSE;
801        }
802
803        if (set_getbool(&ic->set, "translate_to_nicks")) {
804                char nick[MAX_NICK_LENGTH + 1];
805                irc_user_t *iu;
806
807                strncpy(nick, msg, MAX_NICK_LENGTH);
808                nick[MAX_NICK_LENGTH] = '\0';
809                if ((s = strchr(nick, ':')) || (s = strchr(nick, ','))) {
810                        *s = '\0';
811                        if ((iu = irc_user_by_name(ic->irc, nick)) && iu->bu &&
812                            iu->bu->nick && irc_channel_has_user(ic, iu)) {
813                                trans = g_strconcat(iu->bu->nick, msg + (s - nick), NULL);
814                                msg = trans;
815                        }
816                }
817        }
818
819        if (set_getbool(&ic->irc->b->set, "paste_buffer")) {
820                int delay;
821
822                if (ic->pastebuf == NULL) {
823                        ic->pastebuf = g_string_new(msg);
824                } else {
825                        b_event_remove(ic->pastebuf_timer);
826                        g_string_append_printf(ic->pastebuf, "\n%s", msg);
827                }
828
829                if ((delay = set_getint(&ic->irc->b->set, "paste_buffer_delay")) <= 5) {
830                        delay *= 1000;
831                }
832
833                ic->pastebuf_timer = b_timeout_add(delay, bee_irc_channel_chat_privmsg_cb, ic);
834
835                g_free(trans);
836                return TRUE;
837        } else {
838                bee_chat_msg(ic->irc->b, c, msg, 0);
839        }
840
841        g_free(trans);
842        return TRUE;
843}
844
845static gboolean bee_irc_channel_chat_privmsg_cb(gpointer data, gint fd, b_input_condition cond)
846{
847        irc_channel_t *ic = data;
848
849        if (ic->data) {
850                bee_chat_msg(ic->irc->b, ic->data, ic->pastebuf->str, 0);
851        }
852
853        g_string_free(ic->pastebuf, TRUE);
854        ic->pastebuf = 0;
855        ic->pastebuf_timer = 0;
856
857        return FALSE;
858}
859
860static gboolean bee_irc_channel_chat_join(irc_channel_t *ic)
861{
862        char *acc_s, *room;
863        account_t *acc;
864
865        if (strcmp(set_getstr(&ic->set, "chat_type"), "room") != 0) {
866                return TRUE;
867        }
868
869        if ((acc_s = set_getstr(&ic->set, "account")) &&
870            (room = set_getstr(&ic->set, "room")) &&
871            (acc = account_get(ic->irc->b, acc_s)) &&
872            acc->ic && (acc->ic->flags & OPT_LOGGED_IN) &&
873            acc->prpl->chat_join) {
874                char *nick;
875                struct groupchat *gc;
876
877                if (!(nick = set_getstr(&ic->set, "nick"))) {
878                        nick = ic->irc->user->nick;
879                }
880
881                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
882                gc = acc->prpl->chat_join(acc->ic, room, nick, NULL, &ic->set);
883                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
884
885                if (!gc) {
886                        irc_send_num(ic->irc, 403, "%s :Error joining channel (check control channel?)", ic->name);
887                }
888
889                return FALSE;
890        } else {
891                irc_send_num(ic->irc, 403, "%s :Can't join channel, account offline?", ic->name);
892                return FALSE;
893        }
894}
895
896static gboolean bee_irc_channel_chat_part(irc_channel_t *ic, const char *msg)
897{
898        struct groupchat *c = ic->data;
899
900        if (c && c->ic->acc->prpl->chat_leave) {
901                c->ic->acc->prpl->chat_leave(c);
902        }
903
904        if (!(ic->flags & IRC_CHANNEL_TEMP)) {
905                /* Remove the reference.
906                 * We only need it for temp channels that are being freed */
907                ic->data = NULL;
908        }
909
910        return TRUE;
911}
912
913static gboolean bee_irc_channel_chat_topic(irc_channel_t *ic, const char *new)
914{
915        struct groupchat *c = ic->data;
916
917        if (c == NULL) {
918                return FALSE;
919        }
920
921        if (c->ic->acc->prpl->chat_topic == NULL) {
922                irc_send_num(ic->irc, 482, "%s :IM network does not support channel topics", ic->name);
923        } else {
924                /* TODO: Need more const goodness here, sigh */
925                char *topic = g_strdup(new);
926                c->ic->acc->prpl->chat_topic(c, topic);
927                g_free(topic);
928        }
929
930        /* Whatever happened, the IM module should ack the topic change. */
931        return FALSE;
932}
933
934static gboolean bee_irc_channel_chat_invite(irc_channel_t *ic, irc_user_t *iu)
935{
936        struct groupchat *c = ic->data;
937        bee_user_t *bu = iu->bu;
938
939        if (bu == NULL) {
940                return FALSE;
941        }
942
943        if (c) {
944                if (iu->bu->ic != c->ic) {
945                        irc_send_num(ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name);
946                } else if (c->ic->acc->prpl->chat_invite) {
947                        c->ic->acc->prpl->chat_invite(c, iu->bu->handle, NULL);
948                } else {
949                        irc_send_num(ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name);
950                }
951        } else if (bu->ic->acc->prpl->chat_with &&
952                   strcmp(set_getstr(&ic->set, "chat_type"), "groupchat") == 0) {
953                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
954                iu->bu->ic->acc->prpl->chat_with(bu->ic, bu->handle);
955                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
956        } else {
957                irc_send_num(ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name);
958        }
959
960        return TRUE;
961}
962
963static void bee_irc_channel_chat_kick(irc_channel_t *ic, irc_user_t *iu, const char *msg)
964{
965        struct groupchat *c = ic->data;
966        bee_user_t *bu = iu->bu;
967
968        if ((c == NULL) || (bu == NULL)) {
969                return;
970        }
971
972        if (!c->ic->acc->prpl->chat_kick) {
973                irc_send_num(ic->irc, 482, "%s :IM protocol does not support room kicking", ic->name);
974                return;
975        }
976
977        c->ic->acc->prpl->chat_kick(c, iu->bu->handle, msg);
978}
979
980static char *set_eval_room_account(set_t *set, char *value);
981static char *set_eval_chat_type(set_t *set, char *value);
982
983static gboolean bee_irc_channel_init(irc_channel_t *ic)
984{
985        set_t *s;
986
987        set_add(&ic->set, "account", NULL, set_eval_room_account, ic);
988        set_add(&ic->set, "chat_type", "groupchat", set_eval_chat_type, ic);
989
990        s = set_add(&ic->set, "nick", NULL, NULL, ic);
991        s->flags |= SET_NULL_OK;
992
993        set_add(&ic->set, "room", NULL, NULL, ic);
994        set_add(&ic->set, "translate_to_nicks", "true", set_eval_bool, ic);
995
996        /* chat_type == groupchat */
997        ic->flags |= IRC_CHANNEL_TEMP;
998
999        return TRUE;
1000}
1001
1002static char *set_eval_room_account(set_t *set, char *value)
1003{
1004        struct irc_channel *ic = set->data;
1005        account_t *acc, *oa;
1006
1007        if (!(acc = account_get(ic->irc->b, value))) {
1008                return SET_INVALID;
1009        } else if (!acc->prpl->chat_join) {
1010                irc_rootmsg(ic->irc, "Named chatrooms not supported on that account.");
1011                return SET_INVALID;
1012        }
1013
1014        if (set->value && (oa = account_get(ic->irc->b, set->value)) &&
1015            oa->prpl->chat_free_settings) {
1016                oa->prpl->chat_free_settings(oa, &ic->set);
1017        }
1018
1019        if (acc->prpl->chat_add_settings) {
1020                acc->prpl->chat_add_settings(acc, &ic->set);
1021        }
1022
1023        return g_strdup(acc->tag);
1024}
1025
1026static char *set_eval_chat_type(set_t *set, char *value)
1027{
1028        struct irc_channel *ic = set->data;
1029
1030        if (strcmp(value, "groupchat") == 0) {
1031                ic->flags |= IRC_CHANNEL_TEMP;
1032        } else if (strcmp(value, "room") == 0) {
1033                ic->flags &= ~IRC_CHANNEL_TEMP;
1034        } else {
1035                return NULL;
1036        }
1037
1038        return value;
1039}
1040
1041static gboolean bee_irc_channel_free(irc_channel_t *ic)
1042{
1043        struct groupchat *c = ic->data;
1044
1045        set_del(&ic->set, "account");
1046        set_del(&ic->set, "chat_type");
1047        set_del(&ic->set, "nick");
1048        set_del(&ic->set, "room");
1049        set_del(&ic->set, "translate_to_nicks");
1050
1051        ic->flags &= ~IRC_CHANNEL_TEMP;
1052
1053        /* That one still points at this channel. Don't. */
1054        if (c) {
1055                c->ui_data = NULL;
1056        }
1057
1058        return TRUE;
1059}
1060
1061const struct irc_channel_funcs irc_channel_im_chat_funcs = {
1062        bee_irc_channel_chat_privmsg,
1063        bee_irc_channel_chat_join,
1064        bee_irc_channel_chat_part,
1065        bee_irc_channel_chat_topic,
1066        bee_irc_channel_chat_invite,
1067        bee_irc_channel_chat_kick,
1068
1069        bee_irc_channel_init,
1070        bee_irc_channel_free,
1071};
1072
1073
1074/* IM->IRC: File transfers */
1075static file_transfer_t *bee_irc_ft_in_start(bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size)
1076{
1077        return dccs_send_start(bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size);
1078}
1079
1080static gboolean bee_irc_ft_out_start(struct im_connection *ic, file_transfer_t *ft)
1081{
1082        return dccs_recv_start(ft);
1083}
1084
1085static void bee_irc_ft_close(struct im_connection *ic, file_transfer_t *ft)
1086{
1087        return dcc_close(ft);
1088}
1089
1090static void bee_irc_ft_finished(struct im_connection *ic, file_transfer_t *file)
1091{
1092        dcc_file_transfer_t *df = file->priv;
1093
1094        if (file->bytes_transferred >= file->file_size) {
1095                dcc_finish(file);
1096        } else {
1097                df->proto_finished = TRUE;
1098        }
1099}
1100
1101static void bee_irc_log(bee_t *bee, const char *tag, const char *msg)
1102{
1103        irc_t *irc = (irc_t *) bee->ui_data;
1104
1105        irc_rootmsg(irc, "%s - %s", tag, msg);
1106}
1107
1108const struct bee_ui_funcs irc_ui_funcs = {
1109        bee_irc_imc_connected,
1110        bee_irc_imc_disconnected,
1111
1112        bee_irc_user_new,
1113        bee_irc_user_free,
1114        bee_irc_user_fullname,
1115        bee_irc_user_nick_hint,
1116        bee_irc_user_group,
1117        bee_irc_user_status,
1118        bee_irc_user_msg,
1119        bee_irc_user_typing,
1120        bee_irc_user_action_response,
1121
1122        bee_irc_chat_new,
1123        bee_irc_chat_free,
1124        bee_irc_chat_log,
1125        bee_irc_chat_msg,
1126        bee_irc_chat_add_user,
1127        bee_irc_chat_remove_user,
1128        bee_irc_chat_topic,
1129        bee_irc_chat_name_hint,
1130        bee_irc_chat_invite,
1131
1132        bee_irc_ft_in_start,
1133        bee_irc_ft_out_start,
1134        bee_irc_ft_close,
1135        bee_irc_ft_finished,
1136
1137        bee_irc_log,
1138        bee_irc_user_nick_change,
1139};
Note: See TracBrowser for help on using the repository browser.