summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Zabaluev <mikhail.zabaluev@nokia.com>2008-03-18 14:17:48 +0000
committerMikhail Zabaluev <mikhail.zabaluev@nokia.com>2008-03-18 14:17:48 +0000
commit58ad259426f96c968df350c44a3125413e54a658 (patch)
tree3181b43271c74db481911462de1f30bab9b723c9
parent0ad211d26d996833d8e7eb449e5d5ef7c0bbca22 (diff)
Refactored NUA callback handling to use GObject signal emission.
Removed the TpsipConnectionSofia callback state adapter and simplified the stack destruction code. 20080318141748-5b6ca-2654a6c422ffd518ba30a5277dd13997bb358c7a.gz
-rw-r--r--src/Makefile.am2
-rw-r--r--src/debug.c1
-rw-r--r--src/debug.h1
-rw-r--r--src/sip-connection-helpers.c7
-rw-r--r--src/sip-connection-helpers.h2
-rw-r--r--src/sip-connection-manager.c22
-rw-r--r--src/sip-connection-private.h5
-rw-r--r--src/sip-connection-sofia.c926
-rw-r--r--src/sip-connection-sofia.h27
-rw-r--r--src/sip-connection.c451
-rw-r--r--src/sip-connection.h21
-rw-r--r--src/sip-media-channel.c202
-rw-r--r--src/sip-media-channel.h18
-rw-r--r--src/sip-media-session.c20
-rw-r--r--src/sip-text-channel.c89
-rw-r--r--src/sip-text-channel.h10
16 files changed, 738 insertions, 1066 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0267139..77e6394 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -71,7 +71,6 @@ BUILT_SOURCES = \
sip-connection-enumtypes.c
libtpsip_convenience_la_SOURCES = \
- sip-sofia-decls.h \
sip-text-channel.h \
sip-text-channel.c \
sip-media-channel.h \
@@ -100,6 +99,7 @@ nodist_libtpsip_convenience_la_SOURCES = \
$(BUILT_SOURCES)
libtpsip_convenience_la_LIBADD = \
+ $(top_builddir)/tpsip/libtpsip.la \
$(top_builddir)/tpsip-extensions/libtpsip-extensions.la
telepathy_sofiasip_SOURCES = \
diff --git a/src/debug.c b/src/debug.c
index 948a546..d2053cc 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -35,6 +35,7 @@ static const GDebugKey tpsip_debug_keys[] = {
{ "media-channel", TPSIP_DEBUG_MEDIA },
{ "connection", TPSIP_DEBUG_CONNECTION },
{ "im", TPSIP_DEBUG_IM },
+ { "events", TPSIP_DEBUG_EVENTS },
};
void tpsip_debug_set_flags_from_env ()
diff --git a/src/debug.h b/src/debug.h
index 8aed9e1..f68decf 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -33,6 +33,7 @@ typedef enum
TPSIP_DEBUG_CONNECTION = 1 << 0,
TPSIP_DEBUG_MEDIA = 1 << 1,
TPSIP_DEBUG_IM = 1 << 2,
+ TPSIP_DEBUG_EVENTS = 1 << 3,
} TpsipDebugFlags;
void tpsip_debug_set_flags_from_env ();
diff --git a/src/sip-connection-helpers.c b/src/sip-connection-helpers.c
index 1d2f9fc..d1020bb 100644
--- a/src/sip-connection-helpers.c
+++ b/src/sip-connection-helpers.c
@@ -28,7 +28,8 @@
#include <telepathy-glib/interfaces.h>
#include <telepathy-glib/svc-connection.h>
-#include "sip-sofia-decls.h"
+#include "sip-connection-helpers.h"
+
#include <sofia-sip/sip.h>
#include <sofia-sip/sip_header.h>
@@ -513,7 +514,7 @@ tpsip_conn_resolv_stun_server (TpsipConnection *conn, const gchar *stun_host)
if (NULL == priv->sofia_resolver)
{
priv->sofia_resolver =
- sres_resolver_create (priv->sofia->sofia_root, NULL, TAG_END());
+ sres_resolver_create (priv->sofia_root, NULL, TAG_END());
}
g_return_if_fail (priv->sofia_resolver != NULL);
@@ -638,7 +639,7 @@ tpsip_conn_discover_stun_server (TpsipConnection *conn)
if (NULL == priv->sofia_resolver)
{
priv->sofia_resolver =
- sres_resolver_create (priv->sofia->sofia_root, NULL, TAG_END());
+ sres_resolver_create (priv->sofia_root, NULL, TAG_END());
}
g_return_if_fail (priv->sofia_resolver != NULL);
diff --git a/src/sip-connection-helpers.h b/src/sip-connection-helpers.h
index 92c8efd..fca2888 100644
--- a/src/sip-connection-helpers.h
+++ b/src/sip-connection-helpers.h
@@ -24,7 +24,7 @@
#include <glib.h>
#include "sip-connection.h"
-#include "sip-sofia-decls.h"
+#include <tpsip/sofia-decls.h>
G_BEGIN_DECLS
diff --git a/src/sip-connection-manager.c b/src/sip-connection-manager.c
index 080c2c7..46dfdfe 100644
--- a/src/sip-connection-manager.c
+++ b/src/sip-connection-manager.c
@@ -24,18 +24,19 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-protocol.h>
#include <stdlib.h>
#include <string.h>
+#include <dbus/dbus-protocol.h>
+
#include <telepathy-glib/errors.h>
#include <telepathy-glib/svc-connection-manager.h>
+#include <tpsip/sofia-decls.h>
+#include <sofia-sip/su_glib.h>
+
#include "sip-connection-manager.h"
#include "sip-connection.h"
-#include "signals-marshal.h"
-#include "sip-sofia-decls.h"
#define DEBUG_FLAG TPSIP_DEBUG_CONNECTION
#include "debug.h"
@@ -376,12 +377,13 @@ check_not_empty_if_present (const gchar *name,
static TpBaseConnection *
tpsip_connection_manager_new_connection (TpBaseConnectionManager *base,
- const gchar *proto,
- TpIntSet *params_present,
- void *parsed_params,
- GError **error)
+ const gchar *proto,
+ TpIntSet *params_present,
+ void *parsed_params,
+ GError **error)
{
- TpsipConnectionManager *obj = TPSIP_CONNECTION_MANAGER (base);
+ TpsipConnectionManager *self = TPSIP_CONNECTION_MANAGER (base);
+ TpsipConnectionManagerPrivate *priv = TPSIP_CONNECTION_MANAGER_GET_PRIVATE (self);
TpBaseConnection *connection = NULL;
TpsipConnParams *params = (TpsipConnParams *)parsed_params;
gchar *proxy = NULL;
@@ -425,7 +427,7 @@ tpsip_connection_manager_new_connection (TpBaseConnectionManager *base,
connection = (TpBaseConnection *)g_object_new(TPSIP_TYPE_CONNECTION,
"protocol", "sip",
- "sofia-root", obj->priv->sofia_root,
+ "sofia-root", priv->sofia_root,
"address", params->account,
NULL);
diff --git a/src/sip-connection-private.h b/src/sip-connection-private.h
index 9b08127..9ab97cd 100644
--- a/src/sip-connection-private.h
+++ b/src/sip-connection-private.h
@@ -23,11 +23,12 @@
#include <telepathy-glib/channel-factory-iface.h>
-#include "sip-connection-sofia.h"
+#include <tpsip/sofia-decls.h>
+#include <sofia-sip/sresolv.h>
struct _TpsipConnectionPrivate
{
- TpsipConnectionSofia *sofia;
+ su_root_t *sofia_root;
nua_t *sofia_nua;
su_home_t *sofia_home;
nua_handle_t *register_op;
diff --git a/src/sip-connection-sofia.c b/src/sip-connection-sofia.c
index 51fabf4..8272b1e 100644
--- a/src/sip-connection-sofia.c
+++ b/src/sip-connection-sofia.c
@@ -20,57 +20,25 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <dbus/dbus-glib.h>
-
-#include <telepathy-glib/interfaces.h>
-
#include "sip-connection-sofia.h"
-#include "sip-connection-private.h"
-#include "media-factory.h"
-#include "text-factory.h"
-#include <sofia-sip/sip_header.h>
-#include <sofia-sip/sip_parser.h>
-#include <sofia-sip/sip_status.h>
-#include <sofia-sip/msg_parser.h>
-#include <sofia-sip/msg_types.h>
-#include <sofia-sip/su_tag_io.h> /* for tl_print() */
-#include <string.h>
+#include <sofia-sip/su_tag_io.h>
-#define DEBUG_FLAG TPSIP_DEBUG_CONNECTION
+#define DEBUG_FLAG TPSIP_DEBUG_EVENTS
#include "debug.h"
-TpsipConnectionSofia *
-tpsip_connection_sofia_new (TpsipConnection *conn)
-{
- TpsipConnectionSofia *sofia = g_slice_new0 (TpsipConnectionSofia);
- sofia->conn = conn;
- return sofia;
-}
-
-void
-tpsip_connection_sofia_destroy (TpsipConnectionSofia *sofia)
-{
- g_slice_free (TpsipConnectionSofia, sofia);
-}
-
static void
priv_r_shutdown(int status,
- char const *phrase,
- nua_t *nua,
- TpsipConnectionSofia *sofia)
+ nua_t *nua)
{
GSource *source;
gboolean source_recursive;
- DEBUG("nua_shutdown: %03d %s", status, phrase);
-
if (status < 200)
return;
- g_assert(sofia->conn == NULL);
-
- source = su_root_gsource (sofia->sofia_root);
+ /* Should be the source of the Sofia root */
+ source = g_main_current_source ();
/* XXX: temporarily allow recursion in the Sofia source to work around
* nua_destroy() requiring nested mainloop iterations to complete
@@ -88,275 +56,9 @@ priv_r_shutdown(int status,
if (!source_recursive)
g_source_set_can_recurse (source, FALSE);
-
- tpsip_connection_sofia_destroy (sofia);
-}
-
-static void
-priv_auth_save (GHashTable *table, nua_handle_t *nh, const char *auth)
-{
- g_hash_table_insert (table, nh /* nua_handle_ref (nh) */, g_strdup (auth));
-}
-
-static void
-priv_auth_drop (GHashTable *table, nua_handle_t *nh)
-{
- g_hash_table_remove (table, nh);
-}
-
-static gboolean
-priv_auth_check_repeat (GHashTable *table, nua_handle_t *nh, const char *auth)
-{
- const char *last_auth;
-
- g_assert (auth != NULL);
-
- last_auth = g_hash_table_lookup (table, nh);
-
- return (last_auth != NULL && strcmp (last_auth, auth) == 0);
-}
-
-/* We have a monster auth handler method with a traffic light
- * return code. Might think about refactoring it someday... */
-typedef enum {
- TPSIP_AUTH_FAILURE,
- TPSIP_AUTH_PASS,
- TPSIP_AUTH_HANDLED
-} TpsipAuthStatus;
-
-static TpsipAuthStatus
-priv_handle_auth (TpsipConnection* self,
- int status,
- nua_handle_t *nh,
- const sip_t *sip,
- gboolean home_realm)
-{
- TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self);
- sip_www_authenticate_t const *wa;
- sip_proxy_authenticate_t const *pa;
- const char *method = NULL;
- const char *realm = NULL;
- const char *user = NULL;
- const char *password = NULL;
- gchar *auth = NULL;
-
- DEBUG("enter");
-
- if (status != 401 && status != 407)
- {
- /* Clear the last used credentials saved for loop detection
- * and proceed with normal handling */
- priv_auth_drop (priv->auth_table, nh);
- return TPSIP_AUTH_PASS;
- }
-
- wa = sip->sip_www_authenticate;
- pa = sip->sip_proxy_authenticate;
-
- /* step: figure out the realm of the challenge */
- if (wa) {
- realm = msg_params_find(wa->au_params, "realm=");
- method = wa->au_scheme;
- }
- else if (pa) {
- realm = msg_params_find(pa->au_params, "realm=");
- method = pa->au_scheme;
- }
-
- if (realm == NULL)
- {
- g_warning ("no realm presented for authentication");
- return TPSIP_AUTH_FAILURE;
- }
-
- /* step: determine which set of credentials to use */
- if (home_realm)
- {
- /* Save the realm presented by the registrar */
- if (priv->registrar_realm == NULL)
- priv->registrar_realm = g_strdup (realm);
- else if (wa && strcmp(priv->registrar_realm, realm) != 0)
- {
- g_message ("registrar realm changed from '%s' to '%s'", priv->registrar_realm, realm);
- g_free (priv->registrar_realm);
- priv->registrar_realm = g_strdup (realm);
- }
- }
- else if (priv->registrar_realm != NULL
- && strcmp(priv->registrar_realm, realm) == 0)
- home_realm = TRUE;
-
- if (home_realm)
- {
- /* use authentication username if provided */
- user = priv->auth_user;
- password = priv->password;
-
- DEBUG("using the primary auth credentials");
- }
- else
- {
- if (priv->extra_auth_user)
- user = priv->extra_auth_user;
- else
- /* fall back to the main username */
- user = priv->auth_user;
- password = priv->extra_auth_password;
-
- DEBUG("using the extra auth credentials");
- }
-
- if (user == NULL)
- {
- sip_from_t const *sipfrom = sip->sip_from;
- if (sipfrom && sipfrom->a_url[0].url_user)
- /* or use the userpart in "From" header */
- user = sipfrom->a_url[0].url_user;
- }
-
- if (password == NULL)
- password = "";
-
- /* step: if all info is available, create an authorization response */
- g_assert (realm != NULL);
- if (user && method) {
- if (realm[0] == '"')
- auth = g_strdup_printf ("%s:%s:%s:%s",
- method, realm, user, password);
- else
- auth = g_strdup_printf ("%s:\"%s\":%s:%s",
- method, realm, user, password);
-
- DEBUG("%s authenticating user='%s' realm='%s' nh='%p'",
- wa ? "server" : "proxy", user, realm, nh);
- }
-
- /* step: do sanity checks, avoid resubmitting the exact same response */
- /* XXX: we don't check if the nonce is the same, presumably should be
- * taken care of by the stack */
- if (auth == NULL)
- g_warning ("sofiasip: authentication data are incomplete");
- else if (priv_auth_check_repeat (priv->auth_table, nh, auth))
- {
- g_debug ("authentication challenge repeated, dropping");
- g_free (auth);
- auth = NULL;
- /* Also drop the saved response because we're going to ditch the
- * handle anyway */
- priv_auth_drop (priv->auth_table, nh);
- }
-
- if (auth == NULL)
- return TPSIP_AUTH_FAILURE;
-
- priv_auth_save (priv->auth_table, nh, auth);
-
- /* step: authenticate */
- nua_authenticate(nh, NUTAG_AUTH(auth), TAG_END());
-
- g_free (auth);
-
- return TPSIP_AUTH_HANDLED;
-}
-
-static void
-priv_r_invite (int status,
- char const *phrase,
- nua_t *nua,
- TpsipConnection *self,
- nua_handle_t *nh,
- nua_handle_t *nh_magic,
- sip_t const *sip,
- tagi_t tags[])
-{
- if (nh_magic == TPSIP_NH_EXPIRED)
- {
- /* 487 Request Terminated is OK on destroyed channels */
- if (status != 487)
- g_message ("response %03d received for a destroyed media channel", status);
- return;
- }
-
- if (nh_magic == NULL)
- {
- g_message ("response %03d, on a NUA handle %p which does "
- "not belong to any media channel, ignored", status, nh);
- return;
- }
-
- if (priv_handle_auth (self, status, nh, sip, FALSE) == TPSIP_AUTH_HANDLED)
- return;
-
- tpsip_media_channel_call_status (TPSIP_MEDIA_CHANNEL (nh_magic),
- status,
- phrase);
-}
-
-static void
-priv_r_bye (int status,
- char const *phrase,
- nua_t *nua,
- TpsipConnection *self,
- nua_handle_t *nh,
- sip_t const *sip,
- tagi_t tags[])
-{
- DEBUG("BYE got %03d %s", status, phrase);
-
- if (status < 200)
- return;
-
- priv_handle_auth (self, status, nh, sip, FALSE);
-}
-
-static void
-priv_r_register (int status,
- char const *phrase,
- nua_t *nua,
- TpsipConnection *self,
- nua_handle_t *nh,
- sip_t const *sip,
- tagi_t tags[])
-{
- TpBaseConnection *base = (TpBaseConnection *)self;
- TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self);
-
- DEBUG("REGISTER got response: %03d %s", status, phrase);
-
- if (status < 200) {
- return;
- }
-
- switch (priv_handle_auth (self, status, nh, sip, TRUE))
- {
- case TPSIP_AUTH_FAILURE:
- DEBUG("REGISTER failed, insufficient/wrong credentials, disconnecting.");
- tp_base_connection_change_status (base, TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
- return;
- case TPSIP_AUTH_HANDLED:
- return;
- case TPSIP_AUTH_PASS:
- break;
- }
-
- if (status == 403) {
- DEBUG("REGISTER failed, wrong credentials, disconnecting.");
- tp_base_connection_change_status (base, TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
- }
- else if (status >= 300) {
- DEBUG("REGISTER failed, disconnecting.");
- tp_base_connection_change_status (base, TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
- }
- else /* if (status == 200) */ {
- DEBUG("succesfully registered %s to network", priv->address);
- tp_base_connection_change_status (base, TP_CONNECTION_STATUS_CONNECTED,
- TP_CONNECTION_STATUS_REASON_REQUESTED);
- }
}
+#if 0
static void
priv_r_unregister (int status,
char const *phrase,
@@ -376,443 +78,25 @@ priv_r_unregister (int status,
g_warning ("Registrar won't let me unregister: %d %s", status, phrase);
}
}
+#endif
+#ifdef ENABLE_DEBUG
static void
priv_r_get_params (int status,
- char const *phrase,
nua_t *nua,
- TpsipConnection *self,
nua_handle_t *nh,
- sip_t const *sip,
tagi_t tags[])
{
- DEBUG("nua_r_get_params: %03d %s", status, phrase);
-
if (status < 200)
return;
- /* note: print contents of all tags to stdout */
- tl_print(stdout, "tp-sofiasip stack parameters:\n", tags);
-}
-
-static TpHandle
-priv_handle_parse_from (const sip_t *sip,
- su_home_t *home,
- TpHandleRepoIface *contact_repo)
-{
- TpHandle handle = 0;
- gchar *url_str;
-
- g_return_val_if_fail (sip != NULL, 0);
-
- if (sip->sip_from)
- {
- url_str = url_as_string (home, sip->sip_from->a_url);
-
- handle = tp_handle_ensure (contact_repo, url_str, NULL, NULL);
-
- su_free (home, url_str);
-
- /* TODO: set qdata for the display name */
- }
-
- return handle;
-}
-
-static TpHandle
-priv_handle_parse_to (const sip_t *sip,
- su_home_t *home,
- TpHandleRepoIface *contact_repo)
-{
- TpHandle handle = 0;
- gchar *url_str;
-
- g_return_val_if_fail (sip != NULL, 0);
-
- if (sip->sip_to)
- {
- url_str = url_as_string (home, sip->sip_to->a_url);
-
- handle = tp_handle_ensure (contact_repo, url_str, NULL, NULL);
-
- su_free (home, url_str);
-
- /* TODO: set qdata for the display name */
- }
-
- return handle;
-}
-
-static void
-priv_r_message (int status,
- char const *phrase,
- nua_t *nua,
- TpsipConnection *self,
- nua_handle_t *nh,
- sip_t const *sip,
- tagi_t tags[])
-{
- TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self);
- TpsipTextChannel *channel;
- TpHandleRepoIface *contact_repo;
- TpHandle handle;
-
- DEBUG("nua_r_message: %03d %s", status, phrase);
-
- if (priv_handle_auth (self, status, nh, sip, FALSE) == TPSIP_AUTH_HANDLED)
+ if (nh != NULL)
return;
- contact_repo = tp_base_connection_get_handles (
- (TpBaseConnection *)self, TP_HANDLE_TYPE_CONTACT);
-
- handle = priv_handle_parse_to (sip, priv->sofia_home, contact_repo);
-
- if (!handle)
- {
- g_warning ("Message apparently delivered to an invalid recipient, ignoring");
- return;
- }
-
- if (status == 200)
- DEBUG("Message delivered for <%s>",
- tp_handle_inspect (contact_repo, handle));
-
- channel = tpsip_text_factory_lookup_channel (priv->text_factory, handle);
-
- tp_handle_unref (contact_repo, handle);
-
- if (!channel)
- g_warning ("Delivery status ignored for a non-existant channel");
- else if (status >= 200)
- tpsip_text_channel_emit_message_status (channel, nh, status);
-}
-
-static void
-priv_i_invite (int status,
- char const *phrase,
- nua_t *nua,
- TpsipConnection *self,
- nua_handle_t *nh,
- nua_hmagic_t *nh_magic,
- sip_t const *sip,
- tagi_t tags[])
-{
- TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self);
- TpsipMediaChannel *channel;
- TpHandleRepoIface *contact_repo;
- TpHandle handle;
- GError *error = NULL;
-
- if (nh_magic == TPSIP_NH_EXPIRED)
- {
- g_message ("incoming INVITE for a destroyed media channel");
- nua_respond (nh, 481, "Call Does Not Exist", TAG_END());
- return;
- }
-
- if (nh_magic != NULL) {
- /* case 1: we already have a channel for this NH */
- channel = TPSIP_MEDIA_CHANNEL (nh_magic);
- tpsip_media_channel_receive_reinvite (channel);
- }
- else {
- /* case 2: we haven't seen this media session before, so we should
- * create a new channel to go with it */
-
- /* figure out a handle for the identity */
-
- contact_repo = tp_base_connection_get_handles ((TpBaseConnection *)self,
- TP_HANDLE_TYPE_CONTACT);
-
- handle = priv_handle_parse_from (sip, priv->sofia_home, contact_repo);
-
- if (!handle)
- {
- g_message ("incoming INVITE with invalid sender information");
- nua_respond (nh, 400, "Invalid From address", TAG_END());
- return;
- }
-
- DEBUG("Got incoming invite from <%s>",
- tp_handle_inspect (contact_repo, handle));
-
- channel = tpsip_media_factory_new_channel (
- TPSIP_MEDIA_FACTORY (priv->media_factory),
- NULL,
- TP_HANDLE_TYPE_NONE,
- 0,
- &error);
- if (channel)
- {
- tpsip_media_channel_receive_invite (channel, nh, handle);
- }
- else
- {
- g_warning ("creation of SIP media channel failed: %s", error->message);
- g_error_free (error);
- nua_respond (nh, 500, sip_500_Internal_server_error, TAG_END());
- }
-
- tp_handle_unref (contact_repo, handle);
- }
-}
-
-static void
-priv_i_cancel (nua_t *nua,
- TpsipConnection *self,
- nua_handle_t *nh,
- nua_hmagic_t *nh_magic,
- sip_t const *sip,
- tagi_t tags[])
-{
- const sip_reason_t *reason;
- guint cause = 0;
- const gchar *text = NULL;
-
- if (nh_magic == NULL)
- {
- g_message ("nua_i_cancel received for an unknown handle");
- /* nua_respond (nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END()); */
- return;
- }
- if (nh_magic == TPSIP_NH_EXPIRED)
- {
- g_message ("CANCEL received for a destroyed media channel");
- /* nua_respond (nh, SIP_481_NO_CALL, TAG_END()); */
- return;
- }
-
- reason = sip->sip_reason;
- while (reason)
- {
- if (strcmp (reason->re_protocol, "SIP") == 0)
- {
- if (reason->re_cause)
- cause = (guint) g_ascii_strtoull (reason->re_cause, NULL, 10);
- text = reason->re_text;
- break;
- }
- reason = reason->re_next;
- }
-
- tpsip_media_channel_peer_cancel (TPSIP_MEDIA_CHANNEL (nh_magic), cause, text);
-
- /* nua_respond (nh, SIP_200_OK, TAG_END()); */
-}
-
-static void
-priv_i_message (int status,
- char const *phrase,
- nua_t *nua,
- TpsipConnection *self,
- nua_handle_t *nh,
- sip_t const *sip,
- tagi_t tags[])
-{
- TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self);
- TpsipTextChannel *channel;
- TpHandleRepoIface *contact_repo;
- TpHandle handle;
- char *text = NULL;
-
- /* Block anything else except text/plain messages (like isComposings) */
- if (sip->sip_content_type
- && (g_ascii_strcasecmp ("text/plain", sip->sip_content_type->c_type)))
- {
- nua_respond (nh, SIP_415_UNSUPPORTED_MEDIA,
- SIPTAG_ACCEPT_STR("text/plain"),
- NUTAG_WITH_THIS(nua),
- TAG_END());
- return;
- }
-
- /* If there is some text, assure it's in UTF-8 encoding */
- if (sip->sip_payload && sip->sip_payload->pl_len > 0)
- {
- const char *charset = NULL;
- if (sip->sip_content_type && sip->sip_content_type->c_params != 0)
- {
- charset = msg_params_find (sip->sip_content_type->c_params, "charset=");
- }
-
- /* Default charset is UTF-8, we only need to convert if it's a different one */
- if (charset && g_ascii_strcasecmp (charset, "UTF-8"))
- {
- GError *error;
- gsize in_len, out_len;
- text = g_convert (sip->sip_payload->pl_data, sip->sip_payload->pl_len,
- "UTF-8", charset, &in_len, &out_len, &error);
-
- if (text == NULL)
- {
- nua_respond (nh, 500, error->message,
- NUTAG_WITH_THIS(nua),
- TAG_END());
- g_error_free (error);
- return;
- }
- if (in_len != sip->sip_payload->pl_len)
- {
- nua_respond (nh, 400, "Incomplete character sequence at the "
- "end of the message body",
- NUTAG_WITH_THIS(nua),
- TAG_END());
- goto end;
- }
- }
- else
- {
- if (!g_utf8_validate (sip->sip_payload->pl_data,
- sip->sip_payload->pl_len,
- NULL))
- {
- nua_respond (nh, 400, "Invalid characters in the message body",
- NUTAG_WITH_THIS(nua),
- TAG_END());
- return;
- }
- text = g_strndup (sip->sip_payload->pl_data, sip->sip_payload->pl_len);
- }
- }
- else
- {
- text = g_strdup ("");
- }
-
- contact_repo = tp_base_connection_get_handles (
- (TpBaseConnection *)self, TP_HANDLE_TYPE_CONTACT);
-
- handle = priv_handle_parse_from (sip, priv->sofia_home, contact_repo);
-
- if (handle)
- {
- DEBUG("Got incoming message from <%s>",
- tp_handle_inspect (contact_repo, handle));
-
- channel = tpsip_text_factory_lookup_channel (priv->text_factory, handle);
-
- if (!channel)
- {
- channel = tpsip_text_factory_new_channel (priv->text_factory, handle,
- NULL);
- g_assert (channel != NULL);
- }
-
- tpsip_text_channel_receive (channel, handle, text);
-
- tp_handle_unref (contact_repo, handle);
-
- nua_respond (nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END());
- }
- else
- {
- g_warning ("Incoming message has invalid sender information");
- nua_respond (nh, 400, "Invalid From address",
- NUTAG_WITH_THIS(nua),
- TAG_END());
- }
-
-end:
- g_free (text);
-}
-
-static void
-priv_i_state (int status,
- char const *phrase,
- nua_t *nua,
- TpsipConnection *self,
- nua_handle_t *nh,
- nua_hmagic_t *nh_magic,
- sip_t const *sip,
- tagi_t tags[])
-{
- const sdp_session_t *r_sdp = NULL;
- int offer_recv = 0;
- int answer_recv = 0;
- int ss_state = nua_callstate_init;
- TpsipMediaChannel *channel;
-
- DEBUG("nua_i_state: %03d %s", status, phrase);
-
- if (nh_magic == NULL)
- {
- g_warning ("nua_i_state received for an unknown handle %p, ignored", nh);
- return;
- }
-
- tl_gets(tags,
- NUTAG_CALLSTATE_REF(ss_state),
- NUTAG_OFFER_RECV_REF(offer_recv),
- NUTAG_ANSWER_RECV_REF(answer_recv),
- SOATAG_REMOTE_SDP_REF(r_sdp),
- TAG_END());
-
- if (nh_magic == TPSIP_NH_EXPIRED)
- {
- g_message ("call state change %03d '%s' received for a "
- "destroyed media channel (handle %p)", status, phrase, nh);
- switch (ss_state)
- {
- case nua_callstate_terminated:
- case nua_callstate_terminating:
- break;
- default:
- DEBUG("BYE off the orphaned handle");
- nua_bye (nh, TAG_END());
- }
- return;
- }
-
- channel = nh_magic;
-
- if (r_sdp)
- {
- g_return_if_fail(answer_recv || offer_recv);
- if (!tpsip_media_channel_set_remote_media (channel, r_sdp))
- tpsip_media_channel_close (channel);
- }
-
- DEBUG("call with handle %p is %s", nh, nua_callstate_name (ss_state));
-
- switch ((enum nua_callstate)ss_state) {
- case nua_callstate_received:
- case nua_callstate_early:
- break;
-
- case nua_callstate_completing:
- /* In auto-ack mode, we don't need to call nua_ack(), see NUTAG_AUTOACK() */
- break;
-
- case nua_callstate_ready:
- tpsip_media_channel_ready (channel);
- break;
-
- case nua_callstate_terminated:
- {
- TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self);
- priv_auth_drop (priv->auth_table, nh);
- }
- tpsip_media_channel_terminated (channel);
- break;
-
- default:
- break;
- }
-}
-
-static inline const char *
-classify_nh_magic (nua_hmagic_t *nh_magic)
-{
- if (nh_magic == NULL)
- {
- return "no channel yet";
- }
- if (nh_magic == TPSIP_NH_EXPIRED)
- {
- return "channel has been destroyed";
- }
- return "TpsipMediaChannel";
+ /* note: print contents of all tags to stdout */
+ tl_print(stdout, "Sofia-SIP NUA stack parameters:\n", tags);
}
+#endif
/**
* Callback for events delivered by the SIP stack.
@@ -820,154 +104,64 @@ classify_nh_magic (nua_hmagic_t *nh_magic)
* See libsofia-sip-ua/nua/nua.h documentation.
*/
void
-tpsip_connection_sofia_callback(nua_event_t event,
- int status,
- char const *phrase,
- nua_t *nua,
- TpsipConnectionSofia *state,
- nua_handle_t *nh,
- nua_hmagic_t *nh_magic,
- sip_t const *sip,
- tagi_t tags[])
-{
- TpsipConnection *self;
-
- g_return_if_fail (state);
-
- if (event == nua_r_shutdown)
- {
- priv_r_shutdown (status, phrase, nua, state);
+tpsip_connection_sofia_callback (nua_event_t event,
+ int status,
+ char const *phrase,
+ nua_t *nua,
+ TpsipConnection *conn,
+ nua_handle_t *nh,
+ TpsipEventTarget *target,
+ sip_t const *sip,
+ tagi_t tags[])
+{
+ DEBUG("event %s: %03d %s",
+ nua_event_name (event), status, phrase);
+
+ switch (event)
+ {
+#ifdef ENABLE_DEBUG
+ case nua_r_get_params:
+ priv_r_get_params (status, nua, nh, tags);
return;
- }
- else if (event == nua_r_unregister)
- {
- priv_r_unregister (status, phrase, nh);
- return;
- }
-
- self = state->conn;
- if (self == NULL)
- {
- g_warning ("post-shutdown event received for a connection: NUA at %p, event #%d '%s', %d '%s'",
- nua, event, nua_event_name (event), status, phrase);
+#endif
+ case nua_r_shutdown:
+ priv_r_shutdown (status, nua);
return;
+ default:
+ break;
}
- DEBUG("enter: nh %p (conn %p), event #%d '%s', %d '%s'",
- nh, self, (int) event, nua_event_name (event), status, phrase);
- DEBUG ("Connection refcount is %d", ((GObject *)self)->ref_count);
-
- switch (event) {
-
- /* incoming requests
- * ------------------------- */
-
- case nua_i_fork:
- /* self_i_fork(status, phrase, nua, self, nh, sip, tags); */
- break;
-
- case nua_i_invite:
- priv_i_invite (status, phrase, nua, self, nh, nh_magic, sip, tags);
- break;
-
- case nua_i_state:
- priv_i_state (status, phrase, nua, self, nh, nh_magic, sip, tags);
- break;
-
- case nua_i_bye:
- /* self_i_bye(nua, self, nh, sip, tags); */
- break;
-
- case nua_i_message:
- priv_i_message(status, phrase, nua, self, nh, sip, tags);
- break;
-
- case nua_i_refer:
- /* self_i_refer(nua, self, nh, sip, tags); */
- break;
+ g_assert (conn != NULL);
- case nua_i_notify:
- /* self_i_notify(nua, self, nh, sip, tags); */
- break;
+ DEBUG("connection %p, refcount %d", conn, ((GObject *)conn)->ref_count);
- case nua_i_cancel:
- priv_i_cancel (nua, self, nh, nh_magic, sip, tags);
- break;
+ {
+ TpsipNuaEvent ev = {
+ event,
+ status,
+ phrase,
+ nua,
+ nh,
+ sip
+ };
- case nua_i_error:
- /* self_i_error(nua, self, nh, status, phrase, tags); */
- break;
-
- case nua_i_active:
- case nua_i_ack:
- case nua_i_terminated:
- case nua_i_options:
- /* ignore these */
- break;
-
- /* responses to our requests
- * ------------------------- */
-
- case nua_r_register:
- priv_r_register (status, phrase, nua, self, nh, sip, tags);
- break;
-
- case nua_r_invite:
- priv_r_invite(status, phrase, nua, self, nh, nh_magic, sip, tags);
- break;
-
- case nua_r_bye:
- priv_r_bye(status, phrase, nua, self, nh, sip, tags);
- break;
-
- case nua_r_message:
- priv_r_message(status, phrase, nua, self, nh, sip, tags);
- break;
-
- case nua_r_cancel:
- /* priv_r_cancel (status, phrase, nua, self, nh, sip, tags); */
- break;
-
- case nua_r_refer:
- /* self_r_refer(status, phrase, nua, self, nh, sip, tags); */
- break;
-
- case nua_r_subscribe:
- /* self_r_subscribe(status, phrase, nua, self, nh, sip, tags); */
- break;
-
- case nua_r_unsubscribe:
- /* self_r_unsubscribe(status, phrase, nua, self, nh, sip, tags); */
- break;
-
- case nua_r_publish:
- /* self_r_publish(status, phrase, nua, self, nh, sip, tags); */
- break;
-
- case nua_r_notify:
- /* self_r_notify(status, phrase, nua, self, nh, sip, tags); */
- break;
-
- case nua_r_get_params:
- priv_r_get_params(status, phrase, nua, self, nh, sip, tags);
- break;
-
- default:
- g_message ("sip-connection: unknown event #%d '%s', status %03d '%s' "
- " (nh=%p)", event, nua_event_name(event), status, phrase, nh);
-
- if (nh_magic == TPSIP_NH_EXPIRED)
+ if (target == NULL)
+ {
+ target = (TpsipEventTarget *) conn;
+ DEBUG("dispatching to connection %p (unbound handle %p)", conn, nh);
+ }
+ else
{
- /* note: unknown handle, not associated to any existing
- * call, message, registration, etc, so it can
- * be safely destroyed */
- g_message ("NOTE: destroying NUA handle %p (its associated "
- "channel has already gone away)", nh);
- nua_handle_ref (nh);
- nua_handle_destroy (nh);
+ g_assert (nh != NULL);
+ DEBUG("dispatching to target %p (handle %p)", target, nh);
}
- break;
+ if (!tpsip_event_target_emit_nua_event (target,
+ &ev,
+ tags))
+ {
+ DEBUG("event %s for target %p was not consumed", nua_event_name (event), target);
+ }
}
DEBUG ("exit");
diff --git a/src/sip-connection-sofia.h b/src/sip-connection-sofia.h
index f9a43f0..a56b1cb 100644
--- a/src/sip-connection-sofia.h
+++ b/src/sip-connection-sofia.h
@@ -22,31 +22,26 @@
#ifndef __TPSIP_CONNECTION_SOFIA_H__
#define __TPSIP_CONNECTION_SOFIA_H__
-#include "sip-sofia-decls.h"
+#include <tpsip/sofia-decls.h>
+#include <tpsip/event-target.h>
#include "sip-connection.h"
G_BEGIN_DECLS
-typedef struct _TpsipConnectionSofia {
- /* The owner SIP connection object */
- TpsipConnection *conn;
- /* Event loop root for Sofia-SIP */
- su_root_t *sofia_root;
-} TpsipConnectionSofia;
-
-TpsipConnectionSofia * tpsip_connection_sofia_new (TpsipConnection *conn);
-void tpsip_connection_sofia_destroy (TpsipConnectionSofia *conn);
-
/**
* Callback for events delivered by the SIP stack.
*
* See libsofia-sip-ua/nua/nua.h documentation.
*/
-void tpsip_connection_sofia_callback(nua_event_t event,
- int status, char const *phrase,
- nua_t *nua, TpsipConnectionSofia *sofia,
- nua_handle_t *nh, nua_hmagic_t *op, sip_t const *sip,
- tagi_t tags[]);
+void tpsip_connection_sofia_callback (nua_event_t event,
+ int status,
+ char const *phrase,
+ nua_t *nua,
+ TpsipConnection *conn,
+ nua_handle_t *nh,
+ TpsipEventTarget *target,
+ sip_t const *sip,
+ tagi_t tags[]);
G_END_DECLS
diff --git a/src/sip-connection.c b/src/sip-connection.c
index 5ce8941..9f1652c 100644
--- a/src/sip-connection.c
+++ b/src/sip-connection.c
@@ -39,24 +39,30 @@
#include <telepathy-glib/intset.h>
#include <telepathy-glib/svc-connection.h>
+#include <tpsip/event-target.h>
+
+#include "sip-connection.h"
#include "media-factory.h"
#include "text-factory.h"
-#include "sip-connection.h"
+
+#include "sip-connection-enumtypes.h"
+#include "sip-connection-helpers.h"
+#include "sip-connection-private.h"
+#include "sip-connection-sofia.h"
+
+#include <sofia-sip/msg_header.h>
+#include <sofia-sip/sip_status.h>
#define DEBUG_FLAG TPSIP_DEBUG_CONNECTION
#include "debug.h"
+static void event_target_iface_init (gpointer, gpointer);
static void conn_iface_init (gpointer, gpointer);
G_DEFINE_TYPE_WITH_CODE(TpsipConnection, tpsip_connection,
TP_TYPE_BASE_CONNECTION,
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION,
- conn_iface_init))
-
-#include "sip-connection-enumtypes.h"
-#include "sip-connection-helpers.h"
-#include "sip-connection-private.h"
-#include "sip-connection-sofia.h"
+ G_IMPLEMENT_INTERFACE (TPSIP_TYPE_EVENT_TARGET, event_target_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION, conn_iface_init))
#define ERROR_IF_NOT_CONNECTED_ASYNC(BASE, CONTEXT) \
if ((BASE)->status != TP_CONNECTION_STATUS_CONNECTED) \
@@ -181,7 +187,6 @@ static void
tpsip_connection_init (TpsipConnection *obj)
{
TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (obj);
- priv->sofia = tpsip_connection_sofia_new (obj);
priv->sofia_home = su_home_new(sizeof (su_home_t));
priv->auth_table = g_hash_table_new_full (g_direct_hash,
g_direct_equal,
@@ -289,8 +294,7 @@ tpsip_connection_set_property (GObject *object,
break;
}
case PROP_SOFIA_ROOT: {
- g_return_if_fail (priv->sofia != NULL);
- priv->sofia->sofia_root = g_value_get_pointer (value);
+ priv->sofia_root = g_value_get_pointer (value);
break;
}
default:
@@ -370,8 +374,7 @@ tpsip_connection_get_property (GObject *object,
break;
}
case PROP_SOFIA_ROOT: {
- g_return_if_fail (priv->sofia != NULL);
- g_value_set_pointer (value, priv->sofia->sofia_root);
+ g_value_set_pointer (value, priv->sofia_root);
break;
}
default:
@@ -600,6 +603,378 @@ tpsip_connection_class_init (TpsipConnectionClass *klass)
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB);
INST_PROP(PROP_EXTRA_AUTH_PASSWORD);
+
+#undef INST_PROP
+}
+
+static gboolean
+priv_handle_auth (TpsipConnection* self,
+ int status,
+ nua_handle_t *nh,
+ const sip_t *sip,
+ gboolean home_realm)
+{
+ TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self);
+ sip_www_authenticate_t const *wa;
+ sip_proxy_authenticate_t const *pa;
+ const char *method = NULL;
+ const char *realm = NULL;
+ const char *user = NULL;
+ const char *password = NULL;
+ gchar *auth = NULL;
+
+ if (status != 401 && status != 407)
+ return FALSE;
+
+ DEBUG("response presents an authentication challenge");
+
+ g_return_val_if_fail (sip != NULL, FALSE);
+
+ wa = sip->sip_www_authenticate;
+ pa = sip->sip_proxy_authenticate;
+
+ /* step: figure out the realm of the challenge */
+ if (wa) {
+ realm = msg_header_find_param ((msg_common_t *) wa, "realm=");
+ method = wa->au_scheme;
+ }
+ else if (pa) {
+ realm = msg_header_find_param ((msg_common_t *) pa, "realm=");
+ method = pa->au_scheme;
+ }
+
+ if (realm == NULL)
+ {
+ g_warning ("no realm presented for authentication");
+ return FALSE;
+ }
+
+ /* step: determine which set of credentials to use */
+ if (home_realm)
+ {
+ /* Save the realm presented by the registrar */
+ if (priv->registrar_realm == NULL)
+ priv->registrar_realm = g_strdup (realm);
+ else if (wa && strcmp(priv->registrar_realm, realm) != 0)
+ {
+ g_message ("registrar realm changed from %s to %s", priv->registrar_realm, realm);
+ g_free (priv->registrar_realm);
+ priv->registrar_realm = g_strdup (realm);
+ }
+ }
+ else if (priv->registrar_realm != NULL
+ && strcmp(priv->registrar_realm, realm) == 0)
+ home_realm = TRUE;
+
+ if (home_realm)
+ {
+ /* use authentication username if provided */
+ user = priv->auth_user;
+ password = priv->password;
+
+ DEBUG("using the primary auth credentials");
+ }
+ else
+ {
+ if (priv->extra_auth_user)
+ user = priv->extra_auth_user;
+ else
+ /* fall back to the main username */
+ user = priv->auth_user;
+ password = priv->extra_auth_password;
+
+ DEBUG("using the extra auth credentials");
+ }
+
+ if (user == NULL)
+ {
+ sip_from_t const *sipfrom = sip->sip_from;
+ if (sipfrom && sipfrom->a_url[0].url_user)
+ /* or use the userpart in "From" header */
+ user = sipfrom->a_url[0].url_user;
+ }
+
+ if (password == NULL)
+ password = "";
+
+ /* step: if all info is available, create an authorization response */
+ g_assert (realm != NULL);
+ if (user && method) {
+ if (realm[0] == '"')
+ auth = g_strdup_printf ("%s:%s:%s:%s",
+ method, realm, user, password);
+ else
+ auth = g_strdup_printf ("%s:\"%s\":%s:%s",
+ method, realm, user, password);
+
+ DEBUG("%s authenticating user='%s' realm=%s",
+ wa ? "server" : "proxy", user, realm);
+ }
+
+ if (auth == NULL)
+ {
+ g_warning ("authentication data are incomplete");
+ return FALSE;
+ }
+
+ /* step: authenticate */
+ nua_authenticate(nh, NUTAG_AUTH(auth), TAG_END());
+
+ g_free (auth);
+
+ return TRUE;
+}
+
+static gboolean
+tpsip_connection_auth_cb (TpsipEventTarget *target,
+ const TpsipNuaEvent *ev,
+ tagi_t tags[],
+ TpsipConnection *self)
+{
+ return priv_handle_auth (self,
+ ev->status,
+ ev->nua_handle,
+ ev->sip,
+ FALSE);
+}
+
+void
+tpsip_connection_connect_auth_handler (TpsipConnection *self,
+ TpsipEventTarget *target)
+{
+ g_signal_connect_object (target,
+ "nua-event",
+ G_CALLBACK (tpsip_connection_auth_cb),
+ self,
+ 0);
+}
+
+static gboolean
+tpsip_connection_nua_r_register_cb (TpsipConnection *self,
+ const TpsipNuaEvent *ev,
+ tagi_t tags[],
+ gpointer foo)
+{
+ TpConnectionStatus conn_status = TP_CONNECTION_STATUS_DISCONNECTED;
+ TpConnectionStatusReason reason = 0;
+
+ if (ev->status < 200)
+ return TRUE;
+
+ if (priv_handle_auth (self, ev->status, ev->nua_handle, ev->sip, TRUE))
+ return TRUE;
+
+ switch (ev->status)
+ {
+ case 401:
+ case 403:
+ case 407:
+ DEBUG("REGISTER failed, possibly wrong credentials, disconnecting");
+ conn_status = TP_CONNECTION_STATUS_DISCONNECTED;
+ reason = TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED;
+ break;
+ default:
+ if (ev->status >= 300)
+ {
+ DEBUG("REGISTER failed, disconnecting");
+ conn_status = TP_CONNECTION_STATUS_DISCONNECTED;
+ reason = TP_CONNECTION_STATUS_REASON_NETWORK_ERROR;
+ }
+ else /* if (ev->status == 200) */
+ {
+ DEBUG("succesfully registered to the network");
+ conn_status = TP_CONNECTION_STATUS_CONNECTED;
+ reason = TP_CONNECTION_STATUS_REASON_REQUESTED;
+ }
+ }
+
+ tp_base_connection_change_status ((TpBaseConnection *) self,
+ conn_status, reason);
+
+ return TRUE;
+}
+
+static gboolean
+tpsip_connection_nua_i_invite_cb (TpsipConnection *self,
+ const TpsipNuaEvent *ev,
+ tagi_t tags[],
+ gpointer foo)
+{
+ TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self);
+ TpsipMediaChannel *channel;
+ TpHandleRepoIface *contact_repo;
+ TpHandle handle;
+ GError *error = NULL;
+
+ /* figure out a handle for the identity */
+
+ contact_repo = tp_base_connection_get_handles ((TpBaseConnection *)self,
+ TP_HANDLE_TYPE_CONTACT);
+
+ handle = priv_handle_parse_from (ev->sip, priv->sofia_home, contact_repo);
+ if (!handle)
+ {
+ g_message ("incoming INVITE with invalid sender information");
+ nua_respond (ev->nua_handle, 400, "Invalid From address", TAG_END());
+ return TRUE;
+ }
+
+ DEBUG("Got incoming invite from <%s>",
+ tp_handle_inspect (contact_repo, handle));
+
+ channel = tpsip_media_factory_new_channel (
+ TPSIP_MEDIA_FACTORY (priv->media_factory),
+ NULL,
+ TP_HANDLE_TYPE_NONE,
+ 0,
+ &error);
+ if (channel)
+ {
+ tpsip_media_channel_receive_invite (channel, ev->nua_handle, handle);
+ }
+ else
+ {
+ g_warning ("creation of SIP media channel failed: %s", error->message);
+ g_error_free (error);
+ nua_respond (ev->nua_handle, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+ }
+
+ tp_handle_unref (contact_repo, handle);
+
+ return TRUE;
+}
+
+static gboolean
+tpsip_connection_nua_i_message_cb (TpsipConnection *self,
+ const TpsipNuaEvent *ev,
+ tagi_t tags[],
+ gpointer foo)
+{
+ TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self);
+ TpsipTextChannel *channel;
+ TpHandleRepoIface *contact_repo;
+ TpHandle handle;
+ const sip_t *sip = ev->sip;
+ char *text = NULL;
+
+ /* Block anything else except text/plain messages (like isComposings) */
+ if (sip->sip_content_type
+ && (g_ascii_strcasecmp ("text/plain", sip->sip_content_type->c_type)))
+ {
+ nua_respond (ev->nua_handle,
+ SIP_415_UNSUPPORTED_MEDIA,
+ SIPTAG_ACCEPT_STR("text/plain"),
+ NUTAG_WITH_THIS(ev->nua),
+ TAG_END());
+ goto end;
+ }
+
+ /* If there is some text, assure it's in UTF-8 encoding */
+ if (sip->sip_payload && sip->sip_payload->pl_len > 0)
+ {
+ const char *charset = NULL;
+ if (sip->sip_content_type && sip->sip_content_type->c_params != 0)
+ {
+ charset = msg_params_find (sip->sip_content_type->c_params, "charset=");
+ }
+
+ /* Default charset is UTF-8, we only need to convert if it's a different one */
+ if (charset && g_ascii_strcasecmp (charset, "UTF-8"))
+ {
+ GError *error;
+ gsize in_len, out_len;
+ text = g_convert (sip->sip_payload->pl_data, sip->sip_payload->pl_len,
+ "UTF-8", charset, &in_len, &out_len, &error);
+
+ if (text == NULL)
+ {
+ gint status;
+ gchar *message;
+
+ status = (error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
+ ? 400 : 500;
+ message = g_strdup_printf ("Character set conversion failed"
+ " for the message body: %s",
+ error->message);
+ nua_respond (ev->nua_handle,
+ status, message,
+ NUTAG_WITH_THIS(ev->nua),
+ TAG_END());
+
+ g_free (message);
+ g_error_free (error);
+ goto end;
+ }
+ if (in_len != sip->sip_payload->pl_len)
+ {
+ nua_respond (ev->nua_handle,
+ 400, "Incomplete character sequence at the "
+ "end of the message body",
+ NUTAG_WITH_THIS(ev->nua),
+ TAG_END());
+ goto end;
+ }
+ }
+ else
+ {
+ if (!g_utf8_validate (sip->sip_payload->pl_data,
+ sip->sip_payload->pl_len,
+ NULL))
+ {
+ nua_respond (ev->nua_handle,
+ 400, "Invalid characters in the message body",
+ NUTAG_WITH_THIS(ev->nua),
+ TAG_END());
+ goto end;
+ }
+ text = g_strndup (sip->sip_payload->pl_data, sip->sip_payload->pl_len);
+ }
+ }
+ else
+ {
+ text = g_strdup ("");
+ }
+
+ contact_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *)self, TP_HANDLE_TYPE_CONTACT);
+
+ handle = priv_handle_parse_from (sip, priv->sofia_home, contact_repo);
+
+ if (handle)
+ {
+ DEBUG("Got incoming message from <%s>",
+ tp_handle_inspect (contact_repo, handle));
+
+ channel = tpsip_text_factory_lookup_channel (priv->text_factory, handle);
+
+ if (!channel)
+ {
+ channel = tpsip_text_factory_new_channel (priv->text_factory, handle,
+ NULL);
+ g_assert (channel != NULL);
+ }
+
+ tpsip_text_channel_receive (channel, handle, text);
+
+ tp_handle_unref (contact_repo, handle);
+
+ nua_respond (ev->nua_handle,
+ SIP_200_OK,
+ NUTAG_WITH_THIS(ev->nua),
+ TAG_END());
+ }
+ else
+ {
+ nua_respond (ev->nua_handle,
+ 400, "Invalid From address",
+ NUTAG_WITH_THIS(ev->nua),
+ TAG_END());
+ }
+
+end:
+ g_free (text);
+
+ return TRUE;
}
static void
@@ -616,18 +991,9 @@ tpsip_connection_shut_down (TpBaseConnection *base)
/* We disposed of the REGISTER handle in the disconnected method */
g_assert (priv->register_op == NULL);
- g_assert (priv->sofia != NULL);
-
- /* Detach the Sofia adapter and let it destroy the NUA handle and itself
- * in the shutdown callback. If there's no NUA stack, destroy it manually. */
- priv->sofia->conn = NULL;
-
if (priv->sofia_nua != NULL)
- nua_shutdown (priv->sofia_nua);
- else
- tpsip_connection_sofia_destroy (priv->sofia);
+ nua_shutdown (priv->sofia_nua);
- priv->sofia = NULL;
priv->sofia_nua = NULL;
tp_base_connection_finish_shutdown (base);
@@ -705,18 +1071,14 @@ tpsip_connection_start_connecting (TpBaseConnection *base,
{
TpsipConnection *self = TPSIP_CONNECTION (base);
TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (self);
- su_root_t *sofia_root;
TpHandleRepoIface *contact_repo;
const gchar *sip_address;
const url_t *local_url;
g_assert (base->status == TP_INTERNAL_CONNECTION_STATUS_NEW);
- g_assert (priv->sofia != NULL);
-
/* the construct parameters will be non-empty */
- sofia_root = priv->sofia->sofia_root;
- g_assert (sofia_root != NULL);
+ g_assert (priv->sofia_root != NULL);
g_return_val_if_fail (priv->address != NULL, FALSE);
/* FIXME: we should defer setting the self handle until we've found out from
@@ -746,9 +1108,9 @@ tpsip_connection_start_connecting (TpBaseConnection *base,
local_url = tpsip_conn_get_local_url (self);
/* step: create stack instance */
- priv->sofia_nua = nua_create (sofia_root,
+ priv->sofia_nua = nua_create (priv->sofia_root,
tpsip_connection_sofia_callback,
- priv->sofia,
+ self,
SOATAG_AF(SOA_AF_IP4_IP6),
SIPTAG_FROM_STR(sip_address),
NUTAG_URL(local_url),
@@ -781,12 +1143,24 @@ tpsip_connection_start_connecting (TpBaseConnection *base,
else if (priv->discover_stun)
tpsip_conn_discover_stun_server (self);
- DEBUG("Sofia-SIP NUA at address %p (SIP URI: %s)",
- priv->sofia_nua, sip_address);
+ DEBUG("initialized a Sofia-SIP NUA at address %p", priv->sofia_nua);
/* for debugging purposes, request a dump of stack configuration
* at registration time */
- nua_get_params(priv->sofia_nua, TAG_ANY(), TAG_NULL());
+ nua_get_params (priv->sofia_nua, TAG_ANY(), TAG_NULL());
+
+ g_signal_connect (self,
+ "nua-event::nua_i_invite",
+ G_CALLBACK (tpsip_connection_nua_i_invite_cb),
+ NULL);
+ g_signal_connect (self,
+ "nua-event::nua_i_message",
+ G_CALLBACK (tpsip_connection_nua_i_message_cb),
+ NULL);
+ g_signal_connect (self,
+ "nua-event::nua_r_register",
+ G_CALLBACK (tpsip_connection_nua_r_register_cb),
+ NULL);
priv->register_op = tpsip_conn_create_register_handle (self,
base->self_handle);
@@ -797,9 +1171,9 @@ tpsip_connection_start_connecting (TpBaseConnection *base,
return FALSE;
}
- nua_register(priv->register_op, TAG_NULL());
+ tpsip_event_target_attach (priv->register_op, (GObject *) self);
- DEBUG("exit");
+ nua_register (priv->register_op, TAG_NULL());
return TRUE;
}
@@ -839,7 +1213,7 @@ tpsip_connection_disconnected (TpBaseConnection *base)
*/
static void
tpsip_connection_get_interfaces (TpSvcConnection *iface,
- DBusGMethodInvocation *context)
+ DBusGMethodInvocation *context)
{
TpsipConnection *self = TPSIP_CONNECTION (iface);
TpBaseConnection *base = (TpBaseConnection *)self;
@@ -975,3 +1349,8 @@ conn_iface_init(gpointer g_iface, gpointer iface_data)
IMPLEMENT(request_handles);
#undef IMPLEMENT
}
+
+static void
+event_target_iface_init (gpointer g_iface, gpointer iface_data)
+{
+}
diff --git a/src/sip-connection.h b/src/sip-connection.h
index 0af3775..6ddadb7 100644
--- a/src/sip-connection.h
+++ b/src/sip-connection.h
@@ -22,16 +22,15 @@
#define __TPSIP_CONNECTION_H__
#include <glib-object.h>
-#include <dbus/dbus-glib.h>
#include <telepathy-glib/base-connection.h>
-G_BEGIN_DECLS
+#include <tpsip/event-target.h>
-typedef struct _TpsipConnection TpsipConnection;
-typedef struct _TpsipConnectionClass TpsipConnectionClass;
-typedef struct _TpsipConnectionPrivate TpsipConnectionPrivate;
+G_BEGIN_DECLS
+
+#define TPSIP_DEFAULT_STUN_PORT 3478
typedef enum
{
@@ -42,6 +41,9 @@ typedef enum
TPSIP_CONNECTION_KEEPALIVE_STUN, /** Maintain registration with STUN as described in IETF draft-sip-outbound */
} TpsipConnectionKeepaliveMechanism;
+typedef struct _TpsipConnection TpsipConnection;
+typedef struct _TpsipConnectionClass TpsipConnectionClass;
+typedef struct _TpsipConnectionPrivate TpsipConnectionPrivate;
struct _TpsipConnectionClass {
TpBaseConnectionClass parent_class;
@@ -51,10 +53,6 @@ struct _TpsipConnection {
TpBaseConnection parent;
};
-GType tpsip_connection_get_type(void);
-
-#define TPSIP_DEFAULT_STUN_PORT 3478
-
/* TYPE MACROS */
#define TPSIP_TYPE_CONNECTION \
(tpsip_connection_get_type())
@@ -69,6 +67,11 @@ GType tpsip_connection_get_type(void);
#define TPSIP_CONNECTION_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), TPSIP_TYPE_CONNECTION, TpsipConnectionClass))
+GType tpsip_connection_get_type (void) G_GNUC_CONST;
+
+void tpsip_connection_connect_auth_handler (TpsipConnection *self,
+ TpsipEventTarget *target);
+
G_END_DECLS
#endif /* #ifndef __TPSIP_CONNECTION_H__*/
diff --git a/src/sip-media-channel.c b/src/sip-media-channel.c
index 5f211a2..67862f5 100644
--- a/src/sip-media-channel.c
+++ b/src/sip-media-channel.c
@@ -24,6 +24,7 @@
*/
#include <stdlib.h>
+#include <string.h>
#include <telepathy-glib/channel-iface.h>
#include <telepathy-glib/dbus.h>
@@ -33,6 +34,8 @@
#include <telepathy-glib/intset.h>
#include <telepathy-glib/svc-channel.h>
+#include <tpsip/event-target.h>
+
/* Hold and CallState interfaces */
#include <tpsip-extensions/extensions.h>
@@ -46,6 +49,7 @@
#define DEBUG_FLAG TPSIP_DEBUG_MEDIA
#include "debug.h"
+static void event_target_init (gpointer, gpointer);
static void channel_iface_init (gpointer, gpointer);
static void media_signalling_iface_init (gpointer, gpointer);
static void streamed_media_iface_init (gpointer, gpointer);
@@ -56,6 +60,7 @@ static void hold_iface_init (gpointer, gpointer);
G_DEFINE_TYPE_WITH_CODE (TpsipMediaChannel, tpsip_media_channel,
G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (TPSIP_TYPE_EVENT_TARGET, event_target_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
priv_group_mixin_iface_init);
@@ -141,7 +146,7 @@ tpsip_media_channel_init (TpsipMediaChannel *self)
static GObject *
tpsip_media_channel_constructor (GType type, guint n_props,
- GObjectConstructParam *props)
+ GObjectConstructParam *props)
{
GObject *obj;
TpsipMediaChannelPrivate *priv;
@@ -488,18 +493,6 @@ tpsip_media_channel_close (TpsipMediaChannel *obj)
return;
}
-void
-tpsip_media_channel_terminated (TpsipMediaChannel *self)
-{
- TpsipMediaChannelPrivate *priv = TPSIP_MEDIA_CHANNEL_GET_PRIVATE (self);
-
- DEBUG("enter");
-
- if (priv->session)
- tpsip_media_session_change_state (priv->session,
- TPSIP_MEDIA_SESSION_STATE_ENDED);
-}
-
/**
* tpsip_media_channel_get_channel_type
*
@@ -851,36 +844,21 @@ tpsip_media_channel_receive_invite (TpsipMediaChannel *self,
TP_CHANNEL_GROUP_FLAG_CAN_ADD);
}
-/**
- * Handle an incoming re-INVITE request.
- */
-void
-tpsip_media_channel_receive_reinvite (TpsipMediaChannel *self)
+static gboolean
+priv_nua_i_invite_cb (TpsipMediaChannel *self,
+ const TpsipNuaEvent *ev,
+ tagi_t tags[],
+ gpointer foo)
{
TpsipMediaChannelPrivate *priv = TPSIP_MEDIA_CHANNEL_GET_PRIVATE (self);
- g_return_if_fail (priv->session != NULL);
-
- tpsip_media_session_receive_reinvite (priv->session);
-}
-
-gboolean
-tpsip_media_channel_set_remote_media (TpsipMediaChannel *chan,
- const sdp_session_t* r_sdp)
-{
- TpsipMediaChannelPrivate *priv = TPSIP_MEDIA_CHANNEL_GET_PRIVATE (chan);
+ /* nua_i_invite delivered for a bound handle means a re-INVITE */
g_return_val_if_fail (priv->session != NULL, FALSE);
- return tpsip_media_session_set_remote_media (priv->session, r_sdp);
-}
+ tpsip_media_session_receive_reinvite (priv->session);
-void
-tpsip_media_channel_ready (TpsipMediaChannel *self)
-{
- TpsipMediaChannelPrivate *priv = TPSIP_MEDIA_CHANNEL_GET_PRIVATE (self);
- g_return_if_fail (priv->session != NULL);
- tpsip_media_session_accept (priv->session);
+ return TRUE;
}
static void
@@ -960,22 +938,25 @@ priv_clear_call_state (TpsipMediaChannel *self,
0);
}
-void
-tpsip_media_channel_call_status (TpsipMediaChannel *self,
- guint status,
- const char* message)
+static gboolean
+priv_nua_r_invite_cb (TpsipMediaChannel *self,
+ const TpsipNuaEvent *ev,
+ tagi_t tags[],
+ gpointer foo)
{
TpsipMediaChannelPrivate *priv = TPSIP_MEDIA_CHANNEL_GET_PRIVATE (self);
+ gint status = ev->status;
+ const gchar *message = ev->text;
TpHandle peer;
- DEBUG("peer responded with %u %s", status, message);
+ DEBUG("peer responded with %03d %s", status, message);
- g_return_if_fail (priv->session != NULL);
+ g_return_val_if_fail (priv->session != NULL, FALSE);
/* Ignore responses to a re-INVITE */
if (tpsip_media_session_get_state (priv->session)
!= TPSIP_MEDIA_SESSION_STATE_INVITE_SENT)
- return;
+ return TRUE;
peer = tpsip_media_session_get_peer (priv->session);
@@ -995,20 +976,42 @@ tpsip_media_channel_call_status (TpsipMediaChannel *self,
{
priv_set_call_state (self, peer, TPSIP_CHANNEL_CALL_STATE_QUEUED);
}
+
+ return TRUE;
}
-void
-tpsip_media_channel_peer_cancel (TpsipMediaChannel *self,
- guint cause,
- const gchar *message)
+static gboolean
+priv_nua_i_cancel_cb (TpsipMediaChannel *self,
+ const TpsipNuaEvent *ev,
+ tagi_t tags[],
+ gpointer foo)
{
TpsipMediaChannelPrivate *priv = TPSIP_MEDIA_CHANNEL_GET_PRIVATE (self);
TpGroupMixin *mixin = TP_GROUP_MIXIN (self);
TpIntSet *set;
TpHandle actor = 0;
TpHandle peer;
+ const sip_reason_t *reason;
+ guint cause = 0;
+ const gchar *message = NULL;
- g_return_if_fail (priv->session != NULL);
+ g_return_val_if_fail (priv->session != NULL, FALSE);
+
+ if (ev->sip != NULL)
+ for (reason = ev->sip->sip_reason;
+ reason != NULL;
+ reason = reason->re_next)
+ {
+ const char *protocol = reason->re_protocol;
+ if (protocol == NULL || strcmp (protocol, "SIP") != 0)
+ continue;
+ if (reason->re_cause != NULL)
+ {
+ cause = (guint) g_ascii_strtoull (reason->re_cause, NULL, 10);
+ message = reason->re_text;
+ break;
+ }
+ }
peer = tpsip_media_session_get_peer (priv->session);
@@ -1040,11 +1043,67 @@ tpsip_media_channel_peer_cancel (TpsipMediaChannel *self,
TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
tp_intset_destroy (set);
+
+ return TRUE;
}
-/***********************************************************************
- * Set: Helper functions follow (not based on generated templates)
- ***********************************************************************/
+static gboolean
+priv_nua_i_state_cb (TpsipMediaChannel *self,
+ const TpsipNuaEvent *ev,
+ tagi_t tags[],
+ gpointer foo)
+{
+ TpsipMediaChannelPrivate *priv = TPSIP_MEDIA_CHANNEL_GET_PRIVATE (self);
+ const sdp_session_t *r_sdp = NULL;
+ int offer_recv = 0;
+ int answer_recv = 0;
+ int ss_state = nua_callstate_init;
+
+ g_return_val_if_fail (priv->session != NULL, FALSE);
+
+ tl_gets(tags,
+ NUTAG_CALLSTATE_REF(ss_state),
+ NUTAG_OFFER_RECV_REF(offer_recv),
+ NUTAG_ANSWER_RECV_REF(answer_recv),
+ SOATAG_REMOTE_SDP_REF(r_sdp),
+ TAG_END());
+
+ if (r_sdp)
+ {
+ g_return_val_if_fail (answer_recv || offer_recv, FALSE);
+ if (!tpsip_media_session_set_remote_media (priv->session, r_sdp))
+ {
+ tpsip_media_channel_close (self);
+ return TRUE;
+ }
+ }
+
+ DEBUG("call with handle %p is %s", ev->nua_handle, nua_callstate_name (ss_state));
+
+ switch ((enum nua_callstate)ss_state)
+ {
+ case nua_callstate_received:
+ case nua_callstate_early:
+ break;
+
+ case nua_callstate_completing:
+ /* In auto-ack mode, we don't need to call nua_ack(), see NUTAG_AUTOACK() */
+ break;
+
+ case nua_callstate_ready:
+ tpsip_media_session_accept (priv->session);
+ break;
+
+ case nua_callstate_terminated:
+ tpsip_media_session_change_state (priv->session,
+ TPSIP_MEDIA_SESSION_STATE_ENDED);
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
static void priv_session_state_changed_cb (TpsipMediaSession *session,
guint old_state,
@@ -1132,6 +1191,36 @@ static void priv_session_state_changed_cb (TpsipMediaSession *session,
tp_intset_destroy (set);
}
+static void
+priv_connect_nua_handlers (TpsipMediaChannel *self, nua_handle_t *nh)
+{
+ TpsipMediaChannelPrivate *priv = TPSIP_MEDIA_CHANNEL_GET_PRIVATE (self);
+
+ tpsip_event_target_attach (nh, (GObject *) self);
+
+ /* have the connection handle authentication, before all other
+ * response callbacks */
+ tpsip_connection_connect_auth_handler (priv->conn, TPSIP_EVENT_TARGET (self));
+
+ g_signal_connect (self,
+ "nua-event::nua_r_invite",
+ G_CALLBACK (priv_nua_r_invite_cb),
+ NULL);
+ g_signal_connect (self,
+ "nua-event::nua_i_invite",
+ G_CALLBACK (priv_nua_i_invite_cb),
+ NULL);
+ g_signal_connect (self,
+ "nua-event::nua_i_cancel",
+ G_CALLBACK (priv_nua_i_cancel_cb),
+ NULL);
+ g_signal_connect (self,
+ "nua-event::nua_i_state",
+ G_CALLBACK (priv_nua_i_state_cb),
+ NULL);
+
+}
+
/**
* priv_create_session:
*
@@ -1152,11 +1241,15 @@ priv_create_session (TpsipMediaChannel *channel,
DEBUG("enter");
priv = TPSIP_MEDIA_CHANNEL_GET_PRIVATE (channel);
- g_assert (priv->session == NULL);
conn = (TpBaseConnection *)(priv->conn);
contact_repo = tp_base_connection_get_handles (conn,
TP_HANDLE_TYPE_CONTACT);
+ g_assert (priv->session == NULL);
+
+ /* Bind the channel object to the handle to handle NUA events */
+ priv_connect_nua_handlers (channel, nh);
+
object_path = g_strdup_printf ("%s/MediaSession%u", priv->object_path, peer);
DEBUG("allocating session, peer=%u", peer);
@@ -1507,6 +1600,11 @@ tpsip_media_channel_stop_tone (TpSvcChannelInterfaceDTMF *iface,
}
static void
+event_target_init(gpointer g_iface, gpointer iface_data)
+{
+}
+
+static void
channel_iface_init(gpointer g_iface, gpointer iface_data)
{
TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface;
diff --git a/src/sip-media-channel.h b/src/sip-media-channel.h
index 7be6bab..8aae83a 100644
--- a/src/sip-media-channel.h
+++ b/src/sip-media-channel.h
@@ -27,8 +27,7 @@
#include <telepathy-glib/group-mixin.h>
#include <telepathy-glib/properties-mixin.h>
-#include "sip-sofia-decls.h"
-
+#include <tpsip/sofia-decls.h>
#include <sofia-sip/sdp.h>
@@ -72,20 +71,9 @@ void tpsip_media_channel_close (TpsipMediaChannel *self);
* Additional declarations (not based on generated templates)
***********************************************************************/
-void tpsip_media_channel_terminated (TpsipMediaChannel *self);
void tpsip_media_channel_receive_invite (TpsipMediaChannel *self,
- nua_handle_t *nh,
- TpHandle handle);
-void tpsip_media_channel_receive_reinvite (TpsipMediaChannel *self);
-gboolean tpsip_media_channel_set_remote_media (TpsipMediaChannel *chan,
- const sdp_session_t *r_sdp);
-void tpsip_media_channel_ready (TpsipMediaChannel *self);
-void tpsip_media_channel_call_status (TpsipMediaChannel *self,
- guint status,
- const char* message);
-void tpsip_media_channel_peer_cancel (TpsipMediaChannel *self,
- guint cause,
- const char* text);
+ nua_handle_t *nh,
+ TpHandle handle);
G_END_DECLS
diff --git a/src/sip-media-session.c b/src/sip-media-session.c
index 281dc82..1a2d8b9 100644
--- a/src/sip-media-session.c
+++ b/src/sip-media-session.c
@@ -188,21 +188,6 @@ tpsip_media_session_constructor (GType type, guint n_props,
constructor (type, n_props, props);
priv = TPSIP_MEDIA_SESSION_GET_PRIVATE (TPSIP_MEDIA_SESSION (obj));
- if (priv->nua_op)
- {
- nua_hmagic_t *nua_op_magic;
-
- g_assert (priv->channel != NULL);
-
- /* migrating a NUA handle between two active media channels
- * makes no sense either */
- nua_op_magic = nua_handle_magic (priv->nua_op);
- g_return_val_if_fail (nua_op_magic == NULL || nua_op_magic == priv->channel, NULL);
-
- /* tell the NUA that we're handling this call */
- nua_handle_bind (priv->nua_op, priv->channel);
- }
-
bus = tp_get_bus ();
dbus_g_connection_register_g_object (bus, priv->object_path, obj);
@@ -583,10 +568,9 @@ tpsip_media_session_change_state (TpsipMediaSession *session,
break;
case TPSIP_MEDIA_SESSION_STATE_ENDED:
priv_close_all_streams (session);
- DEBUG("freeing the NUA handle %p", priv->nua_op);
+ DEBUG("releasing the NUA handle %p", priv->nua_op);
if (priv->nua_op != NULL)
{
- nua_handle_bind (priv->nua_op, TPSIP_NH_EXPIRED);
nua_handle_unref (priv->nua_op);
priv->nua_op = NULL;
}
@@ -723,8 +707,6 @@ void tpsip_media_session_terminate (TpsipMediaSession *session)
if (priv->nua_op != NULL)
{
- g_assert (nua_handle_magic (priv->nua_op) == priv->channel);
-
switch (priv->state)
{
case TPSIP_MEDIA_SESSION_STATE_ACTIVE:
diff --git a/src/sip-text-channel.c b/src/sip-text-channel.c
index 9413f17..9aaf410 100644
--- a/src/sip-text-channel.c
+++ b/src/sip-text-channel.c
@@ -28,9 +28,6 @@
#include <string.h>
#include <time.h>
-#include <sofia-sip/sip.h>
-#include <sofia-sip/sip_header.h>
-
#include <telepathy-glib/channel-iface.h>
#include <telepathy-glib/dbus.h>
#include <telepathy-glib/errors.h>
@@ -38,18 +35,31 @@
#include <telepathy-glib/interfaces.h>
#include <telepathy-glib/svc-channel.h>
+#include <tpsip/event-target.h>
+
#include "sip-text-channel.h"
#include "sip-connection.h"
#include "sip-connection-helpers.h"
+#include <sofia-sip/sip.h>
+#include <sofia-sip/sip_header.h>
+
#define DEBUG_FLAG TPSIP_DEBUG_IM
#include "debug.h"
+static gboolean
+tpsip_text_channel_nua_r_message_cb (TpsipTextChannel *self,
+ const TpsipNuaEvent *ev,
+ tagi_t tags[],
+ gpointer foo);
+
+static void event_target_iface_init (gpointer, gpointer);
static void channel_iface_init (gpointer, gpointer);
static void text_iface_init (gpointer, gpointer);
G_DEFINE_TYPE_WITH_CODE (TpsipTextChannel, tpsip_text_channel, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (TPSIP_TYPE_EVENT_TARGET, event_target_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, text_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL));
@@ -154,6 +164,13 @@ GObject *tpsip_text_channel_constructor(GType type,
props);
priv = TPSIP_TEXT_CHANNEL_GET_PRIVATE(TPSIP_TEXT_CHANNEL(obj));
+ tpsip_connection_connect_auth_handler (priv->conn, TPSIP_EVENT_TARGET (obj));
+
+ g_signal_connect (obj,
+ "nua-event::nua_r_message",
+ G_CALLBACK (tpsip_text_channel_nua_r_message_cb),
+ NULL);
+
bus = tp_get_bus();
dbus_g_connection_register_g_object(bus, priv->object_path, obj);
@@ -598,9 +615,9 @@ tpsip_text_channel_list_pending_messages(TpSvcChannelTypeText *iface,
*/
static void
tpsip_text_channel_send(TpSvcChannelTypeText *iface,
- guint type,
- const gchar *text,
- DBusGMethodInvocation *context)
+ guint type,
+ const gchar *text,
+ DBusGMethodInvocation *context)
{
TpsipTextChannel *self = TPSIP_TEXT_CHANNEL(iface);
TpsipTextChannelPrivate *priv = TPSIP_TEXT_CHANNEL_GET_PRIVATE (self);
@@ -619,11 +636,16 @@ tpsip_text_channel_send(TpSvcChannelTypeText *iface,
return;
}
- /* XXX: would it be helpful to bind the channel, or the
- * TpsipTextPendingMessage, or something, to the NH? */
-
msg_nh = tpsip_conn_create_request_handle (priv->conn, priv->handle);
- g_assert (msg_nh != NULL);
+ if (msg_nh == NULL)
+ {
+ GError e = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "Request creation failed" };
+ dbus_g_method_return_error (context, &e);
+ return;
+ }
+
+ tpsip_event_target_attach (msg_nh, (GObject *) self);
nua_message(msg_nh,
SIPTAG_CONTENT_TYPE_STR("text/plain"),
@@ -641,39 +663,43 @@ tpsip_text_channel_send(TpSvcChannelTypeText *iface,
tp_svc_channel_type_text_return_from_send (context);
}
-void
-tpsip_text_channel_emit_message_status(TpsipTextChannel *obj,
- nua_handle_t *nh,
- int status)
+static gboolean
+tpsip_text_channel_nua_r_message_cb (TpsipTextChannel *self,
+ const TpsipNuaEvent *ev,
+ tagi_t tags[],
+ gpointer foo)
{
- TpsipTextChannelPrivate *priv = TPSIP_TEXT_CHANNEL_GET_PRIVATE (obj);
+ TpsipTextChannelPrivate *priv = TPSIP_TEXT_CHANNEL_GET_PRIVATE (self);
TpsipTextPendingMessage *msg;
TpChannelTextSendError send_error;
GList *node;
DEBUG("enter");
- node = g_queue_find_custom(priv->messages_to_be_acknowledged, nh,
- tpsip_acknowledged_messages_compare);
+ node = g_queue_find_custom (priv->messages_to_be_acknowledged,
+ ev->nua_handle,
+ tpsip_acknowledged_messages_compare);
/* Shouldn't happen... */
- if (!node) {
- g_warning ("message not found");
- return;
- }
-
+ if (node == NULL)
+ {
+ g_warning ("message pending sent acknowledgement not found");
+ return FALSE;
+ }
+
msg = (TpsipTextPendingMessage *)node->data;
- g_return_if_fail (msg != NULL);
+ g_assert (msg != NULL);
- if (status >= 200 && status < 300)
+ if (ev->status >= 200 && ev->status < 300)
{
- tp_svc_channel_type_text_emit_sent ((TpSvcChannelTypeText *)obj,
+ DEBUG("message with timestamp %u delivered", (guint)msg->timestamp);
+ tp_svc_channel_type_text_emit_sent (self,
msg->timestamp, msg->type, msg->text);
}
else
{
- switch (status)
+ switch (ev->status)
{
case 401:
case 403:
@@ -709,14 +735,14 @@ tpsip_text_channel_emit_message_status(TpsipTextChannel *obj,
send_error = TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
}
- DEBUG("emitting send error %d %s", (int)send_error, msg->text);
-
- tp_svc_channel_type_text_emit_send_error ((TpSvcChannelTypeText *)obj,
+ tp_svc_channel_type_text_emit_send_error (self,
send_error, msg->timestamp, msg->type, msg->text);
}
g_queue_remove(priv->messages_to_be_acknowledged, msg);
_tpsip_text_pending_free(msg);
+
+ return TRUE;
}
void tpsip_text_channel_receive(TpsipTextChannel *chan,
@@ -755,6 +781,11 @@ void tpsip_text_channel_receive(TpsipTextChannel *chan,
}
static void
+event_target_iface_init (gpointer g_iface, gpointer iface_data)
+{
+}
+
+static void
channel_iface_init(gpointer g_iface, gpointer iface_data)
{
TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface;
diff --git a/src/sip-text-channel.h b/src/sip-text-channel.h
index d925e9f..0bddbfb 100644
--- a/src/sip-text-channel.h
+++ b/src/sip-text-channel.h
@@ -25,7 +25,7 @@
#include <telepathy-glib/handle.h>
-#include "sip-sofia-decls.h"
+#include <tpsip/sofia-decls.h>
typedef struct _TpsipHandleStorage TpsipHandleStorage;
@@ -61,13 +61,9 @@ GType tpsip_text_channel_get_type(void);
void tpsip_text_channel_close (TpsipTextChannel *self);
-void tpsip_text_channel_emit_message_status(TpsipTextChannel *obj,
- nua_handle_t *nh,
- int status);
-
void tpsip_text_channel_receive (TpsipTextChannel *obj,
- TpHandle sender,
- const char *message);
+ TpHandle sender,
+ const char *message);
G_END_DECLS