summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2010-09-22 14:52:53 (GMT)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2010-09-22 14:52:56 (GMT)
commit0f7de79bbb067ad706fbebd297cd437cab687564 (patch)
tree2118ce63885ec01d59b94505e904e273bab92606
parentc586f0715933c3bc1b97b11decc75134fe729207 (diff)
parentcde7eede438fae164f68128cf0954381e967dcc1 (diff)
downloadtelepathy-haze-0f7de79bbb067ad706fbebd297cd437cab687564.tar.gz
telepathy-haze-0f7de79bbb067ad706fbebd297cd437cab687564.tar.xz
Merge branch 'protocol'
Reviewed-by: Will Thompson <will.thompson@collabora.co.uk>
-rw-r--r--src/Makefile.am2
-rw-r--r--src/connection-manager.c530
-rw-r--r--src/connection-manager.h25
-rw-r--r--src/connection.c161
-rw-r--r--src/connection.h3
-rw-r--r--src/contact-list.c1
-rw-r--r--src/im-channel-factory.c1
-rw-r--r--src/media-manager.c1
-rw-r--r--src/protocol.c876
-rw-r--r--src/protocol.h69
-rw-r--r--tests/twisted/Makefile.am1
-rw-r--r--tests/twisted/cm/protocols.py191
-rw-r--r--tests/twisted/constants.py7
13 files changed, 1242 insertions, 626 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0359a22..d5b3fa3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -49,6 +49,8 @@ telepathy_haze_SOURCES = main.c \
im-channel-factory.h \
notify.c \
notify.h \
+ protocol.c \
+ protocol.h \
request.c \
request.h \
util.c \
diff --git a/src/connection-manager.c b/src/connection-manager.c
index c89ee9d..6377b9e 100644
--- a/src/connection-manager.c
+++ b/src/connection-manager.c
@@ -42,524 +42,27 @@ struct _HazeConnectionManagerPrivate
TpDebugSender *debug_sender;
};
-/* For some protocols, removing the "prpl-" prefix from its name in libpurple
- * doesn't give the right name for Telepathy. Other protocols need some
- * parameters renaming to match well-known names in the spec, or to have
- * hyphens rather than underscores for consistency.
- */
-
-static const HazeParameterMapping encoding_to_charset[] = {
- { "encoding", "charset" },
- { NULL, NULL }
-};
-
-static const HazeParameterMapping jabber_mappings[] = {
- { "connect_server", "server" },
- { "require_tls", "require-encryption" },
- { NULL, NULL }
-};
-
-static const HazeParameterMapping bonjour_mappings[] = {
- { "first", "first-name" },
- { "last", "last-name" },
- { NULL, NULL }
-};
-
-static const HazeParameterMapping sipe_mappings[] = {
- { "usersplit1", "login" },
- { NULL, NULL }
-};
-
-static const HazeParameterMapping yahoo_mappings[] = {
- { "local_charset", "charset" },
- { NULL, NULL }
-};
-
-static HazeProtocolInfo known_protocol_info[] = {
- { "aim", "prpl-aim", NULL, NULL },
- /* Seriously. */
- { "facebook", "prpl-bigbrownchunx-facebookim", NULL, NULL },
- { "gadugadu", "prpl-gg", NULL, NULL },
- { "groupwise", "prpl-novell", NULL, NULL },
- { "irc", "prpl-irc", NULL, encoding_to_charset },
- { "icq", "prpl-icq", NULL, encoding_to_charset },
- { "jabber", "prpl-jabber", NULL, jabber_mappings },
- { "local-xmpp", "prpl-bonjour", NULL, bonjour_mappings },
- { "msn", "prpl-msn", NULL, NULL },
- { "qq", "prpl-qq", NULL, NULL },
- { "sametime", "prpl-meanwhile", NULL, NULL },
- { "sipe", "prpl-sipe", NULL, sipe_mappings },
- { "yahoo", "prpl-yahoo", NULL, yahoo_mappings },
- { "yahoojp", "prpl-yahoojp", NULL, yahoo_mappings },
- { "zephyr", "prpl-zephyr", NULL, encoding_to_charset },
- { "mxit", "prpl-loubserp-mxit", NULL, NULL },
- { "sip", "prpl-simple", NULL, NULL },
- { NULL, NULL, NULL, NULL }
-};
-
-static void *
-_haze_cm_alloc_params (void)
-{
- /* (gchar *) => (GValue *) */
- return g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
- (GDestroyNotify) tp_g_value_slice_free);
-}
-
static void
-_haze_cm_set_param (const TpCMParamSpec *paramspec,
- const GValue *value,
- gpointer params_)
+_haze_cm_constructed (GObject *object)
{
- GHashTable *params = params_;
- gchar *prpl_param_name = (gchar *) paramspec->setter_data;
+ HazeConnectionManager *self = HAZE_CONNECTION_MANAGER (object);
+ TpBaseConnectionManager *base = (TpBaseConnectionManager *) self;
+ void (*chain_up) (GObject *) =
+ G_OBJECT_CLASS (haze_connection_manager_parent_class)->constructed;
+ GList *protocols;
- DEBUG ("setting parameter %s (telepathy name %s)",
- prpl_param_name, paramspec->name);
-
- g_hash_table_insert (params, prpl_param_name, tp_g_value_slice_dup (value));
-}
-
-static gboolean
-_param_filter_no_blanks (const TpCMParamSpec *paramspec,
- GValue *value,
- GError **error)
-{
- const gchar *str = g_value_get_string (value);
-
- if (*str == '\0')
+ if (chain_up != NULL)
{
- g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "Account parameter '%s' must not be empty",
- paramspec->name);
- return FALSE;
+ chain_up (object);
}
- if (strstr (str, " ") != NULL)
+ for (protocols = haze_protocol_build_list ();
+ protocols != NULL;
+ protocols = g_list_delete_link (protocols, protocols))
{
- g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "Account parameter '%s' may not contain spaces",
- paramspec->name);
- return FALSE;
+ tp_base_connection_manager_add_protocol (base, protocols->data);
+ g_object_unref (protocols->data);
}
-
- return TRUE;
-}
-
-/* Checks whether the supplied string equals one of those in the GList
- * paramspec->filter_data.
- */
-static gboolean
-_param_filter_string_list (const TpCMParamSpec *paramspec,
- GValue *value,
- GError **error)
-{
- const gchar *str = g_value_get_string (value);
- /* grr g_list_find_custom() is not const-correct of course */
- GList *valid_values = (GList *) paramspec->filter_data;
-
- if (g_list_find_custom (valid_values, str, (GCompareFunc) g_strcmp0)
- != NULL)
- return TRUE;
-
- g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "'%s' is not a valid value for parameter '%s'", str, paramspec->name);
- return FALSE;
-}
-
-static const HazeParameterMapping *
-protocol_info_lookup_param (
- HazeProtocolInfo *hpi,
- const gchar *purple_name)
-{
- const HazeParameterMapping *m;
-
- for (m = hpi->parameter_map; m != NULL && m->purple_name != NULL; m++)
- if (!tp_strdiff (m->purple_name, purple_name))
- return m;
-
- return NULL;
-}
-
-/*
- * Adds a separate field for each PurpleAccountUserSplit
- */
-static void
-_translate_protocol_usersplits (HazeProtocolInfo *hpi,
- GArray *paramspecs)
-{
- GList *l = hpi->prpl_info->user_splits;
- const guint count = g_list_length (l);
- guint i;
-
- /* first user split is covered by "account" */
- for (i = 1; i <= count; i++)
- {
- gchar *usersplit = g_strdup_printf ("usersplit%d", i);
- const HazeParameterMapping *m = protocol_info_lookup_param (hpi,
- usersplit);
- gchar *name = NULL;
- TpCMParamSpec usersplit_spec = {
- NULL, /* name */
- DBUS_TYPE_STRING_AS_STRING,
- G_TYPE_STRING,
- 0,
- NULL, 0, NULL, NULL,
- NULL, /* setter_data */
- NULL
- };
-
- if (m != NULL)
- name = g_strdup (m->telepathy_name);
-
- if (name == NULL)
- name = usersplit;
-
- usersplit_spec.name = name;
- usersplit_spec.setter_data = usersplit;
-
- g_array_append_val (paramspecs, usersplit_spec);
- }
-}
-
-/* Populates a TpCMParamSpec from a PurpleAccountOption, possibly renaming the
- * parameter as specified in hpi->parameter_map. paramspec is assumed to be
- * zeroed out.
- *
- * Returns: %TRUE on success, and %FALSE if paramspec could not be populated
- * (and thus should not be used).
- */
-static gboolean
-_translate_protocol_option (PurpleAccountOption *option,
- TpCMParamSpec *paramspec,
- HazeProtocolInfo *hpi)
-{
- const char *pref_name = purple_account_option_get_setting (option);
- PurplePrefType pref_type = purple_account_option_get_type (option);
- gchar *name = NULL;
- const HazeParameterMapping *m = protocol_info_lookup_param (hpi, pref_name);
-
- /* Intentional once-per-protocol-per-process leak. */
- if (m != NULL)
- name = g_strdup (m->telepathy_name);
- else
- name = g_strdup (pref_name);
-
- if (g_str_has_prefix (name, "facebook_"))
- name += strlen ("facebook_");
-
- g_strdelimit (name, "_", '-');
- paramspec->name = name;
-
- paramspec->setter_data = option->pref_name;
- /* TODO: does libpurple ever require a parameter besides the username
- * and possibly password?
- */
- paramspec->flags = 0;
-
- switch (pref_type)
- {
- case PURPLE_PREF_BOOLEAN:
- paramspec->dtype = DBUS_TYPE_BOOLEAN_AS_STRING;
- paramspec->gtype = G_TYPE_BOOLEAN;
- paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT;
- paramspec->def = GINT_TO_POINTER (
- purple_account_option_get_default_bool (option));
- break;
- case PURPLE_PREF_INT:
- /* The spec decrees that ports should be uint16, and people get
- * very upset if they're not. I suppose technically there could be
- * int parameters whose names end in "port" which aren't meant to
- * be unsigned?
- */
- if (g_str_has_suffix (name, "port"))
- {
- paramspec->dtype = DBUS_TYPE_UINT16_AS_STRING;
- paramspec->gtype = G_TYPE_UINT;
- }
- else
- {
- paramspec->dtype = DBUS_TYPE_INT32_AS_STRING;
- paramspec->gtype = G_TYPE_INT;
- }
-
- paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT;
- paramspec->def = GINT_TO_POINTER (
- purple_account_option_get_default_int (option));
- break;
- case PURPLE_PREF_STRING:
- {
- const gchar *def;
-
- paramspec->dtype = DBUS_TYPE_STRING_AS_STRING;
- paramspec->gtype = G_TYPE_STRING;
-
- /* prpl-bonjour chooses the defaults for these parameters with
- * getpwuid(3); but for haze's purposes that's the UI's job.
- */
- if (g_str_equal (hpi->prpl_id, "prpl-bonjour")
- && (g_str_equal (paramspec->name, "first-name")
- || g_str_equal (paramspec->name, "last-name")))
- {
- paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_REQUIRED;
- break;
- }
-
- def = purple_account_option_get_default_string (option);
-
- if (def != NULL && *def != '\0')
- {
- paramspec->def = def;
- paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT;
- }
- break;
- }
- case PURPLE_PREF_STRING_LIST:
- {
- const gchar *def;
- const GList *option_tuples;
- GList *valid_strings = NULL;
- /* tuple->key is human-readable description, tuple->value is the
- * value's ID and is secretly a (const char *).
- */
- const PurpleKeyValuePair *tuple;
-
- paramspec->dtype = DBUS_TYPE_STRING_AS_STRING;
- paramspec->gtype = G_TYPE_STRING;
-
- option_tuples = purple_account_option_get_list (option);
- for (; option_tuples != NULL; option_tuples = option_tuples->next)
- {
- tuple = option_tuples->data;
- valid_strings = g_list_prepend (valid_strings, tuple->value);
- }
- paramspec->filter = _param_filter_string_list;
- paramspec->filter_data = valid_strings;
-
- def = purple_account_option_get_default_list_value (option);
- if (def != NULL && *def != '\0')
- {
- paramspec->def = def;
- paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT;
- }
- break;
- }
- default:
- g_warning ("account option %s has unknown type %u; ignoring",
- pref_name, pref_type);
- return FALSE;
- }
-
- if (g_str_equal (paramspec->name, "server"))
- paramspec->filter = _param_filter_no_blanks;
-
- /* There don't seem to be any secrets except for password at the moment
- * (SILC's private-key is a filename, so its value is not actually secret).
- * If more appear, e.g. http-proxy-password, this would be a good place to
- * set the SECRET flag on them; for future-proofing I'll assume tha
- * anything ending with -password is likely to be secret. */
- if (g_str_has_suffix (paramspec->name, "-password"))
- paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_SECRET;
-
- return TRUE;
-}
-
-/* Constructs a parameter specification from the prpl's options list, renaming
- * protocols and parameters according to known_protocol_info.
- */
-static TpCMParamSpec *
-_build_paramspecs (HazeProtocolInfo *hpi)
-{
- const TpCMParamSpec account_spec =
- { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
- TP_CONN_MGR_PARAM_FLAG_REQUIRED, NULL, 0, NULL, NULL,
- (gpointer) "account", NULL };
- TpCMParamSpec password_spec =
- { "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
- TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_SECRET,
- NULL, 0, NULL, NULL,
- (gpointer) "password", NULL };
-
- GArray *paramspecs = g_array_new (TRUE, TRUE, sizeof (TpCMParamSpec));
- GList *opts;
-
- /* TODO: local-xmpp shouldn't have an account parameter */
- g_array_append_val (paramspecs, account_spec);
-
- /* Translate user splits for protocols that have a mapping */
- if (hpi->prpl_info->user_splits &&
- protocol_info_lookup_param (hpi, "usersplit1") != NULL)
- _translate_protocol_usersplits (hpi, paramspecs);
-
- /* Password parameter: */
- if (!(hpi->prpl_info->options & OPT_PROTO_NO_PASSWORD))
- {
- if (hpi->prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)
- password_spec.flags &= ~TP_CONN_MGR_PARAM_FLAG_REQUIRED;
- g_array_append_val (paramspecs, password_spec);
- }
-
- for (opts = hpi->prpl_info->protocol_options; opts; opts = opts->next)
- {
- PurpleAccountOption *option = (PurpleAccountOption *)opts->data;
- TpCMParamSpec paramspec =
- { NULL, NULL, 0, 0, NULL, 0, NULL, NULL, NULL, NULL};
-
- if (_translate_protocol_option (option, &paramspec, hpi))
- g_array_append_val (paramspecs, paramspec);
- }
-
- return (TpCMParamSpec *) g_array_free (paramspecs, FALSE);
-}
-
-static int
-_compare_protocol_names (gconstpointer a,
- gconstpointer b)
-{
- const TpCMProtocolSpec *protocol_a = a;
- const TpCMProtocolSpec *protocol_b = b;
-
- return strcmp(protocol_a->name, protocol_b->name);
-}
-
-static TpCMProtocolSpec *
-get_protocols (HazeConnectionManagerClass *klass)
-{
- GArray *protocols = g_array_new (TRUE, TRUE, sizeof (TpCMProtocolSpec));
- GHashTableIter iter;
- gpointer key, value;
-
- g_hash_table_iter_init (&iter, klass->protocol_info_table);
- while (g_hash_table_iter_next (&iter, &key, &value))
- {
- HazeProtocolInfo *info = value;
- TpCMProtocolSpec protocol = {
- info->tp_protocol_name, /* name */
- _build_paramspecs (info), /* parameters */
- _haze_cm_alloc_params, /* params_new */
- (GDestroyNotify) g_hash_table_unref, /* params_free */
- _haze_cm_set_param /* set_param */
- };
-
- g_array_append_val (protocols, protocol);
- }
-
- qsort (protocols->data, protocols->len, sizeof (TpCMProtocolSpec),
- _compare_protocol_names);
-
- {
- GString *debug_string = g_string_new ("");
- TpCMProtocolSpec *p = (TpCMProtocolSpec *) protocols->data;
-
- while (p->name != NULL)
- {
- g_string_append (debug_string, p->name);
- p += 1;
-
- if (p->name != NULL)
- g_string_append (debug_string, ", ");
- }
-
- DEBUG ("Found protocols %s", debug_string->str);
- g_string_free (debug_string, TRUE);
- }
-
- return (TpCMProtocolSpec *) g_array_free (protocols, FALSE);
-}
-
-static TpBaseConnection *
-_haze_connection_manager_new_connection (TpBaseConnectionManager *base,
- const gchar *proto,
- TpIntSet *params_present,
- void *parsed_params,
- GError **error)
-{
- HazeConnectionManager *cm = HAZE_CONNECTION_MANAGER(base);
- HazeConnectionManagerClass *klass = HAZE_CONNECTION_MANAGER_GET_CLASS (cm);
- GHashTable *params = (GHashTable *)parsed_params;
- HazeProtocolInfo *info =
- g_hash_table_lookup (klass->protocol_info_table, proto);
- HazeConnection *conn = g_object_new (HAZE_TYPE_CONNECTION,
- "protocol", proto,
- "prpl-id", info->prpl_id,
- "prpl-info", info->prpl_info,
- "parameters", params,
- NULL);
-
- if (!haze_connection_create_account (conn, error))
- {
- g_object_unref (conn);
- return FALSE;
- }
- return (TpBaseConnection *) conn;
-}
-
-/** Predicate for g_hash_table_find to search on prpl_id.
- * @param key (const gchar *)tp_protocol_name
- * @param value (HazeProtocolInfo *)info
- * @param data (const gchar *)prpl_id
- * @return @c TRUE iff info->prpl_id eq prpl_id
- */
-static gboolean
-_compare_protocol_id (gpointer key,
- gpointer value,
- gpointer data)
-{
- HazeProtocolInfo *info = (HazeProtocolInfo *)value;
- const gchar *prpl_id = (const gchar *)data;
- return (!strcmp (info->prpl_id, prpl_id));
-}
-
-static GHashTable *
-build_protocol_table (void)
-{
- GHashTable *table;
- HazeProtocolInfo *i;
- GList *iter;
-
- table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
-
- for (i = known_protocol_info; i->prpl_id != NULL; i++)
- {
- PurplePlugin *plugin = purple_find_prpl (i->prpl_id);
-
- if (plugin == NULL)
- continue;
-
- i->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO (plugin);
-
- g_hash_table_insert (table, i->tp_protocol_name, i);
- }
-
- for (iter = purple_plugins_get_protocols (); iter; iter = iter->next)
- {
- PurplePlugin *plugin = iter->data;
- PurplePluginInfo *p_info = plugin->info;
- PurplePluginProtocolInfo *prpl_info =
- PURPLE_PLUGIN_PROTOCOL_INFO (plugin);
- HazeProtocolInfo *info;
-
- if (g_hash_table_find (table, _compare_protocol_id, p_info->id))
- continue; /* already in the table from the previous loop */
-
- info = g_slice_new (HazeProtocolInfo);
- info->prpl_id = p_info->id;
- info->prpl_info = prpl_info;
- info->parameter_map = NULL;
-
- if (g_str_has_prefix (p_info->id, "prpl-"))
- {
- info->tp_protocol_name = (p_info->id + 5);
- }
- else
- {
- g_warning ("prpl '%s' has a dumb id; spank its author", p_info->id);
- info->tp_protocol_name = p_info->id;
- }
-
- g_hash_table_insert (table, info->tp_protocol_name, info);
- }
-
- return table;
}
static void
@@ -589,13 +92,12 @@ haze_connection_manager_class_init (HazeConnectionManagerClass *klass)
(TpBaseConnectionManagerClass *)klass;
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- klass->protocol_info_table = build_protocol_table ();
-
+ object_class->constructed = _haze_cm_constructed;
object_class->finalize = _haze_cm_finalize;
- base_class->new_connection = _haze_connection_manager_new_connection;
+ base_class->new_connection = NULL;
base_class->cm_dbus_name = "haze";
- base_class->protocol_params = get_protocols (klass);
+ base_class->protocol_params = NULL;
g_type_class_add_private (klass, sizeof (HazeConnectionManagerPrivate));
}
diff --git a/src/connection-manager.h b/src/connection-manager.h
index a4e497d..b90f37f 100644
--- a/src/connection-manager.h
+++ b/src/connection-manager.h
@@ -24,6 +24,7 @@
#include <glib-object.h>
#include <telepathy-glib/base-connection-manager.h>
+#include "protocol.h"
#include "connection.h"
G_BEGIN_DECLS
@@ -33,9 +34,6 @@ typedef struct _HazeConnectionManagerClass HazeConnectionManagerClass;
struct _HazeConnectionManagerClass {
TpBaseConnectionManagerClass parent_class;
-
- /** GHashTable of (gchar *)tp_protocol_name => HazeProtocolInfo */
- GHashTable *protocol_info_table;
};
struct _HazeConnectionManager {
@@ -44,27 +42,6 @@ struct _HazeConnectionManager {
gpointer priv;
};
-typedef struct _HazeParameterMapping HazeParameterMapping;
-struct _HazeParameterMapping
-{
- const gchar *purple_name;
- const gchar *telepathy_name;
-};
-
-typedef struct _HazeProtocolInfo HazeProtocolInfo;
-struct _HazeProtocolInfo
-{
- /** Not const for convenience, but should not be freed */
- gchar *tp_protocol_name;
-
- /** Not const for convenience, but should not be freed */
- gchar *prpl_id;
- PurplePluginProtocolInfo *prpl_info;
-
- /* If not NULL, an array terminated by an entry with both names NULL. */
- const HazeParameterMapping *parameter_map;
-};
-
GType haze_connection_manager_get_type (void);
/* TYPE MACROS */
diff --git a/src/connection.c b/src/connection.c
index 747ab18..631d5a9 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -56,6 +56,8 @@
enum
{
PROP_PARAMETERS = 1,
+ PROP_USERNAME,
+ PROP_PASSWORD,
PROP_PRPL_ID,
PROP_PRPL_INFO,
@@ -83,8 +85,44 @@ G_DEFINE_TYPE_WITH_CODE(HazeConnection,
haze_connection_mail_iface_init);
);
+static const gchar * implemented_interfaces[] = {
+ /* Conditionally present */
+
+ TP_IFACE_CONNECTION_INTERFACE_AVATARS,
+ HAZE_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION,
+# define HAZE_NUM_CONDITIONAL_INTERFACES 2
+
+ /* Always present */
+
+ TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
+ TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
+ TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
+ TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES,
+ TP_IFACE_CONNECTION_INTERFACE_CONTACTS,
+ /* TODO: This is a lie. Not all protocols supported by libpurple
+ * actually have the concept of a user-settable alias, but
+ * there's no way for the UI to know (yet).
+ */
+ TP_IFACE_CONNECTION_INTERFACE_ALIASING,
+ NULL
+};
+
+const gchar **
+haze_connection_get_implemented_interfaces (void)
+{
+ return implemented_interfaces;
+}
+
+const gchar **
+haze_connection_get_guaranteed_interfaces (void)
+{
+ return implemented_interfaces + HAZE_NUM_CONDITIONAL_INTERFACES;
+}
+
struct _HazeConnectionPrivate
{
+ gchar *username;
+ gchar *password;
GHashTable *parameters;
gchar *prpl_id;
@@ -241,63 +279,6 @@ _warn_unhandled_parameter (const gchar *key,
g_warning ("received an unknown parameter '%s'; ignoring", key);
}
-static gchar *
-_haze_connection_get_username (GHashTable *params,
- PurplePluginProtocolInfo *prpl_info)
-{
- const gchar *account = tp_asv_get_string (params, "account");
- gchar *username;
-
- /* 'account' is always flagged as Required. */
- g_return_val_if_fail (account != NULL, NULL);
-
- /* Does the protocol have user splits? */
- if (prpl_info->user_splits != NULL &&
- g_hash_table_lookup (params, "usersplit1") != NULL)
- {
- GString *string = g_string_new (account);
- guint i;
- GList *l;
-
- for (i = 1, l = prpl_info->user_splits;
- l != NULL;
- i++, l = l->next)
- {
- PurpleAccountUserSplit *split = l->data;
- gchar *param_name = g_strdup_printf ("usersplit%d", i);
- GValue *value = g_hash_table_lookup (params, param_name);
-
- g_string_append_c (string,
- purple_account_user_split_get_separator (split));
-
- if (value != NULL)
- {
- /* tp-glib should guarantee that this is a string. */
- g_assert (G_VALUE_TYPE (value) == G_TYPE_STRING);
- g_string_append (string, g_value_get_string (value));
- }
- else
- {
- g_string_append (string,
- purple_account_user_split_get_default_value(split));
- }
-
- g_hash_table_remove (params, param_name);
- g_free (param_name);
- }
-
- username = g_string_free (string, FALSE);
- }
- else
- {
- username = g_strdup (account);
- }
-
- g_hash_table_remove (params, "account");
-
- return username;
-}
-
static void
set_option (
PurpleAccount *account,
@@ -348,41 +329,31 @@ haze_connection_create_account (HazeConnection *self,
HazeConnectionPrivate *priv = self->priv;
GHashTable *params = priv->parameters;
PurplePluginProtocolInfo *prpl_info = priv->prpl_info;
- gchar *username;
- const gchar *password;
GList *l;
g_return_val_if_fail (self->account == NULL, FALSE);
- username = _haze_connection_get_username (params, prpl_info);
- g_return_val_if_fail (username != NULL, FALSE);
-
- if (purple_accounts_find (username, priv->prpl_id) != NULL)
+ if (purple_accounts_find (priv->username, priv->prpl_id) != NULL)
{
g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "a connection already exists to %s on %s", username, priv->prpl_id);
- g_free(username);
+ "a connection already exists to %s on %s", priv->username,
+ priv->prpl_id);
return FALSE;
}
- self->account = purple_account_new (username, priv->prpl_id);
+ self->account = purple_account_new (priv->username, priv->prpl_id);
purple_accounts_add (self->account);
- self->account->ui_data = self;
+ if (priv->password != NULL)
+ purple_account_set_password (self->account, priv->password);
- password = tp_asv_get_string (params, "password");
- if (password)
- {
- purple_account_set_password (self->account, password);
- g_hash_table_remove (params, "password");
- }
+ self->account->ui_data = self;
for (l = prpl_info->protocol_options; l != NULL; l = l->next)
set_option (self->account, l->data, params);
g_hash_table_foreach (params, (GHFunc) _warn_unhandled_parameter, "lala");
- g_free(username);
return TRUE;
}
@@ -508,6 +479,12 @@ haze_connection_get_property (GObject *object,
case PROP_PARAMETERS:
g_value_set_boxed (value, priv->parameters);
break;
+ case PROP_USERNAME:
+ g_value_set_string (value, priv->username);
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, priv->password);
+ break;
case PROP_PRPL_ID:
g_value_set_string (value, priv->prpl_id);
break;
@@ -530,6 +507,12 @@ haze_connection_set_property (GObject *object,
HazeConnectionPrivate *priv = self->priv;
switch (property_id) {
+ case PROP_USERNAME:
+ priv->username = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD:
+ priv->password = g_value_dup_string (value);
+ break;
case PROP_PARAMETERS:
priv->parameters = g_value_dup_boxed (value);
break;
@@ -602,11 +585,14 @@ static void
haze_connection_finalize (GObject *object)
{
HazeConnection *self = HAZE_CONNECTION (object);
+ HazeConnectionPrivate *priv = self->priv;
tp_contacts_mixin_finalize (object);
tp_presence_mixin_finalize (object);
g_strfreev (self->acceptable_avatar_mime_types);
+ g_free (priv->username);
+ g_free (priv->password);
if (self->account != NULL)
{
@@ -623,18 +609,6 @@ haze_connection_class_init (HazeConnectionClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
TpBaseConnectionClass *base_class = TP_BASE_CONNECTION_CLASS (klass);
GParamSpec *param_spec;
- static const gchar *interfaces_always_present[] = {
- TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
- TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
- TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
- TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES,
- TP_IFACE_CONNECTION_INTERFACE_CONTACTS,
- /* TODO: This is a lie. Not all protocols supported by libpurple
- * actually have the concept of a user-settable alias, but
- * there's no way for the UI to know (yet).
- */
- TP_IFACE_CONNECTION_INTERFACE_ALIASING,
- NULL };
static TpDBusPropertiesMixinPropImpl mail_props[] = {
{ "MailNotificationFlags", NULL, NULL },
{ "UnreadMailCount", NULL, NULL },
@@ -671,14 +645,25 @@ haze_connection_class_init (HazeConnectionClass *klass)
haze_connection_get_unique_connection_name;
base_class->start_connecting = _haze_connection_start_connecting;
base_class->shut_down = _haze_connection_shut_down;
- base_class->interfaces_always_present = interfaces_always_present;
+ base_class->interfaces_always_present =
+ haze_connection_get_guaranteed_interfaces();
param_spec = g_param_spec_boxed ("parameters", "gchar * => GValue",
- "Connection parameters (username, password, etc.)",
+ "Connection parameters (password, etc.)",
G_TYPE_HASH_TABLE,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_PARAMETERS, param_spec);
+ param_spec = g_param_spec_string ("username", "username",
+ "protocol plugin username", NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_USERNAME, param_spec);
+
+ param_spec = g_param_spec_string ("password", "password",
+ "protocol plugin password, or NULL if none", NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_PASSWORD, param_spec);
+
param_spec = g_param_spec_string ("prpl-id", "protocol plugin ID",
"protocol plugin ID", NULL,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
diff --git a/src/connection.h b/src/connection.h
index 5166cdc..d0bdd2c 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -116,6 +116,9 @@ GType haze_connection_get_type (void);
const gchar *haze_get_fallback_group (void);
+const gchar **haze_connection_get_implemented_interfaces (void);
+const gchar **haze_connection_get_guaranteed_interfaces (void);
+
G_END_DECLS
#endif /* #ifndef __HAZE_CONNECTION_H__*/
diff --git a/src/contact-list.c b/src/contact-list.c
index 0e88794..ca4e1f0 100644
--- a/src/contact-list.c
+++ b/src/contact-list.c
@@ -23,6 +23,7 @@
#include <telepathy-glib/channel-manager.h>
#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/gtypes.h>
#include <telepathy-glib/interfaces.h>
#include <telepathy-glib/intset.h>
diff --git a/src/im-channel-factory.c b/src/im-channel-factory.c
index 88520ac..44df761 100644
--- a/src/im-channel-factory.c
+++ b/src/im-channel-factory.c
@@ -27,6 +27,7 @@
#include <telepathy-glib/base-connection.h>
#include <telepathy-glib/channel-manager.h>
#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/gtypes.h>
#include <telepathy-glib/handle-repo.h>
#include <telepathy-glib/interfaces.h>
diff --git a/src/media-manager.c b/src/media-manager.c
index 6ab1564..fddd63a 100644
--- a/src/media-manager.c
+++ b/src/media-manager.c
@@ -25,6 +25,7 @@
#include <libpurple/mediamanager.h>
#include <telepathy-glib/channel-manager.h>
#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/gtypes.h>
#include <telepathy-glib/interfaces.h>
#include "connection.h"
diff --git a/src/protocol.c b/src/protocol.c
new file mode 100644
index 0000000..19dbdb7
--- /dev/null
+++ b/src/protocol.c
@@ -0,0 +1,876 @@
+/*
+ * Haze Protocol object
+ *
+ * Copyright © 2007 Will Thompson
+ * Copyright © 2007-2010 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "config.h"
+#include "protocol.h"
+
+#include <string.h>
+
+#include <dbus/dbus-protocol.h>
+#include <libpurple/accountopt.h>
+#include <libpurple/prpl.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+#include "connection.h"
+#include "debug.h"
+
+G_DEFINE_TYPE (HazeProtocol, haze_protocol, TP_TYPE_BASE_PROTOCOL)
+
+typedef struct _HazeParameterMapping HazeParameterMapping;
+struct _HazeParameterMapping
+{
+ const gchar *purple_name;
+ const gchar *telepathy_name;
+};
+
+typedef struct _KnownProtocolInfo KnownProtocolInfo;
+struct _KnownProtocolInfo
+{
+ const gchar *tp_protocol_name;
+ const gchar *prpl_id;
+ /* If not NULL, an array terminated by an entry with both names NULL. */
+ const HazeParameterMapping *parameter_map;
+ /* vCard field in lower case.
+ * If "", we know that there's no applicable vCard field. */
+ const gchar *vcard_field;
+};
+
+struct _HazeProtocolPrivate {
+ PurplePlugin *plugin;
+ gchar *prpl_id;
+ PurplePluginProtocolInfo *prpl_info;
+ TpCMParamSpec *paramspecs;
+ const KnownProtocolInfo *known_protocol;
+};
+
+/* For some protocols, removing the "prpl-" prefix from its name in libpurple
+ * doesn't give the right name for Telepathy. Other protocols need some
+ * parameters renaming to match well-known names in the spec, or to have
+ * hyphens rather than underscores for consistency.
+ */
+
+static const HazeParameterMapping encoding_to_charset[] = {
+ { "encoding", "charset" },
+ { NULL, NULL }
+};
+
+static const HazeParameterMapping irc_mappings[] = {
+ { "encoding", "charset" },
+ { "usersplit1", "server" },
+ { NULL, NULL }
+};
+
+static const HazeParameterMapping jabber_mappings[] = {
+ { "connect_server", "server" },
+ /* usersplit1 => domain is deliberately not in this map, because
+ * Telepathy convention for XMPP is for 'account' to be the bare
+ * JID (user@domain. Ideally we'd have some way to split the resource
+ * off (fd.o #14212). */
+ { "require_tls", "require-encryption" },
+ { NULL, NULL }
+};
+
+static const HazeParameterMapping bonjour_mappings[] = {
+ { "first", "first-name" },
+ { "last", "last-name" },
+ { NULL, NULL }
+};
+
+static const HazeParameterMapping sipe_mappings[] = {
+ { "usersplit1", "login" },
+ { NULL, NULL }
+};
+
+static const HazeParameterMapping yahoo_mappings[] = {
+ { "local_charset", "charset" },
+ { NULL, NULL }
+};
+
+static const KnownProtocolInfo known_protocol_info[] = {
+ { "aim", "prpl-aim", NULL, "x-aim" },
+ /* Seriously. */
+ { "facebook", "prpl-bigbrownchunx-facebookim", NULL, "" },
+ { "gadugadu", "prpl-gg", NULL, "x-gadugadu" },
+ { "groupwise", "prpl-novell", NULL, "x-groupwise" },
+ { "irc", "prpl-irc", irc_mappings, "x-irc" /* ? */ },
+ { "icq", "prpl-icq", encoding_to_charset, "x-icq" },
+ { "jabber", "prpl-jabber", jabber_mappings, "x-jabber" },
+ { "local-xmpp", "prpl-bonjour", bonjour_mappings, "" /* ? */ },
+ { "msn", "prpl-msn", NULL, "x-msn" },
+ { "qq", "prpl-qq", NULL, "x-qq" /* ? */ },
+ { "sametime", "prpl-meanwhile", NULL, "x-sametime" /* ? */ },
+ { "sipe", "prpl-sipe", sipe_mappings, "" /* ? */ },
+ { "yahoo", "prpl-yahoo", yahoo_mappings, "x-yahoo" },
+ { "yahoojp", "prpl-yahoojp", yahoo_mappings, "x-yahoo" /* ? */ },
+ { "zephyr", "prpl-zephyr", encoding_to_charset, "x-zephyr" /* ? */ },
+ { "mxit", "prpl-loubserp-mxit", NULL, "x-mxit" /* ? */ },
+ { "sip", "prpl-simple", NULL, "x-sip" },
+ { NULL, NULL, NULL }
+};
+
+GList *
+haze_protocol_build_list (void)
+{
+ const KnownProtocolInfo *i;
+ GList *iter;
+ GList *ret = NULL;
+
+ for (iter = purple_plugins_get_protocols (); iter; iter = iter->next)
+ {
+ PurplePlugin *plugin = iter->data;
+ PurplePluginInfo *p_info = plugin->info;
+ PurplePluginProtocolInfo *prpl_info =
+ PURPLE_PLUGIN_PROTOCOL_INFO (plugin);
+ HazeProtocol *protocol;
+ const KnownProtocolInfo *info = NULL;
+
+ for (i = known_protocol_info; i->prpl_id != NULL; i++)
+ {
+ if (!tp_strdiff (i->prpl_id, p_info->id))
+ {
+ info = i;
+ break;
+ }
+ }
+
+ if (info == NULL)
+ {
+ const gchar *tp_name;
+
+ if (g_str_has_prefix (p_info->id, "prpl-"))
+ {
+ tp_name = (p_info->id + 5);
+ }
+ else
+ {
+ g_warning ("prpl '%s' has a dumb id; spank its author",
+ p_info->id);
+ tp_name = p_info->id;
+ }
+
+ DEBUG ("using default behaviour for unknown prpl '%s'", p_info->id);
+
+ protocol = g_object_new (HAZE_TYPE_PROTOCOL,
+ "name", tp_name,
+ "plugin", plugin,
+ "prpl-id", p_info->id,
+ "prpl-info", prpl_info,
+ NULL);
+ }
+ else
+ {
+ protocol = g_object_new (HAZE_TYPE_PROTOCOL,
+ "name", info->tp_protocol_name,
+ "plugin", plugin,
+ "prpl-id", p_info->id,
+ "prpl-info", prpl_info,
+ "known-protocol", info,
+ NULL);
+ }
+
+ ret = g_list_prepend (ret, protocol);
+ }
+
+ return ret;
+}
+
+static gboolean
+_param_filter_no_blanks (const TpCMParamSpec *paramspec,
+ GValue *value,
+ GError **error)
+{
+ const gchar *str = g_value_get_string (value);
+
+ if (*str == '\0')
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "Account parameter '%s' must not be empty",
+ paramspec->name);
+ return FALSE;
+ }
+
+ if (strstr (str, " ") != NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "Account parameter '%s' may not contain spaces",
+ paramspec->name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Checks whether the supplied string equals one of those in the GList
+ * paramspec->filter_data.
+ */
+static gboolean
+_param_filter_string_list (const TpCMParamSpec *paramspec,
+ GValue *value,
+ GError **error)
+{
+ const gchar *str = g_value_get_string (value);
+ /* grr g_list_find_custom() is not const-correct of course */
+ GList *valid_values = (GList *) paramspec->filter_data;
+
+ if (g_list_find_custom (valid_values, str, (GCompareFunc) g_strcmp0)
+ != NULL)
+ return TRUE;
+
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is not a valid value for parameter '%s'", str, paramspec->name);
+ return FALSE;
+}
+
+static const HazeParameterMapping *
+haze_protocol_lookup_param (
+ HazeProtocol *self,
+ const gchar *purple_name)
+{
+ const HazeParameterMapping *m;
+
+ if (self->priv->known_protocol == NULL ||
+ self->priv->known_protocol->parameter_map == NULL)
+ return NULL;
+
+ for (m = self->priv->known_protocol->parameter_map;
+ m->purple_name != NULL;
+ m++)
+ {
+ if (!tp_strdiff (m->purple_name, purple_name))
+ return m;
+ }
+
+ return NULL;
+}
+
+/*
+ * Adds a separate field for each PurpleAccountUserSplit
+ */
+static void
+_translate_protocol_usersplits (HazeProtocol *self,
+ GArray *paramspecs)
+{
+ GList *l = self->priv->prpl_info->user_splits;
+ const guint count = g_list_length (l);
+ guint i;
+
+ /* first user split is covered by "account" */
+ for (i = 1; i <= count; i++)
+ {
+ gchar *usersplit = g_strdup_printf ("usersplit%d", i);
+ const HazeParameterMapping *m = haze_protocol_lookup_param (self,
+ usersplit);
+ gchar *name = NULL;
+ TpCMParamSpec usersplit_spec = {
+ NULL, /* name */
+ DBUS_TYPE_STRING_AS_STRING,
+ G_TYPE_STRING,
+ TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT,
+ NULL, 0, NULL, NULL,
+ NULL, /* setter_data */
+ NULL
+ };
+
+ if (m != NULL)
+ name = g_strdup (m->telepathy_name);
+
+ if (name == NULL)
+ name = usersplit;
+
+ usersplit_spec.name = name;
+ usersplit_spec.setter_data = usersplit;
+
+ g_array_append_val (paramspecs, usersplit_spec);
+ }
+}
+
+/* Populates a TpCMParamSpec from a PurpleAccountOption, possibly renaming the
+ * parameter as specified in parameter_map. paramspec is assumed to be
+ * zeroed out.
+ *
+ * Returns: %TRUE on success, and %FALSE if paramspec could not be populated
+ * (and thus should not be used).
+ */
+static gboolean
+_translate_protocol_option (PurpleAccountOption *option,
+ TpCMParamSpec *paramspec,
+ HazeProtocol *self)
+{
+ const char *pref_name = purple_account_option_get_setting (option);
+ PurplePrefType pref_type = purple_account_option_get_type (option);
+ gchar *name = NULL;
+ const HazeParameterMapping *m = haze_protocol_lookup_param (self, pref_name);
+
+ /* Intentional once-per-protocol-per-process leak. */
+ if (m != NULL)
+ name = g_strdup (m->telepathy_name);
+ else
+ name = g_strdup (pref_name);
+
+ if (g_str_has_prefix (name, "facebook_"))
+ name += strlen ("facebook_");
+
+ g_strdelimit (name, "_", '-');
+ paramspec->name = name;
+
+ paramspec->setter_data = option->pref_name;
+ /* TODO: does libpurple ever require a parameter besides the username
+ * and possibly password?
+ */
+ paramspec->flags = 0;
+
+ switch (pref_type)
+ {
+ case PURPLE_PREF_BOOLEAN:
+ paramspec->dtype = DBUS_TYPE_BOOLEAN_AS_STRING;
+ paramspec->gtype = G_TYPE_BOOLEAN;
+ paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT;
+ paramspec->def = GINT_TO_POINTER (
+ purple_account_option_get_default_bool (option));
+ break;
+ case PURPLE_PREF_INT:
+ /* The spec decrees that ports should be uint16, and people get
+ * very upset if they're not. I suppose technically there could be
+ * int parameters whose names end in "port" which aren't meant to
+ * be unsigned?
+ */
+ if (g_str_has_suffix (name, "port"))
+ {
+ paramspec->dtype = DBUS_TYPE_UINT16_AS_STRING;
+ paramspec->gtype = G_TYPE_UINT;
+ }
+ else
+ {
+ paramspec->dtype = DBUS_TYPE_INT32_AS_STRING;
+ paramspec->gtype = G_TYPE_INT;
+ }
+
+ paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT;
+ paramspec->def = GINT_TO_POINTER (
+ purple_account_option_get_default_int (option));
+ break;
+ case PURPLE_PREF_STRING:
+ {
+ const gchar *def;
+
+ paramspec->dtype = DBUS_TYPE_STRING_AS_STRING;
+ paramspec->gtype = G_TYPE_STRING;
+
+ /* prpl-bonjour chooses the defaults for these parameters with
+ * getpwuid(3); but for haze's purposes that's the UI's job.
+ */
+ if (g_str_equal (self->priv->prpl_id, "prpl-bonjour")
+ && (g_str_equal (paramspec->name, "first-name")
+ || g_str_equal (paramspec->name, "last-name")))
+ {
+ paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_REQUIRED;
+ break;
+ }
+
+ def = purple_account_option_get_default_string (option);
+
+ if (def != NULL && *def != '\0')
+ {
+ paramspec->def = def;
+ paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT;
+ }
+ break;
+ }
+ case PURPLE_PREF_STRING_LIST:
+ {
+ const gchar *def;
+ const GList *option_tuples;
+ GList *valid_strings = NULL;
+ /* tuple->key is human-readable description, tuple->value is the
+ * value's ID and is secretly a (const char *).
+ */
+ const PurpleKeyValuePair *tuple;
+
+ paramspec->dtype = DBUS_TYPE_STRING_AS_STRING;
+ paramspec->gtype = G_TYPE_STRING;
+
+ option_tuples = purple_account_option_get_list (option);
+ for (; option_tuples != NULL; option_tuples = option_tuples->next)
+ {
+ tuple = option_tuples->data;
+ valid_strings = g_list_prepend (valid_strings, tuple->value);
+ }
+ paramspec->filter = _param_filter_string_list;
+ paramspec->filter_data = valid_strings;
+
+ def = purple_account_option_get_default_list_value (option);
+ if (def != NULL && *def != '\0')
+ {
+ paramspec->def = def;
+ paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT;
+ }
+ break;
+ }
+ default:
+ g_warning ("account option %s has unknown type %u; ignoring",
+ pref_name, pref_type);
+ return FALSE;
+ }
+
+ if (g_str_equal (paramspec->name, "server"))
+ paramspec->filter = _param_filter_no_blanks;
+
+ /* There don't seem to be any secrets except for password at the moment
+ * (SILC's private-key is a filename, so its value is not actually secret).
+ * If more appear, e.g. http-proxy-password, this would be a good place to
+ * set the SECRET flag on them; for future-proofing I'll assume tha
+ * anything ending with -password is likely to be secret. */
+ if (g_str_has_suffix (paramspec->name, "-password"))
+ paramspec->flags |= TP_CONN_MGR_PARAM_FLAG_SECRET;
+
+ return TRUE;
+}
+
+/* Constructs a parameter specification from the prpl's options list, renaming
+ * protocols and parameters according to known_protocol_info.
+ */
+static const TpCMParamSpec *
+haze_protocol_get_parameters (TpBaseProtocol *protocol)
+{
+ HazeProtocol *self = HAZE_PROTOCOL (protocol);
+ const TpCMParamSpec account_spec =
+ { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
+ TP_CONN_MGR_PARAM_FLAG_REQUIRED, NULL, 0, NULL, NULL,
+ (gpointer) "account", NULL };
+ TpCMParamSpec password_spec =
+ { "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
+ TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_SECRET,
+ NULL, 0, NULL, NULL,
+ (gpointer) "password", NULL };
+ GArray *paramspecs;
+ GList *opts;
+
+ if (self->priv->paramspecs != NULL)
+ goto finally;
+
+ paramspecs = g_array_new (TRUE, TRUE, sizeof (TpCMParamSpec));
+
+ /* TODO: local-xmpp shouldn't have an account parameter */
+ g_array_append_val (paramspecs, account_spec);
+
+ /* Translate user splits for protocols that have a mapping */
+ if (self->priv->prpl_info->user_splits &&
+ haze_protocol_lookup_param (self, "usersplit1") != NULL)
+ _translate_protocol_usersplits (self, paramspecs);
+
+ /* Password parameter: */
+ if (!(self->priv->prpl_info->options & OPT_PROTO_NO_PASSWORD))
+ {
+ if (self->priv->prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)
+ password_spec.flags &= ~TP_CONN_MGR_PARAM_FLAG_REQUIRED;
+ g_array_append_val (paramspecs, password_spec);
+ }
+
+ for (opts = self->priv->prpl_info->protocol_options;
+ opts != NULL;
+ opts = opts->next)
+ {
+ PurpleAccountOption *option = (PurpleAccountOption *)opts->data;
+ TpCMParamSpec paramspec =
+ { NULL, NULL, 0, 0, NULL, 0, NULL, NULL, NULL, NULL};
+
+ if (_translate_protocol_option (option, &paramspec, self))
+ g_array_append_val (paramspecs, paramspec);
+ }
+
+ self->priv->paramspecs = (TpCMParamSpec *) g_array_free (paramspecs,
+ FALSE);
+
+finally:
+ return self->priv->paramspecs;
+}
+
+enum
+{
+ PROP_PLUGIN = 1,
+ PROP_PRPL_ID,
+ PROP_PRPL_INFO,
+ PROP_KNOWN_PROTOCOL,
+} HazeProtocolProperties;
+
+static void
+haze_protocol_init (HazeProtocol *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, HAZE_TYPE_PROTOCOL,
+ HazeProtocolPrivate);
+}
+
+static gchar *
+haze_protocol_get_username (GHashTable *params,
+ PurplePluginProtocolInfo *prpl_info,
+ gboolean remove_params)
+{
+ const gchar *account = tp_asv_get_string (params, "account");
+ gchar *username;
+
+ /* 'account' is always flagged as Required. */
+ g_return_val_if_fail (account != NULL, NULL);
+
+ /* Does the protocol have user splits? */
+ if (prpl_info->user_splits != NULL &&
+ g_hash_table_lookup (params, "usersplit1") != NULL)
+ {
+ GString *string = g_string_new (account);
+ guint i;
+ GList *l;
+
+ for (i = 1, l = prpl_info->user_splits;
+ l != NULL;
+ i++, l = l->next)
+ {
+ PurpleAccountUserSplit *split = l->data;
+ gchar *param_name = g_strdup_printf ("usersplit%d", i);
+ GValue *value = g_hash_table_lookup (params, param_name);
+
+ g_string_append_c (string,
+ purple_account_user_split_get_separator (split));
+
+ if (value != NULL)
+ {
+ /* tp-glib should guarantee that this is a string. */
+ g_assert (G_VALUE_TYPE (value) == G_TYPE_STRING);
+ g_string_append (string, g_value_get_string (value));
+ }
+ else
+ {
+ g_string_append (string,
+ purple_account_user_split_get_default_value(split));
+ }
+
+ if (remove_params)
+ g_hash_table_remove (params, param_name);
+
+ g_free (param_name);
+ }
+
+ username = g_string_free (string, FALSE);
+ }
+ else
+ {
+ username = g_strdup (account);
+ }
+
+ if (remove_params)
+ g_hash_table_remove (params, "account");
+
+ return username;
+}
+
+static GHashTable *
+haze_protocol_translate_parameters (HazeProtocol *self,
+ GHashTable *asv)
+{
+ GHashTable *unused = g_hash_table_new (g_str_hash, g_str_equal);
+ GHashTable *purple_params = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) tp_g_value_slice_free);
+ const TpCMParamSpec *pspecs = haze_protocol_get_parameters (
+ (TpBaseProtocol *) self);
+ const TpCMParamSpec *pspec;
+
+ tp_g_hash_table_update (unused, asv, NULL, NULL);
+
+ for (pspec = pspecs; pspec->name != NULL; pspec++)
+ {
+ gchar *prpl_param_name = (gchar *) pspec->setter_data;
+ const GValue *value = tp_asv_lookup (asv, pspec->name);
+
+ if (value == NULL)
+ continue;
+
+ DEBUG ("setting parameter %s (telepathy name %s)", prpl_param_name,
+ pspec->name);
+
+ g_hash_table_insert (purple_params, prpl_param_name,
+ tp_g_value_slice_dup (value));
+ g_hash_table_remove (unused, pspec->name);
+ }
+
+ /* telepathy-glib isn't meant to give us parameters we don't understand */
+ g_assert (g_hash_table_size (unused) == 0);
+ g_hash_table_unref (unused);
+
+ return purple_params;
+}
+
+static TpBaseConnection *
+haze_protocol_new_connection (TpBaseProtocol *base,
+ GHashTable *asv,
+ GError **error)
+{
+ HazeProtocol *self = HAZE_PROTOCOL (base);
+ HazeConnection *conn;
+ gchar *username;
+ gchar *password;
+ GHashTable *purple_params = haze_protocol_translate_parameters (self, asv);
+
+ username = haze_protocol_get_username (purple_params, self->priv->prpl_info,
+ TRUE);
+ g_return_val_if_fail (username != NULL, FALSE);
+
+ password = g_strdup (tp_asv_get_string (purple_params, "password"));
+
+ if (password != NULL)
+ {
+ g_hash_table_remove (purple_params, "password");
+ }
+
+ conn = g_object_new (HAZE_TYPE_CONNECTION,
+ "protocol", tp_base_protocol_get_name (base),
+ "prpl-id", self->priv->prpl_id,
+ "prpl-info", self->priv->prpl_info,
+ "parameters", purple_params,
+ "username", username,
+ "password", password,
+ NULL);
+
+ g_hash_table_unref (purple_params);
+ g_free (username);
+ g_free (password);
+
+ if (!haze_connection_create_account (conn, error))
+ {
+ g_object_unref (conn);
+ return NULL;
+ }
+
+ return (TpBaseConnection *) conn;
+}
+
+static void
+haze_protocol_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ HazeProtocol *self = HAZE_PROTOCOL (object);
+
+ switch (property_id)
+ {
+ case PROP_KNOWN_PROTOCOL:
+ g_value_set_pointer (value, (gpointer) self->priv->known_protocol);
+ break;
+
+ case PROP_PLUGIN:
+ g_value_set_pointer (value, self->priv->plugin);
+ break;
+
+ case PROP_PRPL_ID:
+ g_value_set_string (value, self->priv->prpl_id);
+ break;
+
+ case PROP_PRPL_INFO:
+ g_value_set_pointer (value, self->priv->prpl_info);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+haze_protocol_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ HazeProtocol *self = HAZE_PROTOCOL (object);
+
+ switch (property_id)
+ {
+ case PROP_KNOWN_PROTOCOL:
+ g_assert (self->priv->known_protocol == NULL); /* construct-only */
+ self->priv->known_protocol = g_value_get_pointer (value);
+ break;
+
+ case PROP_PLUGIN:
+ g_assert (self->priv->plugin == NULL); /* construct-only */
+ self->priv->plugin = g_value_get_pointer (value);
+ break;
+
+ case PROP_PRPL_ID:
+ g_assert (self->priv->prpl_id == NULL); /* construct-only */
+ self->priv->prpl_id = g_value_dup_string (value);
+ break;
+
+ case PROP_PRPL_INFO:
+ g_assert (self->priv->prpl_info == NULL); /* construct-only */
+ self->priv->prpl_info = g_value_get_pointer (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+haze_protocol_finalize (GObject *object)
+{
+ GObjectFinalizeFunc finalize =
+ G_OBJECT_CLASS (haze_protocol_parent_class)->finalize;
+ HazeProtocol *self = HAZE_PROTOCOL (object);
+
+ g_free (self->priv->prpl_id);
+ g_free (self->priv->paramspecs);
+
+ if (finalize != NULL)
+ finalize (object);
+}
+
+static gchar *
+haze_protocol_normalize_contact (TpBaseProtocol *base,
+ const gchar *contact,
+ GError **error)
+{
+ /* FIXME: is it safe to pass a NULL account to prpl_info->normalize for all
+ * prpls? If it is, we could do that to be more likely to normalize right */
+ return g_strdup (purple_normalize (NULL, contact));
+}
+
+static gchar *
+haze_protocol_identify_account (TpBaseProtocol *base,
+ GHashTable *asv,
+ GError **error)
+{
+ HazeProtocol *self = HAZE_PROTOCOL (base);
+ GHashTable *purple_params = haze_protocol_translate_parameters (self, asv);
+ gchar *ret;
+
+ ret = haze_protocol_get_username (purple_params, self->priv->prpl_info,
+ FALSE);
+ g_hash_table_unref (purple_params);
+ return ret;
+}
+
+static GStrv
+haze_protocol_get_interfaces (TpBaseProtocol *base)
+{
+ return g_new0 (gchar *, 1);
+}
+
+static void
+haze_protocol_get_connection_details (TpBaseProtocol *base,
+ GStrv *connection_interfaces,
+ GType **channel_manager_types,
+ gchar **icon_name,
+ gchar **english_name,
+ gchar **vcard_field)
+{
+ HazeProtocol *self = HAZE_PROTOCOL (base);
+
+ if (connection_interfaces != NULL)
+ {
+ *connection_interfaces = g_strdupv (
+ (gchar **) haze_connection_get_implemented_interfaces ());
+ }
+
+ if (channel_manager_types != NULL)
+ {
+ GType types[] = { HAZE_TYPE_IM_CHANNEL_FACTORY,
+ HAZE_TYPE_CONTACT_LIST,
+#ifdef ENABLE_MEDIA
+ HAZE_TYPE_MEDIA_MANAGER,
+#endif
+ G_TYPE_INVALID };
+
+ *channel_manager_types = g_memdup (types, sizeof (types));
+ }
+
+ if (english_name != NULL)
+ *english_name = g_strdup (purple_plugin_get_name (self->priv->plugin));
+
+ if (icon_name != NULL)
+ {
+ /* guess from the protocol name, like TpProtocol and TpAccount do */
+ *icon_name = g_strdup_printf ("im-%s", tp_base_protocol_get_name (base));
+ }
+
+ if (vcard_field != NULL)
+ {
+ if (self->priv->known_protocol != NULL &&
+ self->priv->known_protocol->vcard_field != NULL)
+ {
+ /* this might be "", for cases where we know that there isn't an
+ * applicable vCard field, like local-xmpp and facebook */
+ *vcard_field = g_strdup (
+ self->priv->known_protocol->vcard_field);
+ }
+ else
+ {
+ gchar *p;
+
+ /* wild guess */
+ *vcard_field = g_strdup_printf ("x-%s",
+ tp_base_protocol_get_name (base));
+
+ for (p = *vcard_field; *p != '\0'; p++)
+ *p = g_ascii_tolower (*p);
+ }
+ }
+}
+
+static void
+haze_protocol_class_init (HazeProtocolClass *cls)
+{
+ GObjectClass *object_class = (GObjectClass *) cls;
+ TpBaseProtocolClass *base_class = (TpBaseProtocolClass *) cls;
+ GParamSpec *param_spec;
+
+ base_class->get_parameters = haze_protocol_get_parameters;
+ base_class->new_connection = haze_protocol_new_connection;
+ base_class->normalize_contact = haze_protocol_normalize_contact;
+ base_class->identify_account = haze_protocol_identify_account;
+ base_class->get_interfaces = haze_protocol_get_interfaces;
+ base_class->get_connection_details = haze_protocol_get_connection_details;
+
+ g_type_class_add_private (cls, sizeof (HazeProtocolPrivate));
+ object_class->get_property = haze_protocol_get_property;
+ object_class->set_property = haze_protocol_set_property;
+ object_class->finalize = haze_protocol_finalize;
+
+ param_spec = g_param_spec_pointer ("plugin", "PurplePlugin",
+ "Purple plugin",
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_PLUGIN, param_spec);
+
+ param_spec = g_param_spec_string ("prpl-id", "protocol plugin ID",
+ "protocol plugin ID", NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_PRPL_ID, param_spec);
+
+ param_spec = g_param_spec_pointer ("prpl-info", "PurplePluginProtocolInfo",
+ "protocol plugin info",
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_PRPL_INFO, param_spec);
+
+ param_spec = g_param_spec_pointer ("known-protocol", "KnownProtocolInfo",
+ "optional hard-coded info to override Haze's default guesses",
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_KNOWN_PROTOCOL,
+ param_spec);
+}
diff --git a/src/protocol.h b/src/protocol.h
new file mode 100644
index 0000000..dd1792d
--- /dev/null
+++ b/src/protocol.h
@@ -0,0 +1,69 @@
+/*
+ * Haze Protocol object
+ *
+ * Copyright © 2007 Will Thompson
+ * Copyright © 2007-2010 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __HAZE_PROTOCOL_H__
+#define __HAZE_PROTOCOL_H__
+
+#include <telepathy-glib/base-protocol.h>
+
+#include <libpurple/prpl.h>
+
+G_BEGIN_DECLS
+
+typedef struct _HazeProtocol HazeProtocol;
+typedef struct _HazeProtocolClass HazeProtocolClass;
+typedef struct _HazeProtocolPrivate HazeProtocolPrivate;
+
+GType haze_protocol_get_type (void) G_GNUC_CONST;
+
+#define HAZE_TYPE_PROTOCOL \
+ (haze_protocol_get_type ())
+#define HAZE_PROTOCOL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), HAZE_TYPE_PROTOCOL, \
+ HazeProtocol))
+#define HAZE_PROTOCOL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), HAZE_TYPE_PROTOCOL, \
+ HazeProtocolClass))
+#define HAZE_IS_PROTOCOL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HAZE_TYPE_PROTOCOL))
+#define HAZE_IS_PROTOCOL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), HAZE_TYPE_PROTOCOL))
+#define HAZE_PROTOCOL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), HAZE_TYPE_PROTOCOL, \
+ HazeProtocolClass))
+
+struct _HazeProtocolClass
+{
+ TpBaseProtocolClass parent;
+};
+
+struct _HazeProtocol
+{
+ TpBaseProtocol parent;
+ HazeProtocolPrivate *priv;
+};
+
+GList *haze_protocol_build_list (void);
+
+G_END_DECLS
+
+#endif
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index 7beaca7..f8e1fe2 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -1,5 +1,6 @@
TWISTED_TESTS = \
avatar-requirements.py \
+ cm/protocols.py \
connect/fail.py \
connect/success.py \
connect/twice-to-same-account.py \
diff --git a/tests/twisted/cm/protocols.py b/tests/twisted/cm/protocols.py
new file mode 100644
index 0000000..1367b73
--- /dev/null
+++ b/tests/twisted/cm/protocols.py
@@ -0,0 +1,191 @@
+"""
+Test Protocol objects.
+"""
+
+import dbus
+
+import constants as cs
+from servicetest import (assertEquals, tp_path_prefix, assertContains,
+ assertDoesNotContain, call_async)
+from hazetest import exec_test
+
+def test(q, bus, conn, stream):
+ # Ignore conn here, we're only dealing with the CM and Protocol objects.
+ cm = bus.get_object(cs.CM + '.haze',
+ tp_path_prefix + '/ConnectionManager/haze')
+ cm_iface = dbus.Interface(cm, cs.CM)
+ cm_props = dbus.Interface(cm, cs.PROPERTIES_IFACE)
+
+ protocols = cm_props.Get(cs.CM, 'Protocols')
+ protocol_names = cm_iface.ListProtocols()
+ assertEquals(set(protocols.iterkeys()), set(protocol_names))
+
+ for name in protocol_names:
+ props = protocols[name]
+ protocol = bus.get_object(cm.bus_name,
+ cm.object_path + '/' + name.replace('-', '_'))
+ protocol_iface = dbus.Interface(protocol, cs.PROTOCOL)
+ protocol_props = dbus.Interface(protocol, cs.PROPERTIES_IFACE)
+ flat_props = protocol_props.GetAll(cs.PROTOCOL)
+
+ parameters = cm_iface.GetParameters(name)
+ assertEquals(parameters, props[cs.PROTOCOL + '.Parameters'])
+ assertEquals(parameters, flat_props['Parameters'])
+ assertEquals(parameters, protocol_props.Get(cs.PROTOCOL, 'Parameters'))
+
+ assertEquals(flat_props['VCardField'],
+ props[cs.PROTOCOL + '.VCardField'])
+ assertEquals(flat_props['Interfaces'],
+ props[cs.PROTOCOL + '.Interfaces'])
+ assertEquals(flat_props['EnglishName'],
+ props[cs.PROTOCOL + '.EnglishName'])
+ assertEquals(flat_props['Icon'], props[cs.PROTOCOL + '.Icon'])
+ assertEquals(flat_props['ConnectionInterfaces'],
+ props[cs.PROTOCOL + '.ConnectionInterfaces'])
+ assertEquals(flat_props['RequestableChannelClasses'],
+ props[cs.PROTOCOL + '.RequestableChannelClasses'])
+
+ param_map = {}
+ param_flags = {}
+ param_type = {}
+ param_def = {}
+
+ for p in parameters:
+ param_map[p[0]] = tuple(p[1:])
+ param_flags[p[0]] = p[1]
+ param_type[p[0]] = p[2]
+ param_def[p[0]] = p[3]
+
+ # We use special cases to rename these; make sure they don't come back
+ assertDoesNotContain(name, ('meanwhile', 'simple'))
+ assertDoesNotContain('encoding', param_map)
+ assertDoesNotContain('local_charset', param_map)
+
+ if name not in ('local-xmpp', 'irc'):
+ # it would be more correct for these protocols not to have this
+ # parameter
+ assertEquals((cs.PARAM_REQUIRED, 's', ''), param_map['account'])
+
+ # a random selection of checks for known parameters...
+
+ if name == 'gadugadu':
+ assertEquals('x-gadugadu', flat_props['VCardField'])
+ assertEquals('im-gadugadu', flat_props['Icon'])
+ assertEquals((cs.PARAM_REQUIRED | cs.PARAM_SECRET, 's', ''),
+ param_map['password'])
+ assertEquals('s', param_type['nick'])
+ assertEquals('s', param_type['gg-server'])
+ elif name == 'silc':
+ assertEquals('x-silc', flat_props['VCardField'])
+ assertEquals('im-silc', flat_props['Icon'])
+ assertEquals((cs.PARAM_SECRET, 's', ''), param_map['password'])
+ assertEquals('s', param_type['server'])
+ elif name == 'irc':
+ assertEquals('x-irc', flat_props['VCardField'])
+ assertEquals('im-irc', flat_props['Icon'])
+ assertEquals((cs.PARAM_SECRET, 's', ''), param_map['password'])
+ assertEquals('s', param_type['charset'])
+ assertEquals('s', param_type['username'])
+ assertEquals('s', param_type['realname'])
+ assertEquals('s', param_type['server'])
+ assertEquals(cs.PARAM_HAS_DEFAULT, param_flags['server'])
+
+ assertEquals('smcv@irc.debian.org',
+ protocol_iface.IdentifyAccount({
+ 'account': 'smcv',
+ 'server': 'irc.debian.org'}))
+ elif name == 'myspace':
+ assertEquals('x-myspace', flat_props['VCardField'])
+ assertEquals('im-myspace', flat_props['Icon'])
+ assertEquals('s', param_type['server'])
+ elif name == 'yahoo':
+ assertEquals('x-yahoo', flat_props['VCardField'])
+ assertEquals('im-yahoo', flat_props['Icon'])
+ assertEquals('s', param_type['charset'])
+ elif name == 'yahoojp':
+ assertEquals('x-yahoo', flat_props['VCardField'])
+ assertEquals('im-yahoojp', flat_props['Icon'])
+ assertEquals('s', param_type['charset'])
+ elif name == 'aim':
+ assertEquals('x-aim', flat_props['VCardField'])
+ assertEquals('im-aim', flat_props['Icon'])
+ assertEquals('s', param_type['server'])
+ elif name == 'msn':
+ assertEquals('x-msn', flat_props['VCardField'])
+ assertEquals('im-msn', flat_props['Icon'])
+ assertEquals('s', param_type['server'])
+ elif name == 'jabber':
+ assertEquals('x-jabber', flat_props['VCardField'])
+ assertEquals('im-jabber', flat_props['Icon'])
+ assertDoesNotContain('require_tls', param_map)
+ assertDoesNotContain('connect_server', param_map)
+ assertEquals((cs.PARAM_SECRET, 's', ''), param_map['password'])
+ assertEquals((cs.PARAM_HAS_DEFAULT, 'b', True),
+ param_map['require-encryption'])
+
+ assertEquals('billg@example.com',
+ protocol_iface.IdentifyAccount({
+ 'account': 'billg@example.com',
+ 'password': 'letmein'}))
+ assertEquals(r'billg@example.com',
+ protocol_iface.IdentifyAccount({
+ 'account': 'billg@example.com',
+ 'server': r'corp.example.com',
+ 'password': 'letmein'}))
+
+ # this contains an unsupported parameter
+ call_async(q, protocol_iface, 'IdentifyAccount',
+ { 'account': 'billg@example.com',
+ 'embrace-and-extend': r'WORKGROUP\Bill',
+ 'password': 'letmein'})
+ q.expect('dbus-error', name=cs.INVALID_ARGUMENT)
+ elif name == 'qq':
+ assertEquals('x-qq', flat_props['VCardField'])
+ assertEquals('im-qq', flat_props['Icon'])
+ elif name == 'sametime':
+ assertEquals('x-sametime', flat_props['VCardField'])
+ assertEquals('im-sametime', flat_props['Icon'])
+ elif name == 'zephyr':
+ assertEquals('x-zephyr', flat_props['VCardField'])
+ assertEquals('im-zephyr', flat_props['Icon'])
+ assertEquals('s', param_type['realm'])
+ assertEquals('s', param_type['charset'])
+ elif name == 'local-xmpp':
+ # makes very little sense in an address book
+ assertEquals('', flat_props['VCardField'])
+ assertEquals('im-local-xmpp', flat_props['Icon'])
+ assertEquals((cs.PARAM_REQUIRED, 's', ''), param_map['first-name'])
+ assertDoesNotContain('first', param_map)
+ assertEquals((cs.PARAM_REQUIRED, 's', ''), param_map['last-name'])
+ assertDoesNotContain('last', param_map)
+ assertEquals((0, 's', ''), param_map['email'])
+ assertEquals((0, 's', ''), param_map['jid'])
+ elif name == 'icq':
+ assertEquals('x-icq', flat_props['VCardField'])
+ assertEquals('im-icq', flat_props['Icon'])
+ elif name == 'groupwise':
+ assertEquals('x-groupwise', flat_props['VCardField'])
+ assertEquals('im-groupwise', flat_props['Icon'])
+ elif name == 'sipe':
+ assertEquals('im-sipe', flat_props['Icon'])
+ assertDoesNotContain('usersplit1', param_map)
+ assertEquals((cs.PARAM_HAS_DEFAULT, 's', ''), param_map['login'])
+
+ assertEquals('billg@example.com,',
+ protocol_iface.IdentifyAccount({
+ 'account': 'billg@example.com',
+ 'password': 'letmein'}))
+ assertEquals(r'billg@example.com,WORKGROUP\Bill',
+ protocol_iface.IdentifyAccount({
+ 'account': 'billg@example.com',
+ 'login': r'WORKGROUP\Bill',
+ 'password': 'letmein'}))
+
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[1, 1])
+ conn.Disconnect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
+
+if __name__ == '__main__':
+ exec_test(test)
+
diff --git a/tests/twisted/constants.py b/tests/twisted/constants.py
index 7c57a7e..2ca2865 100644
--- a/tests/twisted/constants.py
+++ b/tests/twisted/constants.py
@@ -349,3 +349,10 @@ MT_ACTION = 1
MT_NOTICE = 2
MT_AUTO_REPLY = 3
MT_DELIVERY_REPORT = 4
+
+PROTOCOL = 'org.freedesktop.Telepathy.Protocol.DRAFT'
+PARAM_REQUIRED = 1
+PARAM_REGISTER = 2
+PARAM_HAS_DEFAULT = 4
+PARAM_SECRET = 8
+PARAM_DBUS_PROPERTY = 16