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