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