1 /*
2 * sip-connection-sofia.c - Source for SIPConnection Sofia event handling
3 * Copyright (C) 2006-2007 Nokia Corporation
4 * Copyright (C) 2007 Collabora Ltd.
5 * @author Kai Vehmanen <first.surname@nokia.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * version 2.1 as published by the Free Software Foundation.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <dbus/dbus-glib.h>
22
23 #include <telepathy-glib/interfaces.h>
24
25 #include "sip-connection-sofia.h"
26 #include "sip-connection-private.h"
27 #include "media-factory.h"
28 #include "text-factory.h"
29
30 #include <sofia-sip/sip_header.h>
31 #include <sofia-sip/sip_parser.h>
32 #include <sofia-sip/sip_status.h>
33 #include <sofia-sip/msg_parser.h>
34 #include <sofia-sip/msg_types.h>
35 #include <sofia-sip/su_tag_io.h> /* for tl_print() */
36 #include <string.h>
37
38 #define DEBUG_FLAG SIP_DEBUG_CONNECTION
39 #include "debug.h"
40
41 SIPConnectionSofia *
sip_connection_sofia_new(SIPConnection * conn)42 sip_connection_sofia_new (SIPConnection *conn)
43 {
44 SIPConnectionSofia *sofia = g_slice_new0 (SIPConnectionSofia);
45 sofia->conn = conn;
46 return sofia;
47 }
48
49 void
sip_connection_sofia_destroy(SIPConnectionSofia * sofia)50 sip_connection_sofia_destroy (SIPConnectionSofia *sofia)
51 {
52 g_slice_free (SIPConnectionSofia, sofia);
53 }
54
55 static void
priv_r_shutdown(int status,char const * phrase,nua_t * nua,SIPConnectionSofia * sofia)56 priv_r_shutdown(int status,
57 char const *phrase,
58 nua_t *nua,
59 SIPConnectionSofia *sofia)
60 {
61 GSource *source;
62 gboolean source_recursive;
63
64 DEBUG("nua_shutdown: %03d %s", status, phrase);
65
66 if (status < 200)
67 return;
68
69 g_assert(sofia->conn == NULL);
70
71 source = su_root_gsource (sofia->sofia_root);
72
73 /* XXX: temporarily allow recursion in the Sofia source to work around
74 * nua_destroy() requiring nested mainloop iterations to complete
75 * (Sofia-SIP bug #1624446). Actual recursion safety of the source is to be
76 * examined. */
77 source_recursive = g_source_get_can_recurse (source);
78 if (!source_recursive)
79 {
80 DEBUG("forcing Sofia root GSource to be recursive");
81 g_source_set_can_recurse (source, TRUE);
82 }
83
84 DEBUG("destroying Sofia-SIP NUA at address %p", nua);
85 nua_destroy (nua);
86
87 if (!source_recursive)
88 g_source_set_can_recurse (source, FALSE);
89
90 sip_connection_sofia_destroy (sofia);
91 }
92
93 static void
priv_auth_save(GHashTable * table,nua_handle_t * nh,const char * auth)94 priv_auth_save (GHashTable *table, nua_handle_t *nh, const char *auth)
95 {
96 g_hash_table_insert (table, nh /* nua_handle_ref (nh) */, g_strdup (auth));
97 }
98
99 static void
priv_auth_drop(GHashTable * table,nua_handle_t * nh)100 priv_auth_drop (GHashTable *table, nua_handle_t *nh)
101 {
102 g_hash_table_remove (table, nh);
103 }
104
105 static gboolean
priv_auth_check_repeat(GHashTable * table,nua_handle_t * nh,const char * auth)106 priv_auth_check_repeat (GHashTable *table, nua_handle_t *nh, const char *auth)
107 {
108 const char *last_auth;
109
110 g_assert (auth != NULL);
111
112 last_auth = g_hash_table_lookup (table, nh);
113
114 return (last_auth != NULL && strcmp (last_auth, auth) == 0);
115 }
116
117 /* We have a monster auth handler method with a traffic light
118 * return code. Might think about refactoring it someday... */
119 typedef enum {
120 SIP_AUTH_FAILURE,
121 SIP_AUTH_PASS,
122 SIP_AUTH_HANDLED
123 } SIPAuthStatus;
124
125 static SIPAuthStatus
priv_handle_auth(SIPConnection * self,int status,nua_handle_t * nh,const sip_t * sip,gboolean home_realm)126 priv_handle_auth (SIPConnection* self,
127 int status,
128 nua_handle_t *nh,
129 const sip_t *sip,
130 gboolean home_realm)
131 {
132 SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
133 sip_www_authenticate_t const *wa;
134 sip_proxy_authenticate_t const *pa;
135 const char *method = NULL;
136 const char *realm = NULL;
137 const char *user = NULL;
138 const char *password = NULL;
139 gchar *auth = NULL;
140
141 DEBUG("enter");
142
143 if (status != 401 && status != 407)
144 {
145 /* Clear the last used credentials saved for loop detection
146 * and proceed with normal handling */
147 priv_auth_drop (priv->auth_table, nh);
148 return SIP_AUTH_PASS;
149 }
150
151 wa = sip->sip_www_authenticate;
152 pa = sip->sip_proxy_authenticate;
153
154 /* step: figure out the realm of the challenge */
155 if (wa) {
156 realm = msg_params_find(wa->au_params, "realm=");
157 method = wa->au_scheme;
158 }
159 else if (pa) {
160 realm = msg_params_find(pa->au_params, "realm=");
161 method = pa->au_scheme;
162 }
163
164 if (realm == NULL)
165 {
166 g_warning ("no realm presented for authentication");
167 return SIP_AUTH_FAILURE;
168 }
169
170 /* step: determine which set of credentials to use */
171 if (home_realm)
172 {
173 /* Save the realm presented by the registrar */
174 if (priv->registrar_realm == NULL)
175 priv->registrar_realm = g_strdup (realm);
176 else if (wa && strcmp(priv->registrar_realm, realm) != 0)
177 {
178 g_message ("registrar realm changed from '%s' to '%s'", priv->registrar_realm, realm);
179 g_free (priv->registrar_realm);
180 priv->registrar_realm = g_strdup (realm);
181 }
182 }
183 else if (priv->registrar_realm != NULL
184 && strcmp(priv->registrar_realm, realm) == 0)
185 home_realm = TRUE;
186
187 if (home_realm)
188 {
189 /* use authentication username if provided */
190 user = priv->auth_user;
191 password = priv->password;
192
193 DEBUG("using the primary auth credentials");
194 }
195 else
196 {
197 if (priv->extra_auth_user)
198 user = priv->extra_auth_user;
199 else
200 /* fall back to the main username */
201 user = priv->auth_user;
202 password = priv->extra_auth_password;
203
204 DEBUG("using the extra auth credentials");
205 }
206
207 if (user == NULL)
208 {
209 sip_from_t const *sipfrom = sip->sip_from;
210 if (sipfrom && sipfrom->a_url[0].url_user)
211 /* or use the userpart in "From" header */
212 user = sipfrom->a_url[0].url_user;
213 }
214
215 if (password == NULL)
216 password = "";
217
218 /* step: if all info is available, create an authorization response */
219 g_assert (realm != NULL);
220 if (user && method) {
221 if (realm[0] == '"')
222 auth = g_strdup_printf ("%s:%s:%s:%s",
223 method, realm, user, password);
224 else
225 auth = g_strdup_printf ("%s:\"%s\":%s:%s",
226 method, realm, user, password);
227
228 DEBUG("%s authenticating user='%s' realm='%s' nh='%p'",
229 wa ? "server" : "proxy", user, realm, nh);
230 }
231
232 /* step: do sanity checks, avoid resubmitting the exact same response */
233 /* XXX: we don't check if the nonce is the same, presumably should be
234 * taken care of by the stack */
235 if (auth == NULL)
236 g_warning ("sofiasip: authentication data are incomplete");
237 else if (priv_auth_check_repeat (priv->auth_table, nh, auth))
238 {
239 g_debug ("authentication challenge repeated, dropping");
240 g_free (auth);
241 auth = NULL;
242 /* Also drop the saved response because we're going to ditch the
243 * handle anyway */
244 priv_auth_drop (priv->auth_table, nh);
245 }
246
247 if (auth == NULL)
248 return SIP_AUTH_FAILURE;
249
250 priv_auth_save (priv->auth_table, nh, auth);
251
252 /* step: authenticate */
253 nua_authenticate(nh, NUTAG_AUTH(auth), TAG_END());
254
255 g_free (auth);
256
257 return SIP_AUTH_HANDLED;
258 }
259
260 static void
priv_r_invite(int status,char const * phrase,nua_t * nua,SIPConnection * self,nua_handle_t * nh,nua_handle_t * nh_magic,sip_t const * sip,tagi_t tags[])261 priv_r_invite (int status,
262 char const *phrase,
263 nua_t *nua,
264 SIPConnection *self,
265 nua_handle_t *nh,
266 nua_handle_t *nh_magic,
267 sip_t const *sip,
268 tagi_t tags[])
269 {
270 DEBUG("outbound INVITE got %03d %s", status, phrase);
271
272 /* Ignore provisional responses in this handler */
273 if (status < 200)
274 return;
275
276 if (nh_magic == SIP_NH_EXPIRED)
277 {
278 /* 487 Request Terminated is OK on destroyed channels */
279 if (status != 487)
280 g_message ("response %03d received for a destroyed media channel", status);
281 return;
282 }
283
284 if (nh_magic == NULL)
285 {
286 g_message ("response %03d, on a NUA handle %p which does "
287 "not belong to any media channel, ignored", status, nh);
288 return;
289 }
290
291 if (priv_handle_auth (self, status, nh, sip, FALSE) == SIP_AUTH_HANDLED)
292 return;
293
294 if (status >= 300)
295 sip_media_channel_peer_error (SIP_MEDIA_CHANNEL (nh_magic),
296 status,
297 phrase);
298 }
299
300 static void
priv_r_bye(int status,char const * phrase,nua_t * nua,SIPConnection * self,nua_handle_t * nh,sip_t const * sip,tagi_t tags[])301 priv_r_bye (int status,
302 char const *phrase,
303 nua_t *nua,
304 SIPConnection *self,
305 nua_handle_t *nh,
306 sip_t const *sip,
307 tagi_t tags[])
308 {
309 DEBUG("BYE got %03d %s", status, phrase);
310
311 if (status < 200)
312 return;
313
314 priv_handle_auth (self, status, nh, sip, FALSE);
315 }
316
317 static void
priv_r_register(int status,char const * phrase,nua_t * nua,SIPConnection * self,nua_handle_t * nh,sip_t const * sip,tagi_t tags[])318 priv_r_register (int status,
319 char const *phrase,
320 nua_t *nua,
321 SIPConnection *self,
322 nua_handle_t *nh,
323 sip_t const *sip,
324 tagi_t tags[])
325 {
326 TpBaseConnection *base = (TpBaseConnection *)self;
327 SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
328
329 DEBUG("REGISTER got response: %03d %s", status, phrase);
330
331 if (status < 200) {
332 return;
333 }
334
335 switch (priv_handle_auth (self, status, nh, sip, TRUE))
336 {
337 case SIP_AUTH_FAILURE:
338 DEBUG("REGISTER failed, insufficient/wrong credentials, disconnecting.");
339 tp_base_connection_change_status (base, TP_CONNECTION_STATUS_DISCONNECTED,
340 TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
341 return;
342 case SIP_AUTH_HANDLED:
343 return;
344 case SIP_AUTH_PASS:
345 break;
346 }
347
348 if (status == 403) {
349 DEBUG("REGISTER failed, wrong credentials, disconnecting.");
350 tp_base_connection_change_status (base, TP_CONNECTION_STATUS_DISCONNECTED,
351 TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
352 }
353 else if (status >= 300) {
354 DEBUG("REGISTER failed, disconnecting.");
355 tp_base_connection_change_status (base, TP_CONNECTION_STATUS_DISCONNECTED,
356 TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
357 }
358 else /* if (status == 200) */ {
359 DEBUG("succesfully registered %s to network", priv->address);
360 tp_base_connection_change_status (base, TP_CONNECTION_STATUS_CONNECTED,
361 TP_CONNECTION_STATUS_REASON_REQUESTED);
362 }
363 }
364
365 static void
priv_r_unregister(int status,char const * phrase,nua_handle_t * nh)366 priv_r_unregister (int status,
367 char const *phrase,
368 nua_handle_t *nh)
369 {
370 DEBUG("un-REGISTER got response: %03d %s", status, phrase);
371
372 if (status < 200)
373 return;
374
375 if (status == 401 || status == 407)
376 {
377 /* In SIP, de-registration can fail! However, there's not a lot we can
378 * do about this in the Telepathy model - once you've gone DISCONNECTED
379 * you're really not meant to go "oops, I'm still CONNECTED after all".
380 * So we ignore it and hope it goes away. */
381 g_warning ("Registrar won't let me unregister: %d %s", status, phrase);
382 }
383 }
384
385 static void
priv_r_get_params(int status,char const * phrase,nua_t * nua,SIPConnection * self,nua_handle_t * nh,sip_t const * sip,tagi_t tags[])386 priv_r_get_params (int status,
387 char const *phrase,
388 nua_t *nua,
389 SIPConnection *self,
390 nua_handle_t *nh,
391 sip_t const *sip,
392 tagi_t tags[])
393 {
394 DEBUG("nua_r_get_params: %03d %s", status, phrase);
395
396 if (status < 200)
397 return;
398
399 /* note: print contents of all tags to stdout */
400 tl_print(stdout, "tp-sofiasip stack parameters:\n", tags);
401 }
402
403 static TpHandle
priv_handle_parse_from(const sip_t * sip,su_home_t * home,TpHandleRepoIface * contact_repo)404 priv_handle_parse_from (const sip_t *sip,
405 su_home_t *home,
406 TpHandleRepoIface *contact_repo)
407 {
408 TpHandle handle = 0;
409 gchar *url_str;
410
411 g_return_val_if_fail (sip != NULL, 0);
412
413 if (sip->sip_from)
414 {
415 url_str = url_as_string (home, sip->sip_from->a_url);
416
417 handle = tp_handle_ensure (contact_repo, url_str, NULL, NULL);
418
419 su_free (home, url_str);
420
421 /* TODO: set qdata for the display name */
422 }
423
424 return handle;
425 }
426
427 static TpHandle
priv_handle_parse_to(const sip_t * sip,su_home_t * home,TpHandleRepoIface * contact_repo)428 priv_handle_parse_to (const sip_t *sip,
429 su_home_t *home,
430 TpHandleRepoIface *contact_repo)
431 {
432 TpHandle handle = 0;
433 gchar *url_str;
434
435 g_return_val_if_fail (sip != NULL, 0);
436
437 if (sip->sip_to)
438 {
439 url_str = url_as_string (home, sip->sip_to->a_url);
440
441 handle = tp_handle_ensure (contact_repo, url_str, NULL, NULL);
442
443 su_free (home, url_str);
444
445 /* TODO: set qdata for the display name */
446 }
447
448 return handle;
449 }
450
451 static void
priv_r_message(int status,char const * phrase,nua_t * nua,SIPConnection * self,nua_handle_t * nh,sip_t const * sip,tagi_t tags[])452 priv_r_message (int status,
453 char const *phrase,
454 nua_t *nua,
455 SIPConnection *self,
456 nua_handle_t *nh,
457 sip_t const *sip,
458 tagi_t tags[])
459 {
460 SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
461 SIPTextChannel *channel;
462 TpHandleRepoIface *contact_repo;
463 TpHandle handle;
464
465 DEBUG("nua_r_message: %03d %s", status, phrase);
466
467 if (priv_handle_auth (self, status, nh, sip, FALSE) == SIP_AUTH_HANDLED)
468 return;
469
470 contact_repo = tp_base_connection_get_handles (
471 (TpBaseConnection *)self, TP_HANDLE_TYPE_CONTACT);
472
473 handle = priv_handle_parse_to (sip, priv->sofia_home, contact_repo);
474
475 if (!handle)
476 {
477 g_warning ("Message apparently delivered to an invalid recipient, ignoring");
478 return;
479 }
480
481 if (status == 200)
482 DEBUG("Message delivered for <%s>",
483 tp_handle_inspect (contact_repo, handle));
484
485 channel = sip_text_factory_lookup_channel (priv->text_factory, handle);
486
487 tp_handle_unref (contact_repo, handle);
488
489 if (!channel)
490 g_warning ("Delivery status ignored for a non-existant channel");
491 else if (status >= 200)
492 sip_text_channel_emit_message_status (channel, nh, status);
493 }
494
495 static void
priv_i_invite(int status,char const * phrase,nua_t * nua,SIPConnection * self,nua_handle_t * nh,nua_hmagic_t * nh_magic,sip_t const * sip,tagi_t tags[])496 priv_i_invite (int status,
497 char const *phrase,
498 nua_t *nua,
499 SIPConnection *self,
500 nua_handle_t *nh,
501 nua_hmagic_t *nh_magic,
502 sip_t const *sip,
503 tagi_t tags[])
504 {
505 SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
506 SIPMediaChannel *channel;
507 TpHandleRepoIface *contact_repo;
508 TpHandle handle;
509 GError *error = NULL;
510
511 if (nh_magic == SIP_NH_EXPIRED)
512 {
513 g_message ("incoming INVITE for a destroyed media channel");
514 nua_respond (nh, 481, "Call Does Not Exist", TAG_END());
515 return;
516 }
517
518 if (nh_magic != NULL) {
519 /* case 1: we already have a channel for this NH */
520 channel = SIP_MEDIA_CHANNEL (nh_magic);
521 sip_media_channel_receive_reinvite (channel);
522 }
523 else {
524 /* case 2: we haven't seen this media session before, so we should
525 * create a new channel to go with it */
526
527 /* figure out a handle for the identity */
528
529 contact_repo = tp_base_connection_get_handles ((TpBaseConnection *)self,
530 TP_HANDLE_TYPE_CONTACT);
531
532 handle = priv_handle_parse_from (sip, priv->sofia_home, contact_repo);
533
534 if (!handle)
535 {
536 g_message ("incoming INVITE with invalid sender information");
537 nua_respond (nh, 400, "Invalid From address", TAG_END());
538 return;
539 }
540
541 DEBUG("Got incoming invite from <%s>",
542 tp_handle_inspect (contact_repo, handle));
543
544 channel = sip_media_factory_new_channel (
545 SIP_MEDIA_FACTORY (priv->media_factory),
546 NULL,
547 TP_HANDLE_TYPE_NONE,
548 0,
549 &error);
550 if (channel)
551 {
552 sip_media_channel_receive_invite (channel, nh, handle);
553 }
554 else
555 {
556 g_warning ("creation of SIP media channel failed: %s", error->message);
557 g_error_free (error);
558 nua_respond (nh, 500, sip_500_Internal_server_error, TAG_END());
559 }
560
561 tp_handle_unref (contact_repo, handle);
562 }
563 }
564
565 static void
priv_i_cancel(nua_t * nua,SIPConnection * self,nua_handle_t * nh,nua_hmagic_t * nh_magic,sip_t const * sip,tagi_t tags[])566 priv_i_cancel (nua_t *nua,
567 SIPConnection *self,
568 nua_handle_t *nh,
569 nua_hmagic_t *nh_magic,
570 sip_t const *sip,
571 tagi_t tags[])
572 {
573 const sip_reason_t *reason;
574 guint cause = 0;
575 const gchar *text = NULL;
576
577 if (nh_magic == NULL)
578 {
579 g_message ("nua_i_cancel received for an unknown handle");
580 /* nua_respond (nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END()); */
581 return;
582 }
583 if (nh_magic == SIP_NH_EXPIRED)
584 {
585 g_message ("CANCEL received for a destroyed media channel");
586 /* nua_respond (nh, SIP_481_NO_CALL, TAG_END()); */
587 return;
588 }
589
590 reason = sip->sip_reason;
591 while (reason)
592 {
593 if (strcmp (reason->re_protocol, "SIP") == 0)
594 {
595 if (reason->re_cause)
596 cause = (guint) g_ascii_strtoull (reason->re_cause, NULL, 10);
597 text = reason->re_text;
598 break;
599 }
600 reason = reason->re_next;
601 }
602
603 sip_media_channel_peer_cancel (SIP_MEDIA_CHANNEL (nh_magic), cause, text);
604
605 /* nua_respond (nh, SIP_200_OK, TAG_END()); */
606 }
607
608 static void
priv_i_message(int status,char const * phrase,nua_t * nua,SIPConnection * self,nua_handle_t * nh,sip_t const * sip,tagi_t tags[])609 priv_i_message (int status,
610 char const *phrase,
611 nua_t *nua,
612 SIPConnection *self,
613 nua_handle_t *nh,
614 sip_t const *sip,
615 tagi_t tags[])
616 {
617 SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
618 SIPTextChannel *channel;
619 TpHandleRepoIface *contact_repo;
620 TpHandle handle;
621 char *text = NULL;
622
623 /* Block anything else except text/plain messages (like isComposings) */
624 if (sip->sip_content_type
625 && (g_ascii_strcasecmp ("text/plain", sip->sip_content_type->c_type)))
626 {
627 nua_respond (nh, SIP_415_UNSUPPORTED_MEDIA,
628 SIPTAG_ACCEPT_STR("text/plain"),
629 NUTAG_WITH_THIS(nua),
630 TAG_END());
631 return;
632 }
633
634 /* If there is some text, assure it's in UTF-8 encoding */
635 if (sip->sip_payload && sip->sip_payload->pl_len > 0)
636 {
637 const char *charset = NULL;
638 if (sip->sip_content_type && sip->sip_content_type->c_params != 0)
639 {
640 charset = msg_params_find (sip->sip_content_type->c_params, "charset=");
641 }
642
643 /* Default charset is UTF-8, we only need to convert if it's a different one */
644 if (charset && g_ascii_strcasecmp (charset, "UTF-8"))
645 {
646 GError *error;
647 gsize in_len, out_len;
648 text = g_convert (sip->sip_payload->pl_data, sip->sip_payload->pl_len,
649 "UTF-8", charset, &in_len, &out_len, &error);
650
651 if (text == NULL)
652 {
653 nua_respond (nh, 500, error->message,
654 NUTAG_WITH_THIS(nua),
655 TAG_END());
656 g_error_free (error);
657 return;
658 }
659 if (in_len != sip->sip_payload->pl_len)
660 {
661 nua_respond (nh, 400, "Incomplete character sequence at the "
662 "end of the message body",
663 NUTAG_WITH_THIS(nua),
664 TAG_END());
665 goto end;
666 }
667 }
668 else
669 {
670 if (!g_utf8_validate (sip->sip_payload->pl_data,
671 sip->sip_payload->pl_len,
672 NULL))
673 {
674 nua_respond (nh, 400, "Invalid characters in the message body",
675 NUTAG_WITH_THIS(nua),
676 TAG_END());
677 return;
678 }
679 text = g_strndup (sip->sip_payload->pl_data, sip->sip_payload->pl_len);
680 }
681 }
682 else
683 {
684 text = g_strdup ("");
685 }
686
687 contact_repo = tp_base_connection_get_handles (
688 (TpBaseConnection *)self, TP_HANDLE_TYPE_CONTACT);
689
690 handle = priv_handle_parse_from (sip, priv->sofia_home, contact_repo);
691
692 if (handle)
693 {
694 DEBUG("Got incoming message from <%s>",
695 tp_handle_inspect (contact_repo, handle));
696
697 channel = sip_text_factory_lookup_channel (priv->text_factory, handle);
698
699 if (!channel)
700 {
701 channel = sip_text_factory_new_channel (priv->text_factory, handle,
702 NULL);
703 g_assert (channel != NULL);
704 }
705
706 sip_text_channel_receive (channel, handle, text);
707
708 tp_handle_unref (contact_repo, handle);
709
710 nua_respond (nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END());
711 }
712 else
713 {
714 g_warning ("Incoming message has invalid sender information");
715 nua_respond (nh, 400, "Invalid From address",
716 NUTAG_WITH_THIS(nua),
717 TAG_END());
718 }
719
720 end:
721 g_free (text);
722 }
723
724 static void
priv_i_state(int status,char const * phrase,nua_t * nua,SIPConnection * self,nua_handle_t * nh,nua_hmagic_t * nh_magic,sip_t const * sip,tagi_t tags[])725 priv_i_state (int status,
726 char const *phrase,
727 nua_t *nua,
728 SIPConnection *self,
729 nua_handle_t *nh,
730 nua_hmagic_t *nh_magic,
731 sip_t const *sip,
732 tagi_t tags[])
733 {
734 const sdp_session_t *r_sdp = NULL;
735 int offer_recv = 0;
736 int answer_recv = 0;
737 int ss_state = nua_callstate_init;
738 SIPMediaChannel *channel;
739
740 DEBUG("nua_i_state: %03d %s", status, phrase);
741
742 if (nh_magic == NULL)
743 {
744 g_warning ("nua_i_state received for an unknown handle %p, ignored", nh);
745 return;
746 }
747
748 tl_gets(tags,
749 NUTAG_CALLSTATE_REF(ss_state),
750 NUTAG_OFFER_RECV_REF(offer_recv),
751 NUTAG_ANSWER_RECV_REF(answer_recv),
752 SOATAG_REMOTE_SDP_REF(r_sdp),
753 TAG_END());
754
755 if (nh_magic == SIP_NH_EXPIRED)
756 {
757 g_message ("call state change %03d '%s' received for a "
758 "destroyed media channel (handle %p)", status, phrase, nh);
759 switch (ss_state)
760 {
761 case nua_callstate_terminated:
762 case nua_callstate_terminating:
763 break;
764 default:
765 DEBUG("BYE off the orphaned handle");
766 nua_bye (nh, TAG_END());
767 }
768 return;
769 }
770
771 channel = nh_magic;
772
773 if (r_sdp)
774 {
775 g_return_if_fail(answer_recv || offer_recv);
776 if (!sip_media_channel_set_remote_media (channel, r_sdp))
777 sip_media_channel_close (channel);
778 }
779
780 DEBUG("call with handle %p is %s", nh, nua_callstate_name (ss_state));
781
782 switch ((enum nua_callstate)ss_state) {
783 case nua_callstate_received:
784 case nua_callstate_early:
785 break;
786
787 case nua_callstate_completing:
788 /* In auto-ack mode, we don't need to call nua_ack(), see NUTAG_AUTOACK() */
789 break;
790
791 case nua_callstate_ready:
792 sip_media_channel_ready (channel);
793 break;
794
795 case nua_callstate_terminated:
796 {
797 SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
798 priv_auth_drop (priv->auth_table, nh);
799 }
800 sip_media_channel_terminated (channel);
801 break;
802
803 default:
804 break;
805 }
806 }
807
808 static inline const char *
classify_nh_magic(nua_hmagic_t * nh_magic)809 classify_nh_magic (nua_hmagic_t *nh_magic)
810 {
811 if (nh_magic == NULL)
812 {
813 return "no channel yet";
814 }
815 if (nh_magic == SIP_NH_EXPIRED)
816 {
817 return "channel has been destroyed";
818 }
819 return "SIPMediaChannel";
820 }
821
822 /**
823 * Callback for events delivered by the SIP stack.
824 *
825 * See libsofia-sip-ua/nua/nua.h documentation.
826 */
827 void
sip_connection_sofia_callback(nua_event_t event,int status,char const * phrase,nua_t * nua,SIPConnectionSofia * state,nua_handle_t * nh,nua_hmagic_t * nh_magic,sip_t const * sip,tagi_t tags[])828 sip_connection_sofia_callback(nua_event_t event,
829 int status,
830 char const *phrase,
831 nua_t *nua,
832 SIPConnectionSofia *state,
833 nua_handle_t *nh,
834 nua_hmagic_t *nh_magic,
835 sip_t const *sip,
836 tagi_t tags[])
837 {
838 SIPConnection *self;
839
840 g_return_if_fail (state);
841
842 if (event == nua_r_shutdown)
843 {
844 priv_r_shutdown (status, phrase, nua, state);
845 return;
846 }
847 else if (event == nua_r_unregister)
848 {
849 priv_r_unregister (status, phrase, nh);
850 return;
851 }
852
853 self = state->conn;
854 if (self == NULL)
855 {
856 g_warning ("post-shutdown event received for a connection: NUA at %p, event #%d '%s', %d '%s'",
857 nua, event, nua_event_name (event), status, phrase);
858 return;
859 }
860
861 DEBUG("enter: nh %p (conn %p), event #%d '%s', %d '%s'",
862 nh, self, (int) event, nua_event_name (event), status, phrase);
863 DEBUG ("Connection refcount is %d", ((GObject *)self)->ref_count);
864
865 switch (event) {
866
867 /* incoming requests
868 * ------------------------- */
869
870 case nua_i_fork:
871 /* self_i_fork(status, phrase, nua, self, nh, sip, tags); */
872 break;
873
874 case nua_i_invite:
875 priv_i_invite (status, phrase, nua, self, nh, nh_magic, sip, tags);
876 break;
877
878 case nua_i_state:
879 priv_i_state (status, phrase, nua, self, nh, nh_magic, sip, tags);
880 break;
881
882 case nua_i_bye:
883 /* self_i_bye(nua, self, nh, sip, tags); */
884 break;
885
886 case nua_i_message:
887 priv_i_message(status, phrase, nua, self, nh, sip, tags);
888 break;
889
890 case nua_i_refer:
891 /* self_i_refer(nua, self, nh, sip, tags); */
892 break;
893
894 case nua_i_notify:
895 /* self_i_notify(nua, self, nh, sip, tags); */
896 break;
897
898 case nua_i_cancel:
899 priv_i_cancel (nua, self, nh, nh_magic, sip, tags);
900 break;
901
902 case nua_i_error:
903 /* self_i_error(nua, self, nh, status, phrase, tags); */
904 break;
905
906 case nua_i_active:
907 case nua_i_ack:
908 case nua_i_terminated:
909 case nua_i_options:
910 /* ignore these */
911 break;
912
913 /* responses to our requests
914 * ------------------------- */
915
916 case nua_r_register:
917 priv_r_register (status, phrase, nua, self, nh, sip, tags);
918 break;
919
920 case nua_r_invite:
921 priv_r_invite(status, phrase, nua, self, nh, nh_magic, sip, tags);
922 break;
923
924 case nua_r_bye:
925 priv_r_bye(status, phrase, nua, self, nh, sip, tags);
926 break;
927
928 case nua_r_message:
929 priv_r_message(status, phrase, nua, self, nh, sip, tags);
930 break;
931
932 case nua_r_cancel:
933 /* priv_r_cancel (status, phrase, nua, self, nh, sip, tags); */
934 break;
935
936 case nua_r_refer:
937 /* self_r_refer(status, phrase, nua, self, nh, sip, tags); */
938 break;
939
940 case nua_r_subscribe:
941 /* self_r_subscribe(status, phrase, nua, self, nh, sip, tags); */
942 break;
943
944 case nua_r_unsubscribe:
945 /* self_r_unsubscribe(status, phrase, nua, self, nh, sip, tags); */
946 break;
947
948 case nua_r_publish:
949 /* self_r_publish(status, phrase, nua, self, nh, sip, tags); */
950 break;
951
952 case nua_r_notify:
953 /* self_r_notify(status, phrase, nua, self, nh, sip, tags); */
954 break;
955
956 case nua_r_get_params:
957 priv_r_get_params(status, phrase, nua, self, nh, sip, tags);
958 break;
959
960 default:
961 g_message ("sip-connection: unknown event #%d '%s', status %03d '%s' "
962 " (nh=%p)", event, nua_event_name(event), status, phrase, nh);
963
964 if (nh_magic == SIP_NH_EXPIRED)
965 {
966 /* note: unknown handle, not associated to any existing
967 * call, message, registration, etc, so it can
968 * be safely destroyed */
969 g_message ("NOTE: destroying NUA handle %p (its associated "
970 "channel has already gone away)", nh);
971 nua_handle_ref (nh);
972 nua_handle_destroy (nh);
973 }
974
975 break;
976 }
977
978 DEBUG ("exit");
979 }
980
981
982 #if 0
983 /* XXX: these methods have not yet been ported to the new NUA API */
984
985 static void
986 cb_subscribe_answered(NuaGlib* obj, NuaGlibOp *op, int status, const char*message, gpointer data)
987 {
988 SIPConnection *sipconn = (SIPConnection *)data;
989
990 if (sipconn);
991
992 g_message ("Subscribe answered: %d with status %d with message %s",
993 nua_glib_op_method_type(op),
994 status, message);
995
996 /* XXX -- mela: emit a signal to our client */
997 }
998
999 static void
1000 cb_incoming_notify(NuaGlib *sofia_nua_glib, NuaGlibOp *op, const char *event,
1001 const char *content_type, const char *message, gpointer data)
1002 {
1003 SIPConnection *self = (SIPConnection *) data;
1004 SIPConnectionPrivate *priv = SIP_CONNECTION_GET_PRIVATE (self);
1005 SIPTextChannel *channel;
1006 TpHandle handle;
1007
1008 handle = 1;
1009 channel = NULL;
1010 if (priv);
1011
1012 }
1013
1014 static void cb_call_terminated(NuaGlib *sofia_nua_glib, NuaGlibOp *op, int status, gpointer data)
1015 {
1016 SIPConnection *self = (SIPConnection *) data;
1017
1018 DEBUG("enter");
1019
1020 /* as we only support one media channel at a time, terminate all */
1021 sip_conn_close_media_channels (self);
1022 }
1023
1024 #endif
1025