source: irc_im.c @ 3bb333c

Last change on this file since 3bb333c was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

  • Property mode set to 100644
File size: 27.2 KB
RevLine 
[5ebff60]1/********************************************************************\
[81e04e1]2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
[81e04e1]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;
[6f10697]22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
[81e04e1]24*/
25
26#include "bitlbee.h"
[17a6ee9]27#include "dcc.h"
[d860a8d]28
[e4816ea]29/* IM->IRC callbacks: Simple IM/buddy-related stuff. */
[d860a8d]30
[81e04e1]31static const struct irc_user_funcs irc_user_im_funcs;
32
[5ebff60]33static void bee_irc_imc_connected(struct im_connection *ic)
[5c7b45c]34{
[5ebff60]35        irc_t *irc = (irc_t *) ic->bee->ui_data;
36
37        irc_channel_auto_joins(irc, ic->acc);
[5c7b45c]38}
39
[5ebff60]40static void bee_irc_imc_disconnected(struct im_connection *ic)
[5c7b45c]41{
42        /* Maybe try to send /QUITs here instead of later on. */
43}
44
[5ebff60]45static gboolean bee_irc_user_new(bee_t *bee, bee_user_t *bu)
[81e04e1]46{
47        irc_user_t *iu;
[5ebff60]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);
[d860a8d]55        iu->bu = bu;
[5ebff60]56
57        if (set_getbool(&irc->b->set, "private")) {
[bb151f7]58                iu->last_channel = NULL;
[5ebff60]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 {
[c92ee728]71                        char *s;
[5ebff60]72                        for (s = bu->ic->acc->tag; g_ascii_isalnum(*s); s++) {
73                                ;
74                        }
[c92ee728]75                        /* Only use the tag if we got to the end of the string.
76                           (So allow alphanumerics only. Hopefully not too
77                           restrictive.) */
[5ebff60]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                        }
[c92ee728]83                }
[81e04e1]84        }
[5ebff60]85
86        while ((s = strchr(iu->user, ' '))) {
[495d21b]87                *s = '_';
[5ebff60]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) {
[92c8d41]94                        iu->last_channel = NULL;
[5ebff60]95                } else if (strcmp(s, "add_channel") == 0) {
[92c8d41]96                        iu->last_channel = irc->default_channel;
[5ebff60]97                }
[ad404ab]98        }
[5ebff60]99
[81e04e1]100        iu->f = &irc_user_im_funcs;
[5ebff60]101
[81e04e1]102        return TRUE;
103}
104
[5ebff60]105static gboolean bee_irc_user_free(bee_t *bee, bee_user_t *bu)
[d860a8d]106{
[5ebff60]107        return irc_user_free(bee->ui_data, (irc_user_t *) bu->ui_data);
[d860a8d]108}
[81e04e1]109
[5ebff60]110static gboolean bee_irc_user_status(bee_t *bee, bee_user_t *bu, bee_user_t *old)
[d860a8d]111{
[231b08b]112        irc_t *irc = bee->ui_data;
[003a12b]113        irc_user_t *iu = bu->ui_data;
[5ebff60]114
[eb50495]115        /* Do this outside the if below since away state can change without
116           the online state changing. */
117        iu->flags &= ~IRC_USER_AWAY;
[5ebff60]118        if (bu->flags & BEE_USER_AWAY || !(bu->flags & BEE_USER_ONLINE)) {
[eb50495]119                iu->flags |= IRC_USER_AWAY;
[5ebff60]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
[0bd948e]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. */
[5ebff60]139                        if (set_getbool(&bee->set, "offline_user_quits")) {
140                                irc_user_quit(iu, "Leaving...");
141                        }
[003a12b]142                }
[231b08b]143        }
[5ebff60]144
[1c8e5f7]145        /* Reset this one since the info may have changed. */
146        iu->away_reply_timeout = 0;
[5ebff60]147
148        bee_irc_channel_update(irc, NULL, iu);
149
[d860a8d]150        return TRUE;
151}
[81e04e1]152
[5ebff60]153void bee_irc_channel_update(irc_t *irc, irc_channel_t *ic, irc_user_t *iu)
[13c1a9f]154{
155        GSList *l;
[5ebff60]156
157        if (ic == NULL) {
158                for (l = irc->channels; l; l = l->next) {
[13c1a9f]159                        ic = l->data;
160                        /* TODO: Just add a type flag or so.. */
[5ebff60]161                        if (ic->f == irc->default_channel->f &&
162                            (ic->flags & IRC_CHANNEL_JOINED)) {
163                                bee_irc_channel_update(irc, ic, iu);
164                        }
[13c1a9f]165                }
166                return;
167        }
[5ebff60]168        if (iu == NULL) {
169                for (l = irc->users; l; l = l->next) {
[13c1a9f]170                        iu = l->data;
[5ebff60]171                        if (iu->bu) {
172                                bee_irc_channel_update(irc, ic, l->data);
173                        }
[13c1a9f]174                }
175                return;
176        }
[5ebff60]177
178        if (!irc_channel_wants_user(ic, iu)) {
179                irc_channel_del_user(ic, iu, IRC_CDU_PART, NULL);
180        } else {
[ac2717b]181                struct irc_control_channel *icc = ic->data;
[94d5da9c]182                int mode = 0;
[5ebff60]183
184                if (!(iu->bu->flags & BEE_USER_ONLINE)) {
[94d5da9c]185                        mode = icc->modes[0];
[5ebff60]186                } else if (iu->bu->flags & BEE_USER_AWAY) {
[94d5da9c]187                        mode = icc->modes[1];
[5ebff60]188                } else if (iu->bu->flags & BEE_USER_SPECIAL) {
[94d5da9c]189                        mode = icc->modes[2];
[5ebff60]190                } else {
[7b8238d]191                        mode = icc->modes[3];
[5ebff60]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);
[94d5da9c]199                }
[13c1a9f]200        }
201}
202
[5ebff60]203static gboolean bee_irc_user_msg(bee_t *bee, bee_user_t *bu, const char *msg_, time_t sent_at)
[f012a9f]204{
205        irc_t *irc = bee->ui_data;
206        irc_user_t *iu = (irc_user_t *) bu->ui_data;
[e67e513]207        const char *dst;
208        char *prefix = NULL;
[21c87a7]209        char *wrapped, *ts = NULL;
[5ebff60]210        char *msg = g_strdup(msg_);
[934db064]211        GSList *l;
[5ebff60]212
213        if (sent_at > 0 && set_getbool(&irc->b->set, "display_timestamps")) {
214                ts = irc_format_timestamp(irc, sent_at);
[f012a9f]215        }
[5ebff60]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 {
[92c8d41]222                prefix = ts;
[3864c08]223                ts = NULL;      /* don't double-free */
[f012a9f]224        }
[5ebff60]225
226        for (l = irc_plugins; l; l = l->next) {
[934db064]227                irc_plugin_t *p = l->data;
[5ebff60]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                                }
[934db064]234                                msg = s;
[5ebff60]235                        } else {
[934db064]236                                /* Modules can swallow messages. */
237                                return TRUE;
238                        }
239                }
240        }
[5ebff60]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);
[934db064]247                msg = s;
248        }
[5ebff60]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
[f012a9f]258        return TRUE;
259}
260
[5ebff60]261static gboolean bee_irc_user_typing(bee_t *bee, bee_user_t *bu, uint32_t flags)
[573dab0]262{
263        irc_t *irc = (irc_t *) bee->ui_data;
[5ebff60]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 {
[573dab0]269                return FALSE;
[5ebff60]270        }
271
[573dab0]272        return TRUE;
273}
274
[5ebff60]275static gboolean bee_irc_user_action_response(bee_t *bee, bee_user_t *bu, const char *action, char * const args[],
276                                             void *data)
[d88c92a]277{
278        irc_t *irc = (irc_t *) bee->ui_data;
[5ebff60]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
[d88c92a]294        return TRUE;
295}
296
[5ebff60]297static gboolean bee_irc_user_nick_update(irc_user_t *iu);
[6ef9065]298
[5ebff60]299static gboolean bee_irc_user_fullname(bee_t *bee, bee_user_t *bu)
[1d39159]300{
301        irc_user_t *iu = (irc_user_t *) bu->ui_data;
302        char *s;
[5ebff60]303
304        if (iu->fullname != iu->nick) {
305                g_free(iu->fullname);
306        }
307        iu->fullname = g_strdup(bu->fullname);
308
[1d39159]309        /* Strip newlines (unlikely, but IRC-unfriendly so they must go)
310           TODO(wilmer): Do the same with away msgs again! */
[5ebff60]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")) {
[fda55fa]318                /* People don't like this /NOTICE. Meh, let's go back to the old one.
[1d39159]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 );
[fda55fa]321                */
[5ebff60]322                imcb_log(bu->ic, "User `%s' changed name to `%s'", iu->nick, iu->fullname);
[1d39159]323        }
[5ebff60]324
325        bee_irc_user_nick_update(iu);
326
[1d39159]327        return TRUE;
328}
329
[5ebff60]330static gboolean bee_irc_user_nick_hint(bee_t *bee, bee_user_t *bu, const char *hint)
[6ef9065]331{
[5ebff60]332        bee_irc_user_nick_update((irc_user_t *) bu->ui_data);
333
[badd148]334        return TRUE;
335}
336
[5ebff60]337static gboolean bee_irc_user_group(bee_t *bee, bee_user_t *bu)
[badd148]338{
339        irc_user_t *iu = (irc_user_t *) bu->ui_data;
340        irc_t *irc = (irc_t *) bee->ui_data;
[51a3d12]341        bee_user_flags_t online;
[5ebff60]342
[51a3d12]343        /* Take the user offline temporarily so we can change the nick (if necessary). */
[5ebff60]344        if ((online = bu->flags & BEE_USER_ONLINE)) {
[51a3d12]345                bu->flags &= ~BEE_USER_ONLINE;
[5ebff60]346        }
347
348        bee_irc_channel_update(irc, NULL, iu);
349        bee_irc_user_nick_update(iu);
350
351        if (online) {
[51a3d12]352                bu->flags |= online;
[5ebff60]353                bee_irc_channel_update(irc, NULL, iu);
[51a3d12]354        }
[5ebff60]355
[badd148]356        return TRUE;
357}
358
[5ebff60]359static gboolean bee_irc_user_nick_update(irc_user_t *iu)
[badd148]360{
361        bee_user_t *bu = iu->bu;
362        char *newnick;
[5ebff60]363
364        if (bu->flags & BEE_USER_ONLINE) {
[6ef9065]365                /* Ignore if the user is visible already. */
366                return TRUE;
[5ebff60]367        }
368
369        if (nick_saved(bu)) {
[6ef9065]370                /* The user already assigned a nickname to this person. */
371                return TRUE;
[5ebff60]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
[6ef9065]381        return TRUE;
382}
383
[5ebff60]384void bee_irc_user_nick_reset(irc_user_t *iu)
[a429907]385{
386        bee_user_t *bu = iu->bu;
387        bee_user_flags_t online;
[5ebff60]388
389        if (bu == FALSE) {
[a429907]390                return;
[5ebff60]391        }
392
[a429907]393        /* In this case, pretend the user is offline. */
[5ebff60]394        if ((online = bu->flags & BEE_USER_ONLINE)) {
[a429907]395                bu->flags &= ~BEE_USER_ONLINE;
[5ebff60]396        }
397
398        nick_del(bu);
399        bee_irc_user_nick_update(iu);
400
[a429907]401        bu->flags |= online;
402}
403
[e4816ea]404/* IRC->IM calls */
405
[5ebff60]406static gboolean bee_irc_user_privmsg_cb(gpointer data, gint fd, b_input_condition cond);
[619dd18]407
[5ebff60]408static gboolean bee_irc_user_privmsg(irc_user_t *iu, const char *msg)
[e4816ea]409{
[1c8e5f7]410        const char *away;
[5ebff60]411
412        if (iu->bu == NULL) {
[e4816ea]413                return FALSE;
[5ebff60]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")) {
[619dd18]431                int delay;
[5ebff60]432
433                if ((delay = set_getint(&iu->irc->b->set, "paste_buffer_delay")) <= 5) {
[619dd18]434                        delay *= 1000;
[5ebff60]435                }
436
437                iu->pastebuf_timer = b_timeout_add(delay, bee_irc_user_privmsg_cb, iu);
438
[619dd18]439                return TRUE;
[5ebff60]440        } else {
441                bee_irc_user_privmsg_cb(iu, 0, 0);
442
[934db064]443                return TRUE;
444        }
[619dd18]445}
446
[5ebff60]447static gboolean bee_irc_user_privmsg_cb(gpointer data, gint fd, b_input_condition cond)
[619dd18]448{
449        irc_user_t *iu = data;
[911d97a]450        char *msg;
[934db064]451        GSList *l;
[5ebff60]452
453        msg = g_string_free(iu->pastebuf, FALSE);
[911d97a]454        iu->pastebuf = NULL;
455        iu->pastebuf_timer = 0;
[5ebff60]456
457        for (l = irc_plugins; l; l = l->next) {
[934db064]458                irc_plugin_t *p = l->data;
[5ebff60]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                                }
[934db064]465                                msg = s;
[5ebff60]466                        } else {
[934db064]467                                /* Modules can swallow messages. */
468                                iu->pastebuf = NULL;
[5ebff60]469                                g_free(msg);
[934db064]470                                return FALSE;
471                        }
472                }
473        }
[5ebff60]474
475        bee_user_msg(iu->irc->b, iu->bu, msg, 0);
476
477        g_free(msg);
478
[619dd18]479        return FALSE;
[e4816ea]480}
481
[5ebff60]482static gboolean bee_irc_user_ctcp(irc_user_t *iu, char *const *ctcp)
[e4816ea]483{
[5ebff60]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
[e4816ea]492                        return TRUE;
493                }
[5ebff60]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]) {
[e4816ea]496                        int st = ctcp[1][0];
[5ebff60]497                        if (st >= '0' && st <= '2') {
[e4816ea]498                                st <<= 8;
[5ebff60]499                                iu->bu->ic->acc->prpl->send_typing(iu->bu->ic, iu->bu->handle, st);
[e4816ea]500                        }
[5ebff60]501
[e4816ea]502                        return TRUE;
503                }
[5ebff60]504        } else if (g_strcasecmp(ctcp[0], "HELP") == 0 && iu->bu) {
505                GString *supp = g_string_new("Supported CTCPs:");
[a97a336]506                GList *l;
[5ebff60]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) {
[a97a336]516                                struct buddy_action *ba = l->data;
[5ebff60]517                                g_string_append_printf(supp, " %s (%s),",
518                                                       ba->name, ba->description);
[a97a336]519                        }
[5ebff60]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);
[d88c92a]526        }
[5ebff60]527
[e4816ea]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
[aea8b68]536
[e4816ea]537/* IM->IRC: Groupchats */
[5a75d15]538const struct irc_channel_funcs irc_channel_im_chat_funcs;
[a87754b]539
[5ebff60]540static gboolean bee_irc_chat_new(bee_t *bee, struct groupchat *c)
[aea8b68]541{
542        irc_t *irc = bee->ui_data;
543        irc_channel_t *ic;
544        char *topic;
[eb37735]545        GSList *l;
[aea8b68]546        int i;
[5ebff60]547
[eb37735]548        /* Try to find a channel that expects to receive a groupchat.
[52a2521]549           This flag is set earlier in our current call trace. */
[5ebff60]550        for (l = irc->channels; l; l = l->next) {
[eb37735]551                ic = l->data;
[5ebff60]552                if (ic->flags & IRC_CHANNEL_CHAT_PICKME) {
[eb37735]553                        break;
[5ebff60]554                }
[eb37735]555        }
[5ebff60]556
[eb37735]557        /* If we found none, just generate some stupid name. */
[5ebff60]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                }
[aea8b68]566        }
[5ebff60]567
568        if (ic == NULL) {
[aea8b68]569                return FALSE;
[5ebff60]570        }
571
[aea8b68]572        c->ui_data = ic;
573        ic->data = c;
[5ebff60]574
575        topic = g_strdup_printf(
576                "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!",
577                c->title);
578        irc_channel_set_topic(ic, topic, irc->root);
579        g_free(topic);
580
[aea8b68]581        return TRUE;
582}
583
[5ebff60]584static gboolean bee_irc_chat_free(bee_t *bee, struct groupchat *c)
[aea8b68]585{
586        irc_channel_t *ic = c->ui_data;
[5ebff60]587
588        if (ic == NULL) {
[b1af3e8]589                return FALSE;
[5ebff60]590        }
591
592        if (ic->flags & IRC_CHANNEL_JOINED) {
593                irc_channel_printf(ic, "Cleaning up channel, bye!");
594        }
595
[5a75d15]596        ic->data = NULL;
[6963230]597        c->ui_data = NULL;
[5ebff60]598        irc_channel_del_user(ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server");
599
[aea8b68]600        return TRUE;
601}
602
[5ebff60]603static gboolean bee_irc_chat_log(bee_t *bee, struct groupchat *c, const char *text)
[aea8b68]604{
[27e2c66]605        irc_channel_t *ic = c->ui_data;
[5ebff60]606
607        if (ic == NULL) {
[b1af3e8]608                return FALSE;
[5ebff60]609        }
610
611        irc_channel_printf(ic, "%s", text);
612
[b17ce85]613        return TRUE;
[aea8b68]614}
615
[5ebff60]616static gboolean bee_irc_chat_msg(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at)
[aea8b68]617{
[27e2c66]618        irc_t *irc = bee->ui_data;
619        irc_user_t *iu = bu->ui_data;
620        irc_channel_t *ic = c->ui_data;
[172aa37f]621        char *wrapped, *ts = NULL;
[5ebff60]622
623        if (ic == NULL) {
[b1af3e8]624                return FALSE;
[5ebff60]625        }
626
627        if (sent_at > 0 && set_getbool(&bee->set, "display_timestamps")) {
628                ts = irc_format_timestamp(irc, sent_at);
629        }
630
631        wrapped = word_wrap(msg, 425);
632        irc_send_msg(iu, "PRIVMSG", ic->name, wrapped, ts);
633        g_free(ts);
634        g_free(wrapped);
635
[27e2c66]636        return TRUE;
[aea8b68]637}
638
[5ebff60]639static gboolean bee_irc_chat_add_user(bee_t *bee, struct groupchat *c, bee_user_t *bu)
[aea8b68]640{
641        irc_t *irc = bee->ui_data;
[b1af3e8]642        irc_channel_t *ic = c->ui_data;
[5ebff60]643
644        if (ic == NULL) {
[b1af3e8]645                return FALSE;
[5ebff60]646        }
647
648        irc_channel_add_user(ic, bu == bee->user ? irc->user : bu->ui_data);
649
[b17ce85]650        return TRUE;
[aea8b68]651}
652
[5ebff60]653static gboolean bee_irc_chat_remove_user(bee_t *bee, struct groupchat *c, bee_user_t *bu)
[aea8b68]654{
[b17ce85]655        irc_t *irc = bee->ui_data;
[b1af3e8]656        irc_channel_t *ic = c->ui_data;
[5ebff60]657
658        if (ic == NULL || bu == NULL) {
[b1af3e8]659                return FALSE;
[5ebff60]660        }
661
[1c40aa7]662        /* TODO: Possible bug here: If a module removes $user here instead of just
663           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
664           a broken state around here. */
[5ebff60]665        irc_channel_del_user(ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL);
666
[b17ce85]667        return TRUE;
[aea8b68]668}
669
[5ebff60]670static gboolean bee_irc_chat_topic(bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu)
[9e27f18]671{
[b1af3e8]672        irc_channel_t *ic = c->ui_data;
[9e27f18]673        irc_t *irc = bee->ui_data;
674        irc_user_t *iu;
[5ebff60]675
676        if (ic == NULL) {
[b1af3e8]677                return FALSE;
[5ebff60]678        }
679
680        if (bu == NULL) {
[9e27f18]681                iu = irc->root;
[5ebff60]682        } else if (bu == bee->user) {
[9e27f18]683                iu = irc->user;
[5ebff60]684        } else {
[9e27f18]685                iu = bu->ui_data;
[5ebff60]686        }
687
688        irc_channel_set_topic(ic, new, iu);
689
[9e27f18]690        return TRUE;
691}
692
[5ebff60]693static gboolean bee_irc_chat_name_hint(bee_t *bee, struct groupchat *c, const char *name)
[d343eaa]694{
695        irc_t *irc = bee->ui_data;
[52a2521]696        irc_channel_t *ic = c->ui_data, *oic;
[5ebff60]697        char stripped[MAX_NICK_LENGTH + 1], *full_name;
698
699        if (ic == NULL) {
[b1af3e8]700                return FALSE;
[5ebff60]701        }
702
[d343eaa]703        /* Don't rename a channel if the user's in it already. */
[5ebff60]704        if (ic->flags & IRC_CHANNEL_JOINED) {
[d343eaa]705                return FALSE;
[5ebff60]706        }
707
708        strncpy(stripped, name, MAX_NICK_LENGTH);
[d343eaa]709        stripped[MAX_NICK_LENGTH] = '\0';
[5ebff60]710        irc_channel_name_strip(stripped);
711        if (set_getbool(&bee->set, "lcnicks")) {
712                nick_lc(irc, stripped);
713        }
714
715        if (stripped[0] == '\0') {
[52a2521]716                return FALSE;
[5ebff60]717        }
718
719        full_name = g_strdup_printf("#%s", stripped);
720        if ((oic = irc_channel_by_name(irc, full_name))) {
[52a2521]721                char *type, *chat_type;
[5ebff60]722
723                type = set_getstr(&oic->set, "type");
724                chat_type = set_getstr(&oic->set, "chat_type");
725
726                if (type && chat_type && oic->data == FALSE &&
727                    strcmp(type, "chat") == 0 &&
728                    strcmp(chat_type, "groupchat") == 0) {
[52a2521]729                        /* There's a channel with this name already, but it looks
730                           like it's not in use yet. Most likely the IRC client
731                           rejoined the channel after a reconnect. Remove it so
732                           we can reuse its name. */
[5ebff60]733                        irc_channel_free(oic);
734                } else {
735                        g_free(full_name);
[52a2521]736                        return FALSE;
737                }
[d343eaa]738        }
[5ebff60]739
740        g_free(ic->name);
[52a2521]741        ic->name = full_name;
[5ebff60]742
[d343eaa]743        return TRUE;
744}
745
[5ebff60]746static gboolean bee_irc_chat_invite(bee_t *bee, bee_user_t *bu, const char *name, const char *msg)
[1aa74f55]747{
748        char *channel, *s;
749        irc_t *irc = bee->ui_data;
750        irc_user_t *iu = bu->ui_data;
751        irc_channel_t *chan;
[5ebff60]752
753        if (strchr(CTYPES, name[0])) {
754                channel = g_strdup(name);
755        } else {
756                channel = g_strdup_printf("#%s", name);
757        }
758
759        if ((s = strchr(channel, '@'))) {
[1aa74f55]760                *s = '\0';
[5ebff60]761        }
762
763        if (strlen(channel) > MAX_NICK_LENGTH) {
[1aa74f55]764                /* If the channel name is very long (like those insane GTalk
765                   UUID names), try if we can use the inviter's nick. */
[5ebff60]766                s = g_strdup_printf("#%s", iu->nick);
767                if (irc_channel_by_name(irc, s) == NULL) {
768                        g_free(channel);
[1aa74f55]769                        channel = s;
770                }
771        }
[5ebff60]772
773        if ((chan = irc_channel_new(irc, channel)) &&
774            set_setstr(&chan->set, "type", "chat") &&
775            set_setstr(&chan->set, "chat_type", "room") &&
776            set_setstr(&chan->set, "account", bu->ic->acc->tag) &&
777            set_setstr(&chan->set, "room", (char *) name)) {
[1aa74f55]778                /* I'm assuming that if the user didn't "chat add" the room
779                   himself but got invited, it's temporary, so make this a
780                   temporary mapping that is removed as soon as we /PART. */
781                chan->flags |= IRC_CHANNEL_TEMP;
[5ebff60]782        } else {
783                irc_channel_free(chan);
[1aa74f55]784                chan = NULL;
785        }
[5ebff60]786        g_free(channel);
787
788        irc_send_msg_f(iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name);
789        if (msg) {
790                irc_send_msg(iu, "PRIVMSG", irc->user->nick, msg, NULL);
791        }
792        if (chan) {
793                irc_send_msg_f(iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name);
794                irc_send_invite(iu, chan);
795        }
796
[1aa74f55]797        return TRUE;
798}
799
[a87754b]800/* IRC->IM */
[5ebff60]801static gboolean bee_irc_channel_chat_privmsg_cb(gpointer data, gint fd, b_input_condition cond);
[619dd18]802
[5ebff60]803static gboolean bee_irc_channel_chat_privmsg(irc_channel_t *ic, const char *msg)
[a87754b]804{
805        struct groupchat *c = ic->data;
[69b896b]806        char *trans = NULL, *s;
[5ebff60]807
808        if (c == NULL) {
[5a75d15]809                return FALSE;
[5ebff60]810        }
811
812        if (set_getbool(&ic->set, "translate_to_nicks")) {
813                char nick[MAX_NICK_LENGTH + 1];
[69b896b]814                irc_user_t *iu;
[5ebff60]815
816                strncpy(nick, msg, MAX_NICK_LENGTH);
[69b896b]817                nick[MAX_NICK_LENGTH] = '\0';
[5ebff60]818                if ((s = strchr(nick, ':')) || (s = strchr(nick, ','))) {
[69b896b]819                        *s = '\0';
[5ebff60]820                        if ((iu = irc_user_by_name(ic->irc, nick)) && iu->bu &&
821                            iu->bu->nick && irc_channel_has_user(ic, iu)) {
822                                trans = g_strconcat(iu->bu->nick, msg + (s - nick), NULL);
[69b896b]823                                msg = trans;
824                        }
825                }
826        }
[5ebff60]827
828        if (set_getbool(&ic->irc->b->set, "paste_buffer")) {
[619dd18]829                int delay;
[5ebff60]830
831                if (ic->pastebuf == NULL) {
832                        ic->pastebuf = g_string_new(msg);
833                } else {
834                        b_event_remove(ic->pastebuf_timer);
835                        g_string_append_printf(ic->pastebuf, "\n%s", msg);
[619dd18]836                }
[5ebff60]837
838                if ((delay = set_getint(&ic->irc->b->set, "paste_buffer_delay")) <= 5) {
[619dd18]839                        delay *= 1000;
[5ebff60]840                }
841
842                ic->pastebuf_timer = b_timeout_add(delay, bee_irc_channel_chat_privmsg_cb, ic);
843
844                g_free(trans);
[619dd18]845                return TRUE;
[5ebff60]846        } else {
847                bee_chat_msg(ic->irc->b, c, msg, 0);
[619dd18]848        }
[5ebff60]849
850        g_free(trans);
[a87754b]851        return TRUE;
[5a75d15]852}
853
[5ebff60]854static gboolean bee_irc_channel_chat_privmsg_cb(gpointer data, gint fd, b_input_condition cond)
[619dd18]855{
856        irc_channel_t *ic = data;
[5ebff60]857
858        if (ic->data) {
859                bee_chat_msg(ic->irc->b, ic->data, ic->pastebuf->str, 0);
860        }
861
862        g_string_free(ic->pastebuf, TRUE);
[619dd18]863        ic->pastebuf = 0;
864        ic->pastebuf_timer = 0;
[5ebff60]865
[619dd18]866        return FALSE;
867}
868
[5ebff60]869static gboolean bee_irc_channel_chat_join(irc_channel_t *ic)
[5a75d15]870{
871        char *acc_s, *room;
872        account_t *acc;
[5ebff60]873
874        if (strcmp(set_getstr(&ic->set, "chat_type"), "room") != 0) {
[5a75d15]875                return TRUE;
[5ebff60]876        }
877
878        if ((acc_s = set_getstr(&ic->set, "account")) &&
879            (room = set_getstr(&ic->set, "room")) &&
880            (acc = account_get(ic->irc->b, acc_s)) &&
881            acc->ic && acc->prpl->chat_join) {
[5a75d15]882                char *nick;
[5ebff60]883
884                if (!(nick = set_getstr(&ic->set, "nick"))) {
[5a75d15]885                        nick = ic->irc->user->nick;
[5ebff60]886                }
887
[5a75d15]888                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
[5ebff60]889                acc->prpl->chat_join(acc->ic, room, nick, NULL, &ic->set);
[5a75d15]890                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
[5ebff60]891
[5a75d15]892                return FALSE;
[5ebff60]893        } else {
894                irc_send_num(ic->irc, 403, "%s :Can't join channel, account offline?", ic->name);
[5a75d15]895                return FALSE;
896        }
[a87754b]897}
898
[5ebff60]899static gboolean bee_irc_channel_chat_part(irc_channel_t *ic, const char *msg)
[bfb99ee]900{
901        struct groupchat *c = ic->data;
[5ebff60]902
903        if (c && c->ic->acc->prpl->chat_leave) {
904                c->ic->acc->prpl->chat_leave(c);
905        }
906
[664bac3]907        /* Remove the reference. We don't need it anymore. */
[cc20520]908        ic->data = NULL;
[5ebff60]909
[bfb99ee]910        return TRUE;
911}
912
[5ebff60]913static gboolean bee_irc_channel_chat_topic(irc_channel_t *ic, const char *new)
[9e27f18]914{
[4469e7e]915        struct groupchat *c = ic->data;
[5ebff60]916
917        if (c == NULL) {
[5a75d15]918                return FALSE;
[5ebff60]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 {
[5a75d15]924                /* TODO: Need more const goodness here, sigh */
[5ebff60]925                char *topic = g_strdup(new);
926                c->ic->acc->prpl->chat_topic(c, topic);
927                g_free(topic);
[4469e7e]928        }
[5ebff60]929
[41e0c00]930        /* Whatever happened, the IM module should ack the topic change. */
[4469e7e]931        return FALSE;
[9e27f18]932}
933
[5ebff60]934static gboolean bee_irc_channel_chat_invite(irc_channel_t *ic, irc_user_t *iu)
[66b9e36a]935{
936        struct groupchat *c = ic->data;
[5a75d15]937        bee_user_t *bu = iu->bu;
[5ebff60]938
939        if (bu == NULL) {
[5a75d15]940                return FALSE;
[5ebff60]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) {
[5a75d15]953                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
[5ebff60]954                iu->bu->ic->acc->prpl->chat_with(bu->ic, bu->handle);
[5a75d15]955                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
[5ebff60]956        } else {
957                irc_send_num(ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name);
[5a75d15]958        }
[5ebff60]959
[66b9e36a]960        return TRUE;
961}
962
[5ebff60]963static void bee_irc_channel_chat_kick(irc_channel_t *ic, irc_user_t *iu, const char *msg)
[7821ee8]964{
965        struct groupchat *c = ic->data;
966        bee_user_t *bu = iu->bu;
[5ebff60]967
968        if ((c == NULL) || (bu == NULL)) {
[7821ee8]969                return;
[5ebff60]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);
[7821ee8]974                return;
975        }
[5ebff60]976
977        c->ic->acc->prpl->chat_kick(c, iu->bu->handle, msg);
[7821ee8]978}
979
[5ebff60]980static char *set_eval_room_account(set_t *set, char *value);
981static char *set_eval_chat_type(set_t *set, char *value);
[5a75d15]982
[5ebff60]983static gboolean bee_irc_channel_init(irc_channel_t *ic)
[5a75d15]984{
[57a65600]985        set_t *s;
986
[5ebff60]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);
[57a65600]989
[5ebff60]990        s = set_add(&ic->set, "nick", NULL, NULL, ic);
[57a65600]991        s->flags |= SET_NULL_OK;
992
[5ebff60]993        set_add(&ic->set, "room", NULL, NULL, ic);
994        set_add(&ic->set, "translate_to_nicks", "true", set_eval_bool, ic);
995
[1c40aa7]996        /* chat_type == groupchat */
997        ic->flags |= IRC_CHANNEL_TEMP;
[5ebff60]998
[5a75d15]999        return TRUE;
1000}
1001
[5ebff60]1002static char *set_eval_room_account(set_t *set, char *value)
[5a75d15]1003{
1004        struct irc_channel *ic = set->data;
[03f3828]1005        account_t *acc, *oa;
[5ebff60]1006
1007        if (!(acc = account_get(ic->irc->b, value))) {
[5a75d15]1008                return SET_INVALID;
[5ebff60]1009        } else if (!acc->prpl->chat_join) {
1010                irc_rootmsg(ic->irc, "Named chatrooms not supported on that account.");
[5a75d15]1011                return SET_INVALID;
1012        }
[5ebff60]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);
[5a75d15]1024}
1025
[5ebff60]1026static char *set_eval_chat_type(set_t *set, char *value)
[1c40aa7]1027{
1028        struct irc_channel *ic = set->data;
[5ebff60]1029
1030        if (strcmp(value, "groupchat") == 0) {
[1c40aa7]1031                ic->flags |= IRC_CHANNEL_TEMP;
[5ebff60]1032        } else if (strcmp(value, "room") == 0) {
[1c40aa7]1033                ic->flags &= ~IRC_CHANNEL_TEMP;
[5ebff60]1034        } else {
[1c40aa7]1035                return NULL;
[5ebff60]1036        }
1037
[1c40aa7]1038        return value;
1039}
1040
[5ebff60]1041static gboolean bee_irc_channel_free(irc_channel_t *ic)
[5a75d15]1042{
[b1af3e8]1043        struct groupchat *c = ic->data;
[5ebff60]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
[1c40aa7]1051        ic->flags &= ~IRC_CHANNEL_TEMP;
[5ebff60]1052
[b1af3e8]1053        /* That one still points at this channel. Don't. */
[5ebff60]1054        if (c) {
[b1af3e8]1055                c->ui_data = NULL;
[5ebff60]1056        }
1057
[5a75d15]1058        return TRUE;
1059}
1060
1061const struct irc_channel_funcs irc_channel_im_chat_funcs = {
[a87754b]1062        bee_irc_channel_chat_privmsg,
[5a75d15]1063        bee_irc_channel_chat_join,
[bfb99ee]1064        bee_irc_channel_chat_part,
[9e27f18]1065        bee_irc_channel_chat_topic,
[66b9e36a]1066        bee_irc_channel_chat_invite,
[7821ee8]1067        bee_irc_channel_chat_kick,
[5a75d15]1068
1069        bee_irc_channel_init,
1070        bee_irc_channel_free,
[a87754b]1071};
1072
[aea8b68]1073
[e4816ea]1074/* IM->IRC: File transfers */
[5ebff60]1075static file_transfer_t *bee_irc_ft_in_start(bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size)
[17a6ee9]1076{
[5ebff60]1077        return dccs_send_start(bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size);
[17a6ee9]1078}
1079
[5ebff60]1080static gboolean bee_irc_ft_out_start(struct im_connection *ic, file_transfer_t *ft)
[17a6ee9]1081{
[5ebff60]1082        return dccs_recv_start(ft);
[17a6ee9]1083}
1084
[5ebff60]1085static void bee_irc_ft_close(struct im_connection *ic, file_transfer_t *ft)
[17a6ee9]1086{
[5ebff60]1087        return dcc_close(ft);
[17a6ee9]1088}
1089
[5ebff60]1090static void bee_irc_ft_finished(struct im_connection *ic, file_transfer_t *file)
[17a6ee9]1091{
1092        dcc_file_transfer_t *df = file->priv;
1093
[5ebff60]1094        if (file->bytes_transferred >= file->file_size) {
1095                dcc_finish(file);
1096        } else {
[17a6ee9]1097                df->proto_finished = TRUE;
[5ebff60]1098        }
[17a6ee9]1099}
1100
[d860a8d]1101const struct bee_ui_funcs irc_ui_funcs = {
[5c7b45c]1102        bee_irc_imc_connected,
1103        bee_irc_imc_disconnected,
[5ebff60]1104
[81e04e1]1105        bee_irc_user_new,
[d860a8d]1106        bee_irc_user_free,
[1d39159]1107        bee_irc_user_fullname,
[6ef9065]1108        bee_irc_user_nick_hint,
[7e83e8e4]1109        bee_irc_user_group,
[d860a8d]1110        bee_irc_user_status,
[f012a9f]1111        bee_irc_user_msg,
[573dab0]1112        bee_irc_user_typing,
[d88c92a]1113        bee_irc_user_action_response,
[5ebff60]1114
[aea8b68]1115        bee_irc_chat_new,
1116        bee_irc_chat_free,
[27e2c66]1117        bee_irc_chat_log,
1118        bee_irc_chat_msg,
[aea8b68]1119        bee_irc_chat_add_user,
[b17ce85]1120        bee_irc_chat_remove_user,
[9e27f18]1121        bee_irc_chat_topic,
[d343eaa]1122        bee_irc_chat_name_hint,
[1aa74f55]1123        bee_irc_chat_invite,
[5ebff60]1124
[17a6ee9]1125        bee_irc_ft_in_start,
1126        bee_irc_ft_out_start,
1127        bee_irc_ft_close,
1128        bee_irc_ft_finished,
[81e04e1]1129};
Note: See TracBrowser for help on using the repository browser.