1 /*
2 * wocky-c2s-porter.c - Source for WockyC2SPorter
3 * Copyright (C) 2009-2011 Collabora Ltd.
4 * @author Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
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 /**
22 * SECTION: wocky-c2s-porter
23 * @title: WockyC2SPorter
24 * @short_description: Wrapper around a #WockyXmppConnection providing a
25 * higher level API.
26 *
27 * Sends and receives #WockyStanza from an underlying
28 * #WockyXmppConnection.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include "wocky-c2s-porter.h"
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44
45 #include <gio/gio.h>
46
47 #include "wocky-porter.h"
48 #include "wocky-utils.h"
49 #include "wocky-namespaces.h"
50 #include "wocky-contact-factory.h"
51
52 #define WOCKY_DEBUG_FLAG WOCKY_DEBUG_PORTER
53 #include "wocky-debug-internal.h"
54
55 static void wocky_porter_iface_init (gpointer g_iface, gpointer iface_data);
56
57 G_DEFINE_TYPE_WITH_CODE (WockyC2SPorter, wocky_c2s_porter, G_TYPE_OBJECT,
58 G_IMPLEMENT_INTERFACE (WOCKY_TYPE_PORTER,
59 wocky_porter_iface_init));
60
61 /* properties */
62 enum
63 {
64 PROP_CONNECTION = 1,
65 PROP_FULL_JID,
66 PROP_BARE_JID,
67 PROP_RESOURCE,
68 };
69
70 /* private structure */
71 struct _WockyC2SPorterPrivate
72 {
73 gboolean dispose_has_run;
74 gboolean forced_shutdown;
75
76 gchar *full_jid;
77 gchar *bare_jid;
78 gchar *resource;
79 gchar *domain;
80
81 /* Queue of (sending_queue_elem *) */
82 GQueue *sending_queue;
83 GCancellable *receive_cancellable;
84 gboolean sending_whitespace_ping;
85
86 GSimpleAsyncResult *close_result;
87 gboolean waiting_to_close;
88 gboolean remote_closed;
89 gboolean local_closed;
90 GCancellable *close_cancellable;
91 GSimpleAsyncResult *force_close_result;
92 GCancellable *force_close_cancellable;
93
94 /* guint => owned (StanzaHandler *) */
95 GHashTable *handlers_by_id;
96 /* Sort listed (by decreasing priority) of borrowed (StanzaHandler *) */
97 GList *handlers;
98 guint next_handler_id;
99 /* (const gchar *) => owned (StanzaIqHandler *)
100 * This key is the ID of the IQ */
101 GHashTable *iq_reply_handlers;
102
103 gboolean power_saving_mode;
104 /* Queue of (owned WockyStanza *) */
105 GQueue *unimportant_queue;
106 /* List of (owned WockyStanza *) */
107 GQueue queueable_stanza_patterns;
108
109 WockyXmppConnection *connection;
110 };
111
112 typedef struct
113 {
114 WockyC2SPorter *self;
115 WockyStanza *stanza;
116 GCancellable *cancellable;
117 GSimpleAsyncResult *result;
118 gulong cancelled_sig_id;
119 } sending_queue_elem;
120
121 static void wocky_c2s_porter_send_async (WockyPorter *porter,
122 WockyStanza *stanza, GCancellable *cancellable,
123 GAsyncReadyCallback callback, gpointer user_data);
124
125 static sending_queue_elem *
sending_queue_elem_new(WockyC2SPorter * self,WockyStanza * stanza,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)126 sending_queue_elem_new (WockyC2SPorter *self,
127 WockyStanza *stanza,
128 GCancellable *cancellable,
129 GAsyncReadyCallback callback,
130 gpointer user_data)
131 {
132 sending_queue_elem *elem = g_slice_new0 (sending_queue_elem);
133
134 elem->self = self;
135 elem->stanza = g_object_ref (stanza);
136 if (cancellable != NULL)
137 elem->cancellable = g_object_ref (cancellable);
138
139 elem->result = g_simple_async_result_new (G_OBJECT (self),
140 callback, user_data, wocky_c2s_porter_send_async);
141
142 return elem;
143 }
144
145 static void
sending_queue_elem_free(sending_queue_elem * elem)146 sending_queue_elem_free (sending_queue_elem *elem)
147 {
148 g_object_unref (elem->stanza);
149 if (elem->cancellable != NULL)
150 {
151 g_object_unref (elem->cancellable);
152 if (elem->cancelled_sig_id != 0)
153 g_signal_handler_disconnect (elem->cancellable, elem->cancelled_sig_id);
154 /* FIXME: we should use g_cancellable_disconnect but it raises a dead
155 * lock (#587300) */
156 }
157 g_object_unref (elem->result);
158
159 g_slice_free (sending_queue_elem, elem);
160 }
161
162 typedef enum {
163 MATCH_ANYONE,
164 MATCH_SERVER,
165 MATCH_JID
166 } SenderMatch;
167
168 typedef struct
169 {
170 WockyStanzaType type;
171 WockyStanzaSubType sub_type;
172 SenderMatch sender_match;
173 gchar *node;
174 gchar *domain;
175 gchar *resource;
176 guint priority;
177 WockyStanza *match;
178 WockyPorterHandlerFunc callback;
179 gpointer user_data;
180 } StanzaHandler;
181
182 static StanzaHandler *
stanza_handler_new(WockyStanzaType type,WockyStanzaSubType sub_type,SenderMatch sender_match,const gchar * from,guint priority,WockyStanza * stanza,WockyPorterHandlerFunc callback,gpointer user_data)183 stanza_handler_new (
184 WockyStanzaType type,
185 WockyStanzaSubType sub_type,
186 SenderMatch sender_match,
187 const gchar *from,
188 guint priority,
189 WockyStanza *stanza,
190 WockyPorterHandlerFunc callback,
191 gpointer user_data)
192 {
193 StanzaHandler *result = g_slice_new0 (StanzaHandler);
194
195 result->type = type;
196 result->sub_type = sub_type;
197 result->priority = priority;
198 result->callback = callback;
199 result->user_data = user_data;
200 result->sender_match = sender_match;
201
202 if (stanza != NULL)
203 result->match = g_object_ref (stanza);
204
205 if (sender_match == MATCH_JID)
206 {
207 gboolean from_valid;
208
209 g_assert (from != NULL);
210 from_valid = wocky_decode_jid (from, &(result->node),
211 &(result->domain), &(result->resource));
212 g_assert (from_valid);
213 }
214 else
215 {
216 g_assert (from == NULL);
217 }
218
219 return result;
220 }
221
222 static void
stanza_handler_free(StanzaHandler * handler)223 stanza_handler_free (StanzaHandler *handler)
224 {
225 g_free (handler->node);
226 g_free (handler->domain);
227 g_free (handler->resource);
228
229 if (handler->match != NULL)
230 g_object_unref (handler->match);
231
232 g_slice_free (StanzaHandler, handler);
233 }
234
235 typedef struct
236 {
237 WockyC2SPorter *self;
238 GSimpleAsyncResult *result;
239 GCancellable *cancellable;
240 gulong cancelled_sig_id;
241 gchar *recipient;
242 gchar *id;
243 gboolean sent;
244 } StanzaIqHandler;
245
246 static StanzaIqHandler *
stanza_iq_handler_new(WockyC2SPorter * self,gchar * id,GSimpleAsyncResult * result,GCancellable * cancellable,const gchar * recipient)247 stanza_iq_handler_new (WockyC2SPorter *self,
248 gchar *id,
249 GSimpleAsyncResult *result,
250 GCancellable *cancellable,
251 const gchar *recipient)
252 {
253 StanzaIqHandler *handler = g_slice_new0 (StanzaIqHandler);
254 gchar *to = NULL;
255
256 if (recipient != NULL)
257 {
258 to = wocky_normalise_jid (recipient);
259
260 if (to == NULL)
261 {
262 DEBUG ("Failed to normalise stanza recipient '%s'", recipient);
263 to = g_strdup (recipient);
264 }
265 }
266
267 handler->self = self;
268 handler->result = result;
269 handler->id = id;
270 if (cancellable != NULL)
271 handler->cancellable = g_object_ref (cancellable);
272 handler->recipient = to;
273
274 return handler;
275 }
276
277 static void
stanza_iq_handler_remove_cancellable(StanzaIqHandler * handler)278 stanza_iq_handler_remove_cancellable (StanzaIqHandler *handler)
279 {
280 if (handler->cancellable != NULL)
281 {
282 /* FIXME: we should use g_cancellable_disconnect but it raises a dead
283 * lock (#587300) */
284 /* We might have already have disconnected the signal handler
285 * from send_head_stanza(), so check whether it's still connected. */
286 if (handler->cancelled_sig_id > 0)
287 g_signal_handler_disconnect (handler->cancellable, handler->cancelled_sig_id);
288 g_object_unref (handler->cancellable);
289 handler->cancelled_sig_id = 0;
290 handler->cancellable = NULL;
291 }
292 }
293
294 static void
stanza_iq_handler_free(StanzaIqHandler * handler)295 stanza_iq_handler_free (StanzaIqHandler *handler)
296 {
297 if (handler->result != NULL)
298 g_object_unref (handler->result);
299
300 stanza_iq_handler_remove_cancellable (handler);
301
302 g_free (handler->id);
303 g_free (handler->recipient);
304 g_slice_free (StanzaIqHandler, handler);
305 }
306
307 static void
stanza_iq_handler_maybe_remove(StanzaIqHandler * handler)308 stanza_iq_handler_maybe_remove (StanzaIqHandler *handler)
309 {
310 /* Always wait till the iq sent operation has finished and something
311 * completed the operation from the perspective of the API user */
312 if (handler->sent && handler->result == NULL)
313 {
314 WockyC2SPorterPrivate *priv = handler->self->priv;
315 g_hash_table_remove (priv->iq_reply_handlers, handler->id);
316 }
317 }
318
319 static void send_stanza_cb (GObject *source,
320 GAsyncResult *res,
321 gpointer user_data);
322
323 static void send_close (WockyC2SPorter *self);
324
325 static gboolean handle_iq_reply (WockyPorter *porter,
326 WockyStanza *reply,
327 gpointer user_data);
328
329 static void remote_connection_closed (WockyC2SPorter *self,
330 GError *error);
331
332 static void
wocky_c2s_porter_init(WockyC2SPorter * self)333 wocky_c2s_porter_init (WockyC2SPorter *self)
334 {
335 WockyC2SPorterPrivate *priv;
336
337 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_C2S_PORTER,
338 WockyC2SPorterPrivate);
339 priv = self->priv;
340
341 priv->sending_queue = g_queue_new ();
342
343 priv->handlers_by_id = g_hash_table_new_full (g_direct_hash, g_direct_equal,
344 NULL, (GDestroyNotify) stanza_handler_free);
345 /* these are guints, reserve 0 for "not a valid handler" */
346 priv->next_handler_id = 1;
347 priv->handlers = NULL;
348 priv->power_saving_mode = FALSE;
349 priv->unimportant_queue = g_queue_new ();
350
351 priv->iq_reply_handlers = g_hash_table_new_full (g_str_hash, g_str_equal,
352 NULL, (GDestroyNotify) stanza_iq_handler_free);
353 }
354
355 static void wocky_c2s_porter_dispose (GObject *object);
356 static void wocky_c2s_porter_finalize (GObject *object);
357
358 static void
wocky_c2s_porter_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)359 wocky_c2s_porter_set_property (GObject *object,
360 guint property_id,
361 const GValue *value,
362 GParamSpec *pspec)
363 {
364 WockyC2SPorter *connection = WOCKY_C2S_PORTER (object);
365 WockyC2SPorterPrivate *priv =
366 connection->priv;
367
368 switch (property_id)
369 {
370 gchar *node;
371
372 case PROP_CONNECTION:
373 g_assert (priv->connection == NULL);
374 priv->connection = g_value_dup_object (value);
375 g_assert (priv->connection != NULL);
376 break;
377
378 case PROP_FULL_JID:
379 g_assert (priv->full_jid == NULL); /* construct-only */
380 g_assert (priv->bare_jid == NULL);
381 g_assert (priv->resource == NULL);
382
383 priv->full_jid = g_value_dup_string (value);
384 g_assert (priv->full_jid != NULL);
385 wocky_decode_jid (priv->full_jid, &node, &priv->domain, &priv->resource);
386 priv->bare_jid = wocky_compose_jid (node, priv->domain, NULL);
387 g_free (node);
388 break;
389
390 default:
391 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
392 break;
393 }
394 }
395
396 static void
wocky_c2s_porter_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)397 wocky_c2s_porter_get_property (GObject *object,
398 guint property_id,
399 GValue *value,
400 GParamSpec *pspec)
401 {
402 WockyC2SPorter *connection = WOCKY_C2S_PORTER (object);
403 WockyC2SPorterPrivate *priv =
404 connection->priv;
405
406 switch (property_id)
407 {
408 case PROP_CONNECTION:
409 g_value_set_object (value, priv->connection);
410 break;
411
412 case PROP_FULL_JID:
413 g_value_set_string (value, priv->full_jid);
414 break;
415
416 case PROP_BARE_JID:
417 g_value_set_string (value, priv->bare_jid);
418 break;
419
420 case PROP_RESOURCE:
421 g_value_set_string (value, priv->resource);
422 break;
423
424 default:
425 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
426 break;
427 }
428 }
429
430 static gboolean
handle_stream_error(WockyPorter * porter,WockyStanza * stanza,gpointer user_data)431 handle_stream_error (WockyPorter *porter,
432 WockyStanza *stanza,
433 gpointer user_data)
434 {
435 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
436 GError *error = NULL;
437 gboolean ret = wocky_stanza_extract_stream_error (stanza, &error);
438
439 /* If wocky_stanza_extract_stream_error() failed, @stanza wasn't a stream
440 * error, in which case we are broken.
441 */
442 g_return_val_if_fail (ret, FALSE);
443
444 DEBUG ("Received stream error; consider the remote connection to be closed");
445 remote_connection_closed (self, error);
446 g_error_free (error);
447 return TRUE;
448 }
449
450 static void
wocky_c2s_porter_constructed(GObject * object)451 wocky_c2s_porter_constructed (GObject *object)
452 {
453 WockyC2SPorter *self = WOCKY_C2S_PORTER (object);
454 WockyC2SPorterPrivate *priv = self->priv;
455
456 if (G_OBJECT_CLASS (wocky_c2s_porter_parent_class)->constructed)
457 G_OBJECT_CLASS (wocky_c2s_porter_parent_class)->constructed (object);
458
459 g_assert (priv->connection != NULL);
460
461 /* Register the IQ reply handler */
462 wocky_porter_register_handler_from_anyone (WOCKY_PORTER (self),
463 WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_RESULT,
464 WOCKY_PORTER_HANDLER_PRIORITY_MAX,
465 handle_iq_reply, self, NULL);
466
467 wocky_porter_register_handler_from_anyone (WOCKY_PORTER (self),
468 WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_ERROR,
469 WOCKY_PORTER_HANDLER_PRIORITY_MAX,
470 handle_iq_reply, self, NULL);
471
472 /* Register the stream error handler. We use _from_anyone() here because we can
473 * trust servers not to relay spurious stream errors to us, and this feels
474 * safer than risking missing a stream error to bugs in the _from_server()
475 * checking code. */
476 wocky_porter_register_handler_from_anyone (WOCKY_PORTER (self),
477 WOCKY_STANZA_TYPE_STREAM_ERROR, WOCKY_STANZA_SUB_TYPE_NONE,
478 WOCKY_PORTER_HANDLER_PRIORITY_MAX,
479 handle_stream_error, self, NULL);
480 }
481
482 static void
wocky_c2s_porter_class_init(WockyC2SPorterClass * wocky_c2s_porter_class)483 wocky_c2s_porter_class_init (
484 WockyC2SPorterClass *wocky_c2s_porter_class)
485 {
486 GObjectClass *object_class = G_OBJECT_CLASS (wocky_c2s_porter_class);
487
488 g_type_class_add_private (wocky_c2s_porter_class,
489 sizeof (WockyC2SPorterPrivate));
490
491 object_class->constructed = wocky_c2s_porter_constructed;
492 object_class->set_property = wocky_c2s_porter_set_property;
493 object_class->get_property = wocky_c2s_porter_get_property;
494 object_class->dispose = wocky_c2s_porter_dispose;
495 object_class->finalize = wocky_c2s_porter_finalize;
496
497 g_object_class_override_property (object_class,
498 PROP_CONNECTION, "connection");
499 g_object_class_override_property (object_class,
500 PROP_FULL_JID, "full-jid");
501 g_object_class_override_property (object_class,
502 PROP_BARE_JID, "bare-jid");
503 g_object_class_override_property (object_class,
504 PROP_RESOURCE, "resource");
505 }
506
507 void
wocky_c2s_porter_dispose(GObject * object)508 wocky_c2s_porter_dispose (GObject *object)
509 {
510 WockyC2SPorter *self = WOCKY_C2S_PORTER (object);
511 WockyC2SPorterPrivate *priv =
512 self->priv;
513
514 if (priv->dispose_has_run)
515 return;
516
517 priv->dispose_has_run = TRUE;
518
519 if (priv->connection != NULL)
520 {
521 g_object_unref (priv->connection);
522 priv->connection = NULL;
523 }
524
525 if (priv->receive_cancellable != NULL)
526 {
527 g_warning ("Disposing an open XMPP porter");
528 g_cancellable_cancel (priv->receive_cancellable);
529 g_object_unref (priv->receive_cancellable);
530 priv->receive_cancellable = NULL;
531 }
532
533 if (priv->close_result != NULL)
534 {
535 g_object_unref (priv->close_result);
536 priv->close_result = NULL;
537 }
538
539 if (priv->close_cancellable != NULL)
540 {
541 g_object_unref (priv->close_cancellable);
542 priv->close_cancellable = NULL;
543 }
544
545 if (priv->force_close_result != NULL)
546 {
547 g_object_unref (priv->force_close_result);
548 priv->force_close_result = NULL;
549 }
550
551 if (priv->force_close_cancellable != NULL)
552 {
553 g_object_unref (priv->force_close_cancellable);
554 priv->force_close_cancellable = NULL;
555 }
556
557 if (G_OBJECT_CLASS (wocky_c2s_porter_parent_class)->dispose)
558 G_OBJECT_CLASS (wocky_c2s_porter_parent_class)->dispose (object);
559 }
560
561 void
wocky_c2s_porter_finalize(GObject * object)562 wocky_c2s_porter_finalize (GObject *object)
563 {
564 WockyC2SPorter *self = WOCKY_C2S_PORTER (object);
565 WockyC2SPorterPrivate *priv =
566 self->priv;
567
568 DEBUG ("finalize porter %p", self);
569
570 /* sending_queue_elem keeps a ref on the Porter (through the
571 * GSimpleAsyncResult) so it shouldn't be destroyed while there are
572 * elements in the queue. */
573 g_assert_cmpuint (g_queue_get_length (priv->sending_queue), ==, 0);
574 g_queue_free (priv->sending_queue);
575
576 g_hash_table_unref (priv->handlers_by_id);
577 g_list_free (priv->handlers);
578 g_hash_table_unref (priv->iq_reply_handlers);
579
580 g_queue_free (priv->unimportant_queue);
581
582 g_queue_foreach (&priv->queueable_stanza_patterns, (GFunc) g_object_unref, NULL);
583 g_queue_clear (&priv->queueable_stanza_patterns);
584
585 g_free (priv->full_jid);
586 g_free (priv->bare_jid);
587 g_free (priv->resource);
588 g_free (priv->domain);
589
590 G_OBJECT_CLASS (wocky_c2s_porter_parent_class)->finalize (object);
591 }
592
593 /**
594 * wocky_c2s_porter_new:
595 * @connection: #WockyXmppConnection which will be used to receive and send
596 * #WockyStanza
597 * @full_jid: the full JID of the user
598 *
599 * Convenience function to create a new #WockyC2SPorter.
600 *
601 * Returns: a new #WockyPorter.
602 */
603 WockyPorter *
wocky_c2s_porter_new(WockyXmppConnection * connection,const gchar * full_jid)604 wocky_c2s_porter_new (WockyXmppConnection *connection,
605 const gchar *full_jid)
606 {
607 return g_object_new (WOCKY_TYPE_C2S_PORTER,
608 "connection", connection,
609 "full-jid", full_jid,
610 NULL);
611 }
612
613 static void
send_head_stanza(WockyC2SPorter * self)614 send_head_stanza (WockyC2SPorter *self)
615 {
616 WockyC2SPorterPrivate *priv = self->priv;
617 sending_queue_elem *elem;
618
619 elem = g_queue_peek_head (priv->sending_queue);
620 if (elem == NULL)
621 /* Nothing to send */
622 return;
623
624 if (elem->cancelled_sig_id != 0)
625 {
626 /* We are going to start sending the stanza. Lower layers are now
627 * responsible of handling the cancellable. */
628 g_signal_handler_disconnect (elem->cancellable, elem->cancelled_sig_id);
629 elem->cancelled_sig_id = 0;
630 }
631
632 wocky_xmpp_connection_send_stanza_async (priv->connection,
633 elem->stanza, elem->cancellable, send_stanza_cb, g_object_ref (self));
634
635 g_signal_emit_by_name (self, "sending", elem->stanza);
636 }
637
638 static void
terminate_sending_operations(WockyC2SPorter * self,GError * error)639 terminate_sending_operations (WockyC2SPorter *self,
640 GError *error)
641 {
642 WockyC2SPorterPrivate *priv = self->priv;
643 sending_queue_elem *elem;
644
645 g_return_if_fail (error != NULL);
646
647 while ((elem = g_queue_pop_head (priv->sending_queue)))
648 {
649 g_simple_async_result_set_from_error (elem->result, error);
650 g_simple_async_result_complete (elem->result);
651 sending_queue_elem_free (elem);
652 }
653 }
654
655 static gboolean
sending_in_progress(WockyC2SPorter * self)656 sending_in_progress (WockyC2SPorter *self)
657 {
658 WockyC2SPorterPrivate *priv = self->priv;
659
660 return g_queue_get_length (priv->sending_queue) > 0 ||
661 priv->sending_whitespace_ping;
662 }
663
664 static void
close_if_waiting(WockyC2SPorter * self)665 close_if_waiting (WockyC2SPorter *self)
666 {
667 WockyC2SPorterPrivate *priv = self->priv;
668
669 if (priv->waiting_to_close && !sending_in_progress (self))
670 {
671 /* Nothing to send left and we are waiting to close the connection. */
672 DEBUG ("Queue has been flushed. Closing the connection.");
673 send_close (self);
674 }
675 }
676
677 static void
send_stanza_cb(GObject * source,GAsyncResult * res,gpointer user_data)678 send_stanza_cb (GObject *source,
679 GAsyncResult *res,
680 gpointer user_data)
681 {
682 WockyC2SPorter *self = WOCKY_C2S_PORTER (user_data);
683 WockyC2SPorterPrivate *priv = self->priv;
684 GError *error = NULL;
685
686 if (!wocky_xmpp_connection_send_stanza_finish (
687 WOCKY_XMPP_CONNECTION (source), res, &error))
688 {
689 /* Sending failed. Cancel this sending operation and all the others
690 * pending ones as we won't be able to send any more stanza. */
691 terminate_sending_operations (self, error);
692 g_error_free (error);
693 }
694 else
695 {
696 sending_queue_elem *elem = g_queue_pop_head (priv->sending_queue);
697
698 if (elem == NULL)
699 /* The elem could have been removed from the queue if its sending
700 * operation has already been completed (for example by forcing to
701 * close the connection). */
702 return;
703
704 g_simple_async_result_complete (elem->result);
705
706 sending_queue_elem_free (elem);
707
708 if (g_queue_get_length (priv->sending_queue) > 0)
709 {
710 /* Send next stanza */
711 send_head_stanza (self);
712 }
713 }
714
715 close_if_waiting (self);
716
717 g_object_unref (self);
718 }
719
720 static void
send_cancelled_cb(GCancellable * cancellable,gpointer user_data)721 send_cancelled_cb (GCancellable *cancellable,
722 gpointer user_data)
723 {
724 sending_queue_elem *elem = (sending_queue_elem *) user_data;
725 WockyC2SPorterPrivate *priv = elem->self->priv;
726 GError error = { G_IO_ERROR, G_IO_ERROR_CANCELLED, "Sending was cancelled" };
727
728 g_simple_async_result_set_from_error (elem->result, &error);
729 g_simple_async_result_complete_in_idle (elem->result);
730
731 g_queue_remove (priv->sending_queue, elem);
732 sending_queue_elem_free (elem);
733 }
734
735 static void
wocky_c2s_porter_send_async(WockyPorter * porter,WockyStanza * stanza,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)736 wocky_c2s_porter_send_async (WockyPorter *porter,
737 WockyStanza *stanza,
738 GCancellable *cancellable,
739 GAsyncReadyCallback callback,
740 gpointer user_data)
741 {
742 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
743 WockyC2SPorterPrivate *priv = self->priv;
744 sending_queue_elem *elem;
745
746 if (priv->close_result != NULL || priv->force_close_result != NULL)
747 {
748 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
749 user_data, WOCKY_PORTER_ERROR,
750 WOCKY_PORTER_ERROR_CLOSING,
751 "Porter is closing");
752 return;
753 }
754
755 elem = sending_queue_elem_new (self, stanza, cancellable, callback,
756 user_data);
757 g_queue_push_tail (priv->sending_queue, elem);
758
759 if (g_queue_get_length (priv->sending_queue) == 1 &&
760 !priv->sending_whitespace_ping)
761 {
762 send_head_stanza (self);
763 }
764 else if (cancellable != NULL)
765 {
766 elem->cancelled_sig_id = g_cancellable_connect (cancellable,
767 G_CALLBACK (send_cancelled_cb), elem, NULL);
768 }
769 }
770
771 static gboolean
wocky_c2s_porter_send_finish(WockyPorter * porter,GAsyncResult * result,GError ** error)772 wocky_c2s_porter_send_finish (WockyPorter *porter,
773 GAsyncResult *result,
774 GError **error)
775 {
776 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
777
778 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
779 error))
780 return FALSE;
781
782 g_return_val_if_fail (g_simple_async_result_is_valid (result,
783 G_OBJECT (self), wocky_c2s_porter_send_async), FALSE);
784
785 return TRUE;
786 }
787
788 static void receive_stanza (WockyC2SPorter *self);
789
790 static void
complete_close(WockyC2SPorter * self)791 complete_close (WockyC2SPorter *self)
792 {
793 WockyC2SPorterPrivate *priv = self->priv;
794 GSimpleAsyncResult *tmp;
795
796 if (g_cancellable_is_cancelled (priv->close_cancellable))
797 {
798 g_simple_async_result_set_error (priv->close_result, G_IO_ERROR,
799 G_IO_ERROR_CANCELLED, "closing operation was cancelled");
800 }
801
802 if (priv->close_cancellable)
803 g_object_unref (priv->close_cancellable);
804
805 priv->close_cancellable = NULL;
806
807 if (priv->force_close_cancellable)
808 g_object_unref (priv->force_close_cancellable);
809
810 priv->force_close_cancellable = NULL;
811
812 tmp = priv->close_result;
813 priv->close_result = NULL;
814 g_simple_async_result_complete (tmp);
815 g_object_unref (tmp);
816 }
817
818 static gboolean
stanza_is_from_server(WockyC2SPorter * self,const gchar * nfrom)819 stanza_is_from_server (
820 WockyC2SPorter *self,
821 const gchar *nfrom)
822 {
823 return (nfrom == NULL ||
824 !wocky_strdiff (nfrom, self->priv->full_jid) ||
825 !wocky_strdiff (nfrom, self->priv->bare_jid) ||
826 !wocky_strdiff (nfrom, self->priv->domain));
827 }
828
829 /* Return TRUE if not spoofed. */
830 static gboolean
check_spoofing(WockyC2SPorter * self,WockyStanza * reply,const gchar * should_be_from)831 check_spoofing (WockyC2SPorter *self,
832 WockyStanza *reply,
833 const gchar *should_be_from)
834 {
835 const gchar *from;
836 gchar *nfrom;
837 gboolean ret = TRUE;
838
839 from = wocky_stanza_get_from (reply);
840
841 /* fast path for a byte-for-byte match */
842 if (G_LIKELY (!wocky_strdiff (from, should_be_from)))
843 return TRUE;
844
845 /* OK, we have to do some work */
846
847 nfrom = wocky_normalise_jid (from);
848
849 /* nearly-as-fast path for a normalized match */
850 if (!wocky_strdiff (nfrom, should_be_from))
851 goto finally;
852
853 /* if we sent an IQ without a 'to' attribute, it's to our server: allow it
854 * to use our full/bare JID or domain to reply */
855 if (should_be_from == NULL)
856 {
857 if (stanza_is_from_server (self, nfrom))
858 goto finally;
859 }
860
861 /* if we sent an IQ to our full or bare JID, allow our server to omit 'to'
862 * in the reply (Prosody 0.6.1 does this with the resulting error if we
863 * send disco#info to our own bare JID), or to use our full JID. */
864 if (from == NULL || !wocky_strdiff (nfrom, self->priv->full_jid))
865 {
866 if (!wocky_strdiff (should_be_from, self->priv->full_jid) ||
867 !wocky_strdiff (should_be_from, self->priv->bare_jid))
868 goto finally;
869 }
870
871 DEBUG ("'%s' (normal: '%s') attempts to spoof an IQ reply from '%s'",
872 from == NULL ? "(null)" : from,
873 nfrom == NULL ? "(null)" : nfrom,
874 should_be_from == NULL ? "(null)" : should_be_from);
875 DEBUG ("Our full JID is '%s' and our bare JID is '%s'",
876 self->priv->full_jid, self->priv->bare_jid);
877
878 ret = FALSE;
879
880 finally:
881 g_free (nfrom);
882 return ret;
883 }
884
885 static gboolean
handle_iq_reply(WockyPorter * porter,WockyStanza * reply,gpointer user_data)886 handle_iq_reply (WockyPorter *porter,
887 WockyStanza *reply,
888 gpointer user_data)
889 {
890 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
891 WockyC2SPorterPrivate *priv = self->priv;
892 const gchar *id;
893 StanzaIqHandler *handler;
894 gboolean ret = FALSE;
895
896 id = wocky_node_get_attribute (wocky_stanza_get_top_node (reply), "id");
897 if (id == NULL)
898 {
899 DEBUG ("Ignoring reply without IQ id");
900 return FALSE;
901 }
902
903 handler = g_hash_table_lookup (priv->iq_reply_handlers, id);
904
905 if (handler == NULL)
906 {
907 DEBUG ("Ignored IQ reply");
908 return FALSE;
909 }
910
911 if (!check_spoofing (self, reply, handler->recipient))
912 return FALSE;
913
914 if (handler->result != NULL)
915 {
916 GSimpleAsyncResult *r = handler->result;
917
918 handler->result = NULL;
919
920 /* Don't want to get cancelled during completion */
921 stanza_iq_handler_remove_cancellable (handler);
922
923 g_simple_async_result_set_op_res_gpointer (r, reply, NULL);
924 g_simple_async_result_complete (r);
925 g_object_unref (r);
926
927 ret = TRUE;
928 }
929
930 stanza_iq_handler_maybe_remove (handler);
931 return ret;
932 }
933
934 static void
handle_stanza(WockyC2SPorter * self,WockyStanza * stanza)935 handle_stanza (WockyC2SPorter *self,
936 WockyStanza *stanza)
937 {
938 WockyC2SPorterPrivate *priv = self->priv;
939 GList *l;
940 const gchar *from;
941 WockyStanzaType type;
942 WockyStanzaSubType sub_type;
943 gchar *node = NULL, *domain = NULL, *resource = NULL;
944 gboolean is_from_server;
945 gboolean handled = FALSE;
946
947 wocky_stanza_get_type_info (stanza, &type, &sub_type);
948
949 /* The from attribute of the stanza need not always be present, for example
950 * when receiving roster items, so don't enforce it. */
951 from = wocky_stanza_get_from (stanza);
952
953 if (from == NULL)
954 {
955 is_from_server = TRUE;
956 }
957 else if (wocky_decode_jid (from, &node, &domain, &resource))
958 {
959 /* FIXME: the stanza should really ensure that 'from' and 'to' are
960 * pre-validated and normalized so we don't have to do this again.
961 */
962 gchar *nfrom = wocky_compose_jid (node, domain, resource);
963
964 is_from_server = stanza_is_from_server (self, nfrom);
965 g_free (nfrom);
966 }
967 else
968 {
969 is_from_server = FALSE;
970 }
971
972 for (l = priv->handlers; l != NULL && !handled; l = g_list_next (l))
973 {
974 StanzaHandler *handler = (StanzaHandler *) l->data;
975
976 if (type != handler->type &&
977 handler->type != WOCKY_STANZA_TYPE_NONE)
978 continue;
979
980 if (sub_type != handler->sub_type &&
981 handler->sub_type != WOCKY_STANZA_SUB_TYPE_NONE)
982 continue;
983
984 switch (handler->sender_match)
985 {
986 case MATCH_ANYONE:
987 break;
988
989 case MATCH_SERVER:
990 if (!is_from_server)
991 continue;
992 break;
993
994 case MATCH_JID:
995 g_assert (handler->domain != NULL);
996
997 if (wocky_strdiff (node, handler->node))
998 continue;
999
1000 if (wocky_strdiff (domain, handler->domain))
1001 continue;
1002
1003 /* If a resource was specified, we need to match against it. */
1004 if (handler->resource != NULL &&
1005 wocky_strdiff (resource, handler->resource))
1006 continue;
1007
1008 break;
1009 }
1010
1011 /* Check if the stanza matches the pattern */
1012 if (handler->match != NULL &&
1013 !wocky_node_is_superset (wocky_stanza_get_top_node (stanza),
1014 wocky_stanza_get_top_node (handler->match)))
1015 continue;
1016
1017 handled = handler->callback (WOCKY_PORTER (self), stanza,
1018 handler->user_data);
1019 }
1020
1021 if (!handled)
1022 {
1023 DEBUG ("Stanza not handled");
1024
1025 if (type == WOCKY_STANZA_TYPE_IQ &&
1026 (sub_type == WOCKY_STANZA_SUB_TYPE_GET ||
1027 sub_type == WOCKY_STANZA_SUB_TYPE_SET))
1028 wocky_porter_send_iq_error (WOCKY_PORTER (self), stanza,
1029 WOCKY_XMPP_ERROR_SERVICE_UNAVAILABLE, NULL);
1030 }
1031
1032 g_free (node);
1033 g_free (domain);
1034 g_free (resource);
1035 }
1036
1037 /* immediately handle any queued stanzas */
1038 static void
flush_unimportant_queue(WockyC2SPorter * self)1039 flush_unimportant_queue (WockyC2SPorter *self)
1040 {
1041 WockyC2SPorterPrivate *priv = self->priv;
1042
1043 while (!g_queue_is_empty (priv->unimportant_queue))
1044 {
1045 WockyStanza *stanza = g_queue_pop_head (priv->unimportant_queue);
1046 handle_stanza (self, stanza);
1047 g_object_unref (stanza);
1048 }
1049 }
1050
1051 /* create a list of patterns of stanzas that can be safely queued */
1052 static void
build_queueable_stanza_patterns(WockyC2SPorter * self)1053 build_queueable_stanza_patterns (WockyC2SPorter *self)
1054 {
1055 WockyC2SPorterPrivate *priv = self->priv;
1056 gchar **node_name = NULL;
1057 gchar *node_names [] = {
1058 "http://jabber.org/protocol/geoloc",
1059 "http://jabber.org/protocol/nick",
1060 "http://laptop.org/xmpp/buddy-properties",
1061 "http://laptop.org/xmpp/activities",
1062 "http://laptop.org/xmpp/current-activity",
1063 "http://laptop.org/xmpp/activity-properties",
1064 NULL};
1065
1066 for (node_name = node_names; *node_name != NULL ; node_name++)
1067 {
1068 WockyStanza *pattern = wocky_stanza_build (
1069 WOCKY_STANZA_TYPE_MESSAGE,
1070 WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL,
1071 '(', "event",
1072 ':', WOCKY_XMPP_NS_PUBSUB_EVENT,
1073 '(', "items",
1074 '@', "node", *node_name,
1075 ')',
1076 ')',
1077 NULL);
1078
1079 g_queue_push_tail (&priv->queueable_stanza_patterns, pattern);
1080 }
1081 }
1082
1083 static gboolean
is_stanza_important(WockyC2SPorter * self,WockyStanza * stanza)1084 is_stanza_important (WockyC2SPorter *self,
1085 WockyStanza *stanza)
1086 {
1087 WockyC2SPorterPrivate *priv = self->priv;
1088 WockyNode *node = wocky_stanza_get_top_node (stanza);
1089 WockyStanzaType type;
1090 GList *l;
1091
1092 wocky_stanza_get_type_info (stanza, &type, NULL);
1093
1094 /* <presence/> and <presence type="unavailable"/> are queueable */
1095 if (type == WOCKY_STANZA_TYPE_PRESENCE)
1096 {
1097 const gchar *ptype = wocky_node_get_attribute (node, "type");
1098 /* presence type is either missing or "unavailable" */
1099 if ((ptype == NULL) || !wocky_strdiff (ptype, "unavailable"))
1100 {
1101 return FALSE;
1102 }
1103 }
1104
1105 if (priv->queueable_stanza_patterns.length == 0)
1106 build_queueable_stanza_patterns (self);
1107
1108 /* check whether stanza matches any of the queueable patterns */
1109 for (l = priv->queueable_stanza_patterns.head; l != NULL; l = l->next)
1110 {
1111 if (wocky_node_is_superset (node, wocky_stanza_get_top_node (
1112 WOCKY_STANZA (l->data))))
1113 return FALSE;
1114 }
1115
1116 /* everything else is important */
1117 return TRUE;
1118 }
1119
1120 static void
queue_or_handle_stanza(WockyC2SPorter * self,WockyStanza * stanza)1121 queue_or_handle_stanza (WockyC2SPorter *self,
1122 WockyStanza *stanza)
1123 {
1124 WockyC2SPorterPrivate *priv = self->priv;
1125
1126 if (priv->power_saving_mode)
1127 {
1128 if (is_stanza_important (self, stanza))
1129 {
1130 flush_unimportant_queue (self);
1131 handle_stanza (self, stanza);
1132 }
1133 else
1134 {
1135 g_queue_push_tail (priv->unimportant_queue, g_object_ref (stanza));
1136 }
1137 }
1138 else
1139 {
1140 handle_stanza (self, stanza);
1141 }
1142 }
1143
1144 static void
abort_pending_iqs(WockyC2SPorter * self,GError * error)1145 abort_pending_iqs (WockyC2SPorter *self,
1146 GError *error)
1147 {
1148 WockyC2SPorterPrivate *priv = self->priv;
1149 GHashTableIter iter;
1150 gpointer value;
1151
1152 g_hash_table_iter_init (&iter, priv->iq_reply_handlers);
1153 while (g_hash_table_iter_next (&iter, NULL, &value))
1154 {
1155 StanzaIqHandler *handler = value;
1156
1157 if (handler->result == NULL)
1158 continue;
1159
1160 /* Don't want to get cancelled during completion */
1161 stanza_iq_handler_remove_cancellable (handler);
1162
1163 g_simple_async_result_set_from_error (handler->result, error);
1164 g_simple_async_result_complete_in_idle (handler->result);
1165
1166 g_object_unref (handler->result);
1167 handler->result = NULL;
1168
1169 if (handler->sent)
1170 g_hash_table_iter_remove (&iter);
1171 }
1172 }
1173
1174 static void
remote_connection_closed(WockyC2SPorter * self,GError * error)1175 remote_connection_closed (WockyC2SPorter *self,
1176 GError *error)
1177 {
1178 WockyC2SPorterPrivate *priv = self->priv;
1179 gboolean error_occured = TRUE;
1180
1181 /* Completing a close operation and firing the remote-closed/remote-error
1182 * signals could make the library user unref the porter. So we take a
1183 * reference to ourself for the duration of this function.
1184 */
1185 g_object_ref (self);
1186
1187 /* Complete pending send IQ operations as we won't be able to receive their
1188 * IQ replies */
1189 abort_pending_iqs (self, error);
1190
1191 if (g_error_matches (error, WOCKY_XMPP_CONNECTION_ERROR,
1192 WOCKY_XMPP_CONNECTION_ERROR_CLOSED))
1193 error_occured = FALSE;
1194
1195 /* This flag MUST be set before we emit the remote-* signals: If it is not *
1196 * some very subtle and hard to debug problems are created, which can in *
1197 * turn conceal further problems in the code. You have been warned. */
1198 priv->remote_closed = TRUE;
1199
1200 if (error_occured)
1201 {
1202 g_signal_emit_by_name (self, "remote-error", error->domain,
1203 error->code, error->message);
1204 }
1205 else
1206 {
1207 g_signal_emit_by_name (self, "remote-closed");
1208 }
1209
1210 if (priv->close_result != NULL && priv->local_closed)
1211 {
1212 if (error_occured)
1213 {
1214 /* We sent our close but something went wrong with the connection
1215 * so we won't be able to receive close from the other side.
1216 * Complete the close operation. */
1217 g_simple_async_result_set_from_error (priv->close_result, error);
1218 }
1219
1220 complete_close (self);
1221 }
1222
1223 if (priv->receive_cancellable != NULL)
1224 {
1225 g_object_unref (priv->receive_cancellable);
1226 priv->receive_cancellable = NULL;
1227 }
1228
1229 g_object_unref (self);
1230 }
1231
1232 static void
connection_force_close_cb(GObject * source,GAsyncResult * res,gpointer user_data)1233 connection_force_close_cb (GObject *source,
1234 GAsyncResult *res,
1235 gpointer user_data)
1236 {
1237 WockyC2SPorter *self = WOCKY_C2S_PORTER (user_data);
1238 WockyC2SPorterPrivate *priv = self->priv;
1239 GSimpleAsyncResult *r = priv->force_close_result;
1240 GError *error = NULL;
1241
1242 /* null out the result so no-one else can use it after us *
1243 * this should never happen, but nullifying it lets us trap *
1244 * that internal inconsistency if it arises */
1245 priv->force_close_result = NULL;
1246 priv->local_closed = TRUE;
1247
1248 /* This can fail if the porter has put two *
1249 * wocky_xmpp_connection_force_close_async ops in flight *
1250 * at the same time: this is bad and should never happen: */
1251 g_assert (r != NULL);
1252
1253 if (!wocky_xmpp_connection_force_close_finish (WOCKY_XMPP_CONNECTION (source),
1254 res, &error))
1255 {
1256 g_simple_async_result_set_from_error (r, error);
1257 g_error_free (error);
1258 }
1259
1260 if (priv->receive_cancellable != NULL)
1261 {
1262 g_object_unref (priv->receive_cancellable);
1263 priv->receive_cancellable = NULL;
1264 }
1265
1266 DEBUG ("XMPP connection has been closed; complete the force close operation");
1267 g_simple_async_result_complete (r);
1268 g_object_unref (r);
1269
1270 g_object_unref (self);
1271 }
1272
1273 static void
stanza_received_cb(GObject * source,GAsyncResult * res,gpointer user_data)1274 stanza_received_cb (GObject *source,
1275 GAsyncResult *res,
1276 gpointer user_data)
1277 {
1278 WockyC2SPorter *self = WOCKY_C2S_PORTER (user_data);
1279 WockyC2SPorterPrivate *priv = self->priv;
1280 WockyStanza *stanza;
1281 GError *error = NULL;
1282
1283 stanza = wocky_xmpp_connection_recv_stanza_finish (
1284 WOCKY_XMPP_CONNECTION (source), res, &error);
1285 if (stanza == NULL)
1286 {
1287 if (g_error_matches (error, WOCKY_XMPP_CONNECTION_ERROR,
1288 WOCKY_XMPP_CONNECTION_ERROR_CLOSED))
1289 {
1290 DEBUG ("Remote connection has been closed");
1291 }
1292 else
1293 {
1294 DEBUG ("Error receiving stanza: %s", error->message);
1295 }
1296
1297 if (priv->force_close_result != NULL)
1298 {
1299 DEBUG ("Receive operation has been cancelled; ");
1300 if (!priv->forced_shutdown)
1301 {
1302 /* We are forcing the closing. Actually close the connection. */
1303 DEBUG ("force shutdown of the XMPP connection");
1304 g_object_ref (self);
1305 priv->forced_shutdown = TRUE;
1306 wocky_xmpp_connection_force_close_async (priv->connection,
1307 priv->force_close_cancellable,
1308 connection_force_close_cb, self);
1309 }
1310 else
1311 {
1312 DEBUG ("forced shutdown of XMPP connection already in progress");
1313 }
1314 }
1315 else
1316 {
1317 remote_connection_closed (self, error);
1318 }
1319
1320 g_error_free (error);
1321 return;
1322 }
1323
1324 /* Calling out to a stanza handler could make the library user unref the
1325 * porter; hence, we take a reference to ourself for the rest of the
1326 * function.
1327 */
1328 g_object_ref (self);
1329
1330 queue_or_handle_stanza (self, stanza);
1331 g_object_unref (stanza);
1332
1333 if (!priv->remote_closed)
1334 {
1335 /* We didn't detect any error on the stream, wait for next stanza */
1336 receive_stanza (self);
1337 }
1338 else
1339 {
1340 DEBUG ("Remote connection has been closed, don't wait for next stanza");
1341 DEBUG ("Remote connection has been closed; ");
1342
1343 if (priv->forced_shutdown)
1344 {
1345 DEBUG ("forced shutdown of the XMPP connection already in progress");
1346 }
1347 else if (priv->force_close_result != NULL)
1348 {
1349 DEBUG ("force shutdown of the XMPP connection");
1350 g_object_ref (self);
1351 priv->forced_shutdown = TRUE;
1352 wocky_xmpp_connection_force_close_async (priv->connection,
1353 priv->force_close_cancellable, connection_force_close_cb, self);
1354 }
1355 }
1356
1357 g_object_unref (self);
1358 }
1359
1360 static void
receive_stanza(WockyC2SPorter * self)1361 receive_stanza (WockyC2SPorter *self)
1362 {
1363 WockyC2SPorterPrivate *priv = self->priv;
1364
1365 wocky_xmpp_connection_recv_stanza_async (priv->connection,
1366 priv->receive_cancellable, stanza_received_cb, self);
1367 }
1368
1369 static void
wocky_c2s_porter_start(WockyPorter * porter)1370 wocky_c2s_porter_start (WockyPorter *porter)
1371 {
1372 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
1373 WockyC2SPorterPrivate *priv = self->priv;
1374
1375 if (priv->receive_cancellable != NULL)
1376 /* Porter has already been started */
1377 return;
1378
1379 priv->receive_cancellable = g_cancellable_new ();
1380
1381 receive_stanza (self);
1382 }
1383
1384 static void
close_sent_cb(GObject * source,GAsyncResult * res,gpointer user_data)1385 close_sent_cb (GObject *source,
1386 GAsyncResult *res,
1387 gpointer user_data)
1388 {
1389 WockyC2SPorter *self = WOCKY_C2S_PORTER (user_data);
1390 WockyC2SPorterPrivate *priv = self->priv;
1391 GError *error = NULL;
1392
1393 priv->local_closed = TRUE;
1394
1395 if (!wocky_xmpp_connection_send_close_finish (WOCKY_XMPP_CONNECTION (source),
1396 res, &error))
1397 {
1398 g_simple_async_result_set_from_error (priv->close_result, error);
1399 g_error_free (error);
1400
1401 goto out;
1402 }
1403
1404 if (!g_cancellable_is_cancelled (priv->close_cancellable)
1405 && !priv->remote_closed)
1406 {
1407 /* we'll complete the close operation once the remote side closes it's
1408 * connection */
1409 return;
1410 }
1411
1412 out:
1413 if (priv->close_result != NULL)
1414 {
1415 /* close operation could already be completed if the other side closes
1416 * before we send our close */
1417 complete_close (self);
1418 }
1419 }
1420
1421 static void
send_close(WockyC2SPorter * self)1422 send_close (WockyC2SPorter *self)
1423 {
1424 WockyC2SPorterPrivate *priv = self->priv;
1425
1426 wocky_xmpp_connection_send_close_async (priv->connection,
1427 NULL, close_sent_cb, self);
1428 priv->waiting_to_close = FALSE;
1429 }
1430
1431 static void
wocky_c2s_porter_close_async(WockyPorter * porter,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1432 wocky_c2s_porter_close_async (WockyPorter *porter,
1433 GCancellable *cancellable,
1434 GAsyncReadyCallback callback,
1435 gpointer user_data)
1436 {
1437 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
1438 WockyC2SPorterPrivate *priv = self->priv;
1439
1440 if (priv->local_closed)
1441 {
1442 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
1443 user_data, WOCKY_PORTER_ERROR,
1444 WOCKY_PORTER_ERROR_CLOSED,
1445 "Porter has already been closed");
1446 return;
1447 }
1448
1449 if (priv->receive_cancellable == NULL && !priv->remote_closed)
1450 {
1451 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
1452 user_data, WOCKY_PORTER_ERROR,
1453 WOCKY_PORTER_ERROR_NOT_STARTED,
1454 "Porter has not been started");
1455 return;
1456 }
1457
1458 if (priv->close_result != NULL)
1459 {
1460 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
1461 user_data, G_IO_ERROR,
1462 G_IO_ERROR_PENDING,
1463 "Another close operation is pending");
1464 return;
1465 }
1466
1467 if (priv->force_close_result != NULL)
1468 {
1469 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
1470 user_data, G_IO_ERROR, G_IO_ERROR_PENDING,
1471 "A force close operation is pending");
1472 return;
1473 }
1474
1475 priv->close_result = g_simple_async_result_new (G_OBJECT (self),
1476 callback, user_data, wocky_c2s_porter_close_async);
1477
1478 g_assert (priv->close_cancellable == NULL);
1479
1480 if (cancellable != NULL)
1481 priv->close_cancellable = g_object_ref (cancellable);
1482
1483 g_signal_emit_by_name (self, "closing");
1484
1485 if (sending_in_progress (self))
1486 {
1487 DEBUG ("Sending queue is not empty. Flushing it before "
1488 "closing the connection.");
1489 priv->waiting_to_close = TRUE;
1490 return;
1491 }
1492
1493 send_close (self);
1494 }
1495
1496 static gboolean
wocky_c2s_porter_close_finish(WockyPorter * self,GAsyncResult * result,GError ** error)1497 wocky_c2s_porter_close_finish (WockyPorter *self,
1498 GAsyncResult *result,
1499 GError **error)
1500 {
1501 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1502 error))
1503 return FALSE;
1504
1505 g_return_val_if_fail (g_simple_async_result_is_valid (result,
1506 G_OBJECT (self), wocky_c2s_porter_close_async), FALSE);
1507
1508 return TRUE;
1509 }
1510
1511 static gint
compare_handler(StanzaHandler * a,StanzaHandler * b)1512 compare_handler (StanzaHandler *a,
1513 StanzaHandler *b)
1514 {
1515 /* List is sorted by decreasing priority */
1516 if (a->priority < b->priority)
1517 return 1;
1518 else if (a->priority > b->priority)
1519 return -1;
1520 else
1521 return 0;
1522 }
1523
1524 static guint
wocky_c2s_porter_register_handler_internal(WockyC2SPorter * self,WockyStanzaType type,WockyStanzaSubType sub_type,SenderMatch sender_match,const gchar * from,guint priority,WockyPorterHandlerFunc callback,gpointer user_data,WockyStanza * stanza)1525 wocky_c2s_porter_register_handler_internal (WockyC2SPorter *self,
1526 WockyStanzaType type,
1527 WockyStanzaSubType sub_type,
1528 SenderMatch sender_match,
1529 const gchar *from,
1530 guint priority,
1531 WockyPorterHandlerFunc callback,
1532 gpointer user_data,
1533 WockyStanza *stanza)
1534 {
1535 WockyC2SPorterPrivate *priv = self->priv;
1536 StanzaHandler *handler;
1537
1538 g_return_val_if_fail (WOCKY_IS_PORTER (self), 0);
1539
1540 handler = stanza_handler_new (type, sub_type, sender_match, from, priority,
1541 stanza, callback, user_data);
1542
1543 g_hash_table_insert (priv->handlers_by_id,
1544 GUINT_TO_POINTER (priv->next_handler_id), handler);
1545 priv->handlers = g_list_insert_sorted (priv->handlers, handler,
1546 (GCompareFunc) compare_handler);
1547
1548 return priv->next_handler_id++;
1549 }
1550
1551 static guint
wocky_c2s_porter_register_handler_from_by_stanza(WockyPorter * porter,WockyStanzaType type,WockyStanzaSubType sub_type,const gchar * from,guint priority,WockyPorterHandlerFunc callback,gpointer user_data,WockyStanza * stanza)1552 wocky_c2s_porter_register_handler_from_by_stanza (WockyPorter *porter,
1553 WockyStanzaType type,
1554 WockyStanzaSubType sub_type,
1555 const gchar *from,
1556 guint priority,
1557 WockyPorterHandlerFunc callback,
1558 gpointer user_data,
1559 WockyStanza *stanza)
1560 {
1561 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
1562
1563 g_return_val_if_fail (from != NULL, 0);
1564
1565 return wocky_c2s_porter_register_handler_internal (self, type, sub_type,
1566 MATCH_JID, from,
1567 priority, callback, user_data, stanza);
1568 }
1569
1570 static guint
wocky_c2s_porter_register_handler_from_anyone_by_stanza(WockyPorter * porter,WockyStanzaType type,WockyStanzaSubType sub_type,guint priority,WockyPorterHandlerFunc callback,gpointer user_data,WockyStanza * stanza)1571 wocky_c2s_porter_register_handler_from_anyone_by_stanza (
1572 WockyPorter *porter,
1573 WockyStanzaType type,
1574 WockyStanzaSubType sub_type,
1575 guint priority,
1576 WockyPorterHandlerFunc callback,
1577 gpointer user_data,
1578 WockyStanza *stanza)
1579 {
1580 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
1581
1582 return wocky_c2s_porter_register_handler_internal (self, type, sub_type,
1583 MATCH_ANYONE, NULL,
1584 priority, callback, user_data, stanza);
1585 }
1586
1587 /**
1588 * wocky_c2s_porter_register_handler_from_server_va:
1589 * @self: A #WockyC2SPorter instance (passed to @callback).
1590 * @type: The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match
1591 * any type of stanza.
1592 * @sub_type: The subtype of stanza to be handled, or
1593 * WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.
1594 * @priority: a priority between %WOCKY_PORTER_HANDLER_PRIORITY_MIN and
1595 * %WOCKY_PORTER_HANDLER_PRIORITY_MAX (often
1596 * %WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority
1597 * (larger number) are called first.
1598 * @callback: A #WockyPorterHandlerFunc, which should return %FALSE to decline
1599 * the stanza (Wocky will continue to the next handler, if any), or %TRUE to
1600 * stop further processing.
1601 * @user_data: Passed to @callback.
1602 * @ap: a wocky_stanza_build() specification. The handler
1603 * will match a stanza only if the stanza received is a superset of the one
1604 * passed to this function, as per wocky_node_is_superset().
1605 *
1606 * A <type>va_list</type> version of
1607 * wocky_c2s_porter_register_handler_from_server(); see that function for more
1608 * details.
1609 *
1610 * Returns: a non-zero ID for use with wocky_porter_unregister_handler().
1611 */
1612 guint
wocky_c2s_porter_register_handler_from_server_va(WockyC2SPorter * self,WockyStanzaType type,WockyStanzaSubType sub_type,guint priority,WockyPorterHandlerFunc callback,gpointer user_data,va_list ap)1613 wocky_c2s_porter_register_handler_from_server_va (
1614 WockyC2SPorter *self,
1615 WockyStanzaType type,
1616 WockyStanzaSubType sub_type,
1617 guint priority,
1618 WockyPorterHandlerFunc callback,
1619 gpointer user_data,
1620 va_list ap)
1621 {
1622 guint ret;
1623 WockyStanza *stanza;
1624
1625 g_return_val_if_fail (WOCKY_IS_C2S_PORTER (self), 0);
1626
1627 if (type == WOCKY_STANZA_TYPE_NONE)
1628 {
1629 stanza = NULL;
1630 g_return_val_if_fail (
1631 (va_arg (ap, WockyNodeBuildTag) == 0) &&
1632 "Pattern-matching is not supported when matching stanzas "
1633 "of any type", 0);
1634 }
1635 else
1636 {
1637 stanza = wocky_stanza_build_va (type, WOCKY_STANZA_SUB_TYPE_NONE,
1638 NULL, NULL, ap);
1639 g_assert (stanza != NULL);
1640 }
1641
1642 ret = wocky_c2s_porter_register_handler_from_server_by_stanza (self, type, sub_type,
1643 priority, callback, user_data, stanza);
1644
1645 if (stanza != NULL)
1646 g_object_unref (stanza);
1647
1648 return ret;
1649 }
1650
1651 /**
1652 * wocky_c2s_porter_register_handler_from_server_by_stanza:
1653 * @self: A #WockyC2SPorter instance (passed to @callback).
1654 * @type: The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match
1655 * any type of stanza.
1656 * @sub_type: The subtype of stanza to be handled, or
1657 * WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.
1658 * @priority: a priority between %WOCKY_PORTER_HANDLER_PRIORITY_MIN and
1659 * %WOCKY_PORTER_HANDLER_PRIORITY_MAX (often
1660 * %WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority
1661 * (larger number) are called first.
1662 * @callback: A #WockyPorterHandlerFunc, which should return %FALSE to decline
1663 * the stanza (Wocky will continue to the next handler, if any), or %TRUE to
1664 * stop further processing.
1665 * @user_data: Passed to @callback.
1666 * @stanza: a #WockyStanza. The handler will match a stanza only if
1667 * the stanza received is a superset of the one passed to this
1668 * function, as per wocky_node_is_superset().
1669 *
1670 * A #WockyStanza version of
1671 * wocky_c2s_porter_register_handler_from_server(); see that function for more
1672 * details.
1673 *
1674 * Returns: a non-zero ID for use with wocky_porter_unregister_handler().
1675 */
1676 guint
wocky_c2s_porter_register_handler_from_server_by_stanza(WockyC2SPorter * self,WockyStanzaType type,WockyStanzaSubType sub_type,guint priority,WockyPorterHandlerFunc callback,gpointer user_data,WockyStanza * stanza)1677 wocky_c2s_porter_register_handler_from_server_by_stanza (
1678 WockyC2SPorter *self,
1679 WockyStanzaType type,
1680 WockyStanzaSubType sub_type,
1681 guint priority,
1682 WockyPorterHandlerFunc callback,
1683 gpointer user_data,
1684 WockyStanza *stanza)
1685 {
1686 g_return_val_if_fail (WOCKY_IS_C2S_PORTER (self), 0);
1687
1688 if (type == WOCKY_STANZA_TYPE_NONE)
1689 g_return_val_if_fail (stanza == NULL, 0);
1690 else
1691 g_return_val_if_fail (WOCKY_IS_STANZA (stanza), 0);
1692
1693 return wocky_c2s_porter_register_handler_internal (self, type, sub_type,
1694 MATCH_SERVER, NULL,
1695 priority, callback, user_data, stanza);
1696 }
1697
1698 /**
1699 * wocky_c2s_porter_register_handler_from_server:
1700 * @self: A #WockyC2SPorter instance (passed to @callback).
1701 * @type: The type of stanza to be handled, or WOCKY_STANZA_TYPE_NONE to match
1702 * any type of stanza.
1703 * @sub_type: The subtype of stanza to be handled, or
1704 * WOCKY_STANZA_SUB_TYPE_NONE to match any type of stanza.
1705 * @priority: a priority between %WOCKY_PORTER_HANDLER_PRIORITY_MIN and
1706 * %WOCKY_PORTER_HANDLER_PRIORITY_MAX (often
1707 * %WOCKY_PORTER_HANDLER_PRIORITY_NORMAL). Handlers with a higher priority
1708 * (larger number) are called first.
1709 * @callback: A #WockyPorterHandlerFunc, which should return %FALSE to decline
1710 * the stanza (Wocky will continue to the next handler, if any), or %TRUE to
1711 * stop further processing.
1712 * @user_data: Passed to @callback.
1713 * @...: a wocky_stanza_build() specification. The handler
1714 * will match a stanza only if the stanza received is a superset of the one
1715 * passed to this function, as per wocky_node_is_superset().
1716 *
1717 * Registers a handler for incoming stanzas from the local user's server; that
1718 * is, stanzas with no "from" attribute, or where the sender is the user's own
1719 * bare or full JID.
1720 *
1721 * For example, to register a handler for roster pushes, call:
1722 *
1723 * |[
1724 * id = wocky_c2s_porter_register_handler_from_server (porter,
1725 * WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_SET,
1726 * WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, roster_push_received_cb, NULL,
1727 * '(',
1728 * "query", ':', WOCKY_XMPP_NS_ROSTER,
1729 * ')', NULL);
1730 * ]|
1731 *
1732 * Returns: a non-zero ID for use with wocky_porter_unregister_handler().
1733 */
1734 guint
wocky_c2s_porter_register_handler_from_server(WockyC2SPorter * self,WockyStanzaType type,WockyStanzaSubType sub_type,guint priority,WockyPorterHandlerFunc callback,gpointer user_data,...)1735 wocky_c2s_porter_register_handler_from_server (
1736 WockyC2SPorter *self,
1737 WockyStanzaType type,
1738 WockyStanzaSubType sub_type,
1739 guint priority,
1740 WockyPorterHandlerFunc callback,
1741 gpointer user_data,
1742 ...)
1743 {
1744 va_list ap;
1745 guint ret;
1746
1747 g_return_val_if_fail (WOCKY_IS_C2S_PORTER (self), 0);
1748
1749 va_start (ap, user_data);
1750 ret = wocky_c2s_porter_register_handler_from_server_va (self, type, sub_type,
1751 priority, callback, user_data, ap);
1752 va_end (ap);
1753
1754 return ret;
1755 }
1756
1757 static void
wocky_c2s_porter_unregister_handler(WockyPorter * porter,guint id)1758 wocky_c2s_porter_unregister_handler (WockyPorter *porter,
1759 guint id)
1760 {
1761 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
1762 WockyC2SPorterPrivate *priv = self->priv;
1763 StanzaHandler *handler;
1764
1765 handler = g_hash_table_lookup (priv->handlers_by_id, GUINT_TO_POINTER (id));
1766 if (handler == NULL)
1767 {
1768 g_warning ("Trying to remove an unregistered handler: %u", id);
1769 return;
1770 }
1771
1772 priv->handlers = g_list_remove (priv->handlers, handler);
1773 g_hash_table_remove (priv->handlers_by_id, GUINT_TO_POINTER (id));
1774 }
1775
1776 /**
1777 * wocky_c2s_porter_enable_power_saving_mode:
1778 * @porter: a #WockyC2SPorter
1779 * @enable: A boolean specifying whether power saving mode should be used
1780 *
1781 * Enable or disable power saving. In power saving mode, Wocky will
1782 * attempt to queue "uninteresting" stanza until it is either manually
1783 * flushed, until important stanza arrives, or until the power saving
1784 * mode is disabled.
1785 *
1786 * Queueable stanzas are:
1787 *
1788 * <itemizedlist>
1789 * <listitem><code><presence/></code> and
1790 * <code><presence type="unavailable"/></code>;</listitem>
1791 * <listitem>PEP updates for a hardcoded list of namespaces.</listitem>
1792 * </itemizedlist>
1793 *
1794 * Whenever stanza is handled, all previously queued stanzas
1795 * (if any) are handled as well, in the order they arrived. This preserves
1796 * stanza ordering.
1797 *
1798 * Note that exiting the power saving mode will immediately handle any
1799 * queued stanzas.
1800 */
1801 void
wocky_c2s_porter_enable_power_saving_mode(WockyC2SPorter * porter,gboolean enable)1802 wocky_c2s_porter_enable_power_saving_mode (WockyC2SPorter *porter,
1803 gboolean enable)
1804 {
1805 WockyC2SPorterPrivate *priv = porter->priv;
1806
1807 if (priv->power_saving_mode && !enable)
1808 {
1809 flush_unimportant_queue (porter);
1810 }
1811
1812 priv->power_saving_mode = enable;
1813 }
1814
1815 static void
send_iq_cancelled_cb(GCancellable * cancellable,gpointer user_data)1816 send_iq_cancelled_cb (GCancellable *cancellable,
1817 gpointer user_data)
1818 {
1819 StanzaIqHandler *handler = (StanzaIqHandler *) user_data;
1820 GError error = { G_IO_ERROR, G_IO_ERROR_CANCELLED,
1821 "IQ sending was cancelled" };
1822
1823 /* The disconnect should always be disconnected if the result has been
1824 * finished */
1825 g_assert (handler->result != NULL);
1826
1827 g_simple_async_result_set_from_error (handler->result, &error);
1828 g_simple_async_result_complete_in_idle (handler->result);
1829 g_object_unref (handler->result);
1830 handler->result = NULL;
1831
1832 stanza_iq_handler_maybe_remove (handler);
1833 }
1834
1835 static void
iq_sent_cb(GObject * source,GAsyncResult * res,gpointer user_data)1836 iq_sent_cb (GObject *source,
1837 GAsyncResult *res,
1838 gpointer user_data)
1839 {
1840 WockyC2SPorter *self = WOCKY_C2S_PORTER (source);
1841 StanzaIqHandler *handler = (StanzaIqHandler *) user_data;
1842 GError *error = NULL;
1843
1844 handler->sent = TRUE;
1845
1846 if (wocky_c2s_porter_send_finish (WOCKY_PORTER (self), res, &error))
1847 goto finished;
1848
1849 /* Raise an error */
1850 if (handler->result != NULL)
1851 {
1852 GSimpleAsyncResult *r = handler->result;
1853 handler->result = NULL;
1854
1855 /* Don't want to get cancelled during completion */
1856 stanza_iq_handler_remove_cancellable (handler);
1857
1858 g_simple_async_result_set_from_error (r, error);
1859 g_simple_async_result_complete (r);
1860 g_object_unref (r);
1861 }
1862 g_error_free (error);
1863
1864 finished:
1865 stanza_iq_handler_maybe_remove (handler);
1866 }
1867
1868 static void
wocky_c2s_porter_send_iq_async(WockyPorter * porter,WockyStanza * stanza,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1869 wocky_c2s_porter_send_iq_async (WockyPorter *porter,
1870 WockyStanza *stanza,
1871 GCancellable *cancellable,
1872 GAsyncReadyCallback callback,
1873 gpointer user_data)
1874 {
1875 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
1876 WockyC2SPorterPrivate *priv = self->priv;
1877 StanzaIqHandler *handler;
1878 const gchar *recipient;
1879 gchar *id = NULL;
1880 GSimpleAsyncResult *result;
1881 WockyStanzaType type;
1882 WockyStanzaSubType sub_type;
1883
1884 if (priv->close_result != NULL || priv->force_close_result != NULL)
1885 {
1886 gchar *node = NULL;
1887
1888 g_assert (stanza != NULL && wocky_stanza_get_top_node (stanza) != NULL);
1889
1890 node = wocky_node_to_string (wocky_stanza_get_top_node (stanza));
1891 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
1892 user_data, WOCKY_PORTER_ERROR,
1893 WOCKY_PORTER_ERROR_CLOSING,
1894 "Porter is closing: iq '%s' aborted", node);
1895 g_free (node);
1896
1897 return;
1898 }
1899
1900 wocky_stanza_get_type_info (stanza, &type, &sub_type);
1901
1902 if (type != WOCKY_STANZA_TYPE_IQ)
1903 goto wrong_stanza;
1904
1905 if (sub_type != WOCKY_STANZA_SUB_TYPE_GET &&
1906 sub_type != WOCKY_STANZA_SUB_TYPE_SET)
1907 goto wrong_stanza;
1908
1909 recipient = wocky_stanza_get_to (stanza);
1910
1911 /* Set an unique ID */
1912 do
1913 {
1914 g_free (id);
1915 id = wocky_xmpp_connection_new_id (priv->connection);
1916 }
1917 while (g_hash_table_lookup (priv->iq_reply_handlers, id) != NULL);
1918
1919 wocky_node_set_attribute (wocky_stanza_get_top_node (stanza), "id", id);
1920
1921 result = g_simple_async_result_new (G_OBJECT (self),
1922 callback, user_data, wocky_c2s_porter_send_iq_async);
1923
1924 handler = stanza_iq_handler_new (self, id, result, cancellable,
1925 recipient);
1926
1927 if (cancellable != NULL)
1928 {
1929 handler->cancelled_sig_id = g_cancellable_connect (cancellable,
1930 G_CALLBACK (send_iq_cancelled_cb), handler, NULL);
1931 }
1932
1933 g_hash_table_insert (priv->iq_reply_handlers, id, handler);
1934
1935 wocky_c2s_porter_send_async (WOCKY_PORTER (self), stanza, cancellable,
1936 iq_sent_cb, handler);
1937 return;
1938
1939 wrong_stanza:
1940 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
1941 user_data, WOCKY_PORTER_ERROR,
1942 WOCKY_PORTER_ERROR_NOT_IQ,
1943 "Stanza is not an IQ query");
1944 }
1945
1946 static WockyStanza *
wocky_c2s_porter_send_iq_finish(WockyPorter * self,GAsyncResult * result,GError ** error)1947 wocky_c2s_porter_send_iq_finish (WockyPorter *self,
1948 GAsyncResult *result,
1949 GError **error)
1950 {
1951 WockyStanza *reply;
1952
1953 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1954 error))
1955 return NULL;
1956
1957 g_return_val_if_fail (g_simple_async_result_is_valid (result,
1958 G_OBJECT (self), wocky_c2s_porter_send_iq_async), NULL);
1959
1960 reply = g_simple_async_result_get_op_res_gpointer (
1961 G_SIMPLE_ASYNC_RESULT (result));
1962
1963 return g_object_ref (reply);
1964 }
1965
1966 static void
wocky_c2s_porter_force_close_async(WockyPorter * porter,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1967 wocky_c2s_porter_force_close_async (WockyPorter *porter,
1968 GCancellable *cancellable,
1969 GAsyncReadyCallback callback,
1970 gpointer user_data)
1971 {
1972 WockyC2SPorter *self = WOCKY_C2S_PORTER (porter);
1973 WockyC2SPorterPrivate *priv = self->priv;
1974 GError err = { WOCKY_PORTER_ERROR, WOCKY_PORTER_ERROR_FORCIBLY_CLOSED,
1975 "Porter was closed forcibly" };
1976
1977 if (priv->force_close_result != NULL)
1978 {
1979 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
1980 user_data, G_IO_ERROR, G_IO_ERROR_PENDING,
1981 "Another force close operation is pending");
1982 return;
1983 }
1984
1985 if (priv->receive_cancellable == NULL && priv->local_closed)
1986 {
1987 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
1988 user_data, WOCKY_PORTER_ERROR,
1989 WOCKY_PORTER_ERROR_CLOSED,
1990 "Porter has already been closed");
1991 return;
1992 }
1993
1994 if (priv->receive_cancellable == NULL && !priv->remote_closed)
1995 {
1996 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
1997 user_data, WOCKY_PORTER_ERROR,
1998 WOCKY_PORTER_ERROR_NOT_STARTED,
1999 "Porter has not been started");
2000 return;
2001 }
2002
2003 /* Ensure to keep us alive during the closing */
2004 g_object_ref (self);
2005
2006 if (priv->close_result != NULL)
2007 {
2008 /* Finish pending close operation */
2009 g_simple_async_result_set_from_error (priv->close_result, &err);
2010 g_simple_async_result_complete_in_idle (priv->close_result);
2011 g_object_unref (priv->close_result);
2012 priv->close_result = NULL;
2013 }
2014 else
2015 {
2016 /* the "closing" signal has already been fired when _close_async has
2017 * been called */
2018 g_signal_emit_by_name (self, "closing");
2019 }
2020
2021 priv->force_close_result = g_simple_async_result_new (G_OBJECT (self),
2022 callback, user_data, wocky_c2s_porter_force_close_async);
2023
2024 g_assert (priv->force_close_cancellable == NULL);
2025
2026 if (cancellable != NULL)
2027 priv->force_close_cancellable = g_object_ref (cancellable);
2028
2029 /* force_close_result now keeps a ref on ourself so we can release the ref
2030 * without risking to destroy the object */
2031 g_object_unref (self);
2032
2033 /* Terminate all the pending sending operations */
2034 terminate_sending_operations (self, &err);
2035
2036 /* Terminate all the pending send IQ operations */
2037 abort_pending_iqs (self, &err);
2038
2039 if (priv->remote_closed)
2040 {
2041 /* forced shutdown in progress. noop */
2042 if (priv->forced_shutdown)
2043 {
2044 g_simple_async_report_error_in_idle (G_OBJECT (self), callback,
2045 user_data, WOCKY_PORTER_ERROR,
2046 WOCKY_PORTER_ERROR_FORCIBLY_CLOSED,
2047 "Porter is already executing a forced-shutdown");
2048 g_object_unref (priv->force_close_result);
2049 priv->force_close_result = NULL;
2050 return;
2051 }
2052 /* No need to wait, close connection right now */
2053 DEBUG ("remote is already closed, close the XMPP connection");
2054 g_object_ref (self);
2055 priv->forced_shutdown = TRUE;
2056 wocky_xmpp_connection_force_close_async (priv->connection,
2057 priv->force_close_cancellable, connection_force_close_cb, self);
2058 return;
2059 }
2060
2061 /* The operation will be completed when:
2062 * - the receive operation has been cancelled
2063 * - the XMPP connection has been closed
2064 */
2065
2066 g_cancellable_cancel (priv->receive_cancellable);
2067 }
2068
2069 static gboolean
wocky_c2s_porter_force_close_finish(WockyPorter * self,GAsyncResult * result,GError ** error)2070 wocky_c2s_porter_force_close_finish (
2071 WockyPorter *self,
2072 GAsyncResult *result,
2073 GError **error)
2074 {
2075 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
2076 error))
2077 return FALSE;
2078
2079 g_return_val_if_fail (g_simple_async_result_is_valid (result,
2080 G_OBJECT (self), wocky_c2s_porter_force_close_async), FALSE);
2081
2082 return TRUE;
2083 }
2084
2085 static void
send_whitespace_ping_cb(GObject * source,GAsyncResult * res,gpointer user_data)2086 send_whitespace_ping_cb (GObject *source,
2087 GAsyncResult *res,
2088 gpointer user_data)
2089 {
2090 GSimpleAsyncResult *res_out = user_data;
2091 WockyC2SPorter *self = WOCKY_C2S_PORTER (
2092 g_async_result_get_source_object (G_ASYNC_RESULT (res_out)));
2093 WockyC2SPorterPrivate *priv = self->priv;
2094 GError *error = NULL;
2095
2096 priv->sending_whitespace_ping = FALSE;
2097
2098 if (!wocky_xmpp_connection_send_whitespace_ping_finish (
2099 WOCKY_XMPP_CONNECTION (source), res, &error))
2100 {
2101 g_simple_async_result_set_from_error (res_out, error);
2102 g_simple_async_result_complete (res_out);
2103
2104 /* Sending the ping failed; there is no point in trying to send
2105 * anything else at this point. */
2106 terminate_sending_operations (self, error);
2107
2108 g_error_free (error);
2109 }
2110 else
2111 {
2112 g_simple_async_result_complete (res_out);
2113
2114 /* Somebody could have tried sending a stanza while we were sending
2115 * the ping */
2116 if (g_queue_get_length (priv->sending_queue) > 0)
2117 send_head_stanza (self);
2118 }
2119
2120 close_if_waiting (self);
2121
2122 g_object_unref (self);
2123 g_object_unref (res_out);
2124 }
2125
2126 /**
2127 * wocky_c2s_porter_send_whitespace_ping_async:
2128 * @self: a #WockyC2SPorter
2129 * @cancellable: optional GCancellable object, NULL to ignore.
2130 * @callback: callback to call when the request is satisfied.
2131 * @user_data: the data to pass to callback function.
2132 *
2133 * Request asynchronous sending of a whitespace ping. When the operation is
2134 * finished @callback will be called. You can then call
2135 * wocky_c2s_porter_send_whitespace_ping_finish() to get the result of the
2136 * operation.
2137 * No pings are sent if there are already other stanzas or pings being sent
2138 * when this function is called; it would be useless.
2139 */
2140 void
wocky_c2s_porter_send_whitespace_ping_async(WockyC2SPorter * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2141 wocky_c2s_porter_send_whitespace_ping_async (WockyC2SPorter *self,
2142 GCancellable *cancellable,
2143 GAsyncReadyCallback callback,
2144 gpointer user_data)
2145 {
2146 WockyC2SPorterPrivate *priv = self->priv;
2147 GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (self),
2148 callback, user_data, wocky_c2s_porter_send_whitespace_ping_async);
2149
2150 if (priv->close_result != NULL || priv->force_close_result != NULL)
2151 {
2152 g_simple_async_result_set_error (result, WOCKY_PORTER_ERROR,
2153 WOCKY_PORTER_ERROR_CLOSING, "Porter is closing");
2154 g_simple_async_result_complete_in_idle (result);
2155 }
2156 else if (sending_in_progress (self))
2157 {
2158 g_simple_async_result_complete_in_idle (result);
2159 }
2160 else
2161 {
2162 priv->sending_whitespace_ping = TRUE;
2163
2164 wocky_xmpp_connection_send_whitespace_ping_async (priv->connection,
2165 cancellable, send_whitespace_ping_cb, g_object_ref (result));
2166
2167 g_signal_emit_by_name (self, "sending", NULL);
2168 }
2169
2170 g_object_unref (result);
2171 }
2172
2173 /**
2174 * wocky_c2s_porter_send_whitespace_ping_finish:
2175 * @self: a #WockyC2SPorter
2176 * @result: a GAsyncResult.
2177 * @error: a GError location to store the error occuring, or NULL to ignore.
2178 *
2179 * Finishes sending a whitespace ping.
2180 *
2181 * Returns: TRUE if the ping was succesfully sent, FALSE on error.
2182 */
2183 gboolean
wocky_c2s_porter_send_whitespace_ping_finish(WockyC2SPorter * self,GAsyncResult * result,GError ** error)2184 wocky_c2s_porter_send_whitespace_ping_finish (WockyC2SPorter *self,
2185 GAsyncResult *result,
2186 GError **error)
2187 {
2188 wocky_implement_finish_void (self,
2189 wocky_c2s_porter_send_whitespace_ping_async);
2190 }
2191
2192 static const gchar *
wocky_c2s_porter_get_full_jid(WockyPorter * porter)2193 wocky_c2s_porter_get_full_jid (WockyPorter *porter)
2194 {
2195 WockyC2SPorter *self;
2196
2197 g_return_val_if_fail (WOCKY_IS_C2S_PORTER (porter), NULL);
2198
2199 self = (WockyC2SPorter *) porter;
2200
2201 return self->priv->full_jid;
2202 }
2203
2204 static const gchar *
wocky_c2s_porter_get_bare_jid(WockyPorter * porter)2205 wocky_c2s_porter_get_bare_jid (WockyPorter *porter)
2206 {
2207 WockyC2SPorter *self;
2208
2209 g_return_val_if_fail (WOCKY_IS_C2S_PORTER (porter), NULL);
2210
2211 self = (WockyC2SPorter *) porter;
2212
2213 return self->priv->bare_jid;
2214 }
2215
2216 static const gchar *
wocky_c2s_porter_get_resource(WockyPorter * porter)2217 wocky_c2s_porter_get_resource (WockyPorter *porter)
2218 {
2219 WockyC2SPorter *self;
2220
2221 g_return_val_if_fail (WOCKY_IS_C2S_PORTER (porter), NULL);
2222
2223 self = (WockyC2SPorter *) porter;
2224
2225 return self->priv->resource;
2226 }
2227
2228 static void
wocky_porter_iface_init(gpointer g_iface,gpointer iface_data)2229 wocky_porter_iface_init (gpointer g_iface,
2230 gpointer iface_data)
2231 {
2232 WockyPorterInterface *iface = g_iface;
2233
2234 iface->get_full_jid = wocky_c2s_porter_get_full_jid;
2235 iface->get_bare_jid = wocky_c2s_porter_get_bare_jid;
2236 iface->get_resource = wocky_c2s_porter_get_resource;
2237
2238 iface->start = wocky_c2s_porter_start;
2239
2240 iface->send_async = wocky_c2s_porter_send_async;
2241 iface->send_finish = wocky_c2s_porter_send_finish;
2242
2243 iface->register_handler_from_by_stanza =
2244 wocky_c2s_porter_register_handler_from_by_stanza;
2245 iface->register_handler_from_anyone_by_stanza =
2246 wocky_c2s_porter_register_handler_from_anyone_by_stanza;
2247
2248 iface->unregister_handler = wocky_c2s_porter_unregister_handler;
2249
2250 iface->close_async = wocky_c2s_porter_close_async;
2251 iface->close_finish = wocky_c2s_porter_close_finish;
2252
2253 iface->send_iq_async = wocky_c2s_porter_send_iq_async;
2254 iface->send_iq_finish = wocky_c2s_porter_send_iq_finish;
2255
2256 iface->force_close_async = wocky_c2s_porter_force_close_async;
2257 iface->force_close_finish = wocky_c2s_porter_force_close_finish;
2258 }
2259