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>&lt;presence/&gt;</code> and
1790  *      <code>&lt;presence type="unavailable"/&gt;</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