Incoming statuses with characters 0x80-0xFF were double-escaped.
[thrasher.git] / thblist.c
blob401ca60c1b0ff70aa42612591ac1466161050fd8
1 /*
2 * Thrasher Bird - XMPP transport via libpurple
3 * Copyright (C) 2008 Barracuda Networks, Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with Thrasher Bird; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
19 #include <stdlib.h>
20 #include "blist.h"
21 #include "thperl.h"
22 #include "thrasher.h"
23 #include "thblist.h"
24 #include "status.h"
26 #include <server.h>
27 #include <account.h>
29 void thrasher_buddy_status_changed (PurpleBuddy *buddy, PurpleStatus *old,
30 PurpleStatus *new);
31 void thrasher_buddy_signed_on (PurpleBuddy *buddy);
32 void thrasher_buddy_signed_off (PurpleBuddy *buddy);
33 void thrasher_buddy_added (PurpleBuddy *buddy);
34 void thrasher_buddy_add_request (PurpleAccount *account, const char *remote_user,
35 const char *id, const char *alias,
36 const char *message);
37 static void * thrasher_buddy_request_authorize
38 (PurpleAccount *account,
39 const char *remote_user,
40 const char *id,
41 const char *alias,
42 const char *message,
43 gboolean on_list,
44 PurpleAccountRequestAuthorizationCb authorize_cb,
45 PurpleAccountRequestAuthorizationCb deny_cb,
46 void *user_data);
47 void thrasher_buddy_removed (PurpleBuddy *buddy);
48 static gpointer thrasher_blist_get_handle (void);
49 static char* status_text_for_buddy(PurpleBuddy *buddy, PurpleStatus *status);
51 static gboolean thrasher_buddy_request_authorize_cb1(gpointer data_in);
52 static gboolean thrasher_buddy_request_authorize_cb2(gpointer data_in);
53 static void no_dot_purple_no_op(void*);
55 static PurpleAccountUiOps thrasher_account_uiops =
57 // notify added
58 thrasher_buddy_add_request,
60 // status changed
61 NULL,
63 // request add
64 thrasher_buddy_add_request,
66 // request_authorize
67 thrasher_buddy_request_authorize,
69 // close_account_request
70 NULL,
71 // save_node
72 NULL,
73 // remove_node
74 NULL,
75 // save_account
76 NULL,
77 // Reserved
78 NULL
81 static PurpleBlistUiOps thrasher_blist_uiops = {
82 /* new_list */
83 NULL,
84 /* new_node */
85 NULL,
86 /* show */
87 NULL,
88 /* update */
89 NULL,
90 /* remove */
91 NULL,
92 /* destroy */
93 NULL,
94 /* set_visible */
95 NULL,
96 /* request_add_buddy */
97 NULL,
98 /* request_add_chat */
99 NULL,
100 /* request_add_group */
101 NULL,
102 /* save_node */
103 (void(*)(PurpleBlistNode*)) no_dot_purple_no_op,
104 /* remove_node */
105 (void(*)(PurpleBlistNode*)) no_dot_purple_no_op,
106 /* save_account */
107 (void(*)(PurpleAccount*)) no_dot_purple_no_op,
108 /* reserved */
109 NULL,
112 struct _PurpleStatusType
114 PurpleStatusPrimitive primitive;
116 char *id;
117 char *name;
118 char *primary_attr_id;
120 gboolean saveable;
121 gboolean user_settable;
122 gboolean independent;
124 GList *attrs;
128 struct _PurpleStatus
130 struct _PurpleStatusType *type;
131 PurplePresence *presence;
133 const char *title;
135 gboolean active;
137 GHashTable *attr_values;
141 * @brief This is the callback for the buddy-status-changed signal
142 * @param buddy PurpleBuddy struct
143 * @param old (unused)
144 * @param new @buddy new PurpleStatus
146 void thrasher_buddy_status_changed (PurpleBuddy *buddy,
147 PurpleStatus *old,
148 PurpleStatus *new)
150 PurpleGroup *group;
151 const char *group_name = NULL;
152 const char *alias = NULL;
154 g_return_if_fail(buddy != NULL);
155 g_return_if_fail(new != NULL);
157 group = purple_buddy_get_group(buddy);
159 if (group)
160 group_name = purple_group_get_name(group);
162 alias = purple_buddy_get_alias_only(buddy);
164 char* message = status_text_for_buddy(buddy, new);
165 if (! message) {
166 message = g_strdup(purple_status_get_attr_string(new, "message"));
169 thrasher_wrapper_presence_in(thrasher_account_get_jid(buddy->account),
170 purple_buddy_get_name(buddy),
171 alias,
172 group_name,
173 purple_status_type_get_primitive(new->type),
174 message);
175 g_free(message);
178 /* Returns a string that becomes owned by the caller or NULL. */
179 static char*
180 status_text_for_buddy(PurpleBuddy *buddy,
181 PurpleStatus *status) {
182 if (! buddy) {
183 return NULL;
185 if (! buddy->account->gc) {
186 return NULL;
189 const char *prpl_id = purple_account_get_protocol_id(buddy->account);
190 if (prpl_id) {
191 PurplePlugin *prpl = purple_find_prpl(prpl_id);
192 if (prpl) {
193 PurplePluginProtocolInfo *prpl_info
194 = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
195 if (prpl_info && prpl_info->status_text) {
196 char* status_text = prpl_info->status_text(buddy);
197 if (status && status_text
198 && purple_status_is_available(status)
199 && 0 == strcmp(prpl_id, "prpl-icq")
200 && 0 == strcmp(status_text,
201 purple_status_get_name(status))) {
202 /* The oscar prpl's status_text tries to suppress
203 * a redundant "Available" message, but won't if
204 * it's in the message attribute instead of the
205 * status name. libpurple's initial log on does
206 * not set this in the message, but subsequent
207 * status changes will.
209 status_text = g_strdup("");
211 return status_text;
215 return NULL;
217 void thrasher_buddy_authorize (const char *jid,
218 const char *legacy_username) {
219 g_return_if_fail(jid);
220 g_return_if_fail(legacy_username);
222 PurpleAccount *account = thrasher_get_account_by_jid(jid);
223 g_return_if_fail(account);
225 PurpleConnection *gc =
226 thrasher_get_connection_by_account(account);
227 g_return_if_fail(gc);
229 purple_debug_info("thrasher", "Authorizing %s for jid %s (%s)\n",
230 legacy_username, jid,
231 purple_account_get_username(account));
233 // defensive; probably only add_permit is necesary
234 serv_rem_deny(gc, legacy_username);
236 serv_add_permit(gc, legacy_username);
239 void thrasher_buddy_deauthorize (const char *jid,
240 const char *legacy_username) {
241 g_return_if_fail(jid);
242 g_return_if_fail(legacy_username);
244 PurpleAccount *account = thrasher_get_account_by_jid(jid);
245 g_return_if_fail(account);
247 PurpleConnection *gc =
248 thrasher_get_connection_by_account(account);
249 g_return_if_fail(gc);
251 purple_debug_info("thrasher", "Deauthorizing %s for jid %s\n",
252 legacy_username, jid);
254 // no corresponding deny, we don't have a reperesentation for that
255 serv_rem_permit(gc, legacy_username);
258 void thrasher_buddy_signed_on (PurpleBuddy *buddy)
260 PurplePresence *presence;
262 presence = purple_buddy_get_presence(buddy);
264 if (!presence)
265 return;
267 // libpurple uses this to populate some stuff about the
268 // buddy, particularly useful for XMPP file transfers but
269 // probably the cause of random other errors if we don't fill this
270 // out.
271 PurpleAccount* account = buddy->account;
272 g_return_if_fail(account);
273 PurpleConnection* connection = thrasher_connection_for_account(account);
274 g_return_if_fail(connection);
276 purple_debug_info("thblist",
277 "getting server info for %s\n\n---------\n\n",
278 buddy->name);
279 serv_get_info(connection, buddy->name);
281 /* Currently active status may be PURPLE_STATUS_UNAVAILABLE
282 * (translates XMPP dnd) instead of just "available".
284 PurpleStatus *status = purple_presence_get_active_status(presence);
286 thrasher_buddy_status_changed(buddy,
287 NULL,
288 status);
291 void thrasher_buddy_signed_off (PurpleBuddy *buddy)
293 PurplePresence *presence;
295 presence = purple_buddy_get_presence(buddy);
297 if (!presence)
298 return;
300 thrasher_buddy_status_changed(buddy,
301 NULL,
302 purple_presence_get_status(presence, "offline"));
305 struct auth_request_cb_data {
306 PurpleAccount *account;
307 char *remote_user;
308 char *alias;
309 char *message;
310 PurpleAccountRequestAuthorizationCb authorize_cb;
311 void *user_data;
314 static void * thrasher_buddy_request_authorize
315 (PurpleAccount *account,
316 const char *remote_user,
317 const char *id,
318 const char *alias,
319 const char *message,
320 gboolean on_list,
321 PurpleAccountRequestAuthorizationCb authorize_cb,
322 PurpleAccountRequestAuthorizationCb deny_cb,
323 void *user_data) {
324 purple_debug_info("thrasher", "%s request_authorize from %s reached cb0\n",
325 remote_user,
326 thrasher_account_get_jid(account));
328 struct auth_request_cb_data *data
329 = g_malloc(sizeof(struct auth_request_cb_data));
330 data->account = account;
331 data->remote_user = g_strdup(remote_user);
332 data->alias = g_strdup(alias);
333 data->message = g_strdup(message);
334 data->authorize_cb = authorize_cb;
335 data->user_data = user_data;
336 purple_timeout_add_seconds(2,
337 thrasher_buddy_request_authorize_cb1,
338 data);
340 void *uihandle = NULL;
341 return uihandle;
344 static gboolean
345 thrasher_buddy_request_authorize_cb1(gpointer data_in) {
346 struct auth_request_cb_data *data = data_in;
347 purple_debug_info("thrasher", "%s request_authorize from %s reached cb1\n",
348 data->remote_user,
349 thrasher_account_get_jid(data->account));
350 if (data->authorize_cb) {
351 data->authorize_cb(data->user_data);
353 else {
354 /* FIXME: I don't think this actually happens? Any more? */
355 purple_debug_info("thrasher", "authorize_cb sometimes NULL?!?\n");
357 purple_timeout_add_seconds(2,
358 thrasher_buddy_request_authorize_cb2,
359 data);
360 return FALSE;
363 static gboolean
364 thrasher_buddy_request_authorize_cb2(gpointer data_in) {
365 struct auth_request_cb_data *data = data_in;
366 purple_debug_info("thrasher", "%s request_authorize from %s reached cb2\n",
367 data->remote_user,
368 thrasher_account_get_jid(data->account));
369 /* FIXME: call through purple_blist_request_add_buddy() and blist ui_ops. */
370 thrasher_buddy_add_request(data->account,
371 data->remote_user,
373 data->alias,
374 data->message);
375 g_free(data->remote_user);
376 g_free(data->alias);
377 g_free(data->message);
378 g_free(data);
379 return FALSE;
382 /* thrasher_buddy_add_request
384 * Triggered on a buddy-added signal, this allows us to push new subscriptions.
387 void thrasher_buddy_added (PurpleBuddy *buddy) {
388 PurplePresence *presence = purple_buddy_get_presence(buddy);
389 PurpleStatus *status = purple_presence_get_active_status(presence);
390 PurpleStatusType *type = purple_status_get_type(status);
392 const char *jid = thrasher_account_get_jid(buddy->account);
393 const char *sender = purple_buddy_get_name(buddy);
394 const guint status_int = purple_status_type_get_primitive(type);
396 purple_debug_info("thrasher",
397 "buddy added to %s: %s\n",
398 jid, sender);
400 thrasher_wrapper_subscription_add(jid, sender, status_int);
404 void thrasher_buddy_add_request (PurpleAccount *account, const char *remote_user,
405 const char *id, const char *alias,
406 const char *message)
408 const char *jid = thrasher_account_get_jid(account);
409 if (! alias) {
410 alias = "(unknown)";
412 purple_debug_info("thrasher",
413 "legacy user %s aka %s added %s to roster\n",
414 remote_user, alias, jid);
415 thrasher_wrapper_legacy_user_add_user(jid, remote_user);
419 void thrasher_buddy_removed (PurpleBuddy *buddy)
421 /* printf("Removed buddy:\t[%s]\t[%s]\n", purple_buddy_get_name(buddy),
422 * purple_status_get_name( purple_presence_get_active_status( purple_buddy_get_presence(buddy) ) )
423 * );
425 PurplePresence *presence = purple_buddy_get_presence(buddy);
426 PurpleStatus *status = purple_presence_get_active_status(presence);
427 PurpleStatusType *type = purple_status_get_type(status);
428 PurpleGroup *group = purple_buddy_get_group(buddy);
430 * Need to fire jid, sender, group_name, alias, and status back to perl */
432 // jid = thrasher_account_get_jid(buddy->account)
433 // sender = purple_buddy_get_name(buddy)
434 // group_name = purple_group_get_name(group)
435 // alias = purple_buddy_get_alias_only(buddy)
436 // status = purple_status_type_get_primitive(type)
440 static gpointer
441 thrasher_blist_get_handle ()
443 static int handle;
444 return &handle;
447 static void
448 no_dot_purple_no_op(void *nope_not_here) {}
450 void thrasher_blist_init()
452 purple_accounts_set_ui_ops(&thrasher_account_uiops);
454 purple_blist_set_ui_ops(&thrasher_blist_uiops);
456 purple_signal_connect(purple_blist_get_handle(),
457 "buddy-status-changed",
458 thrasher_blist_get_handle(),
459 PURPLE_CALLBACK(thrasher_buddy_status_changed),
460 NULL);
462 purple_signal_connect(purple_blist_get_handle(),
463 "buddy-signed-on",
464 thrasher_blist_get_handle(),
465 PURPLE_CALLBACK(thrasher_buddy_signed_on),
466 NULL);
468 purple_signal_connect(purple_blist_get_handle(),
469 "buddy-signed-off",
470 thrasher_blist_get_handle(),
471 PURPLE_CALLBACK(thrasher_buddy_signed_off),
472 NULL);
474 // It looks like this only comes in for things already on our roster.
475 purple_signal_connect(purple_blist_get_handle(),
476 "buddy-added",
477 thrasher_blist_get_handle(),
478 PURPLE_CALLBACK(thrasher_buddy_added),
479 NULL);
482 purple_signal_connect(purple_blist_get_handle(),
483 "buddy-removed",
484 thrasher_blist_get_handle(),
485 PURPLE_CALLBACK(thrasher_buddy_removed),
486 NULL);