source: irc_im.c @ 59c1fe7

Last change on this file since 59c1fe7 was eee3a5a, checked in by dequis <dx@…>, at 2015-02-21T06:14:06Z

Preset topic in groupchat if it's in irc_channel already

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