source: irc_im.c @ 82b0295

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

Add imcb_buddy_nick_change(), like nick_hint but stronger

nick_hint only works when creating new users, it's a no-op after the
user is online. This new function takes care of nick changes after that.

It also helps clean up couple of hacks in irc_im.c \o/

  • 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 ((away = irc_user_get_away(iu)) &&
453            time(NULL) >= iu->away_reply_timeout) {
454                irc_send_num(iu->irc, 301, "%s :%s", iu->nick, away);
455                iu->away_reply_timeout = time(NULL) +
456                                         set_getint(&iu->irc->b->set, "away_reply_timeout");
457        }
458
459        if (iu->pastebuf == NULL) {
460                iu->pastebuf = g_string_new(msg);
461        } else {
462                b_event_remove(iu->pastebuf_timer);
463                g_string_append_printf(iu->pastebuf, "\n%s", msg);
464        }
465
466        if (set_getbool(&iu->irc->b->set, "paste_buffer")) {
467                int delay;
468
469                if ((delay = set_getint(&iu->irc->b->set, "paste_buffer_delay")) <= 5) {
470                        delay *= 1000;
471                }
472
473                iu->pastebuf_timer = b_timeout_add(delay, bee_irc_user_privmsg_cb, iu);
474
475                return TRUE;
476        } else {
477                bee_irc_user_privmsg_cb(iu, 0, 0);
478
479                return TRUE;
480        }
481}
482
483static gboolean bee_irc_user_privmsg_cb(gpointer data, gint fd, b_input_condition cond)
484{
485        irc_user_t *iu = data;
486        char *msg;
487        GSList *l;
488
489        msg = g_string_free(iu->pastebuf, FALSE);
490        iu->pastebuf = NULL;
491        iu->pastebuf_timer = 0;
492
493        for (l = irc_plugins; l; l = l->next) {
494                irc_plugin_t *p = l->data;
495                if (p->filter_msg_out) {
496                        char *s = p->filter_msg_out(iu, msg, 0);
497                        if (s) {
498                                if (s != msg) {
499                                        g_free(msg);
500                                }
501                                msg = s;
502                        } else {
503                                /* Modules can swallow messages. */
504                                iu->pastebuf = NULL;
505                                g_free(msg);
506                                return FALSE;
507                        }
508                }
509        }
510
511        bee_user_msg(iu->irc->b, iu->bu, msg, 0);
512
513        g_free(msg);
514
515        return FALSE;
516}
517
518static gboolean bee_irc_user_ctcp(irc_user_t *iu, char *const *ctcp)
519{
520        if (ctcp[1] && g_strcasecmp(ctcp[0], "DCC") == 0
521            && g_strcasecmp(ctcp[1], "SEND") == 0) {
522                if (iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request) {
523                        file_transfer_t *ft = dcc_request(iu->bu->ic, ctcp);
524                        if (ft) {
525                                iu->bu->ic->acc->prpl->transfer_request(iu->bu->ic, ft, iu->bu->handle);
526                        }
527
528                        return TRUE;
529                }
530        } else if (g_strcasecmp(ctcp[0], "TYPING") == 0) {
531                if (iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1]) {
532                        int st = ctcp[1][0];
533                        if (st >= '0' && st <= '2') {
534                                st <<= 8;
535                                iu->bu->ic->acc->prpl->send_typing(iu->bu->ic, iu->bu->handle, st);
536                        }
537
538                        return TRUE;
539                }
540        } else if (g_strcasecmp(ctcp[0], "HELP") == 0 && iu->bu) {
541                GString *supp = g_string_new("Supported CTCPs:");
542                GList *l;
543
544                if (iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request) {
545                        g_string_append(supp, " DCC SEND,");
546                }
547                if (iu->bu->ic && iu->bu->ic->acc->prpl->send_typing) {
548                        g_string_append(supp, " TYPING,");
549                }
550                if (iu->bu->ic->acc->prpl->buddy_action_list) {
551                        for (l = iu->bu->ic->acc->prpl->buddy_action_list(iu->bu); l; l = l->next) {
552                                struct buddy_action *ba = l->data;
553                                g_string_append_printf(supp, " %s (%s),",
554                                                       ba->name, ba->description);
555                        }
556                }
557                g_string_truncate(supp, supp->len - 1);
558                irc_send_msg_f(iu, "NOTICE", iu->irc->user->nick, "\001HELP %s\001", supp->str);
559                g_string_free(supp, TRUE);
560        } else if (iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->buddy_action) {
561                iu->bu->ic->acc->prpl->buddy_action(iu->bu, ctcp[0], ctcp + 1, NULL);
562        }
563
564        return FALSE;
565}
566
567static const struct irc_user_funcs irc_user_im_funcs = {
568        bee_irc_user_privmsg,
569        bee_irc_user_ctcp,
570};
571
572
573/* IM->IRC: Groupchats */
574const struct irc_channel_funcs irc_channel_im_chat_funcs;
575
576static gboolean bee_irc_chat_new(bee_t *bee, struct groupchat *c)
577{
578        irc_t *irc = bee->ui_data;
579        irc_channel_t *ic;
580        char *topic;
581        GSList *l;
582        int i;
583
584        /* Try to find a channel that expects to receive a groupchat.
585           This flag is set earlier in our current call trace. */
586        for (l = irc->channels; l; l = l->next) {
587                ic = l->data;
588                if (ic->flags & IRC_CHANNEL_CHAT_PICKME) {
589                        break;
590                }
591        }
592
593        /* If we found none, just generate some stupid name. */
594        if (l == NULL) {
595                for (i = 0; i <= 999; i++) {
596                        char name[16];
597                        sprintf(name, "#chat_%03d", i);
598                        if ((ic = irc_channel_new(irc, name))) {
599                                break;
600                        }
601                }
602        }
603
604        if (ic == NULL) {
605                return FALSE;
606        }
607
608        c->ui_data = ic;
609        ic->data = c;
610
611        topic = g_strdup_printf(
612                "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!",
613                c->title);
614        irc_channel_set_topic(ic, topic, irc->root);
615        g_free(topic);
616
617        return TRUE;
618}
619
620static gboolean bee_irc_chat_free(bee_t *bee, struct groupchat *c)
621{
622        irc_channel_t *ic = c->ui_data;
623
624        if (ic == NULL) {
625                return FALSE;
626        }
627
628        if (ic->flags & IRC_CHANNEL_JOINED) {
629                irc_channel_printf(ic, "Cleaning up channel, bye!");
630        }
631
632        ic->data = NULL;
633        c->ui_data = NULL;
634        irc_channel_del_user(ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server");
635
636        return TRUE;
637}
638
639static gboolean bee_irc_chat_log(bee_t *bee, struct groupchat *c, const char *text)
640{
641        irc_channel_t *ic = c->ui_data;
642
643        if (ic == NULL) {
644                return FALSE;
645        }
646
647        irc_channel_printf(ic, "%s", text);
648
649        return TRUE;
650}
651
652static gboolean bee_irc_chat_msg(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at)
653{
654        irc_t *irc = bee->ui_data;
655        irc_user_t *iu = flags & OPT_SELFMESSAGE ? irc->user : bu->ui_data;
656        irc_channel_t *ic = c->ui_data;
657        char *wrapped, *ts = NULL;
658
659        if (ic == NULL) {
660                return FALSE;
661        }
662
663        if (sent_at > 0 && set_getbool(&bee->set, "display_timestamps")) {
664                ts = irc_format_timestamp(irc, sent_at);
665        }
666
667        wrapped = word_wrap(msg, 425);
668        irc_send_msg(iu, "PRIVMSG", ic->name, wrapped, ts);
669        g_free(ts);
670        g_free(wrapped);
671
672        return TRUE;
673}
674
675static gboolean bee_irc_chat_add_user(bee_t *bee, struct groupchat *c, bee_user_t *bu)
676{
677        irc_t *irc = bee->ui_data;
678        irc_channel_t *ic = c->ui_data;
679
680        if (ic == NULL) {
681                return FALSE;
682        }
683
684        irc_channel_add_user(ic, bu == bee->user ? irc->user : bu->ui_data);
685
686        return TRUE;
687}
688
689static gboolean bee_irc_chat_remove_user(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *reason)
690{
691        irc_t *irc = bee->ui_data;
692        irc_channel_t *ic = c->ui_data;
693
694        if (ic == NULL || bu == NULL) {
695                return FALSE;
696        }
697
698        /* TODO: Possible bug here: If a module removes $user here instead of just
699           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
700           a broken state around here. */
701        irc_channel_del_user(ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, reason);
702
703        return TRUE;
704}
705
706static gboolean bee_irc_chat_topic(bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu)
707{
708        irc_channel_t *ic = c->ui_data;
709        irc_t *irc = bee->ui_data;
710        irc_user_t *iu;
711
712        if (ic == NULL) {
713                return FALSE;
714        }
715
716        if (bu == NULL) {
717                iu = irc->root;
718        } else if (bu == bee->user) {
719                iu = irc->user;
720        } else {
721                iu = bu->ui_data;
722        }
723
724        irc_channel_set_topic(ic, new, iu);
725
726        return TRUE;
727}
728
729static gboolean bee_irc_chat_name_hint(bee_t *bee, struct groupchat *c, const char *name)
730{
731        return irc_channel_name_hint(c->ui_data, name);
732}
733
734static gboolean bee_irc_chat_invite(bee_t *bee, bee_user_t *bu, const char *name, const char *msg)
735{
736        char *channel, *s;
737        irc_t *irc = bee->ui_data;
738        irc_user_t *iu = bu->ui_data;
739        irc_channel_t *chan;
740
741        if (strchr(CTYPES, name[0])) {
742                channel = g_strdup(name);
743        } else {
744                channel = g_strdup_printf("#%s", name);
745        }
746
747        if ((s = strchr(channel, '@'))) {
748                *s = '\0';
749        }
750
751        if (strlen(channel) > MAX_NICK_LENGTH) {
752                /* If the channel name is very long (like those insane GTalk
753                   UUID names), try if we can use the inviter's nick. */
754                s = g_strdup_printf("#%s", iu->nick);
755                if (irc_channel_by_name(irc, s) == NULL) {
756                        g_free(channel);
757                        channel = s;
758                } else {
759                        g_free(s);
760                }
761        }
762
763        if ((chan = irc_channel_new(irc, channel)) &&
764            set_setstr(&chan->set, "type", "chat") &&
765            set_setstr(&chan->set, "chat_type", "room") &&
766            set_setstr(&chan->set, "account", bu->ic->acc->tag) &&
767            set_setstr(&chan->set, "room", (char *) name)) {
768                /* I'm assuming that if the user didn't "chat add" the room
769                   himself but got invited, it's temporary, so make this a
770                   temporary mapping that is removed as soon as we /PART. */
771                chan->flags |= IRC_CHANNEL_TEMP;
772        } else {
773                irc_channel_free(chan);
774                chan = NULL;
775        }
776        g_free(channel);
777
778        irc_send_msg_f(iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name);
779        if (msg) {
780                irc_send_msg(iu, "PRIVMSG", irc->user->nick, msg, NULL);
781        }
782        if (chan) {
783                irc_send_msg_f(iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name);
784                irc_send_invite(iu, chan);
785        }
786
787        return TRUE;
788}
789
790/* IRC->IM */
791static gboolean bee_irc_channel_chat_privmsg_cb(gpointer data, gint fd, b_input_condition cond);
792
793static gboolean bee_irc_channel_chat_privmsg(irc_channel_t *ic, const char *msg)
794{
795        struct groupchat *c = ic->data;
796        char *trans = NULL, *s;
797
798        if (c == NULL) {
799                return FALSE;
800        }
801
802        if (set_getbool(&ic->set, "translate_to_nicks")) {
803                char nick[MAX_NICK_LENGTH + 1];
804                irc_user_t *iu;
805
806                strncpy(nick, msg, MAX_NICK_LENGTH);
807                nick[MAX_NICK_LENGTH] = '\0';
808                if ((s = strchr(nick, ':')) || (s = strchr(nick, ','))) {
809                        *s = '\0';
810                        if ((iu = irc_user_by_name(ic->irc, nick)) && iu->bu &&
811                            iu->bu->nick && irc_channel_has_user(ic, iu)) {
812                                trans = g_strconcat(iu->bu->nick, msg + (s - nick), NULL);
813                                msg = trans;
814                        }
815                }
816        }
817
818        if (set_getbool(&ic->irc->b->set, "paste_buffer")) {
819                int delay;
820
821                if (ic->pastebuf == NULL) {
822                        ic->pastebuf = g_string_new(msg);
823                } else {
824                        b_event_remove(ic->pastebuf_timer);
825                        g_string_append_printf(ic->pastebuf, "\n%s", msg);
826                }
827
828                if ((delay = set_getint(&ic->irc->b->set, "paste_buffer_delay")) <= 5) {
829                        delay *= 1000;
830                }
831
832                ic->pastebuf_timer = b_timeout_add(delay, bee_irc_channel_chat_privmsg_cb, ic);
833
834                g_free(trans);
835                return TRUE;
836        } else {
837                bee_chat_msg(ic->irc->b, c, msg, 0);
838        }
839
840        g_free(trans);
841        return TRUE;
842}
843
844static gboolean bee_irc_channel_chat_privmsg_cb(gpointer data, gint fd, b_input_condition cond)
845{
846        irc_channel_t *ic = data;
847
848        if (ic->data) {
849                bee_chat_msg(ic->irc->b, ic->data, ic->pastebuf->str, 0);
850        }
851
852        g_string_free(ic->pastebuf, TRUE);
853        ic->pastebuf = 0;
854        ic->pastebuf_timer = 0;
855
856        return FALSE;
857}
858
859static gboolean bee_irc_channel_chat_join(irc_channel_t *ic)
860{
861        char *acc_s, *room;
862        account_t *acc;
863
864        if (strcmp(set_getstr(&ic->set, "chat_type"), "room") != 0) {
865                return TRUE;
866        }
867
868        if ((acc_s = set_getstr(&ic->set, "account")) &&
869            (room = set_getstr(&ic->set, "room")) &&
870            (acc = account_get(ic->irc->b, acc_s)) &&
871            acc->ic && (acc->ic->flags & OPT_LOGGED_IN) &&
872            acc->prpl->chat_join) {
873                char *nick;
874                struct groupchat *gc;
875
876                if (!(nick = set_getstr(&ic->set, "nick"))) {
877                        nick = ic->irc->user->nick;
878                }
879
880                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
881                gc = acc->prpl->chat_join(acc->ic, room, nick, NULL, &ic->set);
882                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
883
884                if (!gc) {
885                        irc_send_num(ic->irc, 403, "%s :Error joining channel (check control channel?)", ic->name);
886                }
887
888                return FALSE;
889        } else {
890                irc_send_num(ic->irc, 403, "%s :Can't join channel, account offline?", ic->name);
891                return FALSE;
892        }
893}
894
895static gboolean bee_irc_channel_chat_part(irc_channel_t *ic, const char *msg)
896{
897        struct groupchat *c = ic->data;
898
899        if (c && c->ic->acc->prpl->chat_leave) {
900                c->ic->acc->prpl->chat_leave(c);
901        }
902
903        if (!(ic->flags & IRC_CHANNEL_TEMP)) {
904                /* Remove the reference.
905                 * We only need it for temp channels that are being freed */
906                ic->data = NULL;
907        }
908
909        return TRUE;
910}
911
912static gboolean bee_irc_channel_chat_topic(irc_channel_t *ic, const char *new)
913{
914        struct groupchat *c = ic->data;
915
916        if (c == NULL) {
917                return FALSE;
918        }
919
920        if (c->ic->acc->prpl->chat_topic == NULL) {
921                irc_send_num(ic->irc, 482, "%s :IM network does not support channel topics", ic->name);
922        } else {
923                /* TODO: Need more const goodness here, sigh */
924                char *topic = g_strdup(new);
925                c->ic->acc->prpl->chat_topic(c, topic);
926                g_free(topic);
927        }
928
929        /* Whatever happened, the IM module should ack the topic change. */
930        return FALSE;
931}
932
933static gboolean bee_irc_channel_chat_invite(irc_channel_t *ic, irc_user_t *iu)
934{
935        struct groupchat *c = ic->data;
936        bee_user_t *bu = iu->bu;
937
938        if (bu == NULL) {
939                return FALSE;
940        }
941
942        if (c) {
943                if (iu->bu->ic != c->ic) {
944                        irc_send_num(ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name);
945                } else if (c->ic->acc->prpl->chat_invite) {
946                        c->ic->acc->prpl->chat_invite(c, iu->bu->handle, NULL);
947                } else {
948                        irc_send_num(ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name);
949                }
950        } else if (bu->ic->acc->prpl->chat_with &&
951                   strcmp(set_getstr(&ic->set, "chat_type"), "groupchat") == 0) {
952                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
953                iu->bu->ic->acc->prpl->chat_with(bu->ic, bu->handle);
954                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
955        } else {
956                irc_send_num(ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name);
957        }
958
959        return TRUE;
960}
961
962static void bee_irc_channel_chat_kick(irc_channel_t *ic, irc_user_t *iu, const char *msg)
963{
964        struct groupchat *c = ic->data;
965        bee_user_t *bu = iu->bu;
966
967        if ((c == NULL) || (bu == NULL)) {
968                return;
969        }
970
971        if (!c->ic->acc->prpl->chat_kick) {
972                irc_send_num(ic->irc, 482, "%s :IM protocol does not support room kicking", ic->name);
973                return;
974        }
975
976        c->ic->acc->prpl->chat_kick(c, iu->bu->handle, msg);
977}
978
979static char *set_eval_room_account(set_t *set, char *value);
980static char *set_eval_chat_type(set_t *set, char *value);
981
982static gboolean bee_irc_channel_init(irc_channel_t *ic)
983{
984        set_t *s;
985
986        set_add(&ic->set, "account", NULL, set_eval_room_account, ic);
987        set_add(&ic->set, "chat_type", "groupchat", set_eval_chat_type, ic);
988
989        s = set_add(&ic->set, "nick", NULL, NULL, ic);
990        s->flags |= SET_NULL_OK;
991
992        set_add(&ic->set, "room", NULL, NULL, ic);
993        set_add(&ic->set, "translate_to_nicks", "true", set_eval_bool, ic);
994
995        /* chat_type == groupchat */
996        ic->flags |= IRC_CHANNEL_TEMP;
997
998        return TRUE;
999}
1000
1001static char *set_eval_room_account(set_t *set, char *value)
1002{
1003        struct irc_channel *ic = set->data;
1004        account_t *acc, *oa;
1005
1006        if (!(acc = account_get(ic->irc->b, value))) {
1007                return SET_INVALID;
1008        } else if (!acc->prpl->chat_join) {
1009                irc_rootmsg(ic->irc, "Named chatrooms not supported on that account.");
1010                return SET_INVALID;
1011        }
1012
1013        if (set->value && (oa = account_get(ic->irc->b, set->value)) &&
1014            oa->prpl->chat_free_settings) {
1015                oa->prpl->chat_free_settings(oa, &ic->set);
1016        }
1017
1018        if (acc->prpl->chat_add_settings) {
1019                acc->prpl->chat_add_settings(acc, &ic->set);
1020        }
1021
1022        return g_strdup(acc->tag);
1023}
1024
1025static char *set_eval_chat_type(set_t *set, char *value)
1026{
1027        struct irc_channel *ic = set->data;
1028
1029        if (strcmp(value, "groupchat") == 0) {
1030                ic->flags |= IRC_CHANNEL_TEMP;
1031        } else if (strcmp(value, "room") == 0) {
1032                ic->flags &= ~IRC_CHANNEL_TEMP;
1033        } else {
1034                return NULL;
1035        }
1036
1037        return value;
1038}
1039
1040static gboolean bee_irc_channel_free(irc_channel_t *ic)
1041{
1042        struct groupchat *c = ic->data;
1043
1044        set_del(&ic->set, "account");
1045        set_del(&ic->set, "chat_type");
1046        set_del(&ic->set, "nick");
1047        set_del(&ic->set, "room");
1048        set_del(&ic->set, "translate_to_nicks");
1049
1050        ic->flags &= ~IRC_CHANNEL_TEMP;
1051
1052        /* That one still points at this channel. Don't. */
1053        if (c) {
1054                c->ui_data = NULL;
1055        }
1056
1057        return TRUE;
1058}
1059
1060const struct irc_channel_funcs irc_channel_im_chat_funcs = {
1061        bee_irc_channel_chat_privmsg,
1062        bee_irc_channel_chat_join,
1063        bee_irc_channel_chat_part,
1064        bee_irc_channel_chat_topic,
1065        bee_irc_channel_chat_invite,
1066        bee_irc_channel_chat_kick,
1067
1068        bee_irc_channel_init,
1069        bee_irc_channel_free,
1070};
1071
1072
1073/* IM->IRC: File transfers */
1074static file_transfer_t *bee_irc_ft_in_start(bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size)
1075{
1076        return dccs_send_start(bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size);
1077}
1078
1079static gboolean bee_irc_ft_out_start(struct im_connection *ic, file_transfer_t *ft)
1080{
1081        return dccs_recv_start(ft);
1082}
1083
1084static void bee_irc_ft_close(struct im_connection *ic, file_transfer_t *ft)
1085{
1086        return dcc_close(ft);
1087}
1088
1089static void bee_irc_ft_finished(struct im_connection *ic, file_transfer_t *file)
1090{
1091        dcc_file_transfer_t *df = file->priv;
1092
1093        if (file->bytes_transferred >= file->file_size) {
1094                dcc_finish(file);
1095        } else {
1096                df->proto_finished = TRUE;
1097        }
1098}
1099
1100static void bee_irc_log(bee_t *bee, const char *tag, const char *msg)
1101{
1102        irc_t *irc = (irc_t *) bee->ui_data;
1103
1104        irc_rootmsg(irc, "%s - %s", tag, msg);
1105}
1106
1107const struct bee_ui_funcs irc_ui_funcs = {
1108        bee_irc_imc_connected,
1109        bee_irc_imc_disconnected,
1110
1111        bee_irc_user_new,
1112        bee_irc_user_free,
1113        bee_irc_user_fullname,
1114        bee_irc_user_nick_hint,
1115        bee_irc_user_group,
1116        bee_irc_user_status,
1117        bee_irc_user_msg,
1118        bee_irc_user_typing,
1119        bee_irc_user_action_response,
1120
1121        bee_irc_chat_new,
1122        bee_irc_chat_free,
1123        bee_irc_chat_log,
1124        bee_irc_chat_msg,
1125        bee_irc_chat_add_user,
1126        bee_irc_chat_remove_user,
1127        bee_irc_chat_topic,
1128        bee_irc_chat_name_hint,
1129        bee_irc_chat_invite,
1130
1131        bee_irc_ft_in_start,
1132        bee_irc_ft_out_start,
1133        bee_irc_ft_close,
1134        bee_irc_ft_finished,
1135
1136        bee_irc_log,
1137        bee_irc_user_nick_change,
1138};
Note: See TracBrowser for help on using the repository browser.