1 /* |
|
2 * vcard-manager.c - Source for Gabble vCard lookup helper |
|
3 * |
|
4 * Copyright (C) 2006 Collabora Ltd. |
|
5 * |
|
6 * |
|
7 * This library is free software; you can redistribute it and/or |
|
8 * modify it under the terms of the GNU Lesser General Public |
|
9 * License as published by the Free Software Foundation; either |
|
10 * version 2.1 of the License, or (at your option) any later version. |
|
11 * |
|
12 * This library is distributed in the hope that it will be useful, |
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
15 * Lesser General Public License for more details. |
|
16 * |
|
17 * You should have received a copy of the GNU Lesser General Public |
|
18 * License along with this library; if not, write to the Free Software |
|
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|
20 */ |
|
21 |
|
22 |
|
23 #include <dbus/dbus-glib.h> |
|
24 #include <dbus/dbus-glib-lowlevel.h> |
|
25 |
|
26 #include "debug.h" |
|
27 #include "gabble-connection.h" |
|
28 #include "namespaces.h" |
|
29 #include "telepathy-helpers.h" |
|
30 #include "util.h" |
|
31 #include "vcard-manager.h" |
|
32 |
|
33 #include "gabble_enums.h" |
|
34 |
|
35 #define DBUS_API_SUBJECT_TO_CHANGE |
|
36 #define DEBUG_FLAG GABBLE_DEBUG_VCARD |
|
37 #define DEFAULT_REQUEST_TIMEOUT 20000 |
|
38 |
|
39 #ifdef DEBUG_FLAG |
|
40 //#define DEBUG(format, ...) |
|
41 #define DEBUGGING 0 |
|
42 #define NODE_DEBUG(n, s) |
|
43 #endif /* DEBUG_FLAG */ |
|
44 |
|
45 |
|
46 #ifndef EMULATOR |
|
47 G_DEFINE_TYPE(GabbleVCardManager, gabble_vcard_manager, G_TYPE_OBJECT); |
|
48 #endif |
|
49 |
|
50 /* signal enum */ |
|
51 enum |
|
52 { |
|
53 NICKNAME_UPDATE, |
|
54 LAST_SIGNAL |
|
55 #ifdef EMULATOR |
|
56 = LAST_SIGNAL_VCARD_MGR |
|
57 #endif |
|
58 |
|
59 }; |
|
60 |
|
61 #ifdef EMULATOR |
|
62 #include "libgabble_wsd_solution.h" |
|
63 |
|
64 GET_STATIC_ARRAY_FROM_TLS(signals,gabble_vcard_mgr,guint) |
|
65 #define signals (GET_WSD_VAR_NAME(signals,gabble_vcard_mgr, s)()) |
|
66 |
|
67 GET_STATIC_VAR_FROM_TLS(quark1,gabble_vcard_mgr,GQuark) |
|
68 #define quark1 (*GET_WSD_VAR_NAME(quark1,gabble_vcard_mgr, s)()) |
|
69 |
|
70 GET_STATIC_VAR_FROM_TLS(quark2,gabble_vcard_mgr,GQuark) |
|
71 #define quark2 (*GET_WSD_VAR_NAME(quark2,gabble_vcard_mgr, s)()) |
|
72 |
|
73 GET_STATIC_VAR_FROM_TLS(gabble_vcard_manager_parent_class,gabble_vcard_mgr,gpointer) |
|
74 #define gabble_vcard_manager_parent_class (*GET_WSD_VAR_NAME(gabble_vcard_manager_parent_class,gabble_vcard_mgr,s)()) |
|
75 |
|
76 GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_vcard_mgr,GType) |
|
77 #define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_vcard_mgr,s)()) |
|
78 |
|
79 GET_STATIC_ARRAY_FROM_TLS(NO_ALIAS,gabble_vcard_mgr,gchar) |
|
80 #define NO_ALIAS (GET_WSD_VAR_NAME(NO_ALIAS,gabble_vcard_mgr,s)()) |
|
81 |
|
82 static void gabble_vcard_manager_init (GabbleVCardManager *self); |
|
83 static void gabble_vcard_manager_class_init (GabbleVCardManagerClass *klass); |
|
84 static void gabble_vcard_manager_class_intern_init (gpointer klass) |
|
85 { |
|
86 gabble_vcard_manager_parent_class = g_type_class_peek_parent (klass); |
|
87 gabble_vcard_manager_class_init ((GabbleVCardManagerClass*) klass); |
|
88 } |
|
89 EXPORT_C GType gabble_vcard_manager_get_type (void) |
|
90 { |
|
91 if ((g_define_type_id == 0)) { static const GTypeInfo g_define_type_info = { sizeof (GabbleVCardManagerClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_vcard_manager_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleVCardManager), 0, (GInstanceInitFunc) gabble_vcard_manager_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleVCardManager"), &g_define_type_info, (GTypeFlags) 0); { {} ; } } return g_define_type_id; } ; |
|
92 |
|
93 #else |
|
94 |
|
95 static guint signals[LAST_SIGNAL] = {0}; |
|
96 |
|
97 static const gchar *NO_ALIAS = "none"; |
|
98 |
|
99 #endif |
|
100 |
|
101 |
|
102 /* Properties */ |
|
103 enum |
|
104 { |
|
105 PROP_CONNECTION = 1, |
|
106 LAST_PROPERTY |
|
107 }; |
|
108 |
|
109 |
|
110 |
|
111 typedef struct _GabbleVCardManagerPrivate GabbleVCardManagerPrivate; |
|
112 struct _GabbleVCardManagerPrivate |
|
113 { |
|
114 GabbleConnection *connection; |
|
115 GList *requests; |
|
116 gboolean dispose_has_run; |
|
117 }; |
|
118 |
|
119 struct _GabbleVCardManagerRequest |
|
120 { |
|
121 GabbleVCardManager *manager; |
|
122 guint timer_id; |
|
123 guint timeout; |
|
124 |
|
125 GabbleHandle handle; |
|
126 gchar **edit_args; |
|
127 |
|
128 GabbleVCardManagerCb callback; |
|
129 gpointer user_data; |
|
130 GObject *bound_object; |
|
131 }; |
|
132 |
|
133 GQuark |
|
134 gabble_vcard_manager_error_quark (void) |
|
135 { |
|
136 |
|
137 #ifndef EMULATOR |
|
138 static GQuark quark1 = 0; |
|
139 #endif |
|
140 |
|
141 if (!quark1) |
|
142 quark1 = g_quark_from_static_string ("gabble-vcard-manager-error"); |
|
143 return quark1; |
|
144 } |
|
145 |
|
146 GQuark |
|
147 gabble_vcard_manager_cache_quark (void) |
|
148 { |
|
149 |
|
150 #ifndef EMULATOR |
|
151 static GQuark quark2 = 0; |
|
152 #endif |
|
153 |
|
154 if (!quark2) |
|
155 quark2 = g_quark_from_static_string ("gabble-vcard-manager-cache"); |
|
156 return quark2; |
|
157 } |
|
158 |
|
159 #define GABBLE_VCARD_MANAGER_GET_PRIVATE(o) ((GabbleVCardManagerPrivate*)((o)->priv)); |
|
160 |
|
161 static void |
|
162 gabble_vcard_manager_init (GabbleVCardManager *obj) |
|
163 { |
|
164 GabbleVCardManagerPrivate *priv = |
|
165 G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_VCARD_MANAGER, GabbleVCardManagerPrivate); |
|
166 obj->priv = priv; |
|
167 |
|
168 } |
|
169 |
|
170 static void gabble_vcard_manager_set_property (GObject *object, guint property_id, |
|
171 const GValue *value, GParamSpec *pspec); |
|
172 static void gabble_vcard_manager_get_property (GObject *object, guint property_id, |
|
173 GValue *value, GParamSpec *pspec); |
|
174 static void gabble_vcard_manager_dispose (GObject *object); |
|
175 static void gabble_vcard_manager_finalize (GObject *object); |
|
176 |
|
177 static void |
|
178 gabble_vcard_manager_class_init (GabbleVCardManagerClass *gabble_vcard_manager_class) |
|
179 { |
|
180 GObjectClass *object_class = G_OBJECT_CLASS (gabble_vcard_manager_class); |
|
181 GParamSpec *param_spec; |
|
182 |
|
183 g_type_class_add_private (gabble_vcard_manager_class, sizeof (GabbleVCardManagerPrivate)); |
|
184 |
|
185 object_class->get_property = gabble_vcard_manager_get_property; |
|
186 object_class->set_property = gabble_vcard_manager_set_property; |
|
187 |
|
188 object_class->dispose = gabble_vcard_manager_dispose; |
|
189 object_class->finalize = gabble_vcard_manager_finalize; |
|
190 |
|
191 param_spec = g_param_spec_object ("connection", "GabbleConnection object", |
|
192 "Gabble connection object that owns this " |
|
193 "vCard lookup helper object.", |
|
194 GABBLE_TYPE_CONNECTION, |
|
195 G_PARAM_CONSTRUCT_ONLY | |
|
196 G_PARAM_READWRITE | |
|
197 G_PARAM_STATIC_NICK | |
|
198 G_PARAM_STATIC_BLURB); |
|
199 g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); |
|
200 |
|
201 /* signal definitions */ |
|
202 |
|
203 signals[NICKNAME_UPDATE] = |
|
204 g_signal_new ("nickname-update", |
|
205 G_TYPE_FROM_CLASS (gabble_vcard_manager_class), |
|
206 G_SIGNAL_RUN_LAST, |
|
207 0, |
|
208 NULL, NULL, |
|
209 g_cclosure_marshal_VOID__UINT, |
|
210 G_TYPE_NONE, 1, G_TYPE_UINT); |
|
211 |
|
212 } |
|
213 |
|
214 static void |
|
215 gabble_vcard_manager_get_property (GObject *object, |
|
216 guint property_id, |
|
217 GValue *value, |
|
218 GParamSpec *pspec) |
|
219 { |
|
220 GabbleVCardManager *chan = GABBLE_VCARD_MANAGER (object); |
|
221 GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (chan); |
|
222 |
|
223 switch (property_id) { |
|
224 case PROP_CONNECTION: |
|
225 g_value_set_object (value, priv->connection); |
|
226 break; |
|
227 default: |
|
228 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
|
229 break; |
|
230 } |
|
231 } |
|
232 |
|
233 static void |
|
234 gabble_vcard_manager_set_property (GObject *object, |
|
235 guint property_id, |
|
236 const GValue *value, |
|
237 GParamSpec *pspec) |
|
238 { |
|
239 GabbleVCardManager *chan = GABBLE_VCARD_MANAGER (object); |
|
240 GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (chan); |
|
241 |
|
242 switch (property_id) { |
|
243 case PROP_CONNECTION: |
|
244 priv->connection = g_value_get_object (value); |
|
245 break; |
|
246 default: |
|
247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
|
248 break; |
|
249 } |
|
250 } |
|
251 |
|
252 static void cancel_request (GabbleVCardManagerRequest *request); |
|
253 |
|
254 void |
|
255 gabble_vcard_manager_dispose (GObject *object) |
|
256 { |
|
257 DBusGProxy *bus_proxy; |
|
258 GabbleVCardManager *self = GABBLE_VCARD_MANAGER (object); |
|
259 GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); |
|
260 bus_proxy = tp_get_bus_proxy (); |
|
261 |
|
262 if (priv->dispose_has_run) |
|
263 return; |
|
264 |
|
265 priv->dispose_has_run = TRUE; |
|
266 |
|
267 /* cancel request removes the element from the list after cancelling */ |
|
268 while (priv->requests) |
|
269 cancel_request (priv->requests->data); |
|
270 |
|
271 if (G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->dispose) |
|
272 G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->dispose (object); |
|
273 } |
|
274 |
|
275 void |
|
276 gabble_vcard_manager_finalize (GObject *object) |
|
277 { |
|
278 G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->finalize (object); |
|
279 } |
|
280 |
|
281 static void |
|
282 status_changed_cb (GObject *object, |
|
283 guint status, |
|
284 guint reason, |
|
285 gpointer user_data) |
|
286 { |
|
287 GabbleVCardManager *self = GABBLE_VCARD_MANAGER (user_data); |
|
288 GabbleConnection *conn = GABBLE_CONNECTION (object); |
|
289 |
|
290 if (status == TP_CONN_STATUS_CONNECTED) |
|
291 { |
|
292 gchar *alias; |
|
293 GabbleConnectionAliasSource alias_src; |
|
294 |
|
295 /* if we have a better alias, patch it into our vCard on the server */ |
|
296 alias_src = _gabble_connection_get_cached_alias (conn, |
|
297 conn->self_handle, |
|
298 &alias); |
|
299 if (alias_src > GABBLE_CONNECTION_ALIAS_FROM_VCARD) |
|
300 { |
|
301 /* ignore errors, just kick off the request in the background */ |
|
302 gabble_vcard_manager_edit (self, 0, NULL, NULL, G_OBJECT (conn), |
|
303 NULL, "NICKNAME", alias, NULL); |
|
304 } |
|
305 else |
|
306 { |
|
307 /* find out our own alias, so it's in the cache; again, |
|
308 * there's nothing useful we can do with errors really |
|
309 */ |
|
310 gabble_vcard_manager_request (self, conn->self_handle, |
|
311 0, NULL, NULL, NULL, NULL); |
|
312 } |
|
313 |
|
314 g_free(alias); |
|
315 } |
|
316 } |
|
317 |
|
318 /** |
|
319 * gabble_vcard_manager_new: |
|
320 * @conn: The #GabbleConnection to use for vCard lookup |
|
321 * |
|
322 * Creates an object to use for Jabber vCard lookup (JEP 0054). |
|
323 * There should be one of these per connection |
|
324 */ |
|
325 GabbleVCardManager * |
|
326 gabble_vcard_manager_new (GabbleConnection *conn) |
|
327 { |
|
328 GabbleVCardManager *self; |
|
329 |
|
330 g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); |
|
331 |
|
332 self = GABBLE_VCARD_MANAGER (g_object_new (GABBLE_TYPE_VCARD_MANAGER, "connection", conn, NULL)); |
|
333 g_signal_connect (conn, "status-changed", |
|
334 G_CALLBACK (status_changed_cb), self); |
|
335 return self; |
|
336 } |
|
337 |
|
338 static void notify_delete_request (gpointer data, GObject *obj); |
|
339 |
|
340 static void |
|
341 delete_request (GabbleVCardManagerRequest *request) |
|
342 { |
|
343 GabbleVCardManager *manager = request->manager; |
|
344 GabbleVCardManagerPrivate *priv; |
|
345 |
|
346 gabble_debug (DEBUG_FLAG, "Discarding request %p", request); |
|
347 |
|
348 g_assert (NULL != request); |
|
349 g_assert (GABBLE_IS_VCARD_MANAGER (manager)); |
|
350 |
|
351 priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); |
|
352 |
|
353 g_assert (NULL != g_list_find (priv->requests, request)); |
|
354 |
|
355 priv->requests = g_list_remove (priv->requests, request); |
|
356 |
|
357 if (NULL != request->bound_object) |
|
358 { |
|
359 g_object_weak_unref (request->bound_object, notify_delete_request, request); |
|
360 } |
|
361 |
|
362 if (0 != request->timer_id) |
|
363 { |
|
364 g_source_remove (request->timer_id); |
|
365 } |
|
366 |
|
367 gabble_handle_unref (priv->connection->handles, TP_HANDLE_TYPE_CONTACT, |
|
368 request->handle); |
|
369 g_strfreev (request->edit_args); |
|
370 |
|
371 g_free (request); |
|
372 } |
|
373 |
|
374 static gboolean |
|
375 timeout_request (gpointer data) |
|
376 { |
|
377 GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest*) data; |
|
378 GError *err; |
|
379 g_return_val_if_fail (data != NULL, FALSE); |
|
380 |
|
381 err = g_error_new (GABBLE_VCARD_MANAGER_ERROR, GABBLE_VCARD_MANAGER_ERROR_TIMEOUT, |
|
382 "Request timed out"); |
|
383 gabble_debug (DEBUG_FLAG, "Request %p timed out, notifying callback %p", |
|
384 request, request->callback); |
|
385 if (request->callback) |
|
386 { |
|
387 (request->callback)(request->manager, request, request->handle, |
|
388 NULL, err, request->user_data); |
|
389 } |
|
390 g_error_free (err); |
|
391 |
|
392 request->timer_id = 0; |
|
393 delete_request (request); |
|
394 return FALSE; |
|
395 } |
|
396 |
|
397 static void |
|
398 cancel_request (GabbleVCardManagerRequest *request) |
|
399 { |
|
400 GError *err; |
|
401 |
|
402 g_assert (request != NULL); |
|
403 |
|
404 err = g_error_new (GABBLE_VCARD_MANAGER_ERROR, GABBLE_VCARD_MANAGER_ERROR_CANCELLED, |
|
405 "Request cancelled"); |
|
406 gabble_debug (DEBUG_FLAG, "Request %p cancelled, notifying callback %p", |
|
407 request, request->callback); |
|
408 if (request->callback) |
|
409 { |
|
410 (request->callback)(request->manager, request, request->handle, |
|
411 NULL, err, request->user_data); |
|
412 } |
|
413 g_error_free (err); |
|
414 |
|
415 delete_request (request); |
|
416 } |
|
417 |
|
418 static void |
|
419 observe_vcard (GabbleConnection *conn, GabbleVCardManager *manager, |
|
420 GabbleHandle handle, LmMessageNode *vcard_node) |
|
421 { |
|
422 LmMessageNode *nick_node = lm_message_node_get_child (vcard_node, |
|
423 "NICKNAME"); |
|
424 |
|
425 gabble_debug (DEBUG_FLAG, "Observing vCard for %u", handle); |
|
426 NODE_DEBUG(vcard_node, "their vCard is"); |
|
427 |
|
428 if (nick_node) |
|
429 { |
|
430 const gchar *nick = lm_message_node_get_value (nick_node); |
|
431 |
|
432 gabble_debug (DEBUG_FLAG, "%u has <NICKNAME> \"%s\"", handle, nick ? nick : "(null)"); |
|
433 |
|
434 if (nick && *nick) |
|
435 { |
|
436 /* nicknames are comma-separated, let's use the first one */ |
|
437 gchar **bits = g_strsplit (nick, ",", 2); |
|
438 |
|
439 if (bits[0]) |
|
440 { |
|
441 gchar *alias = g_strdup (bits[0]); |
|
442 |
|
443 gabble_debug (DEBUG_FLAG, "... using \"%s\" as their alias", alias); |
|
444 |
|
445 g_signal_emit (G_OBJECT (manager), signals[NICKNAME_UPDATE], |
|
446 0, handle); |
|
447 if (!gabble_handle_set_qdata (conn->handles, |
|
448 TP_HANDLE_TYPE_CONTACT, |
|
449 handle, |
|
450 gabble_vcard_manager_cache_quark(), |
|
451 alias, g_free)) |
|
452 { |
|
453 gabble_debug (DEBUG_FLAG, "failed to cache their alias"); |
|
454 g_free (alias); |
|
455 } |
|
456 |
|
457 } |
|
458 |
|
459 g_strfreev (bits); |
|
460 } |
|
461 } |
|
462 else |
|
463 { |
|
464 const gchar *fn = NULL; |
|
465 /* let's see if they have a FN (formatted name) instead */ |
|
466 nick_node = lm_message_node_get_child (vcard_node, "FN"); |
|
467 if (nick_node) |
|
468 fn = lm_message_node_get_value (nick_node); |
|
469 gabble_debug (DEBUG_FLAG, "%u has no <NICKNAME>, but has <FN> \"%s\"", handle, |
|
470 fn ? fn : "(null)"); |
|
471 if (fn && *fn) |
|
472 { |
|
473 gchar *alias = g_strdup (fn); |
|
474 |
|
475 gabble_debug (DEBUG_FLAG, "... using \"%s\" as their alias", alias); |
|
476 |
|
477 g_signal_emit (G_OBJECT (manager), signals[NICKNAME_UPDATE], |
|
478 0, handle); |
|
479 if (!gabble_handle_set_qdata (conn->handles, |
|
480 TP_HANDLE_TYPE_CONTACT, |
|
481 handle, |
|
482 gabble_vcard_manager_cache_quark(), |
|
483 alias, g_free)) |
|
484 { |
|
485 gabble_debug (DEBUG_FLAG, "failed to cache their alias"); |
|
486 g_free (alias); |
|
487 } |
|
488 } |
|
489 else |
|
490 { |
|
491 /* remember that they don't have an alias */ |
|
492 if (!gabble_handle_set_qdata (conn->handles, |
|
493 TP_HANDLE_TYPE_CONTACT, |
|
494 handle, |
|
495 gabble_vcard_manager_cache_quark (), |
|
496 (gchar *) NO_ALIAS, NULL)) |
|
497 gabble_debug (DEBUG_FLAG, "failed to cache their lack of vcard alias"); |
|
498 } |
|
499 |
|
500 } |
|
501 } |
|
502 |
|
503 static GabbleVCardManagerRequest *request_send (GabbleVCardManagerRequest *, |
|
504 LmMessageNode *replacement, |
|
505 const gchar *jid, |
|
506 GError **); |
|
507 |
|
508 static LmHandlerResult |
|
509 replace_reply_cb (GabbleConnection *conn, LmMessage *sent_msg, |
|
510 LmMessage *reply_msg, GObject *object, gpointer user_data) |
|
511 { |
|
512 LmMessageNode *vcard_node = NULL; |
|
513 GError *err = NULL; |
|
514 GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest*) user_data; |
|
515 GabbleVCardManager *manager = GABBLE_VCARD_MANAGER (object); |
|
516 GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); |
|
517 |
|
518 g_assert (request); |
|
519 |
|
520 gabble_debug (DEBUG_FLAG, "Replace request got a reply: conn@%p, sent_msg@%p, reply_msg@%p, " |
|
521 "bound object@%p, request@%p", conn, sent_msg, reply_msg, object, |
|
522 user_data); |
|
523 |
|
524 if (!g_list_find (priv->requests, request)) |
|
525 { |
|
526 gabble_debug (DEBUG_FLAG, "I don't care about that request any more"); |
|
527 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
528 } |
|
529 |
|
530 if (lm_message_get_sub_type (reply_msg) == LM_MESSAGE_SUB_TYPE_ERROR) |
|
531 { |
|
532 LmMessageNode *error_node; |
|
533 |
|
534 error_node = lm_message_node_get_child (reply_msg->node, "error"); |
|
535 if (error_node) |
|
536 { |
|
537 err = gabble_xmpp_error_to_g_error ( |
|
538 gabble_xmpp_error_from_node (error_node)); |
|
539 } |
|
540 |
|
541 if (err == NULL) |
|
542 { |
|
543 err = g_error_new (GABBLE_VCARD_MANAGER_ERROR, |
|
544 GABBLE_VCARD_MANAGER_ERROR_UNKNOWN, |
|
545 "an unknown error occurred"); |
|
546 } |
|
547 } |
|
548 else |
|
549 { |
|
550 vcard_node = lm_message_node_get_child (sent_msg->node, "vCard"); |
|
551 } |
|
552 |
|
553 gabble_debug (DEBUG_FLAG, "Request %p %s, notifying callback %p", request, |
|
554 err ? "failed" : "succeeded", request->callback); |
|
555 if (request->callback) |
|
556 { |
|
557 request->callback (request->manager, request, request->handle, |
|
558 vcard_node, err, request->user_data); |
|
559 } |
|
560 delete_request (request); |
|
561 |
|
562 if (err) |
|
563 g_error_free (err); |
|
564 |
|
565 return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
566 } |
|
567 |
|
568 static LmHandlerResult |
|
569 request_reply_cb (GabbleConnection *conn, |
|
570 LmMessage *sent_msg, |
|
571 LmMessage *reply_msg, |
|
572 GObject *object, |
|
573 gpointer user_data) |
|
574 { |
|
575 LmMessageNode *vcard_node = NULL; |
|
576 GError *err = NULL; |
|
577 GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest*) user_data; |
|
578 GabbleVCardManager *manager = GABBLE_VCARD_MANAGER (object); |
|
579 GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); |
|
580 |
|
581 g_assert (request); |
|
582 |
|
583 gabble_debug (DEBUG_FLAG, "Fetch request got a reply: conn@%p, sent_msg@%p, reply_msg@%p, " |
|
584 "bound object@%p, request@%p", conn, sent_msg, reply_msg, object, |
|
585 user_data); |
|
586 |
|
587 if (!g_list_find (priv->requests, request)) |
|
588 { |
|
589 gabble_debug (DEBUG_FLAG, "I don't care about that request any more"); |
|
590 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
591 } |
|
592 |
|
593 |
|
594 if (lm_message_get_sub_type (reply_msg) == LM_MESSAGE_SUB_TYPE_ERROR) |
|
595 { |
|
596 LmMessageNode *error_node; |
|
597 |
|
598 error_node = lm_message_node_get_child (reply_msg->node, "error"); |
|
599 if (error_node) |
|
600 { |
|
601 err = gabble_xmpp_error_to_g_error ( |
|
602 gabble_xmpp_error_from_node (error_node)); |
|
603 } |
|
604 |
|
605 if (err == NULL) |
|
606 { |
|
607 err = g_error_new (GABBLE_VCARD_MANAGER_ERROR, |
|
608 GABBLE_VCARD_MANAGER_ERROR_UNKNOWN, |
|
609 "an unknown error occurred"); |
|
610 } |
|
611 } |
|
612 else |
|
613 { |
|
614 vcard_node = lm_message_node_get_child (reply_msg->node, "vCard"); |
|
615 |
|
616 if (NULL == vcard_node) |
|
617 { |
|
618 gabble_debug (DEBUG_FLAG, "successful lookup response contained no <vCard> node, " |
|
619 "creating an empty one"); |
|
620 |
|
621 vcard_node = lm_message_node_add_child (reply_msg->node, "vCard", |
|
622 NULL); |
|
623 lm_message_node_set_attribute (vcard_node, "xmlns", NS_VCARD_TEMP); |
|
624 } |
|
625 |
|
626 observe_vcard (conn, manager, request->handle, vcard_node); |
|
627 } |
|
628 |
|
629 if (vcard_node && request->edit_args) |
|
630 { |
|
631 gchar **ptr; |
|
632 for (ptr = request->edit_args; *ptr; ptr++) |
|
633 { |
|
634 gchar *key = *ptr; |
|
635 gchar *value = *(++ptr); |
|
636 LmMessageNode *node; |
|
637 |
|
638 if (!value) |
|
639 { |
|
640 /* oops, someone passed in an odd number of args. */ |
|
641 g_assert_not_reached (); |
|
642 break; |
|
643 } |
|
644 |
|
645 node = lm_message_node_get_child (vcard_node, key); |
|
646 if (node) |
|
647 { |
|
648 lm_message_node_set_value (node, value); |
|
649 } |
|
650 else |
|
651 { |
|
652 node = lm_message_node_add_child (vcard_node, key, value); |
|
653 } |
|
654 } |
|
655 |
|
656 request_send (request, vcard_node, NULL, &err); |
|
657 |
|
658 if (err) |
|
659 { |
|
660 gabble_debug (DEBUG_FLAG, "Request %p failed, notifying callback %p", |
|
661 request, request->callback); |
|
662 if (request->callback) |
|
663 { |
|
664 request->callback (request->manager, request, request->handle, |
|
665 NULL, err, request->user_data); |
|
666 } |
|
667 } |
|
668 else |
|
669 { |
|
670 gabble_debug (DEBUG_FLAG, "Request %p fetch succeeded", request); |
|
671 /* early return to avoid deleting the request */ |
|
672 return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
673 } |
|
674 } |
|
675 else |
|
676 { |
|
677 gabble_debug (DEBUG_FLAG, "Request %p %s, notifying callback %p", |
|
678 request, err ? "failed" : "succeeded", request->callback); |
|
679 if (request->callback) |
|
680 { |
|
681 request->callback (request->manager, request, request->handle, |
|
682 vcard_node, err, request->user_data); |
|
683 } |
|
684 } |
|
685 |
|
686 delete_request (request); |
|
687 |
|
688 if (err) |
|
689 g_error_free (err); |
|
690 |
|
691 return LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
692 } |
|
693 |
|
694 /* If @replacement is NULL sends a request, calling request_reply_cb when |
|
695 * it returns. |
|
696 * |
|
697 * Otherwise steals its children and sends an update, calling |
|
698 * replace_reply_cb when it returns. |
|
699 * |
|
700 * Frees the @request on error, returns it on success. */ |
|
701 static GabbleVCardManagerRequest * |
|
702 request_send (GabbleVCardManagerRequest *request, |
|
703 LmMessageNode *replacement, |
|
704 const gchar *jid, |
|
705 GError **error) |
|
706 { |
|
707 LmMessage *msg; |
|
708 LmMessageNode *lm_node; |
|
709 GabbleVCardManager *self = request->manager; |
|
710 GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); |
|
711 |
|
712 gabble_debug (DEBUG_FLAG, "Sending off request %p to %s for %s", request, |
|
713 replacement ? "replace vCard" : "retrieve vCard", |
|
714 jid ? jid : "myself"); |
|
715 msg = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ, |
|
716 (replacement |
|
717 ? LM_MESSAGE_SUB_TYPE_SET |
|
718 : LM_MESSAGE_SUB_TYPE_GET)); |
|
719 lm_node = lm_message_node_add_child (msg->node, "vCard", NULL); |
|
720 lm_message_node_set_attribute (lm_node, "xmlns", NS_VCARD_TEMP); |
|
721 |
|
722 if (replacement) |
|
723 lm_message_node_steal_children (lm_node, replacement); |
|
724 |
|
725 if (! _gabble_connection_send_with_reply (priv->connection, msg, |
|
726 (replacement ? replace_reply_cb : request_reply_cb), |
|
727 G_OBJECT(self), request, error)) |
|
728 { |
|
729 delete_request (request); |
|
730 lm_message_unref (msg); |
|
731 return NULL; |
|
732 } |
|
733 else |
|
734 { |
|
735 if (0 == request->timer_id) |
|
736 { |
|
737 request->timer_id = |
|
738 g_timeout_add (request->timeout, timeout_request, request); |
|
739 } |
|
740 lm_message_unref (msg); |
|
741 return request; |
|
742 } |
|
743 } |
|
744 |
|
745 static void |
|
746 notify_delete_request (gpointer data, GObject *obj) |
|
747 { |
|
748 GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest *) data; |
|
749 request->bound_object = NULL; |
|
750 delete_request (request); |
|
751 } |
|
752 |
|
753 /* Request the vCard for the given handle. When it arrives, call the given |
|
754 * callback. |
|
755 * |
|
756 * The callback may be NULL if you just want the side-effect of this |
|
757 * operation, which is to update the cached alias. |
|
758 */ |
|
759 GabbleVCardManagerRequest * |
|
760 gabble_vcard_manager_request (GabbleVCardManager *self, |
|
761 GabbleHandle handle, |
|
762 guint timeout, |
|
763 GabbleVCardManagerCb callback, |
|
764 gpointer user_data, |
|
765 GObject *object, |
|
766 GError **error) |
|
767 { |
|
768 GabbleVCardManagerRequest *request; |
|
769 const gchar *jid; |
|
770 GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); |
|
771 |
|
772 if (timeout == 0) |
|
773 timeout = DEFAULT_REQUEST_TIMEOUT; |
|
774 |
|
775 request = g_new0 (GabbleVCardManagerRequest, 1); |
|
776 gabble_debug (DEBUG_FLAG, "Created request %p to retrieve <%u>'s vCard", |
|
777 request, handle); |
|
778 request->timeout = timeout; |
|
779 request->manager = self; |
|
780 gabble_handle_ref (priv->connection->handles, TP_HANDLE_TYPE_CONTACT, |
|
781 handle); |
|
782 request->handle = handle; |
|
783 request->callback = callback; |
|
784 request->user_data = user_data; |
|
785 request->bound_object = object; |
|
786 |
|
787 if (NULL != object) |
|
788 g_object_weak_ref (object, notify_delete_request, request); |
|
789 |
|
790 priv->requests = g_list_prepend (priv->requests, request); |
|
791 if (handle == priv->connection->self_handle) |
|
792 { |
|
793 jid = NULL; |
|
794 } |
|
795 else |
|
796 { |
|
797 jid = gabble_handle_inspect (priv->connection->handles, |
|
798 TP_HANDLE_TYPE_CONTACT, handle); |
|
799 } |
|
800 |
|
801 return request_send (request, NULL, jid, error); |
|
802 } |
|
803 |
|
804 GabbleVCardManagerRequest * |
|
805 gabble_vcard_manager_replace (GabbleVCardManager *self, |
|
806 LmMessageNode *replacement, |
|
807 guint timeout, |
|
808 GabbleVCardManagerCb callback, |
|
809 gpointer user_data, |
|
810 GObject *object, |
|
811 GError **error) |
|
812 { |
|
813 GabbleVCardManagerRequest *request; |
|
814 GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); |
|
815 |
|
816 if (timeout == 0) |
|
817 timeout = DEFAULT_REQUEST_TIMEOUT; |
|
818 |
|
819 request = g_new0 (GabbleVCardManagerRequest, 1); |
|
820 gabble_debug (DEBUG_FLAG, "Created request %p to replace my vCard", |
|
821 request); |
|
822 request->timeout = timeout; |
|
823 request->manager = self; |
|
824 gabble_handle_ref (priv->connection->handles, TP_HANDLE_TYPE_CONTACT, |
|
825 priv->connection->self_handle); |
|
826 request->handle = priv->connection->self_handle; |
|
827 request->callback = callback; |
|
828 request->user_data = user_data; |
|
829 request->bound_object = object; |
|
830 |
|
831 if (NULL != object) |
|
832 g_object_weak_ref (object, notify_delete_request, request); |
|
833 |
|
834 priv->requests = g_list_prepend (priv->requests, request); |
|
835 |
|
836 return request_send (request, replacement, NULL, error); |
|
837 } |
|
838 |
|
839 GabbleVCardManagerRequest * |
|
840 gabble_vcard_manager_edit (GabbleVCardManager *self, |
|
841 guint timeout, |
|
842 GabbleVCardManagerCb callback, |
|
843 gpointer user_data, |
|
844 GObject *object, |
|
845 GError **error, |
|
846 ...) |
|
847 { |
|
848 va_list ap; |
|
849 size_t i, argc; |
|
850 GabbleVCardManagerRequest *request; |
|
851 GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); |
|
852 |
|
853 if (timeout == 0) |
|
854 timeout = DEFAULT_REQUEST_TIMEOUT; |
|
855 |
|
856 request = g_new0 (GabbleVCardManagerRequest, 1); |
|
857 gabble_debug (DEBUG_FLAG, "Created request %p to edit my vCard", request); |
|
858 request->timeout = timeout; |
|
859 request->manager = self; |
|
860 gabble_handle_ref (priv->connection->handles, TP_HANDLE_TYPE_CONTACT, |
|
861 priv->connection->self_handle); |
|
862 request->handle = priv->connection->self_handle; |
|
863 request->callback = callback; |
|
864 request->user_data = user_data; |
|
865 request->bound_object = object; |
|
866 |
|
867 if (NULL != object) |
|
868 g_object_weak_ref (object, notify_delete_request, request); |
|
869 |
|
870 priv->requests = g_list_prepend (priv->requests, request); |
|
871 |
|
872 argc = 0; |
|
873 va_start (ap, error); |
|
874 while (va_arg (ap, const gchar *) != NULL) |
|
875 { |
|
876 argc++; |
|
877 } |
|
878 va_end (ap); |
|
879 g_return_val_if_fail (argc % 2 == 0, NULL); |
|
880 |
|
881 request->edit_args = g_new (gchar *, argc + 1); |
|
882 |
|
883 va_start (ap, error); |
|
884 for (i = 0; i < argc; i++) |
|
885 { |
|
886 request->edit_args[i] = g_strdup (va_arg (ap, const gchar *)); |
|
887 } |
|
888 request->edit_args[argc] = NULL; |
|
889 va_end (ap); |
|
890 |
|
891 return request_send (request, NULL, NULL, error); |
|
892 } |
|
893 |
|
894 void |
|
895 gabble_vcard_manager_cancel_request (GabbleVCardManager *manager, |
|
896 GabbleVCardManagerRequest *request) |
|
897 { |
|
898 GabbleVCardManagerPrivate *priv; |
|
899 |
|
900 g_return_if_fail (GABBLE_IS_VCARD_MANAGER (manager)); |
|
901 g_return_if_fail (NULL != request); |
|
902 |
|
903 priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); |
|
904 |
|
905 g_return_if_fail (NULL != g_list_find (priv->requests, request)); |
|
906 |
|
907 cancel_request (request); |
|
908 } |
|
909 |
|
910 /** |
|
911 * Return the cached alias derived from the vCard for the given handle, |
|
912 * if any. If there is no cached alias, return NULL. |
|
913 */ |
|
914 const gchar * |
|
915 gabble_vcard_manager_get_cached_alias (GabbleVCardManager *manager, |
|
916 GabbleHandle handle) |
|
917 { |
|
918 GabbleVCardManagerPrivate *priv; |
|
919 const gchar *s; |
|
920 |
|
921 g_return_val_if_fail (GABBLE_IS_VCARD_MANAGER (manager), NULL); |
|
922 |
|
923 priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); |
|
924 |
|
925 s = gabble_handle_get_qdata (priv->connection->handles, |
|
926 TP_HANDLE_TYPE_CONTACT, |
|
927 handle, |
|
928 gabble_vcard_manager_cache_quark()); |
|
929 |
|
930 if (s == NO_ALIAS) |
|
931 s = NULL; |
|
932 |
|
933 gabble_debug (DEBUG_FLAG, "Cached alias for %u is \"%s\"", handle, s ? s : "(null)"); |
|
934 return s; |
|
935 } |
|
936 |
|
937 /** |
|
938 * Return TRUE if we've tried looking up an alias for this handle before. |
|
939 */ |
|
940 gboolean |
|
941 gabble_vcard_manager_has_cached_alias (GabbleVCardManager *manager, |
|
942 GabbleHandle handle) |
|
943 { |
|
944 GabbleVCardManagerPrivate *priv; |
|
945 gpointer p; |
|
946 |
|
947 g_return_val_if_fail (GABBLE_IS_VCARD_MANAGER (manager), FALSE); |
|
948 |
|
949 priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); |
|
950 p = gabble_handle_get_qdata (priv->connection->handles, |
|
951 TP_HANDLE_TYPE_CONTACT, |
|
952 handle, |
|
953 gabble_vcard_manager_cache_quark()); |
|
954 return p != NULL; |
|
955 } |
|
956 |
|