diff options
author | Mikhail Zabaluev <mikhail.zabaluev@nokia.com> | 2008-03-18 14:17:48 +0000 |
---|---|---|
committer | Mikhail Zabaluev <mikhail.zabaluev@nokia.com> | 2008-03-18 14:17:48 +0000 |
commit | 58ad259426f96c968df350c44a3125413e54a658 (patch) | |
tree | 3181b43271c74db481911462de1f30bab9b723c9 | |
parent | 0ad211d26d996833d8e7eb449e5d5ef7c0bbca22 (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.am | 2 | ||||
-rw-r--r-- | src/debug.c | 1 | ||||
-rw-r--r-- | src/debug.h | 1 | ||||
-rw-r--r-- | src/sip-connection-helpers.c | 7 | ||||
-rw-r--r-- | src/sip-connection-helpers.h | 2 | ||||
-rw-r--r-- | src/sip-connection-manager.c | 22 | ||||
-rw-r--r-- | src/sip-connection-private.h | 5 | ||||
-rw-r--r-- | src/sip-connection-sofia.c | 926 | ||||
-rw-r--r-- | src/sip-connection-sofia.h | 27 | ||||
-rw-r--r-- | src/sip-connection.c | 451 | ||||
-rw-r--r-- | src/sip-connection.h | 21 | ||||
-rw-r--r-- | src/sip-media-channel.c | 202 | ||||
-rw-r--r-- | src/sip-media-channel.h | 18 | ||||
-rw-r--r-- | src/sip-media-session.c | 20 | ||||
-rw-r--r-- | src/sip-text-channel.c | 89 | ||||
-rw-r--r-- | src/sip-text-channel.h | 10 |
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 |