source: protocols/bee_user.c @ 59a7dc5

Last change on this file since 59a7dc5 was 345577b, checked in by dequis <dx@…>, at 2015-10-30T10:27:20Z

IRC self-message support (messages sent by yourself from other clients)

This adds an OPT_SELFMESSAGE flag that can be passed to imcb_buddy_msg()
or imcb_chat_msg() to indicate that the protocol knows that the message
being sent is a self message.

This needs to be explicit since the old behavior is to silently drop
these messages, which also removed server echoes.

This commit doesn't break API/ABI, the flags parameters that were added
are all internal (between protocols and UI code)

On the irc protocol side, the situation isn't very nice, since some
clients put these messages in the wrong window. Irssi, hexchat and mirc
get this wrong. Irssi 0.8.18 has a fix for it, and the others have
scripts to patch it.

But meanwhile, there's a "self_messages" global setting that lets users
disable this, or get them as normal messages / notices with a "->"
prefix, which loosely imitates the workaround used by the ZNC
"privmsg_prefix" module.

  • Property mode set to 100644
File size: 7.4 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Stuff to handle, save and search buddies                             */
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#define BITLBEE_CORE
27#include "bitlbee.h"
28
29bee_user_t *bee_user_new(bee_t *bee, struct im_connection *ic, const char *handle, bee_user_flags_t flags)
30{
31        bee_user_t *bu;
32
33        if (bee_user_by_handle(bee, ic, handle) != NULL) {
34                return NULL;
35        }
36
37        bu = g_new0(bee_user_t, 1);
38        bu->bee = bee;
39        bu->ic = ic;
40        bu->flags = flags;
41        bu->handle = g_strdup(handle);
42        bee->users = g_slist_prepend(bee->users, bu);
43
44        if (bee->ui->user_new) {
45                bee->ui->user_new(bee, bu);
46        }
47        if (ic->acc->prpl->buddy_data_add) {
48                ic->acc->prpl->buddy_data_add(bu);
49        }
50
51        /* Offline by default. This will set the right flags. */
52        imcb_buddy_status(ic, handle, 0, NULL, NULL);
53
54        return bu;
55}
56
57int bee_user_free(bee_t *bee, bee_user_t *bu)
58{
59        if (!bu) {
60                return 0;
61        }
62
63        if (bee->ui->user_free) {
64                bee->ui->user_free(bee, bu);
65        }
66        if (bu->ic->acc->prpl->buddy_data_free) {
67                bu->ic->acc->prpl->buddy_data_free(bu);
68        }
69
70        bee->users = g_slist_remove(bee->users, bu);
71
72        g_free(bu->handle);
73        g_free(bu->fullname);
74        g_free(bu->nick);
75        g_free(bu->status);
76        g_free(bu->status_msg);
77        g_free(bu);
78
79        return 1;
80}
81
82bee_user_t *bee_user_by_handle(bee_t *bee, struct im_connection *ic, const char *handle)
83{
84        GSList *l;
85
86        for (l = bee->users; l; l = l->next) {
87                bee_user_t *bu = l->data;
88
89                if (bu->ic == ic && ic->acc->prpl->handle_cmp(bu->handle, handle) == 0) {
90                        return bu;
91                }
92        }
93
94        return NULL;
95}
96
97int bee_user_msg(bee_t *bee, bee_user_t *bu, const char *msg, int flags)
98{
99        char *buf = NULL;
100        int st;
101
102        if ((bu->ic->flags & OPT_DOES_HTML) && (g_strncasecmp(msg, "<html>", 6) != 0)) {
103                buf = escape_html(msg);
104                msg = buf;
105        } else {
106                buf = g_strdup(msg);
107        }
108
109        st = bu->ic->acc->prpl->buddy_msg(bu->ic, bu->handle, buf, flags);
110        g_free(buf);
111
112        return st;
113}
114
115
116/* Groups */
117static bee_group_t *bee_group_new(bee_t *bee, const char *name)
118{
119        bee_group_t *bg = g_new0(bee_group_t, 1);
120
121        bg->name = g_strdup(name);
122        bg->key = g_utf8_casefold(name, -1);
123        bee->groups = g_slist_prepend(bee->groups, bg);
124
125        return bg;
126}
127
128bee_group_t *bee_group_by_name(bee_t *bee, const char *name, gboolean creat)
129{
130        GSList *l;
131        char *key;
132
133        if (name == NULL) {
134                return NULL;
135        }
136
137        key = g_utf8_casefold(name, -1);
138        for (l = bee->groups; l; l = l->next) {
139                bee_group_t *bg = l->data;
140                if (strcmp(bg->key, key) == 0) {
141                        break;
142                }
143        }
144        g_free(key);
145
146        if (!l) {
147                return creat ? bee_group_new(bee, name) : NULL;
148        } else {
149                return l->data;
150        }
151}
152
153void bee_group_free(bee_t *bee)
154{
155        while (bee->groups) {
156                bee_group_t *bg = bee->groups->data;
157                g_free(bg->name);
158                g_free(bg->key);
159                g_free(bg);
160                bee->groups = g_slist_remove(bee->groups, bee->groups->data);
161        }
162}
163
164
165/* IM->UI callbacks */
166void imcb_buddy_status(struct im_connection *ic, const char *handle, int flags, const char *state, const char *message)
167{
168        bee_t *bee = ic->bee;
169        bee_user_t *bu, *old;
170
171        if (!(bu = bee_user_by_handle(bee, ic, handle))) {
172                if (g_strcasecmp(set_getstr(&ic->bee->set, "handle_unknown"), "add") == 0) {
173                        bu = bee_user_new(bee, ic, handle, BEE_USER_LOCAL);
174                } else {
175                        if (g_strcasecmp(set_getstr(&ic->bee->set, "handle_unknown"), "ignore") != 0) {
176                                imcb_log(ic, "imcb_buddy_status() for unknown handle %s:\n"
177                                         "flags = %d, state = %s, message = %s", handle, flags,
178                                         state ? state : "NULL", message ? message : "NULL");
179                        }
180
181                        return;
182                }
183        }
184
185        /* May be nice to give the UI something to compare against. */
186        old = g_memdup(bu, sizeof(bee_user_t));
187
188        /* TODO(wilmer): OPT_AWAY, or just state == NULL ? */
189        bu->flags = flags;
190        bu->status_msg = g_strdup(message);
191        if (state && *state) {
192                bu->status = g_strdup(state);
193        } else if (flags & OPT_AWAY) {
194                bu->status = g_strdup("Away");
195        } else {
196                bu->status = NULL;
197        }
198
199        if (bu->status == NULL && (flags & OPT_MOBILE) &&
200            set_getbool(&bee->set, "mobile_is_away")) {
201                bu->flags |= BEE_USER_AWAY;
202                bu->status = g_strdup("Mobile");
203        }
204
205        if (bee->ui->user_status) {
206                bee->ui->user_status(bee, bu, old);
207        }
208
209        g_free(old->status_msg);
210        g_free(old->status);
211        g_free(old);
212}
213
214/* Same, but only change the away/status message, not any away/online state info. */
215void imcb_buddy_status_msg(struct im_connection *ic, const char *handle, const char *message)
216{
217        bee_t *bee = ic->bee;
218        bee_user_t *bu, *old;
219
220        if (!(bu = bee_user_by_handle(bee, ic, handle))) {
221                return;
222        }
223
224        old = g_memdup(bu, sizeof(bee_user_t));
225
226        bu->status_msg = message && *message ? g_strdup(message) : NULL;
227
228        if (bee->ui->user_status) {
229                bee->ui->user_status(bee, bu, old);
230        }
231
232        g_free(old->status_msg);
233        g_free(old);
234}
235
236void imcb_buddy_times(struct im_connection *ic, const char *handle, time_t login, time_t idle)
237{
238        bee_t *bee = ic->bee;
239        bee_user_t *bu;
240
241        if (!(bu = bee_user_by_handle(bee, ic, handle))) {
242                return;
243        }
244
245        bu->login_time = login;
246        bu->idle_time = idle;
247}
248
249void imcb_buddy_msg(struct im_connection *ic, const char *handle, const char *msg, guint32 flags, time_t sent_at)
250{
251        bee_t *bee = ic->bee;
252        bee_user_t *bu;
253
254        bu = bee_user_by_handle(bee, ic, handle);
255
256        if (!bu && !(ic->flags & OPT_LOGGING_OUT)) {
257                char *h = set_getstr(&bee->set, "handle_unknown");
258
259                if (g_strcasecmp(h, "ignore") == 0) {
260                        return;
261                } else if (g_strncasecmp(h, "add", 3) == 0) {
262                        bu = bee_user_new(bee, ic, handle, BEE_USER_LOCAL);
263                }
264        }
265
266        if (bee->ui->user_msg && bu) {
267                bee->ui->user_msg(bee, bu, msg, flags, sent_at);
268        } else {
269                imcb_log(ic, "Message from unknown handle %s:\n%s", handle, msg);
270        }
271}
272
273void imcb_notify_email(struct im_connection *ic, char *format, ...)
274{
275        const char *handle;
276        va_list params;
277        char *msg;
278
279        if (!set_getbool(&ic->acc->set, "mail_notifications")) {
280                return;
281        }
282
283        va_start(params, format);
284        msg = g_strdup_vprintf(format, params);
285        va_end(params);
286
287        /* up to the protocol to set_add this if they want to use this */
288        handle = set_getstr(&ic->acc->set, "mail_notifications_handle");
289
290        if (handle != NULL) {
291                imcb_buddy_msg(ic, handle, msg, 0, 0);
292        } else {
293                imcb_log(ic, "%s", msg);
294        }
295
296        g_free(msg);
297}
298
299void imcb_buddy_typing(struct im_connection *ic, const char *handle, guint32 flags)
300{
301        bee_user_t *bu;
302
303        if (ic->bee->ui->user_typing &&
304            (bu = bee_user_by_handle(ic->bee, ic, handle))) {
305                ic->bee->ui->user_typing(ic->bee, bu, flags);
306        }
307}
308
309void imcb_buddy_action_response(bee_user_t *bu, const char *action, char * const args[], void *data)
310{
311        if (bu->bee->ui->user_action_response) {
312                bu->bee->ui->user_action_response(bu->bee, bu, action, args, data);
313        }
314}
Note: See TracBrowser for help on using the repository browser.