1 /* |
|
2 * gabble-presence-cache.c - Gabble's contact presence cache |
|
3 * Copyright (C) 2005 Collabora Ltd. |
|
4 * and/or its subsidiaries. All rights reserved. |
|
5 * |
|
6 * This library is free software; you can redistribute it and/or |
|
7 * modify it under the terms of the GNU Lesser General Public |
|
8 * License as published by the Free Software Foundation; either |
|
9 * version 2.1 of the License, or (at your option) any later version. |
|
10 * |
|
11 * This library is distributed in the hope that it will be useful, |
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 * Lesser General Public License for more details. |
|
15 * |
|
16 * You should have received a copy of the GNU Lesser General Public |
|
17 * License along with this library; if not, write to the Free Software |
|
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|
19 */ |
|
20 |
|
21 #include <stdlib.h> |
|
22 #include <string.h> |
|
23 |
|
24 #include "debug.h" |
|
25 #include "disco.h" /* \o\ \o/ /o/ */ |
|
26 #include "gabble-presence.h" |
|
27 #include "namespaces.h" |
|
28 #include "util.h" |
|
29 #include "handle-set.h" |
|
30 #include "gintset.h" |
|
31 |
|
32 #include "gabble-presence-cache.h" |
|
33 |
|
34 #include "gabble-presence-cache-signals-marshal.h" |
|
35 |
|
36 #include "gabble_enums.h" |
|
37 |
|
38 #ifndef EMULATOR |
|
39 G_DEFINE_TYPE (GabblePresenceCache, gabble_presence_cache, G_TYPE_OBJECT); |
|
40 #endif |
|
41 |
|
42 /* when five DIFFERENT guys report the same caps for a given bundle, it'll be enough */ |
|
43 #define CAPABILITY_BUNDLE_ENOUGH_TRUST 5 |
|
44 #define DEBUG_FLAG GABBLE_DEBUG_PRESENCE |
|
45 |
|
46 #ifdef DEBUG_FLAG |
|
47 //#define DEBUG(format, ...) |
|
48 #define DEBUGGING 0 |
|
49 #define NODE_DEBUG(n, s) |
|
50 #endif /* DEBUG_FLAG */ |
|
51 |
|
52 /* properties */ |
|
53 enum |
|
54 { |
|
55 PROP_CONNECTION = 1, |
|
56 LAST_PROPERTY |
|
57 }; |
|
58 |
|
59 /* signal enum */ |
|
60 enum |
|
61 { |
|
62 PRESENCE_UPDATE, |
|
63 NICKNAME_UPDATE, |
|
64 CAPABILITIES_UPDATE, |
|
65 LAST_SIGNAL |
|
66 #ifdef EMULATOR |
|
67 = LAST_SIGNAL_PRE_CACHE |
|
68 #endif |
|
69 |
|
70 }; |
|
71 |
|
72 |
|
73 #ifdef EMULATOR |
|
74 #include "libgabble_wsd_solution.h" |
|
75 |
|
76 GET_STATIC_ARRAY_FROM_TLS(signals,gabble_pre_cache,guint) |
|
77 #define signals (GET_WSD_VAR_NAME(signals,gabble_pre_cache, s)()) |
|
78 |
|
79 GET_STATIC_VAR_FROM_TLS(gabble_presence_cache_parent_class,gabble_pre_cache,gpointer) |
|
80 #define gabble_presence_cache_parent_class (*GET_WSD_VAR_NAME(gabble_presence_cache_parent_class,gabble_pre_cache,s)()) |
|
81 |
|
82 GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_pre_cache,GType) |
|
83 #define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_pre_cache,s)()) |
|
84 |
|
85 |
|
86 static void gabble_presence_cache_init (GabblePresenceCache *self); |
|
87 static void gabble_presence_cache_class_init (GabblePresenceCacheClass *klass); |
|
88 static void gabble_presence_cache_class_intern_init (gpointer klass) |
|
89 { |
|
90 gabble_presence_cache_parent_class = g_type_class_peek_parent (klass); |
|
91 gabble_presence_cache_class_init ((GabblePresenceCacheClass*) klass); |
|
92 } |
|
93 EXPORT_C GType gabble_presence_cache_get_type (void) |
|
94 { |
|
95 if ((g_define_type_id == 0)) |
|
96 { static const GTypeInfo g_define_type_info = { sizeof (GabblePresenceCacheClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_presence_cache_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabblePresenceCache), 0, (GInstanceInitFunc) gabble_presence_cache_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabblePresenceCache"), &g_define_type_info, (GTypeFlags) 0); { {} ; } } return g_define_type_id; |
|
97 }; |
|
98 |
|
99 |
|
100 #else |
|
101 |
|
102 static guint signals[LAST_SIGNAL] = {0}; |
|
103 |
|
104 #endif |
|
105 |
|
106 |
|
107 #define GABBLE_PRESENCE_CACHE_PRIV(account) ((GabblePresenceCachePrivate *)account->priv) |
|
108 |
|
109 typedef struct _GabblePresenceCachePrivate GabblePresenceCachePrivate; |
|
110 |
|
111 struct _GabblePresenceCachePrivate |
|
112 { |
|
113 GabbleConnection *conn; |
|
114 |
|
115 gulong status_changed_cb; |
|
116 LmMessageHandler *lm_message_cb; |
|
117 |
|
118 GHashTable *presence; |
|
119 GabbleHandleSet *presence_handles; |
|
120 |
|
121 GHashTable *capabilities; |
|
122 GHashTable *disco_pending; |
|
123 guint caps_serial; |
|
124 |
|
125 gboolean dispose_has_run; |
|
126 }; |
|
127 |
|
128 typedef struct _DiscoWaiter DiscoWaiter; |
|
129 |
|
130 struct _DiscoWaiter |
|
131 { |
|
132 GabbleHandleRepo *repo; |
|
133 GabbleHandle handle; |
|
134 gchar *resource; |
|
135 guint serial; |
|
136 gboolean disco_requested; |
|
137 }; |
|
138 |
|
139 /** |
|
140 * disco_waiter_new () |
|
141 */ |
|
142 static DiscoWaiter * |
|
143 disco_waiter_new (GabbleHandleRepo *repo, GabbleHandle handle, const gchar *resource, guint serial) |
|
144 { |
|
145 DiscoWaiter *waiter; |
|
146 |
|
147 g_assert (repo); |
|
148 gabble_handle_ref (repo, TP_HANDLE_TYPE_CONTACT, handle); |
|
149 |
|
150 waiter = g_new0 (DiscoWaiter, 1); |
|
151 waiter->repo = repo; |
|
152 waiter->handle = handle; |
|
153 waiter->resource = g_strdup (resource); |
|
154 waiter->serial = serial; |
|
155 |
|
156 gabble_debug (DEBUG_FLAG, "created waiter %p for handle %u with serial %u", waiter, handle, serial); |
|
157 |
|
158 return waiter; |
|
159 } |
|
160 |
|
161 static void |
|
162 disco_waiter_free (DiscoWaiter *waiter) |
|
163 { |
|
164 g_assert (NULL != waiter); |
|
165 |
|
166 gabble_debug (DEBUG_FLAG, "freeing waiter %p for handle %u with serial %u", waiter, waiter->handle, waiter->serial); |
|
167 |
|
168 gabble_handle_unref (waiter->repo, TP_HANDLE_TYPE_CONTACT, waiter->handle); |
|
169 |
|
170 g_free (waiter->resource); |
|
171 g_free (waiter); |
|
172 } |
|
173 |
|
174 static void |
|
175 disco_waiter_list_free (GSList *list) |
|
176 { |
|
177 GSList *i; |
|
178 |
|
179 gabble_debug (DEBUG_FLAG, "list %p", list); |
|
180 |
|
181 for (i = list; NULL != i; i = i->next) |
|
182 disco_waiter_free ((DiscoWaiter *) i->data); |
|
183 |
|
184 g_slist_free (list); |
|
185 } |
|
186 |
|
187 static guint |
|
188 disco_waiter_list_get_request_count (GSList *list) |
|
189 { |
|
190 guint c = 0; |
|
191 GSList *i; |
|
192 |
|
193 for (i = list; i; i = i->next) |
|
194 { |
|
195 DiscoWaiter *waiter = (DiscoWaiter *) i->data; |
|
196 |
|
197 if (waiter->disco_requested) |
|
198 c++; |
|
199 } |
|
200 |
|
201 return c; |
|
202 } |
|
203 |
|
204 typedef struct _CapabilityInfo CapabilityInfo; |
|
205 |
|
206 struct _CapabilityInfo |
|
207 { |
|
208 GabblePresenceCapabilities caps; |
|
209 GIntSet *guys; |
|
210 guint trust; |
|
211 }; |
|
212 |
|
213 static CapabilityInfo * |
|
214 capability_info_get (GabblePresenceCache *cache, const gchar *node, |
|
215 GabblePresenceCapabilities caps) |
|
216 { |
|
217 GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
218 CapabilityInfo *info = g_hash_table_lookup (priv->capabilities, node); |
|
219 |
|
220 if (NULL == info) |
|
221 { |
|
222 info = g_new0 (CapabilityInfo, 1); |
|
223 info->caps = caps; |
|
224 info->guys = g_intset_new (); |
|
225 g_hash_table_insert (priv->capabilities, g_strdup (node), info); |
|
226 } |
|
227 |
|
228 return info; |
|
229 } |
|
230 |
|
231 static guint |
|
232 capability_info_recvd (GabblePresenceCache *cache, const gchar *node, |
|
233 GabbleHandle handle, GabblePresenceCapabilities caps) |
|
234 { |
|
235 CapabilityInfo *info = capability_info_get (cache, node, caps); |
|
236 |
|
237 /* Detect inconsistency in reported caps */ |
|
238 if (info->caps != caps) |
|
239 { |
|
240 g_intset_clear (info->guys); |
|
241 info->caps = caps; |
|
242 info->trust = 0; |
|
243 } |
|
244 |
|
245 if (!g_intset_is_member (info->guys, handle)) |
|
246 { |
|
247 g_intset_add (info->guys, handle); |
|
248 info->trust++; |
|
249 } |
|
250 |
|
251 return info->trust; |
|
252 } |
|
253 |
|
254 static void gabble_presence_cache_init (GabblePresenceCache *presence_cache); |
|
255 static GObject * gabble_presence_cache_constructor (GType type, guint n_props, |
|
256 GObjectConstructParam *props); |
|
257 static void gabble_presence_cache_dispose (GObject *object); |
|
258 static void gabble_presence_cache_finalize (GObject *object); |
|
259 static void gabble_presence_cache_set_property (GObject *object, guint |
|
260 property_id, const GValue *value, GParamSpec *pspec); |
|
261 static void gabble_presence_cache_get_property (GObject *object, guint |
|
262 property_id, GValue *value, GParamSpec *pspec); |
|
263 static GabblePresence *_cache_insert (GabblePresenceCache *cache, |
|
264 GabbleHandle handle); |
|
265 |
|
266 static void gabble_presence_cache_status_changed_cb (GabbleConnection *, |
|
267 TpConnectionStatus, TpConnectionStatusReason, gpointer); |
|
268 static LmHandlerResult gabble_presence_cache_lm_message_cb (LmMessageHandler*, |
|
269 LmConnection*, LmMessage*, gpointer); |
|
270 |
|
271 static void |
|
272 gabble_presence_cache_class_init (GabblePresenceCacheClass *klass) |
|
273 { |
|
274 GObjectClass *object_class = G_OBJECT_CLASS (klass); |
|
275 GParamSpec *param_spec; |
|
276 |
|
277 g_type_class_add_private (object_class, sizeof (GabblePresenceCachePrivate)); |
|
278 |
|
279 object_class->constructor = gabble_presence_cache_constructor; |
|
280 |
|
281 object_class->dispose = gabble_presence_cache_dispose; |
|
282 object_class->finalize = gabble_presence_cache_finalize; |
|
283 |
|
284 object_class->get_property = gabble_presence_cache_get_property; |
|
285 object_class->set_property = gabble_presence_cache_set_property; |
|
286 |
|
287 param_spec = g_param_spec_object ("connection", "GabbleConnection object", |
|
288 "Gabble connection object that owns this " |
|
289 "presence cache.", |
|
290 GABBLE_TYPE_CONNECTION, |
|
291 G_PARAM_CONSTRUCT_ONLY | |
|
292 G_PARAM_READWRITE | |
|
293 G_PARAM_STATIC_NICK | |
|
294 G_PARAM_STATIC_BLURB); |
|
295 g_object_class_install_property (object_class, |
|
296 PROP_CONNECTION, |
|
297 param_spec); |
|
298 |
|
299 signals[PRESENCE_UPDATE] = g_signal_new ( |
|
300 "presence-update", |
|
301 G_TYPE_FROM_CLASS (klass), |
|
302 G_SIGNAL_RUN_LAST, |
|
303 0, |
|
304 NULL, NULL, |
|
305 g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); |
|
306 signals[NICKNAME_UPDATE] = g_signal_new ( |
|
307 "nickname-update", |
|
308 G_TYPE_FROM_CLASS (klass), |
|
309 G_SIGNAL_RUN_LAST, |
|
310 0, |
|
311 NULL, NULL, |
|
312 g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); |
|
313 signals[CAPABILITIES_UPDATE] = g_signal_new ( |
|
314 "capabilities-update", |
|
315 G_TYPE_FROM_CLASS (klass), |
|
316 G_SIGNAL_RUN_LAST, |
|
317 0, |
|
318 NULL, NULL, |
|
319 gabble_presence_cache_marshal_VOID__UINT_UINT_UINT, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); |
|
320 } |
|
321 |
|
322 static void |
|
323 gabble_presence_cache_init (GabblePresenceCache *cache) |
|
324 { |
|
325 GabblePresenceCachePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (cache, |
|
326 GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCachePrivate); |
|
327 |
|
328 cache->priv = priv; |
|
329 |
|
330 priv->presence = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); |
|
331 priv->capabilities = g_hash_table_new (g_str_hash, g_str_equal); |
|
332 priv->disco_pending = g_hash_table_new_full (g_str_hash, g_str_equal, |
|
333 g_free, (GDestroyNotify) disco_waiter_list_free); |
|
334 priv->caps_serial = 1; |
|
335 } |
|
336 |
|
337 static GObject * |
|
338 gabble_presence_cache_constructor (GType type, guint n_props, |
|
339 GObjectConstructParam *props) |
|
340 { |
|
341 GObject *obj; |
|
342 GabblePresenceCachePrivate *priv; |
|
343 |
|
344 obj = G_OBJECT_CLASS (gabble_presence_cache_parent_class)-> |
|
345 constructor (type, n_props, props); |
|
346 priv = GABBLE_PRESENCE_CACHE_PRIV (GABBLE_PRESENCE_CACHE (obj)); |
|
347 |
|
348 priv->status_changed_cb = g_signal_connect (priv->conn, "status-changed", |
|
349 G_CALLBACK (gabble_presence_cache_status_changed_cb), obj); |
|
350 |
|
351 return obj; |
|
352 } |
|
353 |
|
354 static void |
|
355 gabble_presence_cache_dispose (GObject *object) |
|
356 { |
|
357 GabblePresenceCache *self = GABBLE_PRESENCE_CACHE (object); |
|
358 GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (self); |
|
359 |
|
360 if (priv->dispose_has_run) |
|
361 return; |
|
362 |
|
363 gabble_debug (DEBUG_FLAG, "dispose called"); |
|
364 |
|
365 priv->dispose_has_run = TRUE; |
|
366 |
|
367 g_assert (priv->lm_message_cb == NULL); |
|
368 |
|
369 g_signal_handler_disconnect (priv->conn, priv->status_changed_cb); |
|
370 |
|
371 g_hash_table_destroy (priv->presence); |
|
372 priv->presence = NULL; |
|
373 |
|
374 handle_set_destroy (priv->presence_handles); |
|
375 priv->presence_handles = NULL; |
|
376 |
|
377 if (G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose) |
|
378 G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose (object); |
|
379 } |
|
380 |
|
381 static void |
|
382 gabble_presence_cache_finalize (GObject *object) |
|
383 { |
|
384 gabble_debug (DEBUG_FLAG, "called with %p", object); |
|
385 |
|
386 G_OBJECT_CLASS (gabble_presence_cache_parent_class)->finalize (object); |
|
387 } |
|
388 |
|
389 static void |
|
390 gabble_presence_cache_get_property (GObject *object, |
|
391 guint property_id, |
|
392 GValue *value, |
|
393 GParamSpec *pspec) |
|
394 { |
|
395 GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (object); |
|
396 GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
397 |
|
398 switch (property_id) { |
|
399 case PROP_CONNECTION: |
|
400 g_value_set_object (value, priv->conn); |
|
401 break; |
|
402 default: |
|
403 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
|
404 break; |
|
405 } |
|
406 } |
|
407 |
|
408 static void |
|
409 gabble_presence_cache_set_property (GObject *object, |
|
410 guint property_id, |
|
411 const GValue *value, |
|
412 GParamSpec *pspec) |
|
413 { |
|
414 GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (object); |
|
415 GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
416 GabbleHandleSet *new_presence_handles; |
|
417 |
|
418 switch (property_id) { |
|
419 case PROP_CONNECTION: |
|
420 priv->conn = g_value_get_object (value); |
|
421 new_presence_handles = handle_set_new (priv->conn->handles, TP_HANDLE_TYPE_CONTACT); |
|
422 |
|
423 if (priv->presence_handles) |
|
424 { |
|
425 const GIntSet *add; |
|
426 GIntSet *tmp; |
|
427 add = handle_set_peek (priv->presence_handles); |
|
428 tmp = handle_set_update (new_presence_handles, add); |
|
429 handle_set_destroy (priv->presence_handles); |
|
430 g_intset_destroy (tmp); |
|
431 } |
|
432 priv->presence_handles = new_presence_handles; |
|
433 break; |
|
434 default: |
|
435 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
|
436 break; |
|
437 } |
|
438 } |
|
439 |
|
440 #if 0 |
|
441 static gboolean |
|
442 _presence_node_has_google_voice (LmMessageNode *pres_node) |
|
443 { |
|
444 LmMessageNode *node; |
|
445 const gchar *cap_ext; |
|
446 gchar **features, **tmp; |
|
447 gboolean found = FALSE; |
|
448 |
|
449 node = lm_message_node_get_child_with_namespace (pres_node, "c", NS_CAPS); |
|
450 |
|
451 if (node == NULL); |
|
452 return FALSE; |
|
453 |
|
454 cap_ext = lm_message_node_get_attribute (node, "ext"); |
|
455 |
|
456 if (cap_ext == NULL); |
|
457 return FALSE; |
|
458 |
|
459 features = g_strsplit (cap_ext, " ", 0); |
|
460 |
|
461 for (tmp = features; *tmp; tmp++) |
|
462 { |
|
463 if (!g_strdiff (tmp, "voice-v1")) |
|
464 { |
|
465 found = TRUE; |
|
466 break; |
|
467 } |
|
468 } |
|
469 |
|
470 g_strfreev (features); |
|
471 |
|
472 return found; |
|
473 } |
|
474 #endif |
|
475 |
|
476 static void |
|
477 gabble_presence_cache_status_changed_cb (GabbleConnection *conn, |
|
478 TpConnectionStatus status, |
|
479 TpConnectionStatusReason reason, |
|
480 gpointer data) |
|
481 { |
|
482 GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (data); |
|
483 GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
484 |
|
485 g_assert (conn == priv->conn); |
|
486 |
|
487 switch (status) |
|
488 { |
|
489 case TP_CONN_STATUS_CONNECTING: |
|
490 g_assert (priv->lm_message_cb == NULL); |
|
491 |
|
492 priv->lm_message_cb = lm_message_handler_new (gabble_presence_cache_lm_message_cb, |
|
493 cache, NULL); |
|
494 lm_connection_register_message_handler (priv->conn->lmconn, |
|
495 priv->lm_message_cb, |
|
496 LM_MESSAGE_TYPE_PRESENCE, |
|
497 LM_HANDLER_PRIORITY_LAST); |
|
498 lm_connection_register_message_handler (priv->conn->lmconn, |
|
499 priv->lm_message_cb, |
|
500 LM_MESSAGE_TYPE_MESSAGE, |
|
501 LM_HANDLER_PRIORITY_FIRST); |
|
502 break; |
|
503 case TP_CONN_STATUS_CONNECTED: |
|
504 /* TODO: emit self presence */ |
|
505 break; |
|
506 case TP_CONN_STATUS_DISCONNECTED: |
|
507 g_assert (priv->lm_message_cb != NULL); |
|
508 |
|
509 lm_connection_unregister_message_handler (conn->lmconn, |
|
510 priv->lm_message_cb, |
|
511 LM_MESSAGE_TYPE_PRESENCE); |
|
512 lm_connection_unregister_message_handler (conn->lmconn, |
|
513 priv->lm_message_cb, |
|
514 LM_MESSAGE_TYPE_MESSAGE); |
|
515 lm_message_handler_unref (priv->lm_message_cb); |
|
516 priv->lm_message_cb = NULL; |
|
517 break; |
|
518 default: |
|
519 g_assert_not_reached (); |
|
520 } |
|
521 } |
|
522 |
|
523 static GabblePresenceId |
|
524 _presence_node_get_status (LmMessageNode *pres_node) |
|
525 { |
|
526 const gchar *presence_show; |
|
527 LmMessageNode *child_node = lm_message_node_get_child (pres_node, "show"); |
|
528 |
|
529 if (!child_node) |
|
530 { |
|
531 /* |
|
532 NODE_DEBUG (pres_node, |
|
533 "<presence> without <show> received from server, " |
|
534 "setting presence to available"); |
|
535 */ |
|
536 return GABBLE_PRESENCE_AVAILABLE; |
|
537 } |
|
538 |
|
539 presence_show = lm_message_node_get_value (child_node); |
|
540 |
|
541 if (!presence_show) |
|
542 { |
|
543 /* |
|
544 NODE_DEBUG (pres_node, |
|
545 "empty <show> tag received from server, " |
|
546 "setting presence to available"); |
|
547 */ |
|
548 return GABBLE_PRESENCE_AVAILABLE; |
|
549 } |
|
550 |
|
551 if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_AWAY)) |
|
552 return GABBLE_PRESENCE_AWAY; |
|
553 else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_CHAT)) |
|
554 return GABBLE_PRESENCE_CHAT; |
|
555 else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_DND)) |
|
556 return GABBLE_PRESENCE_DND; |
|
557 else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_XA)) |
|
558 return GABBLE_PRESENCE_XA; |
|
559 else |
|
560 { |
|
561 NODE_DEBUG (pres_node, |
|
562 "unrecognised <show/> value received from server, " |
|
563 "setting presence to available"); |
|
564 return GABBLE_PRESENCE_AVAILABLE; |
|
565 } |
|
566 } |
|
567 |
|
568 static void |
|
569 _grab_nickname (GabblePresenceCache *cache, |
|
570 GabbleHandle handle, |
|
571 const gchar *from, |
|
572 LmMessageNode *node) |
|
573 { |
|
574 const gchar *nickname; |
|
575 GabblePresence *presence; |
|
576 |
|
577 node = lm_message_node_get_child_with_namespace (node, "nick", NS_NICK); |
|
578 |
|
579 if (NULL == node) |
|
580 return; |
|
581 |
|
582 presence = gabble_presence_cache_get (cache, handle); |
|
583 |
|
584 if (NULL == presence) |
|
585 return; |
|
586 |
|
587 nickname = lm_message_node_get_value (node); |
|
588 gabble_debug (DEBUG_FLAG, "got nickname \"%s\" for %s", nickname, from); |
|
589 |
|
590 if (g_strdiff (presence->nickname, nickname)) |
|
591 { |
|
592 if (NULL != presence->nickname) |
|
593 g_free (presence->nickname); |
|
594 |
|
595 presence->nickname = g_strdup (nickname); |
|
596 g_signal_emit (cache, signals[NICKNAME_UPDATE], 0, handle); |
|
597 } |
|
598 } |
|
599 |
|
600 static GSList * |
|
601 _extract_cap_bundles (LmMessageNode *lm_node) |
|
602 { |
|
603 const gchar *node, *ver, *ext; |
|
604 GSList *uris = NULL; |
|
605 LmMessageNode *cap_node; |
|
606 |
|
607 cap_node = lm_message_node_get_child_with_namespace (lm_node, "c", NS_CAPS); |
|
608 |
|
609 if (NULL == cap_node) |
|
610 return NULL; |
|
611 |
|
612 node = lm_message_node_get_attribute (cap_node, "node"); |
|
613 |
|
614 if (NULL == node) |
|
615 return NULL; |
|
616 |
|
617 ver = lm_message_node_get_attribute (cap_node, "ver"); |
|
618 |
|
619 if (NULL != ver) |
|
620 uris = g_slist_prepend (uris, g_strdup_printf ("%s#%s", node, ver)); |
|
621 |
|
622 ext = lm_message_node_get_attribute (cap_node, "ext"); |
|
623 |
|
624 if (NULL != ext) |
|
625 { |
|
626 gchar **exts, **i; |
|
627 |
|
628 exts = g_strsplit (ext, " ", 0); |
|
629 |
|
630 for (i = exts; NULL != *i; i++) |
|
631 uris = g_slist_prepend (uris, g_strdup_printf ("%s#%s", node, *i)); |
|
632 |
|
633 g_strfreev (exts); |
|
634 } |
|
635 |
|
636 return uris; |
|
637 } |
|
638 |
|
639 static void |
|
640 _caps_disco_cb (GabbleDisco *disco, |
|
641 GabbleDiscoRequest *request, |
|
642 const gchar *jid, |
|
643 const gchar *node, |
|
644 LmMessageNode *query_result, |
|
645 GError *error, |
|
646 gpointer user_data) |
|
647 { |
|
648 GSList *waiters, *i; |
|
649 LmMessageNode *child; |
|
650 GabblePresenceCache *cache; |
|
651 GabblePresenceCachePrivate *priv; |
|
652 gchar *full_jid = NULL; |
|
653 GabblePresenceCapabilities caps = 0; |
|
654 guint trust; |
|
655 GabbleHandle handle; |
|
656 |
|
657 cache = GABBLE_PRESENCE_CACHE (user_data); |
|
658 priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
659 |
|
660 if (NULL == node) |
|
661 { |
|
662 g_warning ("got disco response with NULL node, ignoring"); |
|
663 return; |
|
664 } |
|
665 |
|
666 waiters = g_hash_table_lookup (priv->disco_pending, node); |
|
667 |
|
668 if (NULL != error) |
|
669 { |
|
670 DiscoWaiter *waiter = NULL; |
|
671 |
|
672 gabble_debug (DEBUG_FLAG, "disco query failed: %s", error->message); |
|
673 |
|
674 for (i = waiters; NULL != i; i = i->next) |
|
675 { |
|
676 waiter = (DiscoWaiter *) i->data; |
|
677 |
|
678 if (!waiter->disco_requested) |
|
679 { |
|
680 const gchar *jid; |
|
681 |
|
682 jid = gabble_handle_inspect (priv->conn->handles, |
|
683 TP_HANDLE_TYPE_CONTACT, |
|
684 waiter->handle); |
|
685 full_jid = g_strdup_printf ("%s/%s", jid, waiter->resource); |
|
686 |
|
687 gabble_disco_request (disco, GABBLE_DISCO_TYPE_INFO, full_jid, node, |
|
688 _caps_disco_cb, cache, G_OBJECT(cache), NULL); |
|
689 waiter->disco_requested = TRUE; |
|
690 break; |
|
691 } |
|
692 } |
|
693 |
|
694 if (NULL != i) |
|
695 { |
|
696 gabble_debug (DEBUG_FLAG, "sent a retry disco request to %s for URI %s", full_jid, node); |
|
697 } |
|
698 else |
|
699 { |
|
700 gabble_debug (DEBUG_FLAG, "failed to find a suitable candidate to retry disco request for URI %s", node); |
|
701 /* FIXME do something very clever here? */ |
|
702 g_hash_table_remove (priv->disco_pending, node); |
|
703 } |
|
704 |
|
705 goto OUT; |
|
706 } |
|
707 |
|
708 for (child = query_result->children; NULL != child; child = child->next) |
|
709 { |
|
710 const gchar *var; |
|
711 |
|
712 if (0 != strcmp (child->name, "feature")) |
|
713 continue; |
|
714 |
|
715 var = lm_message_node_get_attribute (child, "var"); |
|
716 |
|
717 if (NULL == var) |
|
718 continue; |
|
719 |
|
720 /* TODO: use a table that equates disco features to caps */ |
|
721 if (0 == strcmp (var, NS_GOOGLE_TRANSPORT_P2P)) |
|
722 caps |= PRESENCE_CAP_GOOGLE_TRANSPORT_P2P; |
|
723 else if (0 == strcmp (var, NS_GOOGLE_FEAT_VOICE)) |
|
724 caps |= PRESENCE_CAP_GOOGLE_VOICE; |
|
725 else if (0 == strcmp (var, NS_JINGLE)) |
|
726 caps |= PRESENCE_CAP_JINGLE; |
|
727 else if (0 == strcmp (var, NS_JINGLE_DESCRIPTION_AUDIO)) |
|
728 caps |= PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO; |
|
729 else if (0 == strcmp (var, NS_JINGLE_DESCRIPTION_VIDEO)) |
|
730 caps |= PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO; |
|
731 } |
|
732 |
|
733 handle = gabble_handle_for_contact (priv->conn->handles, jid, FALSE); |
|
734 trust = capability_info_recvd (cache, node, handle, caps); |
|
735 |
|
736 for (i = waiters; NULL != i;) |
|
737 { |
|
738 DiscoWaiter *waiter; |
|
739 GabblePresence *presence; |
|
740 |
|
741 waiter = (DiscoWaiter *) i->data; |
|
742 |
|
743 if (trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST || waiter->handle == handle) |
|
744 { |
|
745 GSList *tmp; |
|
746 /* trusted reply */ |
|
747 presence = gabble_presence_cache_get (cache, waiter->handle); |
|
748 |
|
749 if (presence) |
|
750 { |
|
751 GabblePresenceCapabilities save_caps = presence->caps; |
|
752 gabble_debug (DEBUG_FLAG, "setting caps for %d (%s) to %d", handle, jid, caps); |
|
753 gabble_presence_set_capabilities (presence, waiter->resource,caps, |
|
754 waiter->serial); |
|
755 gabble_debug (DEBUG_FLAG, "caps for %d (%s) now %d", handle, jid, presence->caps); |
|
756 g_signal_emit (cache, signals[CAPABILITIES_UPDATE], 0, |
|
757 waiter->handle, save_caps, presence->caps); |
|
758 } |
|
759 |
|
760 tmp = i; |
|
761 i = i->next; |
|
762 |
|
763 waiters = g_slist_delete_link (waiters, tmp); |
|
764 |
|
765 g_hash_table_steal (priv->disco_pending, node); |
|
766 g_hash_table_insert (priv->disco_pending, g_strdup (node), waiters); |
|
767 |
|
768 disco_waiter_free (waiter); |
|
769 } |
|
770 else if (trust + disco_waiter_list_get_request_count (waiters) - 1 |
|
771 < CAPABILITY_BUNDLE_ENOUGH_TRUST) |
|
772 { |
|
773 /* if the possible trust, not counting this guy, is too low, |
|
774 * we have been poisoned and reset our trust meters - disco |
|
775 * anybody we still haven't to be able to get more trusted replies */ |
|
776 |
|
777 if (!waiter->disco_requested) |
|
778 { |
|
779 const gchar *jid; |
|
780 |
|
781 jid = gabble_handle_inspect (priv->conn->handles, |
|
782 TP_HANDLE_TYPE_CONTACT, waiter->handle); |
|
783 full_jid = g_strdup_printf ("%s/%s", jid, waiter->resource); |
|
784 |
|
785 gabble_disco_request (disco, GABBLE_DISCO_TYPE_INFO, full_jid, |
|
786 node, _caps_disco_cb, cache, G_OBJECT(cache), NULL); |
|
787 waiter->disco_requested = TRUE; |
|
788 |
|
789 g_free (full_jid); |
|
790 full_jid = NULL; |
|
791 } |
|
792 |
|
793 i = i->next; |
|
794 } |
|
795 else |
|
796 { |
|
797 /* trust level still uncertain, don't do nothing */ |
|
798 i = i->next; |
|
799 } |
|
800 } |
|
801 |
|
802 if (trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST) |
|
803 g_hash_table_remove (priv->disco_pending, node); |
|
804 |
|
805 OUT: |
|
806 |
|
807 g_free (full_jid); |
|
808 } |
|
809 |
|
810 static void |
|
811 _process_caps_uri (GabblePresenceCache *cache, |
|
812 const gchar *from, |
|
813 const gchar *uri, |
|
814 GabbleHandle handle, |
|
815 const gchar *resource, |
|
816 guint serial) |
|
817 { |
|
818 CapabilityInfo *info; |
|
819 gpointer value; |
|
820 GabblePresenceCachePrivate *priv; |
|
821 |
|
822 priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
823 info = capability_info_get (cache, uri, 0); |
|
824 |
|
825 if (info->trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST |
|
826 || g_intset_is_member (info->guys, handle)) |
|
827 { |
|
828 /* we already have enough trust for this node; apply the cached value to |
|
829 * the (handle, resource) */ |
|
830 |
|
831 GabblePresence *presence = gabble_presence_cache_get (cache, handle); |
|
832 gabble_debug (DEBUG_FLAG, "enough trust for URI %s, setting caps for %u (%s) to %u", |
|
833 uri, handle, from, info->caps); |
|
834 |
|
835 if (presence) |
|
836 { |
|
837 GabblePresenceCapabilities save_caps = presence->caps; |
|
838 gabble_presence_set_capabilities (presence, resource, info->caps, serial); |
|
839 g_signal_emit (cache, signals[CAPABILITIES_UPDATE], 0, |
|
840 handle, save_caps, presence->caps); |
|
841 gabble_debug (DEBUG_FLAG, "caps for %d (%s) now %d", handle, from, presence->caps); |
|
842 } |
|
843 else |
|
844 { |
|
845 gabble_debug (DEBUG_FLAG, "presence not found"); |
|
846 } |
|
847 } |
|
848 else |
|
849 { |
|
850 /* Append the (handle, resource) pair to the list of such pairs |
|
851 * waiting for capabilities for this uri, and send a disco request |
|
852 * if we don't have enough possible trust yet */ |
|
853 |
|
854 GSList *waiters; |
|
855 DiscoWaiter *waiter; |
|
856 guint possible_trust; |
|
857 |
|
858 gabble_debug (DEBUG_FLAG, "not enough trust for URI %s", uri); |
|
859 value = g_hash_table_lookup (priv->disco_pending, uri); |
|
860 |
|
861 if (value) |
|
862 g_hash_table_steal (priv->disco_pending, uri); |
|
863 |
|
864 waiters = (GSList *) value; |
|
865 waiter = disco_waiter_new (priv->conn->handles, handle, resource, serial); |
|
866 waiters = g_slist_prepend (waiters, waiter); |
|
867 g_hash_table_insert (priv->disco_pending, g_strdup (uri), waiters); |
|
868 |
|
869 possible_trust = disco_waiter_list_get_request_count (waiters); |
|
870 |
|
871 if (!value || info->trust + possible_trust < CAPABILITY_BUNDLE_ENOUGH_TRUST) |
|
872 { |
|
873 /* DISCO */ |
|
874 gabble_debug (DEBUG_FLAG, "only %u trust out of %u possible thus far, sending disco for URI %s", |
|
875 info->trust + possible_trust, CAPABILITY_BUNDLE_ENOUGH_TRUST, uri); |
|
876 gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_INFO, |
|
877 from, uri, _caps_disco_cb, cache, G_OBJECT (cache), NULL); |
|
878 /* enough DISCO for you, buddy */ |
|
879 waiter->disco_requested = TRUE; |
|
880 } |
|
881 } |
|
882 } |
|
883 |
|
884 static void |
|
885 _process_caps (GabblePresenceCache *cache, |
|
886 GabbleHandle handle, |
|
887 const gchar *from, |
|
888 LmMessageNode *lm_node) |
|
889 { |
|
890 gchar *resource; |
|
891 GSList *uris, *i; |
|
892 GabblePresenceCachePrivate *priv; |
|
893 guint serial; |
|
894 |
|
895 priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
896 serial = priv->caps_serial++; |
|
897 |
|
898 gabble_decode_jid (from, NULL, NULL, &resource); |
|
899 |
|
900 if (NULL == resource) |
|
901 return; |
|
902 |
|
903 uris = _extract_cap_bundles (lm_node); |
|
904 |
|
905 for (i = uris; NULL != i; i = i->next) |
|
906 { |
|
907 _process_caps_uri (cache, from, (gchar *) i->data, handle, resource, serial); |
|
908 g_free (i->data); |
|
909 } |
|
910 |
|
911 g_free (resource); |
|
912 g_slist_free (uris); |
|
913 } |
|
914 |
|
915 static void |
|
916 _grab_avatar_sha1 (GabblePresenceCache *cache, |
|
917 GabbleHandle handle, |
|
918 const gchar *from, |
|
919 LmMessageNode *node) |
|
920 { |
|
921 const gchar *sha1; |
|
922 LmMessageNode *x_node, *photo_node; |
|
923 GabblePresence *presence; |
|
924 //LIB_TRACE( ELibTraceTypeInfo, "%s", "Out _grab_avatar_sha1" ); |
|
925 presence = gabble_presence_cache_get (cache, handle); |
|
926 |
|
927 if (NULL == presence) |
|
928 return; |
|
929 |
|
930 x_node = lm_message_node_get_child_with_namespace (node, "x", |
|
931 NS_VCARD_TEMP_UPDATE); |
|
932 |
|
933 if (NULL == x_node) |
|
934 { |
|
935 #if 0 |
|
936 if (handle == priv->conn->parent.self_handle) |
|
937 { |
|
938 /* One of my other resources does not support XEP-0153. As per that |
|
939 * XEP, I MUST stop advertising the image hash, at least until all |
|
940 * instances of non-conforming resources have gone offline. |
|
941 * However, we're going to ignore this requirement and hope that |
|
942 * non-conforming clients won't alter the <PHOTO>, which should |
|
943 * in practice be true. |
|
944 */ |
|
945 presence->avatar_sha1 = NULL; |
|
946 } |
|
947 #endif |
|
948 return; |
|
949 } |
|
950 |
|
951 photo_node = lm_message_node_get_child (x_node, "photo"); |
|
952 |
|
953 /* If there is no photo node, the resource supports XEP-0153, but has |
|
954 * nothing in particular to say about the avatar. */ |
|
955 if (NULL == photo_node) |
|
956 return; |
|
957 |
|
958 sha1 = lm_message_node_get_value (photo_node); |
|
959 |
|
960 if (g_strdiff (presence->avatar_sha1, sha1)) |
|
961 { |
|
962 g_free (presence->avatar_sha1); |
|
963 presence->avatar_sha1 = g_strdup (sha1); |
|
964 |
|
965 #if 0 |
|
966 if (handle == priv->conn->parent.self_handle) |
|
967 { |
|
968 /* that would be us, then. According to XEP-0153, we MUST |
|
969 * immediately send a presence update with an empty update child |
|
970 * element (no photo node), then re-download our own vCard; |
|
971 * when that arrives, we may start setting the photo node in our |
|
972 * presence again. |
|
973 * |
|
974 * For the moment I'm going to ignore that requirement and |
|
975 * trust that our other resource is getting its sha1 right! |
|
976 */ |
|
977 /* TODO: I don't trust anyone to get XMPP right, so let's do |
|
978 * this. :D */ |
|
979 } |
|
980 #endif |
|
981 //LIB_TRACE( ELibTraceTypeInfo, "%s", "AVATAR_UPDATE _grab_avatar_sha1" ); |
|
982 //g_signal_emit (cache, signals[AVATAR_UPDATE], 0, handle); |
|
983 } |
|
984 //LIB_TRACE( ELibTraceTypeInfo, "%s", "Out _grab_avatar_sha1" ); |
|
985 } |
|
986 |
|
987 static LmHandlerResult |
|
988 _parse_presence_message (GabblePresenceCache *cache, |
|
989 GabbleHandle handle, |
|
990 const gchar *from, |
|
991 LmMessage *message) |
|
992 { |
|
993 gint8 priority = 0; |
|
994 gchar *resource = NULL; |
|
995 const gchar *status_message = NULL; |
|
996 LmMessageNode *presence_node, *child_node; |
|
997 LmHandlerResult ret = LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
998 GabblePresenceId presence_id; |
|
999 GabblePresence *presence; |
|
1000 |
|
1001 presence_node = message->node; |
|
1002 g_assert (0 == strcmp (presence_node->name, "presence")); |
|
1003 |
|
1004 gabble_decode_jid (from, NULL, NULL, &resource); |
|
1005 |
|
1006 presence = gabble_presence_cache_get (cache, handle); |
|
1007 |
|
1008 if (NULL != presence) |
|
1009 presence->keep_unavailable = FALSE; |
|
1010 |
|
1011 child_node = lm_message_node_get_child (presence_node, "status"); |
|
1012 |
|
1013 if (child_node) |
|
1014 status_message = lm_message_node_get_value (child_node); |
|
1015 |
|
1016 child_node = lm_message_node_get_child (presence_node, "priority"); |
|
1017 |
|
1018 if (child_node) |
|
1019 { |
|
1020 const gchar *prio = lm_message_node_get_value (child_node); |
|
1021 |
|
1022 if (prio != NULL) |
|
1023 priority = CLAMP (atoi (prio), G_MININT8, G_MAXINT8); |
|
1024 } |
|
1025 |
|
1026 switch (lm_message_get_sub_type (message)) |
|
1027 { |
|
1028 case LM_MESSAGE_SUB_TYPE_NOT_SET: |
|
1029 case LM_MESSAGE_SUB_TYPE_AVAILABLE: |
|
1030 presence_id = _presence_node_get_status (presence_node); |
|
1031 gabble_presence_cache_update (cache, handle, resource, presence_id, |
|
1032 status_message, priority); |
|
1033 |
|
1034 #if 0 |
|
1035 if (_presence_node_has_google_voice (presence_node)) |
|
1036 { |
|
1037 presence = gabble_presence_cache_get (cache, handle); |
|
1038 g_assert (NULL != presence); |
|
1039 gabble_debug (DEBUG_FLAG, "%s has voice-v1 support", from); |
|
1040 gabble_presence_set_capabilities (presence, resource, |
|
1041 PRESENCE_CAP_GOOGLE_VOICE); |
|
1042 } |
|
1043 #endif |
|
1044 |
|
1045 ret = LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
1046 break; |
|
1047 |
|
1048 case LM_MESSAGE_SUB_TYPE_ERROR: |
|
1049 NODE_DEBUG (presence_node, "setting contact offline due to error"); |
|
1050 /* fall through */ |
|
1051 |
|
1052 case LM_MESSAGE_SUB_TYPE_UNAVAILABLE: |
|
1053 gabble_presence_cache_update (cache, handle, resource, |
|
1054 GABBLE_PRESENCE_OFFLINE, status_message, priority); |
|
1055 |
|
1056 ret = LM_HANDLER_RESULT_REMOVE_MESSAGE; |
|
1057 break; |
|
1058 |
|
1059 default: |
|
1060 break; |
|
1061 } |
|
1062 |
|
1063 _grab_avatar_sha1 (cache, handle, from, presence_node); |
|
1064 _grab_nickname (cache, handle, from, presence_node); |
|
1065 _process_caps (cache, handle, from, presence_node); |
|
1066 |
|
1067 g_free (resource); |
|
1068 |
|
1069 return ret; |
|
1070 } |
|
1071 |
|
1072 static LmHandlerResult |
|
1073 _parse_message_message (GabblePresenceCache *cache, |
|
1074 GabbleHandle handle, |
|
1075 const gchar *from, |
|
1076 LmMessage *message) |
|
1077 { |
|
1078 LmMessageNode *node; |
|
1079 GabblePresence *presence; |
|
1080 |
|
1081 presence = gabble_presence_cache_get (cache, handle); |
|
1082 |
|
1083 if (NULL == presence) |
|
1084 { |
|
1085 presence = _cache_insert (cache, handle); |
|
1086 presence->keep_unavailable = TRUE; |
|
1087 } |
|
1088 |
|
1089 node = lm_message_get_node (message); |
|
1090 |
|
1091 _grab_nickname (cache, handle, from, node); |
|
1092 _process_caps (cache, handle, from, node); |
|
1093 |
|
1094 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
1095 } |
|
1096 |
|
1097 |
|
1098 /** |
|
1099 * gabble_presence_cache_lm_message_cb: |
|
1100 * @handler: #LmMessageHandler for this message |
|
1101 * @connection: #LmConnection that originated the message |
|
1102 * @message: the presence message |
|
1103 * @user_data: callback data |
|
1104 * |
|
1105 * Called by loudmouth when we get an incoming <presence>. |
|
1106 */ |
|
1107 static LmHandlerResult |
|
1108 gabble_presence_cache_lm_message_cb (LmMessageHandler *handler, |
|
1109 LmConnection *lmconn, |
|
1110 LmMessage *message, |
|
1111 gpointer user_data) |
|
1112 { |
|
1113 GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (user_data); |
|
1114 GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
1115 const char *from; |
|
1116 GabbleHandle handle; |
|
1117 |
|
1118 g_assert (lmconn == priv->conn->lmconn); |
|
1119 |
|
1120 from = lm_message_node_get_attribute (message->node, "from"); |
|
1121 |
|
1122 if (NULL == from) |
|
1123 { |
|
1124 NODE_DEBUG (message->node, "message without from attribute, ignoring"); |
|
1125 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
1126 } |
|
1127 |
|
1128 handle = gabble_handle_for_contact (priv->conn->handles, from, FALSE); |
|
1129 |
|
1130 if (0 == handle) |
|
1131 { |
|
1132 NODE_DEBUG (message->node, "ignoring message from malformed jid"); |
|
1133 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
1134 } |
|
1135 |
|
1136 if (handle == priv->conn->self_handle) |
|
1137 { |
|
1138 NODE_DEBUG (message->node, |
|
1139 "ignoring message from ourselves on another resource"); |
|
1140 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
1141 } |
|
1142 |
|
1143 switch (lm_message_get_type (message)) |
|
1144 { |
|
1145 case LM_MESSAGE_TYPE_PRESENCE: |
|
1146 return _parse_presence_message (cache, handle, from, message); |
|
1147 case LM_MESSAGE_TYPE_MESSAGE: |
|
1148 return _parse_message_message (cache, handle, from, message); |
|
1149 default: |
|
1150 return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; |
|
1151 } |
|
1152 } |
|
1153 |
|
1154 |
|
1155 GabblePresenceCache * |
|
1156 gabble_presence_cache_new (GabbleConnection *conn) |
|
1157 { |
|
1158 return g_object_new (GABBLE_TYPE_PRESENCE_CACHE, |
|
1159 "connection", conn, |
|
1160 NULL); |
|
1161 } |
|
1162 |
|
1163 GabblePresence * |
|
1164 gabble_presence_cache_get (GabblePresenceCache *cache, GabbleHandle handle) |
|
1165 { |
|
1166 GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
1167 |
|
1168 |
|
1169 // g_assert (gabble_handle_is_valid (priv->conn->handles, |
|
1170 // TP_HANDLE_TYPE_CONTACT, handle, NULL)); |
|
1171 if(priv) |
|
1172 if(priv->conn) |
|
1173 if(priv->conn->handles){ |
|
1174 if ( gabble_handle_is_valid (priv->conn->handles, |
|
1175 TP_HANDLE_TYPE_CONTACT, handle, NULL) ) |
|
1176 { |
|
1177 return g_hash_table_lookup (priv->presence, GINT_TO_POINTER (handle)); |
|
1178 } |
|
1179 else |
|
1180 { |
|
1181 return NULL; |
|
1182 } |
|
1183 } |
|
1184 return NULL; |
|
1185 } |
|
1186 |
|
1187 void |
|
1188 gabble_presence_cache_maybe_remove ( |
|
1189 GabblePresenceCache *cache, |
|
1190 GabbleHandle handle) |
|
1191 { |
|
1192 GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
1193 GabblePresence *presence; |
|
1194 |
|
1195 presence = gabble_presence_cache_get (cache, handle); |
|
1196 |
|
1197 if (NULL == presence) |
|
1198 return; |
|
1199 |
|
1200 if (presence->status == GABBLE_PRESENCE_OFFLINE && |
|
1201 presence->status_message == NULL && |
|
1202 !presence->keep_unavailable) |
|
1203 { |
|
1204 const gchar *jid; |
|
1205 |
|
1206 jid = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, |
|
1207 handle); |
|
1208 gabble_debug (DEBUG_FLAG, "discarding cached presence for unavailable jid %s", jid); |
|
1209 g_hash_table_remove (priv->presence, GINT_TO_POINTER (handle)); |
|
1210 handle_set_remove (priv->presence_handles, handle); |
|
1211 } |
|
1212 } |
|
1213 |
|
1214 static GabblePresence * |
|
1215 _cache_insert ( |
|
1216 GabblePresenceCache *cache, |
|
1217 GabbleHandle handle) |
|
1218 { |
|
1219 GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
1220 GabblePresence *presence; |
|
1221 |
|
1222 presence = gabble_presence_new (); |
|
1223 g_hash_table_insert (priv->presence, GINT_TO_POINTER (handle), presence); |
|
1224 handle_set_add (priv->presence_handles, handle); |
|
1225 return presence; |
|
1226 } |
|
1227 |
|
1228 void |
|
1229 gabble_presence_cache_update ( |
|
1230 GabblePresenceCache *cache, |
|
1231 GabbleHandle handle, |
|
1232 const gchar *resource, |
|
1233 GabblePresenceId presence_id, |
|
1234 const gchar *status_message, |
|
1235 gint8 priority) |
|
1236 { |
|
1237 GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); |
|
1238 const gchar *jid; |
|
1239 GabblePresence *presence; |
|
1240 |
|
1241 jid = gabble_handle_inspect (priv->conn->handles, TP_HANDLE_TYPE_CONTACT, |
|
1242 handle); |
|
1243 gabble_debug (DEBUG_FLAG, "%s (%d) resource %s prio %d presence %d message \"%s\"", |
|
1244 jid, handle, resource, priority, presence_id, status_message); |
|
1245 |
|
1246 presence = gabble_presence_cache_get (cache, handle); |
|
1247 |
|
1248 if (presence == NULL) |
|
1249 presence = _cache_insert (cache, handle); |
|
1250 |
|
1251 if (gabble_presence_update (presence, resource, presence_id, status_message, |
|
1252 priority)) |
|
1253 g_signal_emit (cache, signals[PRESENCE_UPDATE], 0, handle); |
|
1254 |
|
1255 gabble_presence_cache_maybe_remove (cache, handle); |
|
1256 } |
|
1257 |
|
1258 void gabble_presence_cache_add_bundle_caps (GabblePresenceCache *cache, |
|
1259 const gchar *node, GabblePresenceCapabilities new_caps) |
|
1260 { |
|
1261 CapabilityInfo *info; |
|
1262 |
|
1263 info = capability_info_get (cache, node, 0); |
|
1264 info->trust = CAPABILITY_BUNDLE_ENOUGH_TRUST; |
|
1265 info->caps |= new_caps; |
|
1266 } |
|
1267 |
|