diff options
author | Mikhail Zabaluev <mikhail.zabaluev@nokia.com> | 2011-01-03 17:45:15 +0200 |
---|---|---|
committer | Mikhail Zabaluev <mikhail.zabaluev@nokia.com> | 2011-01-03 17:45:15 +0200 |
commit | 248b7a0c9b0efac2653c3e2659cbec1eda01d238 (patch) | |
tree | 4a272fbf3aa1ccc8d0c3cf496b0e1ca994692911 /src | |
parent | c89168ecb97280c6e1f732c659c800af832bd7ab (diff) | |
parent | 6ef183a45800b000fe1c402255e822963f90dc0c (diff) |
Merge branch 'master' into messages-retouch
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/media-factory.c | 11 | ||||
-rw-r--r-- | src/protocol.c | 527 | ||||
-rw-r--r-- | src/protocol.h | 75 | ||||
-rw-r--r-- | src/sip-connection-helpers.c | 28 | ||||
-rw-r--r-- | src/sip-connection-helpers.h | 5 | ||||
-rw-r--r-- | src/sip-connection-manager.c | 428 | ||||
-rw-r--r-- | src/sip-connection-private.h | 3 | ||||
-rw-r--r-- | src/sip-connection.c | 181 | ||||
-rw-r--r-- | src/sip-connection.h | 2 | ||||
-rw-r--r-- | src/sip-media-channel.c | 8 | ||||
-rw-r--r-- | src/sip-media-session.c | 28 | ||||
-rw-r--r-- | src/sip-media-stream.c | 21 | ||||
-rw-r--r-- | src/sip-text-channel.c | 6 | ||||
-rw-r--r-- | src/text-factory.c | 9 | ||||
-rw-r--r-- | src/write-mgr-file.c | 374 |
16 files changed, 1176 insertions, 532 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 6684513..ca15a87 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -92,6 +92,8 @@ libtpsip_convenience_la_SOURCES = \ debug.c \ media-factory.h \ media-factory.c \ + protocol.h \ + protocol.c \ text-factory.h \ text-factory.c \ sip-connection-helpers.h \ diff --git a/src/media-factory.c b/src/media-factory.c index 689d773..f4573c5 100644 --- a/src/media-factory.c +++ b/src/media-factory.c @@ -451,15 +451,15 @@ static const gchar * const named_channel_allowed_properties[] = { NULL }; -/* not advertised in foreach_channel_class - can only be requested with +/* not advertised in type_foreach_channel_class - can only be requested with * RequestChannel, not with CreateChannel/EnsureChannel */ static const gchar * const anon_channel_allowed_properties[] = { NULL }; static void -tpsip_media_factory_foreach_channel_class (TpChannelManager *manager, - TpChannelManagerChannelClassFunc func, +tpsip_media_factory_type_foreach_channel_class (GType type, + TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal, @@ -476,7 +476,7 @@ tpsip_media_factory_foreach_channel_class (TpChannelManager *manager, handle_type_value); g_value_set_uint (handle_type_value, TP_HANDLE_TYPE_CONTACT); - func (manager, table, named_channel_allowed_properties, user_data); + func (type, table, named_channel_allowed_properties, user_data); g_hash_table_destroy (table); } @@ -688,7 +688,8 @@ channel_manager_iface_init (gpointer g_iface, TpChannelManagerIface *iface = g_iface; iface->foreach_channel = tpsip_media_factory_foreach_channel; - iface->foreach_channel_class = tpsip_media_factory_foreach_channel_class; + iface->type_foreach_channel_class = + tpsip_media_factory_type_foreach_channel_class; iface->request_channel = tpsip_media_factory_request_channel; iface->create_channel = tpsip_media_factory_create_channel; iface->ensure_channel = tpsip_media_factory_ensure_channel; diff --git a/src/protocol.c b/src/protocol.c new file mode 100644 index 0000000..d9c2383 --- /dev/null +++ b/src/protocol.c @@ -0,0 +1,527 @@ +/* + * protocol.c - source for TpsipProtocol + * Copyright (C) 2007-2010 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "protocol.h" + +#include <string.h> + +#include <telepathy-glib/telepathy-glib.h> +#include <dbus/dbus-protocol.h> +#include <dbus/dbus-glib.h> + +#include <tpsip/sofia-decls.h> +#include <sofia-sip/su_glib.h> + +#define DEBUG_FLAG TPSIP_DEBUG_CONNECTION +#include "debug.h" +#include "media-factory.h" +#include "sip-connection.h" +#include "sip-connection-helpers.h" +#include "text-factory.h" + +#define PROTOCOL_NAME "sip" +#define ICON_NAME "im-" PROTOCOL_NAME +#define VCARD_FIELD_NAME "x-" PROTOCOL_NAME +#define ENGLISH_NAME "SIP" + +G_DEFINE_TYPE (TpsipProtocol, + tpsip_protocol, + TP_TYPE_BASE_PROTOCOL) + +enum { + PROP_SOFIA_ROOT = 1, +}; + +struct _TpsipProtocolPrivate +{ + su_root_t *sofia_root; +}; + +/* Used in the otherwise-unused offset field of the TpCMParamSpec. The first + * one is nonzero to catch implicit zero-initialization. */ +enum { + PARAM_EASY = 1, + PARAM_SET_SEPARATELY +}; + +static TpCMParamSpec tpsip_params[] = { + /* Account (a sip: URI) + * + * FIXME: validate account SIP URI properly, using appropriate RFCs */ + { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER, + NULL, PARAM_SET_SEPARATELY, tp_cm_param_filter_string_nonempty, NULL }, + + /* Username to register with, if different than in the account URI */ + { "auth-user", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + 0, NULL, PARAM_EASY }, + + /* Password */ + { "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_SECRET, NULL, PARAM_EASY }, + + /* Display name for self */ + { "alias", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, PARAM_EASY, + /* setting a 0-length alias makes no sense */ + tp_cm_param_filter_string_nonempty, NULL }, + + /* Registrar */ + { "registrar", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, + PARAM_EASY }, + + /* Used to compose proxy URI */ + { "proxy-host", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, + PARAM_SET_SEPARATELY }, + { "port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(SIP_DEFAULT_PORT), + PARAM_SET_SEPARATELY, tp_cm_param_filter_uint_nonzero }, + { "transport", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "auto", PARAM_SET_SEPARATELY }, + + /* Enables loose routing as per RFC 3261 */ + { "loose-routing", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(FALSE), + PARAM_EASY }, + + /* Used to enable proactive NAT traversal techniques */ + { "discover-binding", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(TRUE), + PARAM_EASY }, + + /* Mechanism used for connection keepalive maintenance */ + { "keepalive-mechanism", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "auto", PARAM_SET_SEPARATELY }, + + /* Keep-alive interval */ + { "keepalive-interval", DBUS_TYPE_UINT32_AS_STRING, G_TYPE_UINT, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(0), PARAM_EASY }, + + /* Use SRV DNS lookup to discover STUN server */ + { "discover-stun", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(TRUE), PARAM_EASY }, + + /* STUN server */ + { "stun-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, + PARAM_EASY }, + + /* STUN port */ + { "stun-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, + GUINT_TO_POINTER(TPSIP_DEFAULT_STUN_PORT), PARAM_EASY, + tp_cm_param_filter_uint_nonzero }, + + /* If the session content cannot be modified once initially set up */ + { "immutable-streams", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, + TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(FALSE), + PARAM_EASY }, + + /* Local IP address to use, workaround purposes only */ + { "local-ip-address", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + 0, NULL, PARAM_EASY }, + + /* Local port for SIP, workaround purposes only */ + { "local-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, 0, NULL, + PARAM_EASY }, + + /* Extra-realm authentication */ + { "extra-auth-user", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + 0, NULL, PARAM_EASY }, + { "extra-auth-password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_SECRET, NULL, PARAM_EASY }, + + { NULL } +}; + +static void +tpsip_protocol_init (TpsipProtocol *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TPSIP_TYPE_PROTOCOL, + TpsipProtocolPrivate); +} + +static const TpCMParamSpec * +get_parameters (TpBaseProtocol *self G_GNUC_UNUSED) +{ + return tpsip_params; +} + +static TpsipConnectionKeepaliveMechanism +priv_parse_keepalive (const gchar *str) +{ + if (str == NULL || strcmp (str, "auto") == 0) + return TPSIP_CONNECTION_KEEPALIVE_AUTO; + if (strcmp (str, "register") == 0) + return TPSIP_CONNECTION_KEEPALIVE_REGISTER; + if (strcmp (str, "options") == 0) + return TPSIP_CONNECTION_KEEPALIVE_OPTIONS; + if (strcmp (str, "stun") == 0) + return TPSIP_CONNECTION_KEEPALIVE_STUN; + if (strcmp (str, "off") == 0) + return TPSIP_CONNECTION_KEEPALIVE_NONE; + + WARNING ("unsupported keepalive-method value \"%s\", falling back to auto", str); + return TPSIP_CONNECTION_KEEPALIVE_AUTO; +} + +static gchar * +priv_compose_proxy_uri (const gchar *host, + const gchar *transport, + guint port) +{ + const gchar *scheme = "sip"; + + if (host == NULL) + return NULL; + + /* Set scheme to SIPS if transport is TLS */ + + if (transport != NULL && !g_ascii_strcasecmp (transport, "tls")) + scheme = "sips"; + + /* Format the resulting URI */ + + if (port) + return g_strdup_printf ("%s:%s:%u", scheme, host, port); + else + return g_strdup_printf ("%s:%s", scheme, host); +} + +/** + * Returns a default SIP proxy address based on the public + * SIP address 'sip_address' and . For instance + * "sip:first.surname@company.com" would result in "sip:company.com". + * The SIP stack will further utilize DNS lookups to find the IP address + * for the SIP server responsible for the domain "company.com". + */ +static gchar * +priv_compose_default_proxy_uri (const gchar *sip_address, + const gchar *transport) +{ + char *result = NULL; + char *host; + char *found; + + g_return_val_if_fail (sip_address != NULL, NULL); + + /* skip sip and sips prefixes, updating transport if necessary */ + found = strchr (sip_address, ':'); + if (found != NULL) { + if (g_ascii_strncasecmp ("sip:", sip_address, 4) == 0) + ; + else if (g_ascii_strncasecmp ("sips:", sip_address, 5) == 0) + { + if (transport == NULL || + strcmp (transport, "auto") == 0) + transport = "tls"; + } + else + { + /* error, unknown URI prefix */ + return NULL; + } + + sip_address = found + 1; + } + + /* skip userinfo */ + found = strchr (sip_address, '@'); + if (found != NULL) + sip_address = found + 1; + + /* copy rest of the string */ + host = g_strdup (sip_address); + + /* mark end (before uri-parameters defs or headers) */ + found = strchr (host, ';'); + if (found == NULL) + found = strchr (host, '?'); + if (found != NULL) + *found = '\0'; + + result = priv_compose_proxy_uri (host, transport, 0); + + g_free (host); + + return result; +} + +static TpBaseConnection * +new_connection (TpBaseProtocol *protocol, + GHashTable *params, + GError **error) +{ + TpsipProtocol *self = TPSIP_PROTOCOL (protocol); + TpsipConnection *conn; + guint i; + const gchar *account; + const gchar *transport; + const gchar *proxy_host; + guint16 port; + gchar *proxy; + TpsipConnectionKeepaliveMechanism keepalive_mechanism; + + account = tp_asv_get_string (params, "account"); + transport = tp_asv_get_string (params, "transport"); + port = tp_asv_get_uint32 (params, "port", NULL); + + conn = g_object_new (TPSIP_TYPE_CONNECTION, + "protocol", PROTOCOL_NAME, + "sofia-root", self->priv->sofia_root, + "address", account, + NULL); + + proxy_host = tp_asv_get_string (params, "proxy-host"); + + if (tp_str_empty (proxy_host)) + { + proxy = priv_compose_default_proxy_uri (account, transport); + DEBUG("set outbound proxy address to <%s>, based on <%s>", proxy, + account); + } + else + { + proxy = priv_compose_proxy_uri (proxy_host, transport, port); + } + + g_object_set (conn, + "proxy", proxy, + NULL); + g_free (proxy); + + if (!tp_str_empty (transport) && tp_strdiff (transport, "auto")) + g_object_set (conn, + "transport", transport, + NULL); + + for (i = 0; tpsip_params[i].name != NULL; i++) + { + if (tpsip_params[i].offset == PARAM_SET_SEPARATELY) + { + DEBUG ("Parameter %s is handled specially", tpsip_params[i].name); + continue; + } + + g_assert (tpsip_params[i].offset == PARAM_EASY); + + switch (tpsip_params[i].gtype) + { + case G_TYPE_STRING: + { + const gchar *s = tp_asv_get_string (params, + tpsip_params[i].name); + + if (!tp_str_empty (s)) + g_object_set (conn, + tpsip_params[i].name, s, + NULL); + } + break; + + case G_TYPE_UINT: + { + gboolean valid = FALSE; + guint u = tp_asv_get_uint32 (params, + tpsip_params[i].name, &valid); + + if (valid) + g_object_set (conn, + tpsip_params[i].name, u, + NULL); + } + break; + + case G_TYPE_BOOLEAN: + { + gboolean valid = FALSE; + gboolean b = tp_asv_get_boolean (params, tpsip_params[i].name, + &valid); + + if (valid) + g_object_set (conn, + tpsip_params[i].name, b, + NULL); + } + break; + + default: + /* no other parameters have been written yet */ + g_assert_not_reached (); + } + } + + keepalive_mechanism = priv_parse_keepalive (tp_asv_get_string (params, + "keepalive-mechanism")); + g_object_set (conn, + "keepalive-mechanism", keepalive_mechanism, + NULL); + + return TP_BASE_CONNECTION (conn); +} + +static gchar * +normalize_contact (TpBaseProtocol *self G_GNUC_UNUSED, + const gchar *contact, + GError **error) +{ + return tpsip_normalize_contact (contact, NULL, NULL, error); +} + +static gchar * +identify_account (TpBaseProtocol *self G_GNUC_UNUSED, + GHashTable *asv, + GError **error) +{ + const gchar *account = tp_asv_get_string (asv, "account"); + + g_assert (account != NULL); + return g_strdup (account); +} + +static GStrv +get_interfaces (TpBaseProtocol *self) +{ + return g_new0 (gchar *, 1); +} + +static void +get_connection_details (TpBaseProtocol *self, + GStrv *connection_interfaces, + GType **channel_managers, + gchar **icon_name, + gchar **english_name, + gchar **vcard_field) +{ + if (connection_interfaces != NULL) + { + *connection_interfaces = g_strdupv ( + (GStrv) tpsip_connection_get_implemented_interfaces ()); + } + + if (channel_managers != NULL) + { + GType types[] = { + TPSIP_TYPE_TEXT_FACTORY, + TPSIP_TYPE_MEDIA_FACTORY, + G_TYPE_INVALID }; + + *channel_managers = g_memdup (types, sizeof(types)); + } + + if (icon_name != NULL) + { + *icon_name = g_strdup (ICON_NAME); + } + + if (vcard_field != NULL) + { + *vcard_field = g_strdup (VCARD_FIELD_NAME); + } + + if (english_name != NULL) + { + *english_name = g_strdup (ENGLISH_NAME); + } +} + +static GStrv +dup_authentication_types (TpBaseProtocol *base) +{ + const gchar * const types[] = { + TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, + NULL + }; + + return g_strdupv ((GStrv) types); +} + +static void +tpsip_protocol_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + TpsipProtocol *self = TPSIP_PROTOCOL (object); + + switch (property_id) + { + case PROP_SOFIA_ROOT: + g_value_set_pointer (value, self->priv->sofia_root); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tpsip_protocol_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + TpsipProtocol *self = TPSIP_PROTOCOL (object); + + switch (property_id) + { + case PROP_SOFIA_ROOT: + self->priv->sofia_root = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tpsip_protocol_class_init (TpsipProtocolClass *klass) +{ + TpBaseProtocolClass *base_class = (TpBaseProtocolClass *) klass; + GObjectClass *object_class = (GObjectClass *) klass; + GParamSpec *param_spec; + + g_type_class_add_private (klass, sizeof (TpsipProtocolPrivate)); + + base_class->get_parameters = get_parameters; + base_class->new_connection = new_connection; + base_class->normalize_contact = normalize_contact; + base_class->identify_account = identify_account; + base_class->get_interfaces = get_interfaces; + base_class->get_connection_details = get_connection_details; + base_class->dup_authentication_types = dup_authentication_types; + + object_class->get_property = tpsip_protocol_get_property; + object_class->set_property = tpsip_protocol_set_property; + + param_spec = g_param_spec_pointer ("sofia-root", "Sofia-SIP root", + "the root object for Sofia-SIP", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_SOFIA_ROOT, + param_spec); +} + +TpBaseProtocol * +tpsip_protocol_new (su_root_t *sofia_root) +{ + return g_object_new (TPSIP_TYPE_PROTOCOL, + "name", PROTOCOL_NAME, + "sofia-root", sofia_root, + NULL); +} diff --git a/src/protocol.h b/src/protocol.h new file mode 100644 index 0000000..7470af1 --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,75 @@ +/* + * protocol.h - header for TpsipProtocol + * Copyright (C) 2007-2010 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef TPSIP_PROTOCOL_H +#define TPSIP_PROTOCOL_H + +#include <glib-object.h> +#include <telepathy-glib/base-protocol.h> + +#include <tpsip/sofia-decls.h> +#include <sofia-sip/su_glib.h> + +G_BEGIN_DECLS + +typedef struct _TpsipProtocol TpsipProtocol; +typedef struct _TpsipProtocolPrivate TpsipProtocolPrivate; +typedef struct _TpsipProtocolClass TpsipProtocolClass; +typedef struct _TpsipProtocolClassPrivate TpsipProtocolClassPrivate; + +struct _TpsipProtocolClass { + TpBaseProtocolClass parent_class; + + TpsipProtocolClassPrivate *priv; +}; + +struct _TpsipProtocol { + TpBaseProtocol parent; + + TpsipProtocolPrivate *priv; +}; + +GType tpsip_protocol_get_type (void); + +#define TPSIP_TYPE_PROTOCOL \ + (tpsip_protocol_get_type ()) +#define TPSIP_PROTOCOL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TPSIP_TYPE_PROTOCOL, \ + TpsipProtocol)) +#define TPSIP_PROTOCOL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + TPSIP_TYPE_PROTOCOL, \ + TpsipProtocolClass)) +#define TPSIP_IS_PROTOCOL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + TPSIP_TYPE_PROTOCOL)) +#define TPSIP_PROTOCOL_GET_CLASS(klass) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TPSIP_TYPE_PROTOCOL, \ + TpsipProtocolClass)) + +gchar *tpsip_protocol_normalize_contact (const gchar *id, + GError **error); + +TpBaseProtocol *tpsip_protocol_new (su_root_t *sofia_root); + +G_END_DECLS + +#endif diff --git a/src/sip-connection-helpers.c b/src/sip-connection-helpers.c index 0fda819..8ad9b42 100644 --- a/src/sip-connection-helpers.c +++ b/src/sip-connection-helpers.c @@ -775,14 +775,11 @@ priv_lowercase_url_part (su_home_t *home, const char *src) #define TPSIP_RESERVED_CHARS_ALLOWED_IN_USERNAME "!*'()&=+$,;?/" gchar * -tpsip_handle_normalize (TpHandleRepoIface *repo, - const gchar *sipuri, - gpointer context, - GError **error) +tpsip_normalize_contact (const gchar *sipuri, + const url_t *base_url, + const gchar *transport, + GError **error) { - TpsipConnection *conn = TPSIP_CONNECTION (context); - TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (conn); - const url_t *base_url = priv->account_url; su_home_t home[1] = { SU_HOME_INIT(home) }; url_t *url; gchar *retval = NULL; @@ -827,8 +824,8 @@ tpsip_handle_normalize (TpHandleRepoIface *repo, { /* Set the scheme to SIP or SIPS accordingly to the connection's * transport preference */ - if (priv->transport != NULL - && g_ascii_strcasecmp (priv->transport, "tls") == 0) + if (transport != NULL + && g_ascii_strcasecmp (transport, "tls") == 0) { url->url_type = url_sips; url->url_scheme = "sips"; @@ -879,6 +876,19 @@ error: return retval; } +gchar * +tpsip_handle_normalize (TpHandleRepoIface *repo, + const gchar *sipuri, + gpointer context, + GError **error) +{ + TpsipConnection *conn = TPSIP_CONNECTION (context); + TpsipConnectionPrivate *priv = TPSIP_CONNECTION_GET_PRIVATE (conn); + + return tpsip_normalize_contact (sipuri, priv->account_url, priv->transport, + error); +} + static GQuark tpsip_handle_url_quark () { diff --git a/src/sip-connection-helpers.h b/src/sip-connection-helpers.h index 327c13c..4644908 100644 --- a/src/sip-connection-helpers.h +++ b/src/sip-connection-helpers.h @@ -66,6 +66,11 @@ gchar * tpsip_handle_normalize (TpHandleRepoIface *repo, gpointer context, GError **error); +gchar *tpsip_normalize_contact (const gchar *sipuri, + const url_t *base_url, + const gchar *transport, + GError **error); + const url_t* tpsip_conn_get_contact_url (TpsipConnection *conn, TpHandle handle); diff --git a/src/sip-connection-manager.c b/src/sip-connection-manager.c index 7a33481..ecb882e 100644 --- a/src/sip-connection-manager.c +++ b/src/sip-connection-manager.c @@ -38,6 +38,7 @@ #include <tpsip/sofia-decls.h> #include <sofia-sip/su_glib.h> +#include "protocol.h" #include "sip-connection-manager.h" #include "sip-connection.h" @@ -48,165 +49,6 @@ G_DEFINE_TYPE(TpsipConnectionManager, tpsip_connection_manager, TP_TYPE_BASE_CONNECTION_MANAGER) -/* private structure *//* typedef struct _TpsipConnectionManagerPrivate TpsipConnectionManagerPrivate; */ - -typedef struct { - gchar *account; - gchar *auth_user; - gchar *password; - gchar *alias; - gchar *registrar; - gchar *proxy_host; - guint port; - gchar *transport; - gboolean loose_routing; - gboolean discover_binding; - gchar *keepalive_mechanism; - guint keepalive_interval; - gboolean discover_stun; - gchar *stun_server; - guint stun_port; - gboolean immutable_streams; - gchar *local_ip_address; - guint local_port; - gchar *extra_auth_user; - gchar *extra_auth_password; -} TpsipConnParams; - -static void * -alloc_params (void) -{ - return g_slice_new0 (TpsipConnParams); -} - -static void -free_params (void *p) -{ - TpsipConnParams *params = (TpsipConnParams *)p; - - g_free (params->account); - g_free (params->auth_user); - g_free (params->password); - g_free (params->alias); - g_free (params->registrar); - g_free (params->proxy_host); - g_free (params->transport); - g_free (params->keepalive_mechanism); - g_free (params->stun_server); - g_free (params->local_ip_address); - g_free (params->extra_auth_user); - g_free (params->extra_auth_password); - - g_slice_free (TpsipConnParams, params); -} - -enum { - TPSIP_CONN_PARAM_ACCOUNT = 0, - TPSIP_CONN_PARAM_AUTH_USER, - TPSIP_CONN_PARAM_PASSWORD, - TPSIP_CONN_PARAM_ALIAS, - TPSIP_CONN_PARAM_REGISTRAR, - TPSIP_CONN_PARAM_PROXY_HOST, - TPSIP_CONN_PARAM_PORT, - TPSIP_CONN_PARAM_TRANSPORT, - TPSIP_CONN_PARAM_LOOSE_ROUTING, - TPSIP_CONN_PARAM_DISCOVER_BINDING, - TPSIP_CONN_PARAM_KEEPALIVE_MECHANISM, - TPSIP_CONN_PARAM_KEEPALIVE_INTERVAL, - TPSIP_CONN_PARAM_DISCOVER_STUN, - TPSIP_CONN_PARAM_STUN_SERVER, - TPSIP_CONN_PARAM_STUN_PORT, - TPSIP_CONN_PARAM_IMMUTABLE_STREAMS, - TPSIP_CONN_PARAM_LOCAL_IP_ADDRESS, - TPSIP_CONN_PARAM_LOCAL_PORT, - TPSIP_CONN_PARAM_EXTRA_AUTH_USER, - TPSIP_CONN_PARAM_EXTRA_AUTH_PASSWORD, - N_TPSIP_CONN_PARAMS -}; - -static const TpCMParamSpec tpsip_params[] = { - /* Account (a sip: URI) */ - { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER, - NULL, G_STRUCT_OFFSET (TpsipConnParams, account) }, - /* Username to register with, if different than in the account URI */ - { "auth-user", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, auth_user) }, - /* Password */ - { "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_SECRET, - NULL, G_STRUCT_OFFSET (TpsipConnParams, password) }, - /* Display name for self */ - { "alias", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, - G_STRUCT_OFFSET(TpsipConnParams, alias), - /* setting a 0-length alias makes no sense */ - tp_cm_param_filter_string_nonempty, NULL }, - /* Registrar */ - { "registrar", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, registrar) }, - /* Used to compose proxy URI */ - { "proxy-host", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, proxy_host) }, - /* Used to compose proxy URI */ - { "port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(SIP_DEFAULT_PORT), - G_STRUCT_OFFSET (TpsipConnParams, port) }, - /* Used to compose proxy URI */ - { "transport", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "auto", - G_STRUCT_OFFSET (TpsipConnParams, transport) }, - /* Enables loose routing as per RFC 3261 */ - { "loose-routing", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(FALSE), - G_STRUCT_OFFSET (TpsipConnParams, loose_routing) }, - /* Used to enable proactive NAT traversal techniques */ - { "discover-binding", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(TRUE), - G_STRUCT_OFFSET (TpsipConnParams, discover_binding) }, - /* Mechanism used for connection keepalive maintenance */ - { "keepalive-mechanism", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, "auto", - G_STRUCT_OFFSET (TpsipConnParams, keepalive_mechanism) }, - /* Keep-alive interval */ - { "keepalive-interval", DBUS_TYPE_UINT32_AS_STRING, G_TYPE_UINT, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(0), - G_STRUCT_OFFSET (TpsipConnParams, keepalive_interval) }, - /* Use SRV DNS lookup to discover STUN server */ - { "discover-stun", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(TRUE), - G_STRUCT_OFFSET (TpsipConnParams, discover_stun) }, - /* STUN server */ - { "stun-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, stun_server) }, - /* STUN port */ - { "stun-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, - GUINT_TO_POINTER(TPSIP_DEFAULT_STUN_PORT), - G_STRUCT_OFFSET (TpsipConnParams, stun_port) }, - /* If the session content cannot be modified once initially set up */ - { "immutable-streams", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GUINT_TO_POINTER(FALSE), - G_STRUCT_OFFSET (TpsipConnParams, immutable_streams) }, - /* Local IP address to use, workaround purposes only */ - { "local-ip-address", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, local_ip_address) }, - /* Local port for SIP, workaround purposes only */ - { "local-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, local_port) }, - /* Extra-realm authentication */ - { "extra-auth-user", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET (TpsipConnParams, extra_auth_user) }, - { "extra-auth-password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_SECRET, - NULL, G_STRUCT_OFFSET (TpsipConnParams, extra_auth_password) }, - { NULL, NULL, 0, 0, NULL, 0 } -}; - -const TpCMProtocolSpec tpsip_protocols[] = { - { "sip", tpsip_params, alloc_params, free_params }, - { NULL, NULL } -}; - struct _TpsipConnectionManagerPrivate { su_root_t *sofia_root; @@ -237,10 +79,24 @@ tpsip_connection_manager_init (TpsipConnectionManager *obj) #endif } +static void +tpsip_connection_manager_constructed (GObject *object) +{ + TpsipConnectionManager *self = TPSIP_CONNECTION_MANAGER (object); + TpBaseConnectionManager *base = (TpBaseConnectionManager *) self; + void (*constructed) (GObject *) = + ((GObjectClass *) tpsip_connection_manager_parent_class)->constructed; + TpBaseProtocol *protocol; + + if (constructed != NULL) + constructed (object); + + protocol = tpsip_protocol_new (self->priv->sofia_root); + tp_base_connection_manager_add_protocol (base, protocol); + g_object_unref (protocol); +} + static void tpsip_connection_manager_finalize (GObject *object); -static TpBaseConnection *tpsip_connection_manager_new_connection ( - TpBaseConnectionManager *base, const gchar *proto, - TpIntSet *params_present, void *parsed_params, GError **error); static void tpsip_connection_manager_class_init (TpsipConnectionManagerClass *klass) @@ -251,11 +107,10 @@ tpsip_connection_manager_class_init (TpsipConnectionManagerClass *klass) g_type_class_add_private (klass, sizeof (TpsipConnectionManagerPrivate)); + object_class->constructed = tpsip_connection_manager_constructed; object_class->finalize = tpsip_connection_manager_finalize; - base_class->new_connection = tpsip_connection_manager_new_connection; base_class->cm_dbus_name = "sofiasip"; - base_class->protocol_params = tpsip_protocols; } void @@ -279,248 +134,3 @@ tpsip_connection_manager_finalize (GObject *object) G_OBJECT_CLASS (tpsip_connection_manager_parent_class)->finalize (object); } - -static gchar * -priv_compose_proxy_uri (const gchar *host, - const gchar *transport, - guint port) -{ - const gchar *scheme = "sip"; - - if (host == NULL) - return NULL; - - /* Set scheme to SIPS if transport is TLS */ - - if (transport != NULL && !g_ascii_strcasecmp (transport, "tls")) - scheme = "sips"; - - /* Format the resulting URI */ - - if (port) - return g_strdup_printf ("%s:%s:%u", scheme, host, port); - else - return g_strdup_printf ("%s:%s", scheme, host); -} - -/** - * Returns a default SIP proxy address based on the public - * SIP address 'sip_address' and . For instance - * "sip:first.surname@company.com" would result in "sip:company.com". - * The SIP stack will further utilize DNS lookups to find the IP address - * for the SIP server responsible for the domain "company.com". - */ -static gchar * -priv_compose_default_proxy_uri (const gchar *sip_address, - const gchar *transport) -{ - char *result = NULL; - char *host; - char *found; - - g_return_val_if_fail (sip_address != NULL, NULL); - - /* skip sip and sips prefixes, updating transport if necessary */ - found = strchr (sip_address, ':'); - if (found != NULL) { - if (g_ascii_strncasecmp ("sip:", sip_address, 4) == 0) - ; - else if (g_ascii_strncasecmp ("sips:", sip_address, 5) == 0) - { - if (transport == NULL || - strcmp (transport, "auto") == 0) - transport = "tls"; - } - else - { - /* error, unknown URI prefix */ - return NULL; - } - - sip_address = found + 1; - } - - /* skip userinfo */ - found = strchr (sip_address, '@'); - if (found != NULL) - sip_address = found + 1; - - /* copy rest of the string */ - host = g_strdup (sip_address); - - /* mark end (before uri-parameters defs or headers) */ - found = strchr (host, ';'); - if (found == NULL) - found = strchr (host, '?'); - if (found != NULL) - *found = '\0'; - - result = priv_compose_proxy_uri (host, transport, 0); - - g_free (host); - - return result; -} - -static TpsipConnectionKeepaliveMechanism -priv_parse_keepalive (const gchar *str) -{ - if (str == NULL || strcmp (str, "auto") == 0) - return TPSIP_CONNECTION_KEEPALIVE_AUTO; - if (strcmp (str, "register") == 0) - return TPSIP_CONNECTION_KEEPALIVE_REGISTER; - if (strcmp (str, "options") == 0) - return TPSIP_CONNECTION_KEEPALIVE_OPTIONS; - if (strcmp (str, "stun") == 0) - return TPSIP_CONNECTION_KEEPALIVE_STUN; - if (strcmp (str, "off") == 0) - return TPSIP_CONNECTION_KEEPALIVE_NONE; - - WARNING ("unsupported keepalive-method value \"%s\", falling back to auto", str); - return TPSIP_CONNECTION_KEEPALIVE_AUTO; -} - -#define SET_PROPERTY_IF_PARAM_SET(prop, param, member) \ - if (tp_intset_is_member (params_present, param)) \ - { \ - g_object_set (connection, prop, member, NULL); \ - } - -#define NULLIFY_IF_EMPTY(param) \ - if ((param) != NULL && (param)[0] == '\0') \ - param = NULL; - -static gboolean -check_not_empty_if_present (const gchar *name, - const gchar *value, - GError **error) -{ - if (value != NULL && value[0] == '\0') - { - g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "If supplied, '%s' account parameter may not be empty", name); - return FALSE; - } - return TRUE; -} - -static TpBaseConnection * -tpsip_connection_manager_new_connection (TpBaseConnectionManager *base, - const gchar *proto, - TpIntSet *params_present, - void *parsed_params, - GError **error) -{ - 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; - TpsipConnectionKeepaliveMechanism keepalive_mechanism; - - if (strcmp (proto, "sip")) { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, - "This connection manager only implements protocol 'sip', not '%s'", - proto); - return NULL; - } - - /* TpBaseConnectionManager code has already checked that required params - * are present (but not that they are non-empty, if we're using >= 0.5.8) - */ - g_assert (params->account); - - /* FIXME: validate account SIP URI properly, using appropriate RFCs */ - if (!check_not_empty_if_present ("account", params->account, error)) - return FALSE; - NULLIFY_IF_EMPTY (params->auth_user); - NULLIFY_IF_EMPTY (params->password); - /* FIXME: validate registrar SIP URI properly, using appropriate RFCs */ - NULLIFY_IF_EMPTY (params->registrar); - /* FIXME: validate proxy host properly */ - NULLIFY_IF_EMPTY (params->proxy_host); - /* FIXME: check against the list (which presumably exists) of valid - * transports */ - NULLIFY_IF_EMPTY (params->transport); - /* FIXME: check against the list (which presumably exists) of valid - * KA mechanisms */ - NULLIFY_IF_EMPTY (params->keepalive_mechanism); - /* FIXME: validate STUN server properly */ - NULLIFY_IF_EMPTY (params->stun_server); - /* FIXME: validate local IP address properly */ - NULLIFY_IF_EMPTY (params->local_ip_address); - NULLIFY_IF_EMPTY (params->extra_auth_user); - NULLIFY_IF_EMPTY (params->extra_auth_password); - - DEBUG("New SIP connection to %s", params->account); - - connection = (TpBaseConnection *)g_object_new(TPSIP_TYPE_CONNECTION, - "protocol", "sip", - "sofia-root", priv->sofia_root, - "address", params->account, - NULL); - - if (params->proxy_host == NULL) { - proxy = priv_compose_default_proxy_uri (params->account, - params->transport); - DEBUG("set outbound proxy address to <%s>, based on <%s>", proxy, params->account); - } else - proxy = priv_compose_proxy_uri (params->proxy_host, - params->transport, - params->port); - - g_object_set (connection, "proxy", proxy, NULL); - g_free (proxy); - - if (params->transport != NULL && strcmp (params->transport, "auto") != 0) - g_object_set (connection, "transport", params->transport, NULL); - - SET_PROPERTY_IF_PARAM_SET ("auth-user", TPSIP_CONN_PARAM_AUTH_USER, - params->auth_user); - - SET_PROPERTY_IF_PARAM_SET ("password", TPSIP_CONN_PARAM_PASSWORD, - params->password); - - SET_PROPERTY_IF_PARAM_SET ("alias", TPSIP_CONN_PARAM_ALIAS, - params->alias); - - SET_PROPERTY_IF_PARAM_SET ("registrar", TPSIP_CONN_PARAM_REGISTRAR, - params->registrar); - - SET_PROPERTY_IF_PARAM_SET ("loose-routing", TPSIP_CONN_PARAM_LOOSE_ROUTING, - params->loose_routing); - - SET_PROPERTY_IF_PARAM_SET ("discover-binding", TPSIP_CONN_PARAM_DISCOVER_BINDING, - params->discover_binding); - - SET_PROPERTY_IF_PARAM_SET ("discover-stun", TPSIP_CONN_PARAM_DISCOVER_STUN, - params->discover_stun); - - SET_PROPERTY_IF_PARAM_SET ("stun-server", TPSIP_CONN_PARAM_STUN_SERVER, - params->stun_server); - - SET_PROPERTY_IF_PARAM_SET ("stun-port", TPSIP_CONN_PARAM_STUN_PORT, - params->stun_port); - - SET_PROPERTY_IF_PARAM_SET ("immutable-streams", TPSIP_CONN_PARAM_IMMUTABLE_STREAMS, - params->immutable_streams); - - SET_PROPERTY_IF_PARAM_SET ("keepalive-interval", - TPSIP_CONN_PARAM_KEEPALIVE_INTERVAL, params->keepalive_interval); - - keepalive_mechanism = priv_parse_keepalive (params->keepalive_mechanism); - g_object_set (connection, "keepalive-mechanism", keepalive_mechanism, NULL); - - SET_PROPERTY_IF_PARAM_SET ("local-ip-address", TPSIP_CONN_PARAM_LOCAL_IP_ADDRESS, - params->local_ip_address); - - SET_PROPERTY_IF_PARAM_SET ("local-port", TPSIP_CONN_PARAM_LOCAL_PORT, - params->local_port); - - SET_PROPERTY_IF_PARAM_SET ("extra-auth-user", TPSIP_CONN_PARAM_EXTRA_AUTH_USER, - params->extra_auth_user); - SET_PROPERTY_IF_PARAM_SET ("extra-auth-password", TPSIP_CONN_PARAM_EXTRA_AUTH_PASSWORD, - params->extra_auth_password); - - return connection; -} diff --git a/src/sip-connection-private.h b/src/sip-connection-private.h index 6a7efcf..2250694 100644 --- a/src/sip-connection-private.h +++ b/src/sip-connection-private.h @@ -27,6 +27,8 @@ #include <tpsip/sofia-decls.h> #include <sofia-sip/sresolv.h> +#include <telepathy-glib/simple-password-manager.h> + #ifdef HAVE_LIBIPHB #include <libiphb.h> #endif @@ -51,6 +53,7 @@ struct _TpsipConnectionPrivate gchar *registrar_realm; TpsipMediaFactory *media_factory; + TpSimplePasswordManager *password_manager; gchar *address; gchar *auth_user; diff --git a/src/sip-connection.c b/src/sip-connection.c index bc175cf..b515699 100644 --- a/src/sip-connection.c +++ b/src/sip-connection.c @@ -160,6 +160,10 @@ tpsip_connection_create_channel_managers (TpBaseConnection *conn) "connection", self, NULL); g_ptr_array_add (channel_managers, priv->media_factory); + priv->password_manager = tp_simple_password_manager_new ( + conn); + g_ptr_array_add (channel_managers, priv->password_manager); + return channel_managers; } @@ -422,15 +426,22 @@ static void tpsip_connection_shut_down (TpBaseConnection *base); static gboolean tpsip_connection_start_connecting (TpBaseConnection *base, GError **error); +static const gchar *interfaces_always_present[] = { + TP_IFACE_CONNECTION_INTERFACE_REQUESTS, + TP_IFACE_CONNECTION_INTERFACE_CONTACTS, + TP_IFACE_CONNECTION_INTERFACE_ALIASING, + NULL }; + +const gchar ** +tpsip_connection_get_implemented_interfaces (void) +{ + /* we don't have any conditionally-implemented interfaces */ + return interfaces_always_present; +} + static void tpsip_connection_class_init (TpsipConnectionClass *klass) { - static const gchar *interfaces_always_present[] = { - TP_IFACE_CONNECTION_INTERFACE_REQUESTS, - TP_IFACE_CONNECTION_INTERFACE_CONTACTS, - TP_IFACE_CONNECTION_INTERFACE_ALIASING, - NULL }; - GObjectClass *object_class = G_OBJECT_CLASS (klass); TpBaseConnectionClass *base_class = (TpBaseConnectionClass *)klass; @@ -593,6 +604,54 @@ tpsip_connection_class_init (TpsipConnectionClass *klass) G_STRUCT_OFFSET (TpsipConnectionClass, contacts_class)); } +typedef struct { + TpsipConnection* self; + nua_handle_t *nh; + gchar *method; + gchar *realm; + gchar *user; +} PrivHandleAuthData; + +static PrivHandleAuthData * +priv_handle_auth_data_new (TpsipConnection* self, + nua_handle_t *nh, + const gchar *method, + const gchar *realm, + const gchar *user) +{ + PrivHandleAuthData *data = g_slice_new (PrivHandleAuthData); + + data->self = g_object_ref (self); + data->nh = nua_handle_ref (nh); + data->method = g_strdup (method); + data->realm = g_strdup (realm); + data->user = g_strdup (user); + + return data; +} + +static void +priv_handle_auth_data_free (PrivHandleAuthData *data) +{ + g_object_unref (data->self); + nua_handle_unref (data->nh); + g_free (data->method); + g_free (data->realm); + g_free (data->user); + + g_slice_free (PrivHandleAuthData, data); +} + +static void priv_password_manager_prompt_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data); +static void priv_handle_auth_continue (TpsipConnection* self, + nua_handle_t *nh, + const gchar *method, + const gchar *realm, + const gchar *user, + const gchar *password); + static gboolean priv_handle_auth (TpsipConnection* self, int status, @@ -607,7 +666,6 @@ priv_handle_auth (TpsipConnection* self, const char *realm = NULL; const char *user = NULL; const char *password = NULL; - gchar *auth = NULL; if (status != 401 && status != 407) return FALSE; @@ -635,6 +693,12 @@ priv_handle_auth (TpsipConnection* self, return FALSE; } + if (method == NULL) + { + WARNING ("no method presented for authentication"); + return FALSE; + } + /* step: determine which set of credentials to use */ if (home_realm) { @@ -668,6 +732,9 @@ priv_handle_auth (TpsipConnection* self, /* fall back to the main username */ user = priv->auth_user; password = priv->extra_auth_password; + if (password == NULL) + /* note that this prevents asking the user for a password */ + password = ""; DEBUG("using the extra auth credentials"); } @@ -678,37 +745,101 @@ priv_handle_auth (TpsipConnection* self, if (sipfrom && sipfrom->a_url[0].url_user) /* or use the userpart in "From" header */ user = sipfrom->a_url[0].url_user; + else + return FALSE; } if (password == NULL) - password = ""; + { + PrivHandleAuthData *data = NULL; + + DEBUG("asking the user for a password."); + data = priv_handle_auth_data_new (self, nh, method, realm, + user); + tp_simple_password_manager_prompt_async (priv->password_manager, + priv_password_manager_prompt_cb, data); + /* Promise that we'll handle it eventually, even if we end up just + * handling it with a blank password. */ + return TRUE; + } - /* 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); + priv_handle_auth_continue (self, nh, method, realm, + user, password); + return TRUE; +} - DEBUG("%s authenticating user='%s' realm=%s", - wa ? "server" : "proxy", user, realm); - } +static void +priv_password_manager_prompt_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + PrivHandleAuthData *data = user_data; + GError *error = NULL; + const GString *password_string; + const gchar *password; + + password_string = tp_simple_password_manager_prompt_finish ( + TP_SIMPLE_PASSWORD_MANAGER (source_object), result, &error); - if (auth == NULL) + if (error != NULL) { - WARNING ("authentication data are incomplete"); - return FALSE; + /* we promised to handle the auth challenge in priv_handle_auth() by + * returning TRUE, so we need to handle it anyway, even if it means + * doing it with a blank password. + */ + DEBUG ("Auth channel failed: %s. Using blank password.", error->message); + + password = ""; + + g_error_free (error); } + else + { + TpsipConnectionPrivate *priv = + TPSIP_CONNECTION_GET_PRIVATE (data->self); + + password = password_string->str; + /* also save it for later. */ + g_free (priv->password); + priv->password = g_strdup (password); + } + + priv_handle_auth_continue (data->self, data->nh, data->method, data->realm, + data->user, password); + + priv_handle_auth_data_free (data); +} + +static void +priv_handle_auth_continue (TpsipConnection* self, + nua_handle_t *nh, + const gchar *method, + const gchar *realm, + const gchar *user, + const gchar *password) +{ + gchar *auth = NULL; + + /* step: if all info is available, create an authorization response */ + g_assert (realm != NULL); + g_assert (method != NULL); + g_assert (user != NULL); + g_assert (password != NULL); + + 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", + method, user, realm); /* step: authenticate */ nua_authenticate(nh, NUTAG_AUTH(auth), TAG_END()); g_free (auth); - - return TRUE; } static gboolean diff --git a/src/sip-connection.h b/src/sip-connection.h index ac025ca..2280be9 100644 --- a/src/sip-connection.h +++ b/src/sip-connection.h @@ -80,6 +80,8 @@ GType tpsip_connection_get_type (void) G_GNUC_CONST; void tpsip_connection_connect_auth_handler (TpsipConnection *self, TpsipEventTarget *target); +const gchar **tpsip_connection_get_implemented_interfaces (void); + G_END_DECLS #endif /* #ifndef __TPSIP_CONNECTION_H__*/ diff --git a/src/sip-media-channel.c b/src/sip-media-channel.c index c6d6df5..8f896ed 100644 --- a/src/sip-media-channel.c +++ b/src/sip-media-channel.c @@ -189,7 +189,7 @@ tpsip_media_channel_constructed (GObject *obj) TpBaseConnection *conn = (TpBaseConnection *)(priv->conn); GObjectClass *parent_object_class = G_OBJECT_CLASS (tpsip_media_channel_parent_class); - DBusGConnection *bus; + TpDBusDaemon *bus; TpHandleRepoIface *contact_repo; TpIntSet *set; @@ -203,10 +203,10 @@ tpsip_media_channel_constructed (GObject *obj) tp_handle_ref (contact_repo, priv->handle); /* register object on the bus */ - bus = tp_get_bus (); + bus = tp_base_connection_get_dbus_daemon (conn); DEBUG("registering object to dbus path=%s", priv->object_path); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); + tp_dbus_daemon_register_object (bus, priv->object_path, obj); /* initialize group mixin */ tp_group_mixin_init (obj, @@ -1622,6 +1622,8 @@ priv_create_session (TpsipMediaChannel *channel, NULL); session = g_object_new (TPSIP_TYPE_MEDIA_SESSION, + "dbus-daemon", + tp_base_connection_get_dbus_daemon (conn), "media-channel", channel, "object-path", object_path, "nua-handle", nh, diff --git a/src/sip-media-session.c b/src/sip-media-session.c index 73b7482..8272200 100644 --- a/src/sip-media-session.c +++ b/src/sip-media-session.c @@ -72,6 +72,7 @@ enum enum { PROP_MEDIA_CHANNEL = 1, + PROP_DBUS_DAEMON, PROP_OBJECT_PATH, PROP_NUA_OP, PROP_PEER, @@ -122,6 +123,7 @@ typedef struct _TpsipMediaSessionPrivate TpsipMediaSessionPrivate; struct _TpsipMediaSessionPrivate { + TpDBusDaemon *dbus_daemon; TpsipMediaChannel *channel; /* see gobj. prop. 'media-channel' */ gchar *object_path; /* see gobj. prop. 'object-path' */ nua_handle_t *nua_op; /* see gobj. prop. 'nua-handle' */ @@ -192,14 +194,13 @@ tpsip_media_session_constructor (GType type, guint n_props, { GObject *obj; TpsipMediaSessionPrivate *priv; - DBusGConnection *bus; obj = G_OBJECT_CLASS (tpsip_media_session_parent_class)-> constructor (type, n_props, props); priv = TPSIP_MEDIA_SESSION_GET_PRIVATE (TPSIP_MEDIA_SESSION (obj)); - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); + g_assert (TP_IS_DBUS_DAEMON (priv->dbus_daemon)); + tp_dbus_daemon_register_object (priv->dbus_daemon, priv->object_path, obj); return obj; } @@ -212,7 +213,11 @@ static void tpsip_media_session_get_property (GObject *object, TpsipMediaSession *session = TPSIP_MEDIA_SESSION (object); TpsipMediaSessionPrivate *priv = TPSIP_MEDIA_SESSION_GET_PRIVATE (session); - switch (property_id) { + switch (property_id) + { + case PROP_DBUS_DAEMON: + g_value_set_object (value, priv->dbus_daemon); + break; case PROP_MEDIA_CHANNEL: g_value_set_object (value, priv->channel); break; @@ -296,7 +301,12 @@ static void tpsip_media_session_set_property (GObject *object, TpsipMediaSession *session = TPSIP_MEDIA_SESSION (object); TpsipMediaSessionPrivate *priv = TPSIP_MEDIA_SESSION_GET_PRIVATE (session); - switch (property_id) { + switch (property_id) + { + case PROP_DBUS_DAEMON: + g_assert (priv->dbus_daemon == NULL); /* construct-only */ + priv->dbus_daemon = g_value_dup_object (value); + break; case PROP_MEDIA_CHANNEL: priv->channel = TPSIP_MEDIA_CHANNEL (g_value_get_object (value)); break; @@ -343,6 +353,11 @@ tpsip_media_session_class_init (TpsipMediaSessionClass *klass) object_class->dispose = tpsip_media_session_dispose; object_class->finalize = tpsip_media_session_finalize; + param_spec = g_param_spec_object ("dbus-daemon", "TpDBusDaemon", + "Connection to D-Bus.", TP_TYPE_DBUS_DAEMON, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_DBUS_DAEMON, param_spec); + param_spec = g_param_spec_object ("media-channel", "TpsipMediaChannel object", "SIP media channel object that owns this media session object" " (not reference counted).", @@ -442,6 +457,8 @@ tpsip_media_session_dispose (GObject *object) if (priv->glare_timer_id) g_source_remove (priv->glare_timer_id); + tp_clear_object (&priv->dbus_daemon); + if (G_OBJECT_CLASS (tpsip_media_session_parent_class)->dispose) G_OBJECT_CLASS (tpsip_media_session_parent_class)->dispose (object); @@ -2118,6 +2135,7 @@ tpsip_media_session_add_stream (TpsipMediaSession *self, direction &= ~TP_MEDIA_STREAM_DIRECTION_RECEIVE; stream = g_object_new (TPSIP_TYPE_MEDIA_STREAM, + "dbus-daemon", priv->dbus_daemon, "media-session", self, "media-type", media_type, "object-path", object_path, diff --git a/src/sip-media-stream.c b/src/sip-media-stream.c index 37e14df..d3826bb 100644 --- a/src/sip-media-stream.c +++ b/src/sip-media-stream.c @@ -84,6 +84,7 @@ static guint signals[SIG_LAST_SIGNAL] = {0}; enum { PROP_MEDIA_SESSION = 1, + PROP_DBUS_DAEMON, PROP_OBJECT_PATH, PROP_ID, PROP_MEDIA_TYPE, @@ -105,6 +106,7 @@ typedef struct _TpsipMediaStreamPrivate TpsipMediaStreamPrivate; struct _TpsipMediaStreamPrivate { + TpDBusDaemon *dbus_daemon; TpsipMediaSession *session; /* see gobj. prop. 'media-session' */ gchar *object_path; /* see gobj. prop. 'object-path' */ guint id; /* see gobj. prop. 'id' */ @@ -200,7 +202,6 @@ tpsip_media_stream_constructed (GObject *obj) TPSIP_MEDIA_STREAM (obj)); GObjectClass *parent_object_class = G_OBJECT_CLASS (tpsip_media_stream_parent_class); - DBusGConnection *bus; /* call base class method */ if (parent_object_class->constructed != NULL) @@ -220,8 +221,8 @@ tpsip_media_stream_constructed (GObject *obj) } /* go for the bus */ - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); + g_assert (TP_IS_DBUS_DAEMON (priv->dbus_daemon)); + tp_dbus_daemon_register_object (priv->dbus_daemon, priv->object_path, obj); } static void @@ -235,6 +236,9 @@ tpsip_media_stream_get_property (GObject *object, switch (property_id) { + case PROP_DBUS_DAEMON: + g_value_set_object (value, priv->dbus_daemon); + break; case PROP_MEDIA_SESSION: g_value_set_object (value, priv->session); break; @@ -288,6 +292,10 @@ tpsip_media_stream_set_property (GObject *object, switch (property_id) { + case PROP_DBUS_DAEMON: + g_assert (priv->dbus_daemon == NULL); /* construct-only */ + priv->dbus_daemon = g_value_dup_object (value); + break; case PROP_MEDIA_SESSION: priv->session = g_value_get_object (value); break; @@ -358,6 +366,11 @@ tpsip_media_stream_class_init (TpsipMediaStreamClass *klass) object_class->dispose = tpsip_media_stream_dispose; object_class->finalize = tpsip_media_stream_finalize; + param_spec = g_param_spec_object ("dbus-daemon", "TpDBusDaemon", + "Connection to D-Bus.", TP_TYPE_DBUS_DAEMON, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_DBUS_DAEMON, param_spec); + param_spec = g_param_spec_object ("media-session", "TpsipMediaSession object", "SIP media session object that owns this media stream object.", TPSIP_TYPE_MEDIA_SESSION, @@ -516,7 +529,7 @@ tpsip_media_stream_dispose (GObject *object) priv->dispose_has_run = TRUE; - /* release any references held by the object here */ + tp_clear_object (&priv->dbus_daemon); if (G_OBJECT_CLASS (tpsip_media_stream_parent_class)->dispose) G_OBJECT_CLASS (tpsip_media_stream_parent_class)->dispose (object); diff --git a/src/sip-text-channel.c b/src/sip-text-channel.c index ec0ed32..aeb0209 100644 --- a/src/sip-text-channel.c +++ b/src/sip-text-channel.c @@ -162,7 +162,7 @@ tpsip_text_channel_constructed (GObject *obj) TpsipTextChannelPrivate *priv; TpBaseConnection *base_conn; TpHandleRepoIface *contact_handles; - DBusGConnection *bus; + TpDBusDaemon *bus; TpChannelTextMessageType types[] = { TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, }; @@ -202,8 +202,8 @@ tpsip_text_channel_constructed (GObject *obj) TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_SUCCESSES, supported_content_types); - bus = tp_get_bus(); - dbus_g_connection_register_g_object(bus, priv->object_path, obj); + bus = tp_base_connection_get_dbus_daemon (base_conn); + tp_dbus_daemon_register_object (bus, priv->object_path, obj); } diff --git a/src/text-factory.c b/src/text-factory.c index 36730de..d62b2b6 100644 --- a/src/text-factory.c +++ b/src/text-factory.c @@ -328,8 +328,8 @@ static const gchar * const text_channel_allowed_properties[] = { }; static void -tpsip_text_factory_foreach_channel_class (TpChannelManager *manager, - TpChannelManagerChannelClassFunc func, +tpsip_text_factory_type_foreach_channel_class (GType type, + TpChannelManagerTypeChannelClassFunc func, gpointer user_data) { GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal, @@ -346,7 +346,7 @@ tpsip_text_factory_foreach_channel_class (TpChannelManager *manager, g_hash_table_insert (table, (gchar *) text_channel_fixed_properties[1], value); - func (manager, table, text_channel_allowed_properties, user_data); + func (type, table, text_channel_allowed_properties, user_data); g_hash_table_destroy (table); } @@ -635,7 +635,8 @@ channel_manager_iface_init (gpointer g_iface, gpointer iface_data) TpChannelManagerIface *iface = g_iface; iface->foreach_channel = tpsip_text_factory_foreach_channel; - iface->foreach_channel_class = tpsip_text_factory_foreach_channel_class; + iface->type_foreach_channel_class = + tpsip_text_factory_type_foreach_channel_class; iface->create_channel = tpsip_text_factory_create_channel; iface->request_channel = tpsip_text_factory_request_channel; iface->ensure_channel = tpsip_text_factory_ensure_channel; diff --git a/src/write-mgr-file.c b/src/write-mgr-file.c index e718d91..402e89e 100644 --- a/src/write-mgr-file.c +++ b/src/write-mgr-file.c @@ -1,113 +1,357 @@ /* - * write_mgr_file.c - utility to produce .manager files. - * Copyright (C) 2006-2007 Collabora Ltd. - * Copyright (C) 2006-2007 Nokia Corporation + * write_mgr_file.c - utility to produce manager files. Derived from Gabble. + * Copyright (C) 2006-2010 Collabora Ltd. + * Copyright (C) 2006-2010 Nokia Corporation * - * This work is free software; you can redistribute it and/or + * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * This work is distributed in the hope that it will be useful, + * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this work; if not, write to the Free Software + * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "config.h" + #include <stdio.h> +#include <string.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-protocol.h> +#include <telepathy-glib/telepathy-glib.h> -#include <telepathy-glib/enums.h> -#include "sip-connection-manager.h" +#include "protocol.h" -/* Cloned straight from Gabble - this might eventually go in a library */ -static gchar * -mgr_file_contents (const char *busname, - const char *objpath, - const TpCMProtocolSpec protocols[], - GError **error) + +#define WRITE_STR(prop, key) \ + { \ + const gchar *val = tp_asv_get_string (props, prop); \ + g_assert (!tp_str_empty (val)); \ + g_key_file_set_string (f, section_name, key, val); \ + } + +static void +write_parameters (GKeyFile *f, gchar *section_name, TpBaseProtocol *protocol) { - GKeyFile *f = g_key_file_new(); - const TpCMProtocolSpec *protocol; + const TpCMParamSpec *parameters = + tp_base_protocol_get_parameters (protocol); const TpCMParamSpec *row; - g_key_file_set_string(f, "ConnectionManager", "BusName", busname); - g_key_file_set_string(f, "ConnectionManager", "ObjectPath", objpath); + for (row = parameters; row->name; row++) + { + gchar *param_name = g_strdup_printf ("param-%s", row->name); + gchar *param_value = g_strdup_printf ("%s%s%s%s", row->dtype, + (row->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED ? " required" : ""), + (row->flags & TP_CONN_MGR_PARAM_FLAG_REGISTER ? " register" : ""), + (row->flags & TP_CONN_MGR_PARAM_FLAG_SECRET ? " secret" : "")); + g_key_file_set_string (f, section_name, param_name, param_value); + g_free (param_value); + g_free (param_name); + } - for (protocol = protocols; protocol->name; protocol++) + for (row = parameters; row->name; row++) { - gchar *section_name = g_strdup_printf("Protocol %s", protocol->name); + if (row->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT) + { + gchar *default_name = g_strdup_printf ("default-%s", row->name); - for (row = protocol->parameters; row->name; row++) + switch (row->gtype) + { + case G_TYPE_STRING: + g_key_file_set_string (f, section_name, default_name, + row->def); + break; + case G_TYPE_INT: + case G_TYPE_UINT: + g_key_file_set_integer (f, section_name, default_name, + GPOINTER_TO_INT(row->def)); + break; + case G_TYPE_BOOLEAN: + g_key_file_set_boolean (f, section_name, default_name, + GPOINTER_TO_INT(row->def) ? 1 : 0); + break; + default: + /* can't be in the case because G_TYPE_STRV is actually a + * function */ + if (row->gtype == G_TYPE_STRV) + { + g_key_file_set_string_list (f, section_name, default_name, + (const gchar **) row->def, + g_strv_length ((gchar **) row->def)); + } + } + g_free (default_name); + } + } +} + +static void +write_rcc_property (GKeyFile *keyfile, + const gchar *group_name, + const gchar *key, + GValue *val) +{ + switch (G_VALUE_TYPE (val)) + { + case G_TYPE_BOOLEAN: { - gchar *param_name = g_strdup_printf("param-%s", row->name); - gchar *param_value = g_strdup_printf("%s%s%s%s", row->dtype, - (row->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED ? " required" : ""), - (row->flags & TP_CONN_MGR_PARAM_FLAG_REGISTER ? " register" : ""), - (row->flags & TP_CONN_MGR_PARAM_FLAG_SECRET ? " secret" : "")); - g_key_file_set_string(f, section_name, param_name, param_value); - g_free(param_value); - g_free(param_name); + gchar *kf_key = g_strconcat (key, + " " DBUS_TYPE_BOOLEAN_AS_STRING, NULL); + g_key_file_set_boolean (keyfile, group_name, kf_key, + g_value_get_boolean (val)); + g_free (kf_key); + break; } - for (row = protocol->parameters; row->name; row++) + case G_TYPE_STRING: { - if (row->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT) - { - gchar *default_name = g_strdup_printf("default-%s", row->name); + gchar *kf_key = g_strconcat (key, + " " DBUS_TYPE_STRING_AS_STRING, NULL); + g_key_file_set_string (keyfile, group_name, kf_key, + g_value_get_string (val)); + g_free (kf_key); + break; + } - switch (row->gtype) - { - case G_TYPE_STRING: - g_key_file_set_string(f, section_name, default_name, - row->def); - break; - case G_TYPE_INT: - case G_TYPE_UINT: - g_key_file_set_integer(f, section_name, default_name, - GPOINTER_TO_INT(row->def)); - break; - case G_TYPE_BOOLEAN: - g_key_file_set_boolean(f, section_name, default_name, - GPOINTER_TO_INT(row->def) ? 1 : 0); - } - g_free(default_name); + case G_TYPE_UINT: + { + gchar *kf_key = g_strconcat (key, + " " DBUS_TYPE_UINT32_AS_STRING, NULL); + gchar *kf_val = g_strdup_printf ("%u", g_value_get_uint (val)); + g_key_file_set_value (keyfile, group_name, kf_key, kf_val); + g_free (kf_key); + g_free (kf_val); + break; + } + + /* FIXME: when we depend on Glib 2.26, we can use + * g_key_file_set_[u]int64 (g.o #614864). */ + case G_TYPE_UINT64: + { + gchar *kf_key = g_strconcat (key, + " " DBUS_TYPE_UINT64_AS_STRING, NULL); + gchar *kf_val = g_strdup_printf ("%" G_GUINT64_FORMAT, + g_value_get_uint64 (val)); + g_key_file_set_value (keyfile, group_name, kf_key, kf_val); + g_free (kf_key); + g_free (kf_val); + break; + } + + case G_TYPE_INT: + { + gchar *kf_key = g_strconcat (key, + " " DBUS_TYPE_INT32_AS_STRING, NULL); + g_key_file_set_integer (keyfile, group_name, kf_key, + g_value_get_int (val)); + g_free (kf_key); + break; + } + + case G_TYPE_INT64: + { + gchar *kf_key = g_strconcat (key, + " " DBUS_TYPE_UINT64_AS_STRING, NULL); + gchar *kf_val = g_strdup_printf ("%" G_GINT64_FORMAT, + g_value_get_int64 (val)); + g_key_file_set_value (keyfile, group_name, kf_key, kf_val); + g_free (kf_key); + g_free (kf_val); + break; + } + + default: + { + if (G_VALUE_TYPE (val) == G_TYPE_STRV) + { + gchar **list = g_value_get_boxed (val); + gchar *kf_key = g_strconcat (key, " " + DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING, NULL); + g_key_file_set_string_list (keyfile, group_name, + kf_key, (const gchar **) list, g_strv_length (list)); + g_free (kf_key); + break; } + + /* we'd rather crash than forget to write required rcc property */ + g_assert_not_reached (); } - g_free(section_name); } - return g_key_file_to_data(f, NULL, error); } -/* defined by telepathy-glib if your version is new enough - included here - * so telepathy-glib 0.5.4 and 0.5.5 will still work - */ -#ifndef TP_CM_BUS_NAME_BASE -# define TP_CM_BUS_NAME_BASE "org.freedesktop.Telepathy.ConnectionManager." -# define TP_CM_OBJECT_PATH_BASE "/org/freedesktop/Telepathy/ConnectionManager/" -#endif +static gchar * +generate_group_name (GHashTable *props) +{ + static guint counter = 0; + gchar *retval; + gchar *chan_type = g_ascii_strdown (tp_asv_get_string (props, + TP_PROP_CHANNEL_CHANNEL_TYPE), -1); + gchar *chan_type_suffix; + gchar *handle_type_name; + guint handle_type = tp_asv_get_uint32 (props, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL); + + g_assert (chan_type != NULL); + chan_type_suffix = strrchr (chan_type, '.'); + g_assert (chan_type_suffix != NULL); + chan_type_suffix++; + + switch (handle_type) + { + case TP_HANDLE_TYPE_CONTACT: + handle_type_name = "-1on1"; + break; + + case TP_HANDLE_TYPE_ROOM: + handle_type_name = "-multi"; + break; + + case TP_HANDLE_TYPE_GROUP: + handle_type_name = "-group"; + break; + + case TP_HANDLE_TYPE_LIST: + handle_type_name = "-list"; + break; + + default: + handle_type_name = ""; + } + + retval = g_strdup_printf ("%s%s-%d", chan_type_suffix, handle_type_name, + ++counter); + + g_free (chan_type); + return retval; +} + +static void +write_rccs (GKeyFile *f, const gchar *section_name, GHashTable *props) +{ + GPtrArray *rcc_list = tp_asv_get_boxed (props, + TP_PROP_PROTOCOL_REQUESTABLE_CHANNEL_CLASSES, + TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST); + guint i; + gchar **group_names = g_new0 (gchar *, rcc_list->len + 1); + + for (i = 0; i < rcc_list->len; i++) + { + gchar **allowed; + gchar *group_name; + GHashTable *fixed; + GHashTableIter iter; + gpointer k, v; + + tp_value_array_unpack (g_ptr_array_index (rcc_list, i), 2, + &fixed, &allowed); + + group_name = generate_group_name (fixed); + + g_hash_table_iter_init (&iter, fixed); + while (g_hash_table_iter_next (&iter, &k, &v)) + { + const gchar *key = k; + GValue *val = v; + + write_rcc_property (f, group_name, key, val); + } + + /* takes ownership */ + group_names[i] = group_name; + + g_key_file_set_string_list (f, group_name, "allowed", + (const gchar **) allowed, g_strv_length (allowed)); + } + + g_key_file_set_string_list (f, section_name, "RequestableChannelClasses", + (const gchar **) group_names, rcc_list->len); + + g_strfreev (group_names); +} + +static gchar * +mgr_file_contents (const char *busname, + const char *objpath, + GSList *protocols, + GError **error) +{ + GKeyFile *f = g_key_file_new (); + gchar *file_data; + + g_key_file_set_string (f, "ConnectionManager", "BusName", busname); + g_key_file_set_string (f, "ConnectionManager", "ObjectPath", objpath); + + /* there are no CM interfaces defined yet, so we cheat */ + g_key_file_set_string (f, "ConnectionManager", "Interfaces", ""); + + while (protocols != NULL) + { + TpBaseProtocol *protocol = protocols->data; + GHashTable *props = + tp_base_protocol_get_immutable_properties (protocol); + gchar *section_name = g_strdup_printf ("Protocol %s", + tp_base_protocol_get_name (protocol)); + const gchar * const *ifaces = tp_asv_get_strv (props, + TP_PROP_PROTOCOL_INTERFACES); + const gchar * const *c_ifaces = tp_asv_get_strv (props, + TP_PROP_PROTOCOL_CONNECTION_INTERFACES); + + write_parameters (f, section_name, protocol); + write_rccs (f, section_name, props); + + g_key_file_set_string_list (f, section_name, "Interfaces", + ifaces, g_strv_length ((gchar **) ifaces)); + g_key_file_set_string_list (f, section_name, "ConnectionInterfaces", + c_ifaces, g_strv_length ((gchar **) c_ifaces)); + + WRITE_STR (TP_PROP_PROTOCOL_VCARD_FIELD, "VCardField"); + WRITE_STR (TP_PROP_PROTOCOL_ENGLISH_NAME, "EnglishName"); + WRITE_STR (TP_PROP_PROTOCOL_ICON, "Icon"); + + g_free (section_name); + g_hash_table_destroy (props); + protocols = protocols->next; + } + + file_data = g_key_file_to_data (f, NULL, error); + g_key_file_free (f); + return file_data; +} int main (void) { GError *error = NULL; + gchar *s; + GSList *protocols = NULL; + + g_type_init (); + dbus_g_type_specialized_init (); + + protocols = g_slist_prepend (protocols, + tpsip_protocol_new (NULL)); + + s = mgr_file_contents (TP_CM_BUS_NAME_BASE "sofiasip", + TP_CM_OBJECT_PATH_BASE "sofiasip", + protocols, &error); + + g_object_unref (protocols->data); + g_slist_free (protocols); - gchar *s = mgr_file_contents(TP_CM_BUS_NAME_BASE "sofiasip", - TP_CM_OBJECT_PATH_BASE "sofiasip", - tpsip_protocols, &error); if (!s) { - fprintf(stderr, "%s\n", error->message); - g_error_free(error); + fprintf (stderr, "%s", error->message); + g_error_free (error); return 1; } - printf("%s", s); - g_free(s); + printf ("%s", s); + g_free (s); return 0; } |