1 /*
2  * sip-connection.c - Source for SIPConnection
3  * Copyright (C) 2005-2007 Collabora Ltd.
4  * Copyright (C) 2005-2007 Nokia Corporation
5  *   @author Kai Vehmanen <first.surname@nokia.com>
6  *   @author Martti Mela <first.surname@nokia.com>
7  *
8  * Based on telepathy-gabble implementation (gabble-connection).
9  *   @author See gabble-connection.c
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * version 2.1 as published by the Free Software Foundation.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  */
24 
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #define DBUS_API_SUBJECT_TO_CHANGE 1
29 #include <dbus/dbus-glib-lowlevel.h>
30 
31 #include <telepathy-glib/enums.h>
32 #include <telepathy-glib/errors.h>
33 #include <telepathy-glib/dbus.h>
34 #include <telepathy-glib/handle-repo-dynamic.h>
35 #include <telepathy-glib/handle-repo-static.h>
36 #include <telepathy-glib/interfaces.h>
37 #include <telepathy-glib/intset.h>
38 #include <telepathy-glib/svc-connection.h>
39 
40 #include "media-factory.h"
41 #include "text-factory.h"
42 #include "sip-connection.h"
43 
44 #define DEBUG_FLAG SIP_DEBUG_CONNECTION
45 #include "debug.h"
46 
47 static void conn_iface_init (gpointer, gpointer);
48 
49 G_DEFINE_TYPE_WITH_CODE(SIPConnection, sip_connection,
50     TP_TYPE_BASE_CONNECTION,
51     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION,
52       conn_iface_init))
53 
54 #include "sip-connection-enumtypes.h"
55 #include "sip-connection-helpers.h"
56 #include "sip-connection-private.h"
57 #include "sip-connection-sofia.h"
58 
59 #define ERROR_IF_NOT_CONNECTED_ASYNC(BASE, CONTEXT) \
60   if ((BASE)->status != TP_CONNECTION_STATUS_CONNECTED) \
61     { \
62       GError e = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE, \
63           "Connection is disconnected" }; \
64       DEBUG ("rejected request as disconnected"); \
65       dbus_g_method_return_error ((CONTEXT), &e); \
66       return; \
67     }
68 
69 
70 /* properties */
71 enum
72 {
73   PROP_ADDRESS = 1,      /**< public SIP address (SIP URI) */
74   PROP_AUTH_USER,        /**< account username (if different from public address userinfo part) */
75   PROP_PASSWORD,         /**< account password (for registration) */
76 
77   PROP_PROXY,            /**< outbound SIP proxy (SIP URI) */
78   PROP_REGISTRAR,        /**< SIP registrar (SIP URI) */
79 
80   PROP_KEEPALIVE_MECHANISM, /**< keepalive mechanism as defined by SIPConnectionKeepaliveMechanism */
81   PROP_KEEPALIVE_INTERVAL, /**< keepalive interval in seconds */
82   PROP_DISCOVER_BINDING,   /**< enable discovery of public binding */
83   PROP_DISCOVER_STUN,      /**< Discover STUN server name using DNS SRV lookup */
84   PROP_STUN_SERVER,        /**< STUN server address (if not set, derived
85 			        from public SIP address */
86   PROP_STUN_PORT,          /**< STUN port */
87   PROP_LOCAL_IP_ADDRESS,   /**< Local IP address (normally not needed, chosen by stack) */
88   PROP_LOCAL_PORT,         /**< Local port for SIP (normally not needed, chosen by stack) */
89   PROP_EXTRA_AUTH_USER,	   /**< User name to use for extra authentication challenges */
90   PROP_EXTRA_AUTH_PASSWORD,/**< Password to use for extra authentication challenges */
91   PROP_SOFIA_ROOT,         /**< Event root pointer from the Sofia-SIP stack */
92   LAST_PROPERTY
93 };
94 
95 
96 static void
priv_value_set_url_as_string(GValue * value,const url_t * url)97 priv_value_set_url_as_string (GValue *value, const url_t *url)
98 {
99   if (url == NULL)
100     {
101       g_value_set_string (value, NULL);
102     }
103   else
104     {
105       su_home_t temphome[1] = { SU_HOME_INIT(temphome) };
106       char *tempstr;
107       tempstr = url_as_string (temphome, url);
108       g_value_set_string (value, tempstr);
109       su_home_deinit (temphome);
110     }
111 }
112 
113 static url_t *
priv_url_from_string_value(su_home_t * home,const GValue * value)114 priv_url_from_string_value (su_home_t *home, const GValue *value)
115 {
116   const gchar *url_str;
117   g_assert (home != NULL);
118   url_str = g_value_get_string (value);
119   return (url_str)? url_make (home, url_str) : NULL;
120 }
121 
122 /* keep these two in sync */
123 enum
124 {
125   LIST_HANDLE_PUBLISH = 1,
126   LIST_HANDLE_SUBSCRIBE,
127   LIST_HANDLE_KNOWN,
128 };
129 static const char *list_handle_strings[] =
130 {
131     "publish",    /* LIST_HANDLE_PUBLISH */
132     "subscribe",  /* LIST_HANDLE_SUBSCRIBE */
133     "known",      /* LIST_HANDLE_KNOWN */
134     NULL
135 };
136 
137 static gchar *normalize_sipuri (TpHandleRepoIface *repo, const gchar *sipuri,
138     gpointer context, GError **error);
139 
140 static void
sip_create_handle_repos(TpBaseConnection * conn,TpHandleRepoIface * repos[NUM_TP_HANDLE_TYPES])141 sip_create_handle_repos (TpBaseConnection *conn,
142                          TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES])
143 {
144   repos[TP_HANDLE_TYPE_CONTACT] =
145       (TpHandleRepoIface *)g_object_new (TP_TYPE_DYNAMIC_HANDLE_REPO,
146           "handle-type", TP_HANDLE_TYPE_CONTACT,
147           "normalize-function", normalize_sipuri,
148           "default-normalize-context", conn,
149           NULL);
150   repos[TP_HANDLE_TYPE_LIST] =
151       (TpHandleRepoIface *)g_object_new (TP_TYPE_STATIC_HANDLE_REPO,
152           "handle-type", TP_HANDLE_TYPE_LIST,
153           "handle-names", list_handle_strings, NULL);
154 }
155 
156 static GPtrArray *
sip_connection_create_channel_factories(TpBaseConnection * base)157 sip_connection_create_channel_factories (TpBaseConnection *base)
158 {
159   SIPConnection *self = SIP_CONNECTION (base);
160   SIPConnectionPrivate *priv;
161   GPtrArray *factories = g_ptr_array_sized_new (2);
162 
163   g_assert (SIP_IS_CONNECTION (self));
164   priv = SIP_CONNECTION_GET_PRIVATE (self);
165 
166   priv->text_factory = (TpChannelFactoryIface *)g_object_new (
167       SIP_TYPE_TEXT_FACTORY, "connection", self, NULL);
168   g_ptr_array_add (factories, priv->text_factory);
169 
170   priv->media_factory = (TpChannelFactoryIface *)g_object_new (
171       SIP_TYPE_MEDIA_FACTORY, "connection", self, NULL);
172   g_ptr_array_add (factories, priv->media_factory);
173 
174   return factories;
175 }
176 
177 static void
sip_connection_init(SIPConnection * obj)178 sip_connection_init (SIPConnection *obj)
179 {
180   SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (obj);
181   priv->sofia = sip_connection_sofia_new (obj);
182   priv->sofia_home = su_home_new(sizeof (su_home_t));
183   priv->auth_table = g_hash_table_new_full (g_direct_hash,
184                                             g_direct_equal,
185                                             NULL /* (GDestroyNotify) nua_handle_unref */,
186                                             g_free);
187 }
188 
189 static void
sip_connection_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)190 sip_connection_set_property (GObject      *object,
191                              guint         property_id,
192                              const GValue *value,
193                              GParamSpec   *pspec)
194 {
195   SIPConnection *self = (SIPConnection*) object;
196   SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
197 
198   switch (property_id) {
199   case PROP_ADDRESS: {
200     /* just store the address, self_handle set in start_connecting */
201     priv->address = g_value_dup_string (value);
202     break;
203   }
204   case PROP_AUTH_USER: {
205     g_free(priv->auth_user);
206     priv->auth_user = g_value_dup_string (value);
207     break;
208   }
209   case PROP_PASSWORD: {
210     g_free(priv->password);
211     priv->password = g_value_dup_string (value);
212     break;
213   }
214   case PROP_PROXY: {
215     priv->proxy_url = priv_url_from_string_value (priv->sofia_home, value);
216     if (priv->sofia_nua)
217       nua_set_params(priv->sofia_nua,
218                      NUTAG_PROXY(priv->proxy_url),
219                      TAG_END());
220     break;
221   }
222   case PROP_REGISTRAR: {
223     priv->registrar_url = priv_url_from_string_value (priv->sofia_home, value);
224     if (priv->sofia_nua)
225       nua_set_params(priv->sofia_nua,
226                      NUTAG_REGISTRAR(priv->registrar_url),
227                      TAG_END());
228     break;
229   }
230   case PROP_KEEPALIVE_MECHANISM: {
231     priv->keepalive_mechanism = g_value_get_enum (value);
232     if (priv->sofia_nua) {
233       sip_conn_update_nua_outbound (self);
234       sip_conn_update_nua_keepalive_interval (self);
235     }
236     break;
237   }
238   case PROP_KEEPALIVE_INTERVAL: {
239     priv->keepalive_interval = g_value_get_int (value);
240     if (priv->sofia_nua
241 	&& priv->keepalive_mechanism != SIP_CONNECTION_KEEPALIVE_NONE) {
242       sip_conn_update_nua_keepalive_interval(self);
243     }
244     break;
245   }
246   case PROP_DISCOVER_BINDING: {
247     priv->discover_binding = g_value_get_boolean (value);
248     if (priv->sofia_nua)
249       sip_conn_update_nua_outbound (self);
250     break;
251   }
252   case PROP_DISCOVER_STUN:
253     priv->discover_stun = g_value_get_boolean (value);
254     break;
255   case PROP_STUN_PORT: {
256     priv->stun_port = g_value_get_uint (value);
257     break;
258   }
259   case PROP_STUN_SERVER: {
260     g_free (priv->stun_host);
261     priv->stun_host = g_value_dup_string (value);
262     break;
263   }
264   case PROP_LOCAL_IP_ADDRESS: {
265     g_free (priv->local_ip_address);
266     priv->local_ip_address = g_value_dup_string (value);
267     break;
268   }
269   case PROP_LOCAL_PORT: {
270     priv->local_port = g_value_get_uint (value);
271     break;
272   }
273   case PROP_EXTRA_AUTH_USER: {
274     g_free((gpointer)priv->extra_auth_user);
275     priv->extra_auth_user =  g_value_dup_string (value);
276     break;
277   }
278   case PROP_EXTRA_AUTH_PASSWORD: {
279     g_free((gpointer)priv->extra_auth_password);
280     priv->extra_auth_password =  g_value_dup_string (value);
281     break;
282   }
283   case PROP_SOFIA_ROOT: {
284     g_return_if_fail (priv->sofia != NULL);
285     priv->sofia->sofia_root = g_value_get_pointer (value);
286     break;
287   }
288   default:
289     /* We don't have any other property... */
290     G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec);
291     break;
292   }
293 }
294 
295 static void
sip_connection_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)296 sip_connection_get_property (GObject      *object,
297                              guint         property_id,
298                              GValue       *value,
299                              GParamSpec   *pspec)
300 {
301   SIPConnection *self = (SIPConnection *) object;
302   SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
303 
304   switch (property_id) {
305   case PROP_ADDRESS: {
306     g_value_set_string (value, priv->address);
307     break;
308   }
309   case PROP_AUTH_USER: {
310     g_value_set_string (value, priv->auth_user);
311     break;
312   }
313   case PROP_PASSWORD: {
314     g_value_set_string (value, priv->password);
315     break;
316   }
317   case PROP_PROXY: {
318     priv_value_set_url_as_string (value, priv->proxy_url);
319     break;
320   }
321   case PROP_REGISTRAR: {
322     priv_value_set_url_as_string (value, priv->registrar_url);
323     break;
324   }
325   case PROP_KEEPALIVE_MECHANISM: {
326     g_value_set_enum (value, priv->keepalive_mechanism);
327     break;
328   }
329   case PROP_KEEPALIVE_INTERVAL: {
330     g_value_set_int (value, priv->keepalive_interval);
331     break;
332   }
333   case PROP_DISCOVER_BINDING: {
334     g_value_set_boolean (value, priv->discover_binding);
335     break;
336   }
337   case PROP_DISCOVER_STUN:
338     g_value_set_boolean (value, priv->discover_stun);
339     break;
340   case PROP_STUN_SERVER: {
341     g_value_set_string (value, priv->stun_host);
342     break;
343   }
344   case PROP_STUN_PORT: {
345     g_value_set_uint (value, priv->stun_port);
346     break;
347   }
348   case PROP_LOCAL_IP_ADDRESS: {
349     g_value_set_string (value, priv->local_ip_address);
350     break;
351   }
352   case PROP_LOCAL_PORT: {
353     g_value_set_uint (value, priv->local_port);
354     break;
355   }
356   case PROP_SOFIA_ROOT: {
357     g_return_if_fail (priv->sofia != NULL);
358     g_value_set_pointer (value, priv->sofia->sofia_root);
359     break;
360   }
361   default:
362     /* We don't have any other property... */
363     G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec);
364     break;
365   }
366 }
367 
368 static void sip_connection_dispose (GObject *object);
369 static void sip_connection_finalize (GObject *object);
370 
371 static gchar *
sip_connection_unique_name(TpBaseConnection * base)372 sip_connection_unique_name (TpBaseConnection *base)
373 {
374   SIPConnection *conn = SIP_CONNECTION (base);
375   SIPConnectionPrivate *priv;
376 
377   g_assert (SIP_IS_CONNECTION (conn));
378   priv = SIP_CONNECTION_GET_PRIVATE (conn);
379   return g_strdup (priv->address);
380 }
381 
382 static void sip_connection_disconnected (TpBaseConnection *base);
383 static void sip_connection_shut_down (TpBaseConnection *base);
384 static gboolean sip_connection_start_connecting (TpBaseConnection *base,
385     GError **error);
386 
387 static void
sip_connection_class_init(SIPConnectionClass * sip_connection_class)388 sip_connection_class_init (SIPConnectionClass *sip_connection_class)
389 {
390   GObjectClass *object_class = G_OBJECT_CLASS (sip_connection_class);
391   TpBaseConnectionClass *base_class =
392     (TpBaseConnectionClass *)sip_connection_class;
393   GParamSpec *param_spec;
394 
395 #define INST_PROP(x) \
396   g_object_class_install_property (object_class,  x, param_spec)
397 
398   /* Implement pure-virtual methods */
399   base_class->create_handle_repos = sip_create_handle_repos;
400   base_class->get_unique_connection_name = sip_connection_unique_name;
401   base_class->create_channel_factories =
402     sip_connection_create_channel_factories;
403   base_class->disconnected = sip_connection_disconnected;
404   base_class->start_connecting = sip_connection_start_connecting;
405   base_class->shut_down = sip_connection_shut_down;
406 
407   g_type_class_add_private (sip_connection_class, sizeof (SIPConnectionPrivate));
408 
409   object_class->dispose = sip_connection_dispose;
410   object_class->finalize = sip_connection_finalize;
411 
412   object_class->set_property = sip_connection_set_property;
413   object_class->get_property = sip_connection_get_property;
414 
415   param_spec = g_param_spec_pointer("sofia-root",
416                                     "Sofia root",
417                                     "Event root from Sofia-SIP stack",
418                                     G_PARAM_CONSTRUCT_ONLY |
419                                     G_PARAM_READWRITE |
420                                     G_PARAM_STATIC_NAME |
421                                     G_PARAM_STATIC_BLURB);
422   INST_PROP(PROP_SOFIA_ROOT);
423 
424   param_spec = g_param_spec_string("address",
425                                    "SIPConnection construction property",
426                                    "Public SIP address",
427                                    NULL, /*default value*/
428                                    G_PARAM_CONSTRUCT_ONLY |
429                                    G_PARAM_READWRITE |
430                                    G_PARAM_STATIC_NAME |
431                                    G_PARAM_STATIC_BLURB);
432   INST_PROP(PROP_ADDRESS);
433 
434   param_spec = g_param_spec_string("auth-user",
435                                    "Auth username",
436                                    "Username to use when registering (if different "
437                                    "than userinfo part of public SIP address)",
438                                    NULL, /*default value*/
439                                    G_PARAM_READWRITE |
440                                    G_PARAM_STATIC_NAME |
441                                    G_PARAM_STATIC_BLURB);
442   INST_PROP(PROP_AUTH_USER);
443 
444   param_spec = g_param_spec_string("password",
445                                    "SIP account password",
446                                    "Password for SIP registration",
447                                    "", /*default value*/
448                                    G_PARAM_READWRITE |
449                                    G_PARAM_STATIC_NAME |
450                                    G_PARAM_STATIC_BLURB);
451   INST_PROP(PROP_PASSWORD);
452 
453   param_spec = g_param_spec_string("proxy",
454                                    "Outbound proxy",
455                                    "SIP URI for outbound proxy (e.g. 'sip:sipproxy.myprovider.com') [optional]",
456                                    NULL, /*default value*/
457                                    G_PARAM_READWRITE |
458                                    G_PARAM_STATIC_NAME |
459                                    G_PARAM_STATIC_BLURB);
460   INST_PROP(PROP_PROXY);
461 
462   param_spec = g_param_spec_string("registrar",
463                                    "Registrar",
464                                    "SIP URI for registrar (e.g. 'sip:sip.myprovider.com') [optional]",
465                                    NULL, /*default value*/
466                                    G_PARAM_READWRITE |
467                                    G_PARAM_STATIC_NAME |
468                                    G_PARAM_STATIC_BLURB);
469   INST_PROP(PROP_REGISTRAR);
470 
471   param_spec = g_param_spec_enum ("keepalive-mechanism",
472                                   "Keepalive mechanism",
473                                   "SIP registration keepalive mechanism",
474                                   sip_connection_keepalive_mechanism_get_type (),
475                                   SIP_CONNECTION_KEEPALIVE_AUTO,
476                                   G_PARAM_READWRITE |
477                                   G_PARAM_STATIC_NAME |
478                                   G_PARAM_STATIC_BLURB);
479   INST_PROP(PROP_KEEPALIVE_MECHANISM);
480 
481   param_spec = g_param_spec_int("keepalive-interval",
482 				"Keepalive interval",
483 				"Interval between keepalives in seconds (0 = disable, -1 = let stack decide.",
484 				-1, G_MAXINT32, -1,
485                                 G_PARAM_READWRITE |
486                                 G_PARAM_STATIC_NAME |
487                                 G_PARAM_STATIC_BLURB);
488   INST_PROP(PROP_KEEPALIVE_INTERVAL);
489 
490   param_spec = g_param_spec_boolean("discover-binding",
491                                     "Discover public contact",
492                                     "Enable discovery of public IP address beyond NAT",
493                                     TRUE, /*default value*/
494                                     G_PARAM_READWRITE |
495                                     G_PARAM_STATIC_NAME |
496                                     G_PARAM_STATIC_BLURB);
497   INST_PROP(PROP_DISCOVER_BINDING);
498 
499   param_spec = g_param_spec_boolean("discover-stun", "Discover STUN server",
500                                     "Enable discovery of STUN server host name "
501                                     "using DNS SRV lookup",
502                                     TRUE, /*default value*/
503                                     G_PARAM_READWRITE |
504                                     G_PARAM_STATIC_NAME |
505                                     G_PARAM_STATIC_BLURB);
506   INST_PROP(PROP_DISCOVER_STUN);
507 
508   param_spec = g_param_spec_string("stun-server",
509                                    "STUN server address",
510                                    "STUN server address (FQDN or IP address, "
511                                    "e.g. 'stun.myprovider.com') [optional]",
512                                    NULL, /*default value*/
513                                    G_PARAM_READWRITE |
514                                    G_PARAM_STATIC_NAME |
515                                    G_PARAM_STATIC_BLURB);
516   INST_PROP(PROP_STUN_SERVER);
517 
518   param_spec = g_param_spec_uint ("stun-port",
519                                   "STUN port",
520                                   "STUN port.",
521                                   0, G_MAXUINT16, SIP_DEFAULT_STUN_PORT,
522                                   G_PARAM_CONSTRUCT |
523                                   G_PARAM_READWRITE |
524                                   G_PARAM_STATIC_NAME |
525                                   G_PARAM_STATIC_BLURB);
526   INST_PROP(PROP_STUN_PORT);
527 
528   param_spec = g_param_spec_string("local-ip-address",
529                                    "Local IP address",
530                                    "Local IP address to use [optional]",
531                                    NULL, /*default value*/
532                                    G_PARAM_READWRITE |
533                                    G_PARAM_STATIC_NAME |
534                                    G_PARAM_STATIC_BLURB);
535   INST_PROP(PROP_LOCAL_IP_ADDRESS);
536 
537   param_spec = g_param_spec_uint ("local-port",
538                                   "Local port",
539                                   "Local port for SIP [optional]",
540                                   0, G_MAXUINT16, 0,
541                                   G_PARAM_READWRITE |
542                                   G_PARAM_STATIC_NAME |
543                                   G_PARAM_STATIC_BLURB);
544   INST_PROP(PROP_LOCAL_PORT);
545 
546   param_spec = g_param_spec_string("extra-auth-user",
547                                    "Extra auth username",
548                                    "Username to use for extra authentication challenges",
549                                    NULL, /*default value*/
550                                    G_PARAM_READWRITE |
551                                    G_PARAM_STATIC_NAME |
552                                    G_PARAM_STATIC_BLURB);
553   INST_PROP(PROP_EXTRA_AUTH_USER);
554 
555   param_spec = g_param_spec_string("extra-auth-password",
556                                    "Extra auth password",
557                                    "Password to use for extra authentication challenges",
558                                    NULL, /*default value*/
559                                    G_PARAM_READWRITE |
560                                    G_PARAM_STATIC_NAME |
561                                    G_PARAM_STATIC_BLURB);
562   INST_PROP(PROP_EXTRA_AUTH_PASSWORD);
563 }
564 
565 static void
sip_connection_shut_down(TpBaseConnection * base)566 sip_connection_shut_down (TpBaseConnection *base)
567 {
568   SIPConnection *self = SIP_CONNECTION (base);
569   SIPConnectionPrivate *priv;
570 
571   DEBUG ("enter");
572 
573   g_assert (SIP_IS_CONNECTION (self));
574   priv = SIP_CONNECTION_GET_PRIVATE (self);
575 
576   /* We disposed of the REGISTER handle in the disconnected method */
577   g_assert (priv->register_op == NULL);
578 
579   g_assert (priv->sofia != NULL);
580 
581   /* Detach the Sofia adapter and let it destroy the NUA handle and itself
582    * in the shutdown callback. If there's no NUA stack, destroy it manually. */
583   priv->sofia->conn = NULL;
584 
585   if (priv->sofia_nua != NULL)
586       nua_shutdown (priv->sofia_nua);
587   else
588       sip_connection_sofia_destroy (priv->sofia);
589 
590   priv->sofia = NULL;
591   priv->sofia_nua = NULL;
592 
593   tp_base_connection_finish_shutdown (base);
594 }
595 
596 void
sip_connection_dispose(GObject * object)597 sip_connection_dispose (GObject *object)
598 {
599   SIPConnection *self = SIP_CONNECTION (object);
600   TpBaseConnection *base = (TpBaseConnection *)self;
601   SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
602 
603   if (priv->dispose_has_run)
604     return;
605 
606   priv->dispose_has_run = TRUE;
607 
608   /* release any references held by the object here */
609 
610   DEBUG ("Disposing of SIPConnection %p", self);
611 
612   /* these are borrowed refs, the real ones are owned by the superclass */
613   priv->media_factory = NULL;
614   priv->text_factory = NULL;
615 
616   /* may theoretically involve NUA handle unrefs */
617   g_hash_table_destroy (priv->auth_table);
618 
619   /* the base class is responsible for unreffing the self handle when we
620    * disconnect */
621   g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED
622       || base->status == TP_INTERNAL_CONNECTION_STATUS_NEW);
623   g_assert (base->self_handle == 0);
624 
625   if (G_OBJECT_CLASS (sip_connection_parent_class)->dispose)
626     G_OBJECT_CLASS (sip_connection_parent_class)->dispose (object);
627 }
628 
629 void
sip_connection_finalize(GObject * obj)630 sip_connection_finalize (GObject *obj)
631 {
632   SIPConnection *self = SIP_CONNECTION (obj);
633   SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
634 
635   /* free any data held directly by the object here */
636 
637   DEBUG("enter");
638 
639   if (NULL != priv->sofia_resolver)
640     {
641       DEBUG("destroying sofia resolver");
642       sres_resolver_destroy (priv->sofia_resolver);
643       priv->sofia_resolver = NULL;
644     }
645 
646   su_home_unref (priv->sofia_home);
647 
648   g_free (priv->address);
649   g_free (priv->auth_user);
650   g_free (priv->password);
651   g_free (priv->stun_host);
652   g_free (priv->local_ip_address);
653   g_free (priv->extra_auth_user);
654   g_free (priv->extra_auth_password);
655 
656   g_free (priv->registrar_realm);
657 
658   G_OBJECT_CLASS (sip_connection_parent_class)->finalize (obj);
659 }
660 
661 
662 /**
663  * sip_connection_connect
664  *
665  * Implements DBus method Connect
666  * on interface org.freedesktop.Telepathy.Connection
667  */
668 static gboolean
sip_connection_start_connecting(TpBaseConnection * base,GError ** error)669 sip_connection_start_connecting (TpBaseConnection *base,
670                                  GError **error)
671 {
672   SIPConnection *self = SIP_CONNECTION (base);
673   SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
674   su_root_t *sofia_root;
675   TpHandleRepoIface *contact_repo;
676   const gchar *sip_address;
677   const url_t *local_url;
678 
679   g_message("%s: Connection %p ref-count=%u (obj)", G_STRFUNC, self,
680       G_OBJECT(self)->ref_count);
681 
682   g_assert (base->status == TP_INTERNAL_CONNECTION_STATUS_NEW);
683 
684   g_assert (priv->sofia != NULL);
685 
686   /* the construct parameters will be non-empty */
687   sofia_root = priv->sofia->sofia_root;
688   g_assert (sofia_root != NULL);
689   g_return_val_if_fail (priv->address != NULL, FALSE);
690 
691   /* FIXME: we should defer setting the self handle until we've found out from
692    * the stack what handle we actually got, at which point we set it; and
693    * not tell Telepathy that connection has succeeded until we've done so
694    */
695   contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT);
696   base->self_handle = tp_handle_ensure (contact_repo, priv->address,
697       NULL, error);
698   if (base->self_handle == 0)
699     {
700       return FALSE;
701     }
702 
703   sip_address = tp_handle_inspect(contact_repo, base->self_handle);
704 
705   DEBUG("self_handle = %d, sip_address = %s", base->self_handle, sip_address);
706 
707   priv->account_url = url_make (priv->sofia_home, sip_address);
708   if (priv->account_url == NULL)
709     {
710       g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
711           "Failed to create the account URI");
712       return FALSE;
713     }
714 
715   local_url = sip_conn_get_local_url (self);
716 
717   /* step: create stack instance */
718   priv->sofia_nua = nua_create (sofia_root,
719       sip_connection_sofia_callback,
720       priv->sofia,
721       SOATAG_AF(SOA_AF_IP4_IP6),
722       SIPTAG_FROM_STR(sip_address),
723       NUTAG_URL(local_url),
724       TAG_IF(local_url && local_url->url_type == url_sips,
725              NUTAG_SIPS_URL(local_url)),
726       NUTAG_M_USERNAME(priv->account_url->url_user),
727       NUTAG_USER_AGENT("Telepathy-SofiaSIP/" TELEPATHY_SIP_VERSION),
728       NUTAG_ENABLEMESSAGE(1),
729       NUTAG_ENABLEINVITE(1),
730       NUTAG_AUTOALERT(0),
731       NUTAG_AUTOANSWER(0),
732       NUTAG_APPL_METHOD("MESSAGE"),
733       SIPTAG_ALLOW_STR("INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, UPDATE"),
734       TAG_NULL());
735   if (priv->sofia_nua == NULL)
736     {
737       g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
738           "Unable to create SIP stack");
739       return FALSE;
740     }
741 
742   /* Set configuration-dependent tags */
743   sip_conn_update_proxy_and_transport (self);
744   sip_conn_update_nua_outbound (self);
745   sip_conn_update_nua_keepalive_interval (self);
746   sip_conn_update_nua_contact_features (self);
747 
748   if (priv->stun_host != NULL)
749     sip_conn_resolv_stun_server (self, priv->stun_host);
750   else if (priv->discover_stun)
751     sip_conn_discover_stun_server (self);
752 
753   g_message ("Sofia-SIP NUA at address %p (SIP URI: %s)",
754 	     priv->sofia_nua, sip_address);
755 
756   /* XXX: should there be configuration option to disable use
757    *      of outbound proxy, any use-cases? */
758 
759   /* for debugging purposes, request a dump of stack configuration
760    * at registration time */
761   nua_get_params(priv->sofia_nua, TAG_ANY(), TAG_NULL());
762 
763   priv->register_op = sip_conn_create_register_handle (self,
764                                                        base->self_handle);
765   if (priv->register_op == NULL)
766     {
767       g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
768           "Unable to create registration handle for address %s", sip_address);
769       return FALSE;
770     }
771 
772   nua_register(priv->register_op, TAG_NULL());
773 
774   DEBUG("exit");
775 
776   return TRUE;
777 }
778 
779 
780 /**
781  * sip_connection_disconnected
782  *
783  * Called after the connection becomes disconnected.
784  */
785 static void
sip_connection_disconnected(TpBaseConnection * base)786 sip_connection_disconnected (TpBaseConnection *base)
787 {
788   SIPConnection *obj = SIP_CONNECTION (base);
789   SIPConnectionPrivate *priv;
790 
791   g_assert (SIP_IS_CONNECTION (obj));
792   priv = SIP_CONNECTION_GET_PRIVATE (obj);
793 
794   DEBUG("enter");
795 
796   /* Dispose of the register use */
797   if (priv->register_op != NULL)
798     {
799       DEBUG("unregistering");
800       nua_unregister (priv->register_op, TAG_NULL());
801       nua_handle_unref (priv->register_op);
802       priv->register_op = NULL;
803     }
804 }
805 
806 /**
807  * sip_connection_get_interfaces
808  *
809  * Implements DBus method GetInterfaces
810  * on interface org.freedesktop.Telepathy.Connection
811  */
812 static void
sip_connection_get_interfaces(TpSvcConnection * iface,DBusGMethodInvocation * context)813 sip_connection_get_interfaces (TpSvcConnection *iface,
814                                DBusGMethodInvocation *context)
815 {
816   SIPConnection *self = SIP_CONNECTION (iface);
817   TpBaseConnection *base = (TpBaseConnection *)self;
818   const char *interfaces[] = {
819       TP_IFACE_PROPERTIES_INTERFACE,
820       NULL };
821 
822   DEBUG ("called");
823 
824   ERROR_IF_NOT_CONNECTED_ASYNC (base, context)
825   tp_svc_connection_return_from_get_interfaces (context, interfaces);
826 }
827 
828 static gchar *
normalize_sipuri(TpHandleRepoIface * repo,const gchar * sipuri,gpointer context,GError ** error)829 normalize_sipuri (TpHandleRepoIface *repo,
830                   const gchar *sipuri,
831                   gpointer context,
832                   GError **error)
833 {
834     SIPConnection *conn = SIP_CONNECTION (context);
835 
836     return sip_conn_normalize_uri (conn, sipuri, error);
837 }
838 
839 
840 /**
841  * sip_connection_request_handles
842  *
843  * Implements DBus method RequestHandles
844  * on interface org.freedesktop.Telepathy.Connection
845  *
846  * @error: Used to return a pointer to a GError detailing any error
847  *         that occured, DBus will throw the error only if this
848  *         function returns false.
849  *
850  * Returns: TRUE if successful, FALSE if an error was thrown.
851  */
852 static void
sip_connection_request_handles(TpSvcConnection * iface,guint handle_type,const gchar ** names,DBusGMethodInvocation * context)853 sip_connection_request_handles (TpSvcConnection *iface,
854                                 guint handle_type,
855                                 const gchar **names,
856                                 DBusGMethodInvocation *context)
857 {
858   SIPConnection *obj = SIP_CONNECTION (iface);
859   TpBaseConnection *base = (TpBaseConnection *)obj;
860   gint count = 0;
861   gint i;
862   const gchar **h;
863   GArray *handles;
864   GError *error = NULL;
865   const gchar *client_name;
866   TpHandleRepoIface *repo = tp_base_connection_get_handles (base, handle_type);
867 
868   DEBUG("enter");
869 
870   ERROR_IF_NOT_CONNECTED_ASYNC (base, context)
871 
872   if (!tp_handle_type_is_valid (handle_type, &error))
873     {
874       dbus_g_method_return_error (context, error);
875       g_error_free (error);
876       return;
877     }
878 
879   if (repo == NULL)
880     {
881       tp_g_set_error_unsupported_handle_type (handle_type, &error);
882       dbus_g_method_return_error (context, error);
883       g_error_free (error);
884       return;
885     }
886 
887   for (h = names; *h != NULL; h++)
888     ++count;
889 
890   handles = g_array_sized_new(FALSE, FALSE, sizeof(guint), count);
891 
892   client_name = dbus_g_method_get_sender (context);
893 
894   for (i = 0; i < count; i++) {
895     TpHandle handle;
896 
897     handle = tp_handle_ensure (repo, names[i], NULL, &error);
898 
899     if (handle == 0)
900       {
901         DEBUG("requested handle %s was invalid", names[i]);
902         goto ERROR_IN_LOOP;
903       }
904 
905     DEBUG("verify handle '%s' => %u (%s)", names[i],
906         handle, tp_handle_inspect (repo, handle));
907 
908     if (!tp_handle_client_hold (repo, client_name, handle, &error))
909       {
910         /* oops */
911         tp_handle_unref (repo, handle);
912         goto ERROR_IN_LOOP;
913       }
914 
915     /* now the client owns the handle, so we can drop our reference */
916     tp_handle_unref (repo, handle);
917 
918     g_array_append_val(handles, handle);
919     continue;
920 
921 ERROR_IN_LOOP:
922     for (; i >= 0; --i)
923       {
924         tp_handle_client_release (repo, client_name,
925             (TpHandle) g_array_index (handles, guint, i),
926             NULL);
927       }
928 
929     dbus_g_method_return_error (context, error);
930     g_error_free (error);
931     g_array_free (handles, TRUE);
932     return;
933   }
934 
935   tp_svc_connection_return_from_request_handles (context, handles);
936   g_array_free (handles, TRUE);
937 }
938 
939 static void
conn_iface_init(gpointer g_iface,gpointer iface_data)940 conn_iface_init(gpointer g_iface, gpointer iface_data)
941 {
942   TpSvcConnectionClass *klass = (TpSvcConnectionClass *)g_iface;
943 
944 #define IMPLEMENT(x) tp_svc_connection_implement_##x (klass,\
945     sip_connection_##x)
946   IMPLEMENT(get_interfaces);
947   IMPLEMENT(request_handles);
948 #undef IMPLEMENT
949 }
950