diff options
197 files changed, 6683 insertions, 17883 deletions
@@ -36,7 +36,6 @@ Android.mk Makefile Makefile.in -_gen/ INSTALL aclocal.m4 autom4te.cache diff --git a/Makefile.am b/Makefile.am index 858f2be7..f0ee6f39 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,7 +10,6 @@ SUBDIRS = \ data \ m4 \ tools \ - xml \ mission-control-plugins \ plugins \ src \ @@ -30,7 +29,7 @@ DISTCHECK_CONFIGURE_FLAGS = \ maintainer-upload-release: _maintainer-upload-release-local _maintainer-upload-release-local: _maintainer-upload-release-check rsync -rvzPp --chmod=Dg+s,ug+rwX,o=rX doc/reference/mission-control-plugins/html/ \ - telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/doc/mission-control-plugins-5.16.x/ + telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/doc/mission-control-plugins/ include tools/lcov.am include tools/telepathy.am @@ -1,3 +1,81 @@ +telepathy-mission-control 5.17.0 (UNRELEASED) +============================================= + +Running this version of Mission Control for the first time will automatically +migrate some account data to a new format. If you subsequently downgrade +to an older version, it will no longer understand all account data. +Taking a backup copy of ~/.local/share/telepathy/mission-control +before you upgrade is recommended. + +Incompatible changes: + +• The mission-control-plugins library has broken API/ABI; deprecated functions + and the "D-Bus ACL" plugin API have been removed. For plugins not using + this functionality (e.g. Empathy's GNOME Online Accounts integration), + a simple recompile should be sufficient. + +• mcp_dispatch_operation_ref_connection(), + mcp_dispatch_operation_find_channel_by_type() and + mcp_dispatch_operation_ref_nth_channel() now take a + TpSimpleClientFactory argument. + +• The mcp_account_storage_create() virtual method now takes a single string + (the result of IdentifyAccount()), instead of a GHashTable (containing all + the new account's initial parameters). + +• Channel requests that specify an emergency service point by handle, + rather than by ID, are no longer treated specially. Channel requests + to an emergency service point by its normalized identifier (e.g. + from a user dialling 911 on a phone keypad) still get special treatment: + request policy plugins are bypassed for these requests. + +• The Account.I.Hidden and AccountManager.I.Hidden interfaces have been + removed. See fd.o #70427 for a somewhat similar idea. + +• The BypassObservers feature has been removed. See fd.o #30043 + for attempts to design a better version. + +• The Account.I.Conditions interface has finally been removed. + Most of its supporting code was already deleted between 5.14 and 5.16. + +• The ChannelDispatcher.I.Messages.DRAFT interface is now called + ChannelDispatcher.I.Messages1. This will require some minor changes to + Telepathy-Qt, its only known user. + +• Support for Maemo features (Aegis, MCE and an old libaccounts-glib + account storage plugin) has been removed. Users of modern libaccounts-glib + versions (like the one in Ubuntu) should use the UOA plugin in Empathy, + or help us to disentangle it from Empathy and add it to Mission Control + (fd.o #66459). + +• Account.I.ExternalPasswordStorage and CM.I.AccountStorage have been + removed (fd.o #33485, #44512, #69006) + +Dependencies: + +• GLib ≥ 2.36 (including GObject, GModule and GIO) is now required +• telepathy-glib ≥ 0.23.1 is now required + +Enhancements: + +• Use IdentifyAccount to name new accounts (fd.o #34640, Simon) + +• Store accounts in one file per account, with a GVariant-based + text format that preserves data types (fd.o #54875, Simon) + +• Re-save parameters with their types when edited, or when we discover + what type the connection manager expects (fd.o #71093, Simon) + +• Use telepathy-glib 0.23 for CD.I.Messages (fd.o #37380, Simon) + +Fixes: + +• UPower is no longer an optional dependency, since the API we used + has been removed. This version only supports "take accounts offline before + suspending" on systems with either systemd-logind, or a reimplementation + with the same D-Bus API. (fd.o #70458, Simon) + + telepathy-mission-control 5.16.5 (2019-09-15) ============================================= diff --git a/configure.ac b/configure.ac index 4289f333..d1f7e37e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,12 +1,12 @@ dnl Set the version number to e.g. 5.x.y immediately before a release. dnl Set the version number to e.g. 5.x.y+ immediately after (this will dnl enable -Werror). -AC_INIT([telepathy-mission-control], [5.16.5]) +AC_INIT([telepathy-mission-control], [5.16.99+]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR(Makefile.am) -AM_INIT_AUTOMAKE([1.9 tar-ustar -Wno-portability]) +AM_INIT_AUTOMAKE([1.9 tar-ustar -Wno-portability subdir-objects]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_CONFIG_HEADERS(config.h) @@ -17,7 +17,7 @@ AC_PROG_CPP AC_PROG_CXX AC_PROG_INSTALL AC_PROG_LIBTOOL -AM_PROG_MKDIR_P +AC_PROG_MKDIR_P AC_HEADER_STDC AC_CHECK_HEADERS([sys/stat.h sys/types.h sysexits.h]) @@ -35,7 +35,7 @@ esac # If API has been removed or changed since last release, change MCP_API (which # is part of the directory name under /usr/include) to the version that # changed it -MCP_API_VERSION=5.5 +MCP_API_VERSION=5.18 AC_SUBST([MCP_API_VERSION]) # If ABI has been removed or changed since last release @@ -46,9 +46,9 @@ AC_SUBST([MCP_API_VERSION]) # set revision to 0 # else if library source has changed since last release # increment revision -MCP_LT_CURRENT=5 -MCP_LT_REVISION=1 -MCP_LT_AGE=5 +MCP_LT_CURRENT=6 +MCP_LT_REVISION=0 +MCP_LT_AGE=0 AC_SUBST([MCP_LT_CURRENT]) AC_SUBST([MCP_LT_REVISION]) AC_SUBST([MCP_LT_AGE]) @@ -74,7 +74,6 @@ TP_COMPILER_WARNINGS([ERROR_CFLAGS], init-self \ ], [missing-field-initializers \ - deprecated-declarations \ unused-parameter]) AC_SUBST([ERROR_CFLAGS]) @@ -95,24 +94,23 @@ AC_MSG_RESULT([$mc_want_twisted_tests]) AM_CONDITIONAL([WANT_TWISTED_TESTS], test yes = "$mc_want_twisted_tests") AC_ARG_ENABLE(debug, [ --disable-debug compile without debug code],[enable_debug=${enableval}], enable_debug=yes ) -if test "x$enable_debug" = "xyes"; then - CFLAGS="$CFLAGS -DENABLE_DEBUG" -fi +AS_IF([test "x$enable_debug" = "xyes"], + [AC_DEFINE([ENABLE_DEBUG], [1], [Define to compile in debug messages])]) AC_ARG_ENABLE(cast-checks, [ --disable-cast-checks compile with GLIB cast checks disabled],[cchecks=${enableval}],cchecks=yes) -if test "x$cchecks" = "xno"; then - CFLAGS="$CFLAGS -DG_DISABLE_CAST_CHECKS" -fi +AS_IF([test "x$cchecks" = "xno"], + [AC_DEFINE([G_DISABLE_CAST_CHECKS], [1], + [Define to disable checked casts (not recommended)])]) AC_ARG_ENABLE(asserts, [ --disable-asserts compile with GLIB assertions disabled],[asserts=${enableval}],asserts=yes) -if test "x$asserts" = "xno"; then - CFLAGS="$CFLAGS -DG_DISABLE_ASSERTS" -fi +AS_IF([test "x$asserts" = "xno"], + [AC_DEFINE([G_DISABLE_ASSERTS], [1], + [Define to disable assertions (not recommended)])]) AC_ARG_ENABLE(checks, [ --disable-checks compile with GLIB checks disabled],[checks=${enableval}],checks=yes) -if test "x$checks" = "xno"; then - CFLAGS="$CFLAGS -DG_DISABLE_CHECKS" -fi +AS_IF([test "x$checks" = "xno"], + [AC_DEFINE([G_DISABLE_CHECKS], [1], + [Define to disable checks (not recommended)])]) AC_ARG_ENABLE(coverage, [ --enable-coverage compile with coverage info],[enable_coverage=${enableval}],enable_coverage=no) if test "x$enable_coverage" = "xyes"; then @@ -152,85 +150,17 @@ fi AC_SUBST(ACCOUNTS_CACHE_DIR) AC_DEFINE_UNQUOTED(ACCOUNTS_CACHE_DIR,"$ACCOUNTS_CACHE_DIR", [Directory for account/connection mapping for crash recovery]) -aegis_enabled="no" -AC_MSG_CHECKING([whether to build with Aegis (libcreds) support]) -AC_ARG_ENABLE(aegis, - AC_HELP_STRING([--enable-aegis], - [Aegis security framework support]), - [ - AC_CHECK_LIB(creds, creds_init, - [ - AC_DEFINE([ENABLE_AEGIS], [1], - [Define if Aegis is supported]) - AC_SUBST([AEGIS_LIBS], [-lcreds]) - ], - [ AC_MSG_ERROR([Aegis: libcreds not found]) ]) - aegis_enabled="yes" - ], - [ - aegis_enabled="no" - AC_MSG_RESULT(no) - AC_DEFINE([ENABLE_AEGIS], [0], [Define if Aegis is supported]) - ]) - -AM_CONDITIONAL(ENABLE_AEGIS, [test x$aegis_enabled = xyes]) - PKG_PROG_PKG_CONFIG() -dnl libaccounts-glib SSO -libaccounts_sso_enabled="no" -AC_MSG_CHECKING(whether to build with libaccounts-glib SSO suport) - -AC_ARG_ENABLE([libaccounts-sso], - AC_HELP_STRING([--enable-libaccounts-sso], - [build with SSO suport. default=no]), - [ AC_MSG_RESULT(${enableval}) - libaccounts_sso_enabled="${enableval}" ], - [ AC_MSG_RESULT(no) ] ) - -AS_IF([ test "x$libaccounts_sso_enabled" = xauto ], - [ PKG_CHECK_EXISTS([libaccounts-glib], - [libaccounts_sso_enabled=yes], - [libaccounts_sso_enabled=no]) ]) - -dnl set up the shell/make variable, the #define and check the package version -AS_IF([ test "x$libaccounts_sso_enabled" = xyes ], - [ ENABLE_LIBACCOUNTS_SSO=yes - AC_DEFINE(ENABLE_LIBACCOUNTS_SSO, [1], - [Define if libaccounts SSO support is required]) - PKG_CHECK_MODULES([LIBACCOUNTS_SSO], [libaccounts-glib >= 0.26]) ], - [ AC_DEFINE(ENABLE_LIBACCOUNTS_SSO, [0]) ]) - -dnl export the CFLAGS and LDFLAGS equivalents determined by PKG_CHECK_MODULES -AC_SUBST(LIBACCOUNTS_SSO_CFLAGS) -AC_SUBST(LIBACCOUNTS_SSO_LIBS) - -dnl the automake conditional -AM_CONDITIONAL(ENABLE_LIBACCOUNTS_SSO, [test x$libaccounts_sso_enabled = xyes]) -dnl /libaccounts-glib SSO - -AC_ARG_WITH([accounts-glib-hidden-service-type], - [AS_HELP_STRING([--with-accounts-glib-hidden-service-type], - [mark accounts with the provided service type as hidden @<:@default=no@:>@])], - [], - [with_accounts_glib_hidden_service_type=no]) -AS_IF([ test "x$with_accounts_glib_hidden_service_type" != xno ], - [AC_DEFINE_UNQUOTED(ACCOUNTS_GLIB_HIDDEN_SERVICE_TYPE, - ["$with_accounts_glib_hidden_service_type"], - [Defined to the non-IM service type for hidden accounts, if any])]) - -AM_CONDITIONAL(ENABLE_LIBACCOUNTS_GLIB_HIDDEN, - [test x$with_accounts_glib_hidden_service_type != xno]) - PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.95, dbus-glib-1 >= 0.82]) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) -PKG_CHECK_MODULES([TELEPATHY], [telepathy-glib >= 0.20.0]) -AC_DEFINE([TP_VERSION_MIN_REQUIRED], [TP_VERSION_0_18], - [Ignore post-0.18 deprecations]) -AC_DEFINE([TP_VERSION_MAX_ALLOWED], [TP_VERSION_0_20], - [Prevent post-0.20 APIs]) +PKG_CHECK_MODULES([TELEPATHY], [telepathy-glib >= 0.23.1]) +AC_DEFINE([TP_VERSION_MIN_REQUIRED], [TP_VERSION_0_24], + [Ignore post-0.24 deprecations]) +AC_DEFINE([TP_VERSION_MAX_ALLOWED], [TP_VERSION_0_24], + [Prevent post-0.24 APIs]) AC_DEFINE([TP_SEAL_ENABLE], [], [Define to hide deprecated struct fields]) AC_DEFINE([TP_DISABLE_SINGLE_INCLUDE], [], [Avoid individual headers]) @@ -248,13 +178,6 @@ PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0], AC_DEFINE([GLIB_VERSION_MIN_REQUIRED], [GLIB_VERSION_2_46], [Ignore post 2.46 deprecations]) AC_DEFINE([GLIB_VERSION_MAX_ALLOWED], [GLIB_VERSION_2_46], [Prevent post 2.46 APIs]) -dnl Check for MCE, a Maemo service used to determine when the device is idle. -PKG_CHECK_MODULES([MCE], mce >= 1.5, [HAVE_MCE=yes], [HAVE_MCE=no]) -AM_CONDITIONAL([HAVE_MCE], [test x"$HAVE_MCE" = xyes]) -if test x"$HAVE_MCE" = xyes; then - AC_DEFINE([HAVE_MCE], [1], [Define if mce is available]) -fi - # ----------------------------------------------------------- # Connectivity integration # ----------------------------------------------------------- @@ -296,34 +219,6 @@ if test x"$enable_conn_setting" = xyes; then fi AM_CONDITIONAL([ENABLE_CONN_SETTING], [test x"$enable_conn_setting" = xyes]) -# ----------------------------------------------------------- -# Suspend/resume tracking goop -# ----------------------------------------------------------- - -AC_ARG_ENABLE([upower], - [AS_HELP_STRING([--enable-upower], - [monitor device suspending and resuming using upower-glib @<:@default=auto@:>@])], - [], - [enable_upower=auto]) - -if test "x$enable_upower" != xno; then - PKG_CHECK_MODULES([UPOWER_GLIB], [upower-glib < 0.99], - [AC_DEFINE([HAVE_UPOWER], [1], [Define to use upower-glib]) - have_upower=yes - ], - [if test "x$enable_upower" == xyes; then - AC_MSG_ERROR([$UPOWER_GLIB_PKG_ERRORS]) - else - have_upower=no - fi - ]) -else - have_upower=no -fi - -AC_SUBST([UPOWER_GLIB_CFLAGS]) -AC_SUBST([UPOWER_GLIB_LIBS]) - dnl *************************************************************************** dnl Check for marshal and enum generators dnl *************************************************************************** @@ -356,7 +251,6 @@ tests/twisted/Makefile \ tests/twisted/tools/Makefile \ tools/Makefile \ util/Makefile \ -xml/Makefile \ ]) echo " @@ -377,11 +271,5 @@ Configure summary: Features: Plugin API documentation.....: ${enable_gtk_doc} Network Manager integration..: ${have_nm} - ConnMan integration..........: ${have_connman} Connectivity GSetting........: ${enable_conn_setting} - Suspend tracking with UPower.: ${have_upower} - Aegis........................: ${aegis_enabled} - libaccounts-glib backend.....: ${libaccounts_sso_enabled} - Hidden accounts-glib accounts: ${with_accounts_glib_hidden_service_type} - Nokia Mode Control Entity....: ${HAVE_MCE} " diff --git a/doc/Makefile.am b/doc/Makefile.am index 41c97bc9..f3ddc22d 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,14 +1 @@ SUBDIRS = reference -SPECS = $(top_srcdir)/xml/all.xml -SPECS_DOC = mc-dbus-iface.html - -all-local: $(SPECS_DOC) - -$(SPECS_DOC): $(top_srcdir)/xml/all.xml $(wildcard $(top_srcdir)/xml/*.xml) $(top_srcdir)/tools/doc-generator.xsl - $(XSLTPROC) --xinclude --novalid \ - --param allow-undefined-interfaces "true()" \ - $(top_srcdir)/tools/doc-generator.xsl $< > $@ - -clean-local: - rm -f $(SPECS_DOC) - diff --git a/doc/reference/mission-control-plugins/Makefile.am b/doc/reference/mission-control-plugins/Makefile.am index 65ec75e2..061d1b77 100644 --- a/doc/reference/mission-control-plugins/Makefile.am +++ b/doc/reference/mission-control-plugins/Makefile.am @@ -145,6 +145,14 @@ check-local: echo "$$file missing from $(DOC_MODULE)-docs.sgml"; \ err=1; \ fi; \ - done + done; \ + case $(ENABLE_DOC_CHECKS)/$$err in \ + (yes/1) \ + echo "Documentation check failed" >&2; \ + exit $$err; \ + ;; \ + (*) \ + exit 0; \ + ;; \ + esac endif -# ... but don't fail on incomplete documentation, this is a stable branch diff --git a/doc/reference/mission-control-plugins/mission-control-plugins-docs.sgml b/doc/reference/mission-control-plugins/mission-control-plugins-docs.sgml index 77e663f7..7ca6b9fc 100644 --- a/doc/reference/mission-control-plugins/mission-control-plugins-docs.sgml +++ b/doc/reference/mission-control-plugins/mission-control-plugins-docs.sgml @@ -8,8 +8,8 @@ <bookinfo> <title>Mission Control Plugins Reference Manual</title> <releaseinfo> - The latest version of this documentation in this stable branch can be found on-line at - <ulink role="online-location" url="http://telepathy.freedesktop.org/doc/mission-control-plugins-5.16.x/">http://telepathy.freedesktop.org/doc/mission-control-plugins-5.16.x/</ulink>. + The latest version of this documentation can be found on-line at + <ulink role="online-location" url="http://telepathy.freedesktop.org/doc/mission-control-plugins/">http://telepathy.freedesktop.org/doc/mission-control-plugins/</ulink>. </releaseinfo> </bookinfo> diff --git a/mission-control-plugins/Makefile.am b/mission-control-plugins/Makefile.am index 1c6abf15..ae4c848b 100644 --- a/mission-control-plugins/Makefile.am +++ b/mission-control-plugins/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = \ +AM_CPPFLAGS = \ $(TELEPATHY_CFLAGS) \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ @@ -21,7 +21,6 @@ plugins_include_HEADERS = \ mission-control-plugins.h \ account.h \ account-storage.h \ - dbus-acl.h \ debug.h \ dispatch-operation.h \ dispatch-operation-policy.h \ @@ -54,7 +53,6 @@ libmission_control_plugins_la_SOURCES = \ debug.c \ account.c \ account-storage.c \ - dbus-acl.c \ dispatch-operation.c \ dispatch-operation-policy.c \ implementation.h \ @@ -71,7 +69,7 @@ Android.mk: Makefile.am $(BUILT_SOURCES) -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ -:SOURCES $(BUILT_SOURCES) $(libmission_control_plugins_la_SOURCES) \ -:CFLAGS $(DEFAULT_INCLUDES) $(DEFS) $(CFLAGS) $(AM_CFLAGS) \ - -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) $(INCLUDES) \ + -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) \ -:LDFLAGS $(libmission_control_plugins_la_LIBADD) \ > $@ diff --git a/mission-control-plugins/account-storage.c b/mission-control-plugins/account-storage.c index 3991c0cd..081597c8 100644 --- a/mission-control-plugins/account-storage.c +++ b/mission-control-plugins/account-storage.c @@ -56,18 +56,19 @@ * iface->desc = "The FOO storage backend"; * iface->provider = "org.freedesktop.Telepathy.MissionControl5.FooStorage"; * - * iface->get = foo_plugin_get; - * iface->set = foo_plugin_get; - * iface->delete = foo_plugin_delete; + * iface->get_flags = foo_plugin_get_flags; + * iface->delete_async = foo_plugin_delete_async; + * iface->delete_finish = foo_plugin_delete_finish; * iface->commit = foo_plugin_commit; - * iface->commit_one = foo_plugin_commit_one; * iface->list = foo_plugin_list; - * iface->ready = foo_plugin_ready; * iface->get_identifier = foo_plugin_get_identifier; * iface->get_additional_info = foo_plugin_get_additional_info; * iface->get_restrictions = foo_plugin_get_restrictions; * iface->create = foo_plugin_create; - * iface->owns = foo_plugin_owns; + * iface->get_attribute = foo_plugin_get_attribute; + * iface->get_parameter = foo_plugin_get_parameter; + * iface->list_typed_parameters = foo_plugin_list_typed_parameters; + * iface->list_untyped_parameters = foo_plugin_list_untyped_parameters; * iface->set_attribute = foo_plugin_set_attribute; * iface->set_parameter = foo_plugin_set_parameter; * } @@ -100,10 +101,18 @@ #endif /* ENABLE_DEBUG */ +/** + * McpAccountStorageFlags: + * @MCP_ACCOUNT_STORAGE_FLAG_NONE: no particular flags + * @MCP_ACCOUNT_STORAGE_FLAG_STORES_TYPES: this backend can store parameter + * values' types + * + * Flags describing the features and capabilities of a backend. + */ + enum { CREATED, - ALTERED, TOGGLED, DELETED, ALTERED_ONE, @@ -113,17 +122,72 @@ enum static guint signals[NO_SIGNAL] = { 0 }; -static gboolean -default_set (const McpAccountStorage *storage, - const McpAccountManager *am, +static void +default_delete_async (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account, - const gchar *key, - const gchar *val) + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - return FALSE; + g_task_report_new_error (storage, callback, user_data, + default_delete_async, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, + "This storage plugin cannot delete accounts"); +} + +static gboolean +default_delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); } static gboolean +default_commit (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account) +{ + return FALSE; +} + +static gchar * +default_create (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + const gchar *identification, + GError **error) +{ + g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, + "This storage does not implement the create() function"); + return NULL; +} + +static void +default_get_identifier (McpAccountStorage *storage, + const gchar *account, + GValue *identifier) +{ + g_value_init (identifier, G_TYPE_STRING); + g_value_set_string (identifier, account); +} + +static GHashTable * +default_get_additional_info (McpAccountStorage *storage, + const gchar *account) +{ + return g_hash_table_new (g_str_hash, g_str_equal); +} + +static TpStorageRestrictionFlags +default_get_restrictions (McpAccountStorage *storage, + const gchar *account) +{ + return 0; +} + +static McpAccountStorageSetResult default_set_attribute (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, @@ -131,10 +195,10 @@ default_set_attribute (McpAccountStorage *storage, GVariant *value, McpAttributeFlags flags) { - return FALSE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED; } -static gboolean +static McpAccountStorageSetResult default_set_parameter (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, @@ -142,20 +206,22 @@ default_set_parameter (McpAccountStorage *storage, GVariant *value, McpParameterFlags flags) { - return FALSE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED; } -static gboolean -default_owns (McpAccountStorage *storage, +static gchar ** +default_list_untyped_parameters (McpAccountStorage *storage, McpAccountManager *am, const gchar *account) { - /* This has the side-effect of pushing the "manager" key back into @am, - * but that should be a no-op in practice: we always call this - * method in priority order and stop at the first one that says "yes", - * and @am's idea of what "manager" is should have come from that same - * plugin anyway. */ - return mcp_account_storage_get (storage, am, account, "manager"); + return NULL; +} + +static McpAccountStorageFlags +default_get_flags (McpAccountStorage *storage, + const gchar *account) +{ + return MCP_ACCOUNT_STORAGE_FLAG_NONE; } static void @@ -165,10 +231,17 @@ class_init (gpointer klass, GType type = G_TYPE_FROM_CLASS (klass); McpAccountStorageIface *iface = klass; - iface->owns = default_owns; - iface->set = default_set; + iface->get_flags = default_get_flags; + iface->create = default_create; + iface->delete_async = default_delete_async; + iface->delete_finish = default_delete_finish; + iface->commit = default_commit; + iface->get_identifier = default_get_identifier; + iface->get_additional_info = default_get_additional_info; + iface->get_restrictions = default_get_restrictions; iface->set_attribute = default_set_attribute; iface->set_parameter = default_set_parameter; + iface->list_untyped_parameters = default_list_untyped_parameters; if (signals[CREATED] != 0) { @@ -183,8 +256,10 @@ class_init (gpointer klass, * Emitted if an external entity creates an account * in the backend the emitting plugin handles. * - * Should not be fired until mcp_account_storage_ready() has been called - * + * This signal does not need to be emitted before mcp_account_storage_list() + * returns (if it is, it will be ignored). All accounts that exist + * at the time that mcp_account_storage_list() returns must be included + * in its result, even if they were also signalled via this signal. */ signals[CREATED] = g_signal_new ("created", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, @@ -192,41 +267,23 @@ class_init (gpointer klass, 1, G_TYPE_STRING); /** - * McpAccountStorage::altered - * @account: the unique name of the altered account - * - * This signal does not appear to be fully implemented - * (see <ulink href="https://bugs.freedesktop.org/show_bug.cgi?id=28288" - * >freedesktop.org bug 28288</ulink>). - * Emit #McpAccountStorage::altered-one instead. - * - * Should not be fired until mcp_account_storage_ready() has been called - * - */ - signals[ALTERED] = g_signal_new ("altered", - type, G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED, 0, NULL, NULL, - g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, - 1, G_TYPE_STRING); - - /** * McpAccountStorage::altered-one * @account: the unique name of the altered account - * @name: the name of the altered property (its key) + * @name: either an attribute name such as DisplayName, + * or "param-" plus a parameter name, e.g. "param-require-encryption" * * Emitted if an external entity alters an account * in the backend that the emitting plugin handles. * - * Before emitting this signal, the plugin must call - * either mcp_account_manager_set_attribute(), - * either mcp_account_manager_set_parameter() or - * mcp_account_manager_set_value() to push the new value - * into the account manager. + * Before emitting this signal, the plugin must update its + * internal cache (if any) so that mcp_account_storage_get_attribute() + * or mcp_account_storage_get_parameter() will return the new value + * when queried. * - * Note that mcp_account_manager_set_parameter() does not use the - * "param-" prefix, but this signal and mcp_account_manager_set_value() - * both do. - * - * Should not be fired until mcp_account_storage_ready() has been called + * Note that mcp_account_storage_get_parameter(), + * mcp_account_storage_list_typed_parameters() and + * mcp_account_storage_set_parameter() do not use the + * "param-" prefix, but this signal does. */ signals[ALTERED_ONE] = g_signal_new ("altered-one", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, @@ -240,9 +297,6 @@ class_init (gpointer klass, * * Emitted if an external entity deletes an account * in the backend the emitting plugin handles. - * - * Should not be fired until mcp_account_storage_ready() has been called - * */ signals[DELETED] = g_signal_new ("deleted", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, @@ -257,11 +311,11 @@ class_init (gpointer klass, * Emitted if an external entity enables/disables an account * in the backend the emitting plugin handles. This is similar to * emitting #McpAccountStorage::altered-one for the attribute - * "Enabled", except that the plugin is not required to call - * a function like mcp_account_manager_set_value() first. - * - * Should not be fired until mcp_account_storage_ready() has been called + * "Enabled". * + * Before emitting this signal, the plugin must update its + * internal cache (if any) so that mcp_account_storage_get_attribute() + * will return the new value for Enabled when queried. */ signals[TOGGLED] = g_signal_new ("toggled", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, @@ -274,8 +328,6 @@ class_init (gpointer klass, * * emitted if an external entity modified important parameters of the * account and a reconnection is required in order to apply them. - * - * Should not be fired until mcp_account_storage_ready() has been called **/ signals[RECONNECT] = g_signal_new ("reconnect", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, @@ -328,131 +380,28 @@ mcp_account_storage_get_type (void) * @name: returned by mcp_account_storage_name() * @desc: returned by mcp_account_storage_description() * @provider: returned by mcp_account_storage_provider() - * @set: implementation of mcp_account_storage_set() - * @get: implementation of mcp_account_storage_get() - * @delete: implementation of mcp_account_storage_delete() + * @delete_async: implementation of mcp_account_storage_delete_async() + * @delete_finish: implementation of mcp_account_storage_delete_finish() * @commit: implementation of mcp_account_storage_commit() * @list: implementation of mcp_account_storage_list() - * @ready: implementation of mcp_account_storage_ready() - * @commit_one: implementation of mcp_account_storage_commit_one() * @get_identifier: implementation of mcp_account_storage_get_identifier() * @get_additional_info: implementation of * mcp_account_storage_get_additional_info() * @get_restrictions: implementation of mcp_account_storage_get_restrictions() * @create: implementation of mcp_account_storage_create() + * @get_attribute: implementation of mcp_account_storage_get_attribute() + * @get_parameter: implementation of mcp_account_storage_get_parameter() + * @set_attribute: implementation of mcp_account_storage_set_attribute() + * @set_parameter: implementation of mcp_account_storage_set_parameter() + * @list_typed_parameters: implementation + * of mcp_account_storage_list_typed_parameters() + * @list_untyped_parameters: implementation + * of mcp_account_storage_list_untyped_parameters() + * @get_flags: implementation of mcp_account_storage_get_flags() * * The interface vtable for an account storage plugin. */ -void -mcp_account_storage_iface_set_priority (McpAccountStorageIface *iface, - guint prio) -{ - iface->priority = prio; -} - -void -mcp_account_storage_iface_set_name (McpAccountStorageIface *iface, - const gchar *name) -{ - iface->name = name; -} - -void -mcp_account_storage_iface_set_desc (McpAccountStorageIface *iface, - const gchar *desc) -{ - iface->desc = desc; -} - -void -mcp_account_storage_iface_set_provider (McpAccountStorageIface *iface, - const gchar *provider) -{ - iface->provider = provider; -} - -void -mcp_account_storage_iface_implement_get (McpAccountStorageIface *iface, - McpAccountStorageGetFunc method) -{ - iface->get = method; -} - -void -mcp_account_storage_iface_implement_set (McpAccountStorageIface *iface, - McpAccountStorageSetFunc method) -{ - iface->set = method; -} - -void -mcp_account_storage_iface_implement_delete (McpAccountStorageIface *iface, - McpAccountStorageDeleteFunc method) -{ - iface->delete = method; -} - -void -mcp_account_storage_iface_implement_commit (McpAccountStorageIface *iface, - McpAccountStorageCommitFunc method) -{ - iface->commit = method; -} - -void -mcp_account_storage_iface_implement_commit_one (McpAccountStorageIface *iface, - McpAccountStorageCommitOneFunc method) -{ - iface->commit_one = method; -} - -void -mcp_account_storage_iface_implement_list (McpAccountStorageIface *iface, - McpAccountStorageListFunc method) -{ - iface->list = method; -} - -void -mcp_account_storage_iface_implement_ready (McpAccountStorageIface *iface, - McpAccountStorageReadyFunc method) -{ - iface->ready = method; -} - -void -mcp_account_storage_iface_implement_get_identifier ( - McpAccountStorageIface *iface, - McpAccountStorageGetIdentifierFunc method) -{ - iface->get_identifier = method; -} - -void -mcp_account_storage_iface_implement_get_additional_info ( - McpAccountStorageIface *iface, - McpAccountStorageGetAdditionalInfoFunc method) -{ - iface->get_additional_info = method; -} - -void -mcp_account_storage_iface_implement_get_restrictions ( - McpAccountStorageIface *iface, - McpAccountStorageGetRestrictionsFunc method) -{ - iface->get_restrictions = method; -} - -void -mcp_account_storage_iface_implement_create ( - McpAccountStorageIface *iface, - McpAccountStorageCreate method) -{ - iface->create = method; -} - /** * mcp_account_storage_priority: * @storage: an #McpAccountStorage instance @@ -488,7 +437,7 @@ mcp_account_storage_iface_implement_create ( * Returns: the priority of this plugin **/ gint -mcp_account_storage_priority (const McpAccountStorage *storage) +mcp_account_storage_priority (McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); @@ -498,111 +447,168 @@ mcp_account_storage_priority (const McpAccountStorage *storage) } /** - * McpAccountStorageGetFunc: - * @storage: the account storage plugin - * @am: object used to call back into the account manager + * mcp_account_storage_get_attribute: + * @storage: an #McpAccountStorage instance + * @am: an #McpAccountManager instance * @account: the unique name of the account - * @key: the setting whose value we wish to fetch: either an attribute - * like "DisplayName", or "param-" plus a parameter like "account" + * @attribute: the name of an attribute, e.g. "DisplayName" + * @type: the expected type of @attribute, as a hint for + * legacy account storage plugins that do not store attributes' types + * @flags: (allow-none) (out): used to return attribute flags + * + * Retrieve an attribute. + * + * There is no default implementation. + * All account storage plugins must override this method. * - * An implementation of mcp_account_storage_get(). + * The returned variant does not necessarily have to match @type: + * Mission Control will coerce it to an appropriate type if required. In + * particular, plugins that store strongly-typed attributes may return + * the stored type, not the expected type, if they differ. * - * Returns: %TRUE if @storage is responsible for @account + * Returns: (transfer full): the value of the attribute, or %NULL if it + * is not present */ +GVariant * +mcp_account_storage_get_attribute (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) +{ + McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + + SDEBUG (storage, "%s.%s (type '%.*s')", account, attribute, + (int) g_variant_type_get_string_length (type), + g_variant_type_peek_string (type)); + + g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->get_attribute != NULL, FALSE); + + return iface->get_attribute (storage, am, account, attribute, type, flags); +} /** - * mcp_account_storage_get: + * mcp_account_storage_get_parameter: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: the unique name of the account - * @key: the setting whose value we wish to fetch: either an attribute - * like "DisplayName", or "param-" plus a parameter like "account" - * - * Get a value from the plugin's in-memory cache. - * Before emitting this signal, the plugin must call - * either mcp_account_manager_set_attribute(), - * mcp_account_manager_set_parameter(), - * or mcp_account_manager_set_value() and (if appropriate) - * mcp_account_manager_parameter_make_secret() - * before returning from this method call. - * - * Note that mcp_account_manager_set_parameter() does not use the - * "param-" prefix, even if called from this function. - * - * If @key is %NULL the plugin should iterate through all attributes and - * parameters, and push each of them into @am, as if this method had - * been called once for each attribute or parameter. It must then return - * %TRUE if any attributes or parameters were found, or %FALSE if it - * was not responsible for @account. - * - * Returns: %TRUE if @storage is responsible for @account + * @parameter: the name of a parameter, e.g. "require-encryption" + * @type: (allow-none): the expected type of @parameter, as a hint for + * legacy account storage plugins that do not store parameters' types + * @flags: (allow-none) (out): used to return parameter flags + * + * Retrieve a parameter. + * + * There is no default implementation. + * All account storage plugins must override this method. + * + * The returned variant does not necessarily have to match @type: + * Mission Control will coerce it to an appropriate type if required. In + * particular, plugins that store strongly-typed parameters may return + * the stored type, not the expected type, if they differ. + * + * If @type is %NULL, the plugin must return the parameter with its stored + * type, or return %NULL if the type is not stored. + * + * Returns: (transfer full): the value of the parameter, or %NULL if it + * is not present */ -gboolean -mcp_account_storage_get (const McpAccountStorage *storage, +GVariant * +mcp_account_storage_get_parameter (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, - const gchar *key) + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); + if (type == NULL) + SDEBUG (storage, "%s.%s (if type is stored)", account, parameter); + else + SDEBUG (storage, "%s.%s (type '%.*s')", account, parameter, + (int) g_variant_type_get_string_length (type), + g_variant_type_peek_string (type)); + g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->get != NULL, FALSE); + g_return_val_if_fail (iface->get_parameter != NULL, FALSE); - return iface->get (storage, am, account, key); + return iface->get_parameter (storage, am, account, parameter, type, flags); } /** - * McpAccountStorageSetFunc: + * mcp_account_storage_list_typed_parameters: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: the unique name of the account - * @key: the setting whose value we wish to store: either an attribute - * like "DisplayName", or "param-" plus a parameter like "account" - * @val: a non-%NULL value for @key * - * An implementation of mcp_account_storage_set(). + * List the names of all parameters whose corresponding types are known. + * + * Ideally, all parameters are <firstterm>typed parameters</firstterm>, whose + * types are stored alongside the values. This function produces + * those as its return value. + * + * However, the Mission Control API has not traditionally required + * account-storage backends to store parameters' types, so some backends + * will contain <firstterm>untyped parameters</firstterm>, + * returned by mcp_account_storage_list_untyped_parameters(). * - * Returns: %TRUE if @storage is responsible for @account + * This method is mandatory to implement. + * + * Returns: (array zero-terminated=1) (transfer full) (element-type utf8): a #GStrv + * containing the typed parameters; %NULL or empty if there are no + * typed parameters */ +gchar ** +mcp_account_storage_list_typed_parameters (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account) +{ + McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + + SDEBUG (storage, "%s", account); + + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->list_typed_parameters != NULL, NULL); + + return iface->list_typed_parameters (storage, am, account); +} /** - * mcp_account_storage_set: + * mcp_account_storage_list_untyped_parameters: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: the unique name of the account - * @key: the non-%NULL setting whose value we wish to store: either an - * attribute like "DisplayName", or "param-" plus a parameter like "account" - * @value: a value to associate with @key, escaped as if for a #GKeyFile * - * The plugin is expected to either quickly and synchronously - * update its internal cache of values with @value, or to - * decline to store the setting. + * List the names of all parameters whose types are unknown. + * The values are not listed, because interpreting the value + * correctly requires a type. * - * The plugin is not expected to write to its long term storage - * at this point. It can expect Mission Control to call either - * mcp_account_storage_commit() or mcp_account_storage_commit_one() - * after a short delay. + * See mcp_account_storage_list_typed_parameters() for more on + * typed vs. untyped parameters. * - * Plugins that implement mcp_storage_set_attribute() and - * mcp_account_storage_set_parameter() can just return %FALSE here. - * There is a default implementation, which just returns %FALSE. + * The default implementation just returns %NULL, and is appropriate + * for "legacy-free" backends that store a type with every parameter. * - * Returns: %TRUE if the attribute was claimed, %FALSE otherwise + * Returns: (array zero-terminated=1) (transfer full) (element-type utf8): a #GStrv + * containing the untyped parameters; %NULL or empty if there are no + * untyped parameters */ -gboolean -mcp_account_storage_set (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key, - const gchar *value) +gchar ** +mcp_account_storage_list_untyped_parameters (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); - g_return_val_if_fail (iface != NULL, FALSE); + SDEBUG (storage, "%s", account); + + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->list_untyped_parameters != NULL, NULL); - return iface->set (storage, am, account, key, value); + return iface->list_untyped_parameters (storage, am, account); } /** @@ -611,7 +617,8 @@ mcp_account_storage_set (const McpAccountStorage *storage, * @am: an #McpAccountManager instance * @account: the unique name of the account * @attribute: the name of an attribute, e.g. "DisplayName" - * @value: a value to associate with @attribute + * @value: (allow-none): a value to associate with @attribute, + * or %NULL to delete * @flags: flags influencing how the attribute is to be stored * * Store an attribute. @@ -623,15 +630,14 @@ mcp_account_storage_set (const McpAccountStorage *storage, * The plugin is not expected to write to its long term storage * at this point. * - * There is a default implementation, which just returns %FALSE. - * Mission Control will call mcp_account_storage_set() instead, - * using a keyfile-escaped version of @value. + * There is a default implementation, which just returns %FALSE for read-only + * storage plugins. * * Returns: %TRUE if the attribute was claimed, %FALSE otherwise * * Since: 5.15.0 */ -gboolean +McpAccountStorageSetResult mcp_account_storage_set_attribute (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, @@ -641,9 +647,13 @@ mcp_account_storage_set_attribute (McpAccountStorage *storage, { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->set_attribute != NULL, FALSE); + SDEBUG (storage, "%s.%s (type '%s')", account, attribute, + value == NULL ? "null" : g_variant_get_type_string (value)); + + g_return_val_if_fail (iface != NULL, + MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (iface->set_attribute != NULL, + MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); return iface->set_attribute (storage, am, account, attribute, value, flags); } @@ -655,7 +665,8 @@ mcp_account_storage_set_attribute (McpAccountStorage *storage, * @account: the unique name of the account * @parameter: the name of a parameter, e.g. "account" (note that there * is no "param-" prefix here) - * @value: a value to associate with @parameter + * @value: (allow-none): a value to associate with @parameter, + * or %NULL to delete * @flags: flags influencing how the parameter is to be stored * * Store a parameter. @@ -667,16 +678,14 @@ mcp_account_storage_set_attribute (McpAccountStorage *storage, * The plugin is not expected to write to its long term storage * at this point. * - * There is a default implementation, which just returns %FALSE. - * Mission Control will call mcp_account_storage_set() instead, - * using "param-" + @parameter as key and a keyfile-escaped version - * of @value as value. + * There is a default implementation, which just returns %FALSE for read-only + * storage plugins. * * Returns: %TRUE if the parameter was claimed, %FALSE otherwise * * Since: 5.15.0 */ -gboolean +McpAccountStorageSetResult mcp_account_storage_set_parameter (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, @@ -686,9 +695,14 @@ mcp_account_storage_set_parameter (McpAccountStorage *storage, { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); + SDEBUG (storage, "%s.%s (type '%s')", account, parameter, + value == NULL ? "null" : g_variant_get_type_string (value)); + g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->set_parameter != NULL, FALSE); + g_return_val_if_fail (iface != NULL, + MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (iface->set_parameter != NULL, + MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); return iface->set_parameter (storage, am, account, parameter, value, flags); } @@ -713,13 +727,14 @@ mcp_account_storage_set_parameter (McpAccountStorage *storage, * @am: an object which can be used to call back into the account manager * @manager: the name of the manager * @protocol: the name of the protocol - * @params: A gchar * / GValue * hash table of account parameters + * @identification: a normalized form of the account name, or "account" + * if nothing is suitable (e.g. for telepathy-salut) * @error: a GError to fill * * Inform the plugin that a new account is being created. @manager, @protocol - * and @params are given to help determining the account's unique name, but does - * not need to be stored on the account yet, mcp_account_storage_set() and - * mcp_account_storage_commit() will be called later. + * and @identification are given to help determining the account's unique name, + * but does not need to be stored on the account yet, mcp_account_storage_set() + * and mcp_account_storage_commit() will be called later. * * It is recommended to use mcp_account_manager_get_unique_name() to create the * unique name, but it's not mandatory. One could base the unique name on an @@ -729,90 +744,105 @@ mcp_account_storage_set_parameter (McpAccountStorage *storage, * #McpAccountStorage::created signal should not be emitted for this account, * not even when mcp_account_storage_commit() will be called. * + * The default implementation just returns %NULL, and is appropriate for + * read-only storage. + * + * Since Mission Control 5.17, all storage plugins in which new accounts + * can be created by Mission Control must implement this method. + * Previously, it was not mandatory. + * * Returns: (transfer full): the newly allocated account name, which should * be freed once the caller is done with it, or %NULL if that couldn't * be done. */ gchar * -mcp_account_storage_create (const McpAccountStorage *storage, - const McpAccountManager *am, +mcp_account_storage_create (McpAccountStorage *storage, + McpAccountManager *am, const gchar *manager, const gchar *protocol, - GHashTable *params, + const gchar *identification, GError **error) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - g_return_val_if_fail (iface != NULL, NULL); + SDEBUG (storage, "%s/%s \"%s\"", manager, protocol, identification); - if (iface->create == NULL) - { - g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, - "This storage does not implement create function"); - return NULL; - } + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->create != NULL, NULL); - return iface->create (storage, am, manager, protocol, params, error); + return iface->create (storage, am, manager, protocol, identification, error); } /** - * McpAccountStorageDeleteFunc: + * mcp_account_storage_delete_async: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: the unique name of the account - * @key: (allow-none): the setting whose value we wish to store - either an - * attribute like "DisplayName", or "param-" plus a parameter like - * "account" - or %NULL to delete the entire account + * @cancellable: (allow-none): optionally used to (try to) cancel the operation + * @callback: called on success or failure + * @user_data: data for @callback + * + * Delete the account @account, and commit the change, + * emitting #McpAccountStorage::deleted afterwards. + * + * Unlike the 'delete' virtual method in earlier MC versions, this + * function is expected to commit the change to long-term storage, + * is expected to emit #McpAccountStorage::deleted, and is + * not called for the deletion of individual attributes or parameters. * - * An implementation of mcp_account_storage_delete(). + * The default implementation just returns failure (asynchronously), + * and is appropriate for read-only storage. * - * Returns: %TRUE if the setting or settings are not - * the plugin's cache after this operation, %FALSE otherwise. + * Implementations that override delete_async must also override + * delete_finish. */ +void +mcp_account_storage_delete_async (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + + SDEBUG (storage, "%s", account); + + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->delete_async != NULL); + + iface->delete_async (storage, am, account, cancellable, callback, user_data); +} /** - * mcp_account_storage_delete: + * mcp_account_storage_delete_finish: * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * @account: the unique name of the account - * @key: (allow-none): the setting whose value we wish to store - either an - * attribute like "DisplayName", or "param-" plus a parameter like - * "account" - or %NULL to delete the entire account - * - * The plugin is expected to remove the setting for @key from its - * internal cache and to remember that its state has changed, so - * that it can delete said setting from its long term storage if - * its long term storage method makes this necessary. - * - * If @key is %NULL, the plugin should forget all its settings for - * @account,and remember to delete the entire account from its storage later. + * @res: the result of mcp_account_storage_delete_async() + * @error: used to raise an error if %FALSE is returned * - * The plugin is not expected to update its long term storage at - * this point. + * Process the result of mcp_account_storage_delete_async(). * - * Returns: %TRUE if the setting or settings are not - * the plugin's cache after this operation, %FALSE otherwise. - * This is very unlikely to ever be %FALSE, as a plugin is always - * expected to be able to manipulate its own cache. + * Returns: %TRUE on success, %FALSE if the account could not be deleted */ gboolean -mcp_account_storage_delete (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key) +mcp_account_storage_delete_finish (McpAccountStorage *storage, + GAsyncResult *result, + GError **error) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->delete_finish != NULL, FALSE); - return iface->delete (storage, am, account, key); + return iface->delete_finish (storage, result, error); } /** * McpAccountStorageCommitFunc: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance + * @account: the unique suffix of an account's object path * * An implementation of mcp_account_storage_commit(). * @@ -823,6 +853,7 @@ mcp_account_storage_delete (const McpAccountStorage *storage, * mcp_account_storage_commit: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance + * @account: the unique suffix of an account's object path * * The plugin is expected to write its cache to long term storage, * deleting, adding or updating entries in said storage as needed. @@ -831,87 +862,28 @@ mcp_account_storage_delete (const McpAccountStorage *storage, * not required to have finished its commit operation when it returns, * merely to have started the operation. * - * If the @commit_one method is implemented, it will be called preferentially - * if only one account is to be committed. If the @commit_one method is - * implemented but @commit is not, @commit_one will be called with - * @account_name = %NULL to commit all accounts. - * - * Returns: %TRUE if the commit process was started (but not necessarily - * completed) successfully; %FALSE if there was a problem that was immediately - * obvious. - */ -gboolean -mcp_account_storage_commit (const McpAccountStorage *storage, - const McpAccountManager *am) -{ - McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - - SDEBUG (storage, "committing all accounts"); - g_return_val_if_fail (iface != NULL, FALSE); - - if (iface->commit != NULL) - { - return iface->commit (storage, am); - } - else if (iface->commit_one != NULL) - { - return iface->commit_one (storage, am, NULL); - } - else - { - SDEBUG (storage, - "neither commit nor commit_one is implemented; cannot save accounts"); - return FALSE; - } -} - -/** - * McpAccountStorageCommitOneFunc: - * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * @account: (allow-none): the unique suffix of an account's object path, - * or %NULL - * - * An implementation of mcp_account_storage_commit_one(). - * - * Returns: %TRUE if the commit process was started successfully - */ - -/** - * mcp_account_storage_commit_one: - * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * @account: (allow-none): the unique suffix of an account's object path, - * or %NULL if all accounts are to be committed and - * mcp_account_storage_commit() is unimplemented - * - * The same as mcp_account_storage_commit(), but only commit the given - * account. This is optional to implement; the default implementation - * is to call @commit. + * The default implementation just returns %FALSE, and is appropriate for + * read-only storage. * - * If both mcp_account_storage_commit_one() and mcp_account_storage_commit() - * are implemented, Mission Control will never pass @account = %NULL to - * this method. + * Mission Control 5.17+ no longer requires plugins to cope with + * account == NULL. * * Returns: %TRUE if the commit process was started (but not necessarily * completed) successfully; %FALSE if there was a problem that was immediately * obvious. */ gboolean -mcp_account_storage_commit_one (const McpAccountStorage *storage, - const McpAccountManager *am, +mcp_account_storage_commit (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, "called for %s", account ? account : "<all accounts>"); g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->commit != NULL, FALSE); - if (iface->commit_one != NULL) - return iface->commit_one (storage, am, account); - else - /* Fall back to plain ->commit() */ - return mcp_account_storage_commit (storage, am); + return iface->commit (storage, am, account); } /** @@ -935,56 +907,28 @@ mcp_account_storage_commit_one (const McpAccountStorage *storage, * This method is called only at initialisation time, before the dbus name * has been claimed, and is the only one permitted to block. * + * There is no default implementation. All implementations of this interface + * must override this method. + * * Returns: (element-type utf8) (transfer full): a list of account names that * the plugin has settings for. The account names should be freed with * g_free(), and the list with g_list_free(), when the caller is done with * them. */ GList * -mcp_account_storage_list (const McpAccountStorage *storage, - const McpAccountManager *am) +mcp_account_storage_list (McpAccountStorage *storage, + McpAccountManager *am) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->list != NULL, NULL); return iface->list (storage, am); } /** - * McpAccountStorageReadyFunc: - * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * - * An implementation of mcp_account_storage_ready(). - */ - -/** - * mcp_account_storage_ready: - * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * - * Informs the plugin that it is now permitted to create new accounts, - * ie it can now fire its "created", "altered", "toggled" and "deleted" - * signals. - */ -void -mcp_account_storage_ready (const McpAccountStorage *storage, - const McpAccountManager *am) -{ - McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - - g_return_if_fail (iface != NULL); - - /* plugins that can't create accounts from external sources don't * - * need to implement this method, as they can never fire the async * - * account change signals: */ - if (iface->ready != NULL) - iface->ready (storage, am); -} - -/** * McpAccountStorageGetIdentifierFunc: * @storage: an #McpAccountStorage instance * @account: the unique name of the account @@ -1004,30 +948,25 @@ mcp_account_storage_ready (const McpAccountStorage *storage, * Get the storage-specific identifier for this account. The type is variant, * hence the GValue. * + * The default implementation returns @account as a %G_TYPE_STRING. + * * This method will only be called for the storage plugin that "owns" * the account. */ void -mcp_account_storage_get_identifier (const McpAccountStorage *storage, +mcp_account_storage_get_identifier (McpAccountStorage *storage, const gchar *account, GValue *identifier) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - SDEBUG (storage, ""); + SDEBUG (storage, "%s", account); g_return_if_fail (iface != NULL); + g_return_if_fail (iface->get_identifier != NULL); g_return_if_fail (identifier != NULL); g_return_if_fail (!G_IS_VALUE (identifier)); - if (iface->get_identifier == NULL) - { - g_value_init (identifier, G_TYPE_STRING); - g_value_set_string (identifier, account); - } - else - { - iface->get_identifier (storage, account, identifier); - } + iface->get_identifier (storage, account, identifier); } /** @@ -1052,26 +991,22 @@ mcp_account_storage_get_identifier (const McpAccountStorage *storage, * This method will only be called for the storage plugin that "owns" * the account. * + * The default implementation returns an empty map. + * * Returns: (transfer container) (element-type utf8 GObject.Value): additional - * storage-specific information + * storage-specific information, which must not be %NULL */ GHashTable * -mcp_account_storage_get_additional_info (const McpAccountStorage *storage, +mcp_account_storage_get_additional_info (McpAccountStorage *storage, const gchar *account) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); - GHashTable *ret = NULL; - SDEBUG (storage, ""); + SDEBUG (storage, "%s", account); g_return_val_if_fail (iface != NULL, FALSE); + g_return_val_if_fail (iface->get_additional_info != NULL, FALSE); - if (iface->get_additional_info != NULL) - ret = iface->get_additional_info (storage, account); - - if (ret == NULL) - ret = g_hash_table_new (g_str_hash, g_str_equal); - - return ret; + return iface->get_additional_info (storage, account); } /** @@ -1092,22 +1027,22 @@ mcp_account_storage_get_additional_info (const McpAccountStorage *storage, * This method will only be called for the storage plugin that "owns" * the account. * + * The default implementation returns 0, i.e. no restrictions. + * * Returns: a bitmask of %TpStorageRestrictionFlags with the restrictions to * account storage. */ -/* FIXME: when breaking API, make this return TpStorageRestrictionFlags */ -guint -mcp_account_storage_get_restrictions (const McpAccountStorage *storage, +TpStorageRestrictionFlags +mcp_account_storage_get_restrictions (McpAccountStorage *storage, const gchar *account) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + SDEBUG (storage, "%s", account); g_return_val_if_fail (iface != NULL, 0); + g_return_val_if_fail (iface->get_restrictions != NULL, 0); - if (iface->get_restrictions == NULL) - return 0; - else - return iface->get_restrictions (storage, account); + return iface->get_restrictions (storage, account); } /** @@ -1119,7 +1054,7 @@ mcp_account_storage_get_restrictions (const McpAccountStorage *storage, * Returns: the plugin's name (for logging etc) */ const gchar * -mcp_account_storage_name (const McpAccountStorage *storage) +mcp_account_storage_name (McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); @@ -1137,7 +1072,7 @@ mcp_account_storage_name (const McpAccountStorage *storage) * Returns: the plugin's description (for logging etc) */ const gchar * -mcp_account_storage_description (const McpAccountStorage *storage) +mcp_account_storage_description (McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); @@ -1156,7 +1091,7 @@ mcp_account_storage_description (const McpAccountStorage *storage) * was provided in #McpAccountStorageIface.provider. */ const gchar * -mcp_account_storage_provider (const McpAccountStorage *storage) +mcp_account_storage_provider (McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); @@ -1166,7 +1101,7 @@ mcp_account_storage_provider (const McpAccountStorage *storage) } /** - * mcp_account_storage_emit_create: + * mcp_account_storage_emit_created: * @storage: an #McpAccountStorage instance * @account: the unique name of the created account * @@ -1176,24 +1111,11 @@ void mcp_account_storage_emit_created (McpAccountStorage *storage, const gchar *account) { + SDEBUG (storage, "%s", account); g_signal_emit (storage, signals[CREATED], 0, account); } /** - * mcp_account_storage_emit_altered: - * @storage: an #McpAccountStorage instance - * @account: the unique name of the altered account - * - * Emits the #McpAccountStorage::altered signal - */ -void -mcp_account_storage_emit_altered (McpAccountStorage *storage, - const gchar *account) -{ - g_signal_emit (storage, signals[ALTERED], 0, account); -} - -/** * mcp_account_storage_emit_altered_one: * @storage: an #McpAccountStorage instance * @account: the unique name of the altered account @@ -1207,6 +1129,7 @@ mcp_account_storage_emit_altered_one (McpAccountStorage *storage, const gchar *account, const gchar *key) { + SDEBUG (storage, "%s", account); g_signal_emit (storage, signals[ALTERED_ONE], 0, account, key); } @@ -1221,6 +1144,7 @@ void mcp_account_storage_emit_deleted (McpAccountStorage *storage, const gchar *account) { + SDEBUG (storage, "%s", account); g_signal_emit (storage, signals[DELETED], 0, account); } @@ -1237,6 +1161,7 @@ mcp_account_storage_emit_toggled (McpAccountStorage *storage, const gchar *account, gboolean enabled) { + SDEBUG (storage, "%s: Enabled=%s", account, enabled ? "True" : "False"); g_signal_emit (storage, signals[TOGGLED], 0, account, enabled); } @@ -1251,35 +1176,78 @@ void mcp_account_storage_emit_reconnect (McpAccountStorage *storage, const gchar *account) { + SDEBUG (storage, "%s", account); g_signal_emit (storage, signals[RECONNECT], 0, account); } /** - * mcp_account_storage_owns: + * mcp_account_storage_get_flags: * @storage: an #McpAccountStorage instance - * @am: an #McpAccountManager instance - * @account: the unique name (object-path tail) of an account + * @account: the unique name of the account to inspect * - * Check whether @account is stored in @storage. The highest-priority - * plugin for which this function returns %TRUE is considered to be - * responsible for @account. + * Get the backend's features and capabilities. The default implementation + * returns %MCP_ACCOUNT_STORAGE_FLAG_NONE. Additionally providing + * %MCP_ACCOUNT_STORAGE_FLAG_STORES_TYPES is strongly recommended. * - * There is a default implementation, which calls mcp_account_storage_get() - * for the well-known key "manager". + * Returns: a bitmask of API features that apply to @account + */ +McpAccountStorageFlags +mcp_account_storage_get_flags (McpAccountStorage *storage, + const gchar *account) +{ + McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + + g_return_val_if_fail (iface != NULL, MCP_ACCOUNT_STORAGE_FLAG_NONE); + g_return_val_if_fail (iface->get_flags != NULL, + MCP_ACCOUNT_STORAGE_FLAG_NONE); + + return iface->get_flags (storage, account); +} + +/** + * mcp_account_storage_has_all_flags: + * @storage: an #McpAccountStorage instance + * @account: the unique name of the account to inspect + * @require_all: bitwise "or" of zero or more flags * - * Returns: %TRUE if @account is stored in @storage + * Return whether this account has all of the specified flags, + * according to mcp_account_storage_get_flags(). * - * Since: 5.15.0 + * If @require_all is 0, the result will always be %TRUE + * (the account has all of the flags in the empty set). + * + * Returns: %TRUE if @account has every flag in @require_all */ gboolean -mcp_account_storage_owns (McpAccountStorage *storage, - McpAccountManager *am, - const gchar *account) +mcp_account_storage_has_all_flags (McpAccountStorage *storage, + const gchar *account, + McpAccountStorageFlags require_all) { - McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + return ((mcp_account_storage_get_flags (storage, account) & require_all) == + require_all); +} - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->owns != NULL, FALSE); - return iface->owns (storage, am, account); +/** + * mcp_account_storage_has_any_flag: + * @storage: an #McpAccountStorage instance + * @account: the unique name of the account to inspect + * @require_one: bitwise "or" of one or more flags + * + * Return whether this account has at least one of the required flags, + * according to mcp_account_storage_get_flags(). + * + * If @require_one is 0, the result will always be %FALSE + * (it is not true that the account has at least one of the flags + * in the empty set). + * + * Returns: %TRUE if @account has every flag in @require_all + */ +gboolean +mcp_account_storage_has_any_flag (McpAccountStorage *storage, + const gchar *account, + McpAccountStorageFlags require_one) +{ + return ((mcp_account_storage_get_flags (storage, account) & require_one) + != 0); } diff --git a/mission-control-plugins/account-storage.h b/mission-control-plugins/account-storage.h index 91cdd78a..b8015217 100644 --- a/mission-control-plugins/account-storage.h +++ b/mission-control-plugins/account-storage.h @@ -30,7 +30,28 @@ G_BEGIN_DECLS #define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_READONLY -1 #define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_DEFAULT 0 #define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_NORMAL 100 -#define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING 10000 + +typedef enum /*< flags >*/ +{ + MCP_PARAMETER_FLAG_NONE = 0 +} McpParameterFlags; + +typedef enum /*< flags >*/ +{ + MCP_ATTRIBUTE_FLAG_NONE = 0 +} McpAttributeFlags; + +typedef enum { + MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED = 0, + MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED, + MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED +} McpAccountStorageSetResult; + +typedef enum /*< flags >*/ +{ + MCP_ACCOUNT_STORAGE_FLAG_NONE = 0, + MCP_ACCOUNT_STORAGE_FLAG_STORES_TYPES = (1 << 0) +} McpAccountStorageFlags; /* API for plugins to implement */ typedef struct _McpAccountStorage McpAccountStorage; @@ -58,53 +79,15 @@ struct _McpAccountStorage { }; GType mcp_account_storage_get_type (void); -/* Virtual method implementation signatures */ -typedef gboolean (*McpAccountStorageGetFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key); -typedef gboolean (*McpAccountStorageSetFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key, - const gchar *val); typedef gchar * (*McpAccountStorageCreate) ( - const McpAccountStorage *storage, - const McpAccountManager *am, + McpAccountStorage *storage, + McpAccountManager *am, const gchar *manager, const gchar *protocol, - GHashTable *params, + const gchar *identification, GError **error); -typedef gboolean (*McpAccountStorageDeleteFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key); -typedef GList * (*McpAccountStorageListFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am); -typedef gboolean (*McpAccountStorageCommitFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am); -typedef gboolean (*McpAccountStorageCommitOneFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account); -typedef void (*McpAccountStorageReadyFunc) ( - const McpAccountStorage *storage, - const McpAccountManager *am); -typedef void (*McpAccountStorageGetIdentifierFunc) ( - const McpAccountStorage *storage, - const gchar *account, - GValue *identifier); -typedef GHashTable * (*McpAccountStorageGetAdditionalInfoFunc) ( - const McpAccountStorage *storage, - const gchar *account); -/* FIXME: when breaking API, make this return TpStorageRestrictionFlags */ -typedef guint (*McpAccountStorageGetRestrictionsFunc) ( - const McpAccountStorage *storage, +typedef TpStorageRestrictionFlags (*McpAccountStorageGetRestrictionsFunc) ( + McpAccountStorage *storage, const gchar *account); struct _McpAccountStorageIface @@ -116,176 +99,169 @@ struct _McpAccountStorageIface const gchar *desc; const gchar *provider; - McpAccountStorageSetFunc set; - McpAccountStorageGetFunc get; - McpAccountStorageDeleteFunc delete; - McpAccountStorageCommitFunc commit; - McpAccountStorageListFunc list; - McpAccountStorageReadyFunc ready; - McpAccountStorageCommitOneFunc commit_one; - McpAccountStorageGetIdentifierFunc get_identifier; - McpAccountStorageGetAdditionalInfoFunc get_additional_info; - McpAccountStorageGetRestrictionsFunc get_restrictions; - McpAccountStorageCreate create; - - /* Since 5.15.0 */ - gboolean (*owns) (McpAccountStorage *storage, + void (*delete_async) (McpAccountStorage *storage, McpAccountManager *am, - const gchar *account); - gboolean (*set_attribute) (McpAccountStorage *storage, + const gchar *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*delete_finish) (McpAccountStorage *storage, + GAsyncResult *res, + GError **error); + + gboolean (*commit) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account); + + GList * (*list) (McpAccountStorage *storage, + McpAccountManager *am); + + void (*get_identifier) (McpAccountStorage *storage, + const gchar *account, + GValue *identifier); + + GHashTable * (*get_additional_info) (McpAccountStorage *storage, + const gchar *account); + + TpStorageRestrictionFlags (*get_restrictions) (McpAccountStorage *storage, + const gchar *account); + + gchar * (*create) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + const gchar *identification, + GError **error); + + GVariant *(*get_attribute) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags); + GVariant *(*get_parameter) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags); + + McpAccountStorageSetResult (*set_attribute) (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, const gchar *attribute, GVariant *val, McpAttributeFlags flags); - gboolean (*set_parameter) (McpAccountStorage *storage, + McpAccountStorageSetResult (*set_parameter) (McpAccountStorage *storage, McpAccountManager *am, const gchar *account, const gchar *parameter, GVariant *val, McpParameterFlags flags); -}; -#ifndef __GTK_DOC_IGNORE__ -#ifndef MC_DISABLE_DEPRECATED - -/* functions with which to fill in the vtable */ -G_DEPRECATED_FOR (iface->priority = prio) -void mcp_account_storage_iface_set_priority (McpAccountStorageIface *iface, - guint prio); - -G_DEPRECATED_FOR (iface->name = name) -void mcp_account_storage_iface_set_name (McpAccountStorageIface *iface, - const gchar *name); - -G_DEPRECATED_FOR (iface->desc = desc) -void mcp_account_storage_iface_set_desc (McpAccountStorageIface *iface, - const gchar *desc); - -G_DEPRECATED_FOR (iface->provider = provider) -void mcp_account_storage_iface_set_provider (McpAccountStorageIface *iface, - const gchar *provider); - -G_DEPRECATED_FOR (iface->get = method) -void mcp_account_storage_iface_implement_get (McpAccountStorageIface *iface, - McpAccountStorageGetFunc method); -G_DEPRECATED_FOR (iface->set = method) -void mcp_account_storage_iface_implement_set (McpAccountStorageIface *iface, - McpAccountStorageSetFunc method); -G_DEPRECATED_FOR (iface->create = method) -void mcp_account_storage_iface_implement_create ( - McpAccountStorageIface *iface, - McpAccountStorageCreate method); -G_DEPRECATED_FOR (iface->delete = method) -void mcp_account_storage_iface_implement_delete (McpAccountStorageIface *iface, - McpAccountStorageDeleteFunc method); -G_DEPRECATED_FOR (iface->list = method) -void mcp_account_storage_iface_implement_list (McpAccountStorageIface *iface, - McpAccountStorageListFunc method); -G_DEPRECATED_FOR (iface->commit = method) -void mcp_account_storage_iface_implement_commit (McpAccountStorageIface *iface, - McpAccountStorageCommitFunc method); -G_DEPRECATED_FOR (iface->commit_one = method) -void mcp_account_storage_iface_implement_commit_one ( - McpAccountStorageIface *iface, - McpAccountStorageCommitOneFunc method); -G_DEPRECATED_FOR (iface->ready = method) -void mcp_account_storage_iface_implement_ready (McpAccountStorageIface *iface, - McpAccountStorageReadyFunc method); -G_DEPRECATED_FOR (iface->get_identifier = method) -void mcp_account_storage_iface_implement_get_identifier ( - McpAccountStorageIface *iface, - McpAccountStorageGetIdentifierFunc method); -G_DEPRECATED_FOR (iface->get_additional_info = method) -void mcp_account_storage_iface_implement_get_additional_info ( - McpAccountStorageIface *iface, - McpAccountStorageGetAdditionalInfoFunc method); -G_DEPRECATED_FOR (iface->get_restrictions = method) -void mcp_account_storage_iface_implement_get_restrictions ( - McpAccountStorageIface *iface, - McpAccountStorageGetRestrictionsFunc method); - -#endif /* !defined(MC_DISABLE_DEPRECATED) */ -#endif /* !defined(__GTK_DOC_IGNORE__) */ + gchar **(*list_typed_parameters) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account); + gchar **(*list_untyped_parameters) (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account); + + McpAccountStorageFlags (*get_flags) (McpAccountStorage *storage, + const gchar *account); +}; /* virtual methods */ -gint mcp_account_storage_priority (const McpAccountStorage *storage); +gint mcp_account_storage_priority (McpAccountStorage *storage); -gboolean mcp_account_storage_get (const McpAccountStorage *storage, +gchar * mcp_account_storage_create (McpAccountStorage *storage, McpAccountManager *am, - const gchar *account, - const gchar *key); - -gboolean mcp_account_storage_set (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account, - const gchar *key, - const gchar *value); - -gchar * mcp_account_storage_create (const McpAccountStorage *storage, - const McpAccountManager *am, const gchar *manager, const gchar *protocol, - GHashTable *params, + const gchar *identification, GError **error); -gboolean mcp_account_storage_delete (const McpAccountStorage *storage, - const McpAccountManager *am, +void mcp_account_storage_delete_async (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account, - const gchar *key); - -void mcp_account_storage_ready (const McpAccountStorage *storage, - const McpAccountManager *am); - -gboolean -mcp_account_storage_commit (const McpAccountStorage *storage, - const McpAccountManager *am); + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mcp_account_storage_delete_finish (McpAccountStorage *storage, + GAsyncResult *result, + GError **error); gboolean -mcp_account_storage_commit_one (const McpAccountStorage *storage, - const McpAccountManager *am, +mcp_account_storage_commit (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account); -GList *mcp_account_storage_list (const McpAccountStorage *storage, - const McpAccountManager *am); +GList *mcp_account_storage_list (McpAccountStorage *storage, + McpAccountManager *am); -void mcp_account_storage_get_identifier (const McpAccountStorage *storage, +void mcp_account_storage_get_identifier (McpAccountStorage *storage, const gchar *account, GValue *identifier); GHashTable *mcp_account_storage_get_additional_info ( - const McpAccountStorage *storage, + McpAccountStorage *storage, const gchar *account); -guint mcp_account_storage_get_restrictions (const McpAccountStorage *storage, +TpStorageRestrictionFlags mcp_account_storage_get_restrictions ( + McpAccountStorage *storage, const gchar *account); -const gchar *mcp_account_storage_name (const McpAccountStorage *storage); +const gchar *mcp_account_storage_name (McpAccountStorage *storage); -const gchar *mcp_account_storage_description (const McpAccountStorage *storage); -const gchar *mcp_account_storage_provider (const McpAccountStorage *storage); +const gchar *mcp_account_storage_description (McpAccountStorage *storage); +const gchar *mcp_account_storage_provider (McpAccountStorage *storage); -gboolean mcp_account_storage_owns (McpAccountStorage *storage, +GVariant *mcp_account_storage_get_attribute (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags); +GVariant *mcp_account_storage_get_parameter (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags); +gchar **mcp_account_storage_list_typed_parameters (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account); +gchar **mcp_account_storage_list_untyped_parameters (McpAccountStorage *storage, McpAccountManager *am, const gchar *account); -gboolean mcp_account_storage_set_attribute (McpAccountStorage *storage, +McpAccountStorageSetResult mcp_account_storage_set_attribute ( + McpAccountStorage *storage, McpAccountManager *am, const gchar *account, const gchar *attribute, GVariant *value, McpAttributeFlags flags); -gboolean mcp_account_storage_set_parameter (McpAccountStorage *storage, +McpAccountStorageSetResult mcp_account_storage_set_parameter ( + McpAccountStorage *storage, McpAccountManager *am, const gchar *account, const gchar *parameter, GVariant *value, McpParameterFlags flags); -void mcp_account_storage_emit_created (McpAccountStorage *storage, +McpAccountStorageFlags mcp_account_storage_get_flags ( + McpAccountStorage *storage, const gchar *account); -G_DEPRECATED_FOR (something that is actually implemented) -void mcp_account_storage_emit_altered (McpAccountStorage *storage, +gboolean mcp_account_storage_has_all_flags ( + McpAccountStorage *storage, + const gchar *account, + McpAccountStorageFlags require_all); +gboolean mcp_account_storage_has_any_flag ( + McpAccountStorage *storage, + const gchar *account, + McpAccountStorageFlags require_one); + +void mcp_account_storage_emit_created (McpAccountStorage *storage, const gchar *account); void mcp_account_storage_emit_altered_one (McpAccountStorage *storage, const gchar *account, diff --git a/mission-control-plugins/account.c b/mission-control-plugins/account.c index e962857c..2c194616 100644 --- a/mission-control-plugins/account.c +++ b/mission-control-plugins/account.c @@ -74,212 +74,11 @@ mcp_account_manager_get_type (void) } /** - * mcp_account_manager_set_value: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @key: the setting whose value we wish to change: either an attribute - * like "DisplayName", or "param-" plus a parameter like "account" - * @value: the new value, escaped as if for a #GKeyFile, or %NULL to delete - * the setting/parameter - * - * Inform Mission Control that @key has changed its value to @value. - * - * This function may either be called from mcp_account_storage_get(), - * or just before emitting #McpAccountStorage::altered-one. - * - * New plugins should call mcp_account_manager_set_attribute() or - * mcp_account_manager_set_parameter() instead. - */ -void -mcp_account_manager_set_value (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key, - const gchar *value) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->set_value != NULL); - - iface->set_value (mcpa, account, key, value); -} - -/** - * mcp_account_manager_set_attribute: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @attribute: the name of an attribute, such as "DisplayName" - * @value: (allow-none): the new value, or %NULL to delete the attribute - * @flags: flags for the new value (only used if @value is non-%NULL) - * - * Inform Mission Control that @attribute has changed its value to @value. - * - * If @value is a floating reference, Mission Control will take ownership - * of it, much like g_variant_builder_add_value(). - * - * This function may either be called from mcp_account_storage_get(), - * or just before emitting #McpAccountStorage::altered-one. - */ -void -mcp_account_manager_set_attribute (const McpAccountManager *mcpa, - const gchar *account, - const gchar *attribute, - GVariant *value, - McpAttributeFlags flags) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->set_attribute != NULL); - - iface->set_attribute (mcpa, account, attribute, value, flags); -} - -/** - * mcp_account_manager_set_parameter: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @parameter: the name of a parameter, such as "account", without - * the "param-" prefix - * @value: (allow-none): the new value, or %NULL to delete the parameter - * @flags: flags for the new value (only used if @value is non-%NULL) - * - * Inform Mission Control that @parameter has changed its value to @value. - * - * If @value is a floating reference, Mission Control will take ownership - * of it, much like g_variant_builder_add_value(). - * - * This function may either be called from mcp_account_storage_get(), - * or just before emitting #McpAccountStorage::altered-one. - */ -void -mcp_account_manager_set_parameter (const McpAccountManager *mcpa, - const gchar *account, - const gchar *parameter, - GVariant *value, - McpParameterFlags flags) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->set_parameter != NULL); - - iface->set_parameter (mcpa, account, parameter, value, flags); -} - -/** - * mcp_account_manage_list_keys: - * @mcpa: a #McpAccountManager instance - * @account: the unique name of an account - * - * <!-- --> - * - * Returns: (transfer full): a list of all keys (attributes and - * "param-"-prefixed parameters) stored for @account by any plugin - */ -GStrv -mcp_account_manager_list_keys (const McpAccountManager *mcpa, - const gchar *account) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_val_if_fail (iface != NULL, NULL); - g_return_val_if_fail (iface->list_keys != NULL, NULL); - g_return_val_if_fail (account != NULL, NULL); - - return iface->list_keys (mcpa, account); -} - -/** - * mcp_account_manager_get_value: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @key: the setting whose value we wish to fetch: either an attribute - * like "DisplayName", or "param-" plus a parameter like "account" - * - * Fetch a copy of the current value of an account setting held by - * the account manager. - * - * Returns: (transfer full): the value of @key - */ -gchar * -mcp_account_manager_get_value (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_val_if_fail (iface != NULL, NULL); - g_return_val_if_fail (iface->set_value != NULL, NULL); - - return iface->get_value (mcpa, account, key); -} - -/** - * mcp_account_manager_parameter_is_secret: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @key: the constant string "param-", plus a parameter name like - * "account" or "password" - * - * Determine whether a given account parameter is secret. - * Generally this is determined by MC and passed down to plugins, - * but any #McpAccountStorage plugin may decide a parameter is - * secret, in which case the return value for this call will - * indicate that fact too. - * - * For historical reasons, this function only operates on parameters, - * but requires its argument to be prefixed with "param-". - * - * Returns: %TRUE for secret settings, %FALSE otherwise - */ -gboolean -mcp_account_manager_parameter_is_secret (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->is_secret != NULL, FALSE); - - return iface->is_secret (mcpa, account, key); -} - -/** - * mcp_account_manager_parameter_make_secret: - * @mcpa: an #McpAccountManager instance - * @account: the unique name of an account - * @key: the constant string "param-", plus a parameter name like - * "account" or "password" - * - * Flag an account setting as secret for the lifetime of this - * #McpAccountManager. For instance, this should be called if - * @key has been retrieved from gnome-keyring. - * - * For historical reasons, this function only operates on parameters, - * but requires its argument to be prefixed with "param-". - */ -void -mcp_account_manager_parameter_make_secret (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->make_secret != NULL); - - g_debug ("%s.%s should be secret", account, key); - iface->make_secret (mcpa, account, key); -} - -/** * mcp_account_manager_get_unique_name: * @mcpa: an #McpAccountManager instance * @manager: the name of the manager * @protocol: the name of the protocol - * @params: A gchar * / GValue * hash table of account parameters. + * @identification: the result of calling IdentifyAccount for this account * * Generate and return the canonical unique name of this [new] account. * Should not be called for accounts which have already had a name @@ -287,6 +86,11 @@ mcp_account_manager_parameter_make_secret (const McpAccountManager *mcpa, * MC has not previously seen before (ie one created by a 3rd party * in the back-end that the plugin in question provides an interface to). * + * Changed in 5.17: instead of a map from string to GValue, the last + * argument is the result of calling IdentifyAccount on the parameters, + * which normalizes the account's name in a protocol-dependent way. + * Use mcp_account_manager_identify_account_async() to do that. + * * Returns: the newly allocated account name, which should be freed * once the caller is done with it. */ @@ -294,42 +98,55 @@ gchar * mcp_account_manager_get_unique_name (McpAccountManager *mcpa, const gchar *manager, const gchar *protocol, - const GHashTable *params) + const gchar *identification) { McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); g_return_val_if_fail (iface != NULL, NULL); g_return_val_if_fail (iface->unique_name != NULL, NULL); - return iface->unique_name (mcpa, manager, protocol, params); + return iface->unique_name (mcpa, manager, protocol, identification); +} + +void +mcp_account_manager_identify_account_async (McpAccountManager *mcpa, + const gchar *manager, + const gchar *protocol, + GVariant *parameters, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); + + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->identify_account_async != NULL); + g_return_if_fail (iface->identify_account_finish != NULL); + + g_return_if_fail (manager != NULL); + g_return_if_fail (protocol != NULL); + g_return_if_fail (parameters != NULL); + g_return_if_fail (g_variant_is_of_type (parameters, G_VARIANT_TYPE_VARDICT)); + + iface->identify_account_async (mcpa, manager, protocol, parameters, + cancellable, callback, user_data); } /** - * mcp_account_manager_escape_value_from_keyfile: - * @mcpa: a #McpAccountManager - * @value: a value with a supported #GType - * - * Escape @value so it could be passed to g_key_file_set_value(). - * For instance, escaping the boolean value TRUE returns "true", - * and escaping the string value containing one space returns "\s". - * - * It is a programming error to use an unsupported type. - * The supported types are currently %G_TYPE_STRING, %G_TYPE_BOOLEAN, - * %G_TYPE_INT, %G_TYPE_UINT, %G_TYPE_INT64, %G_TYPE_UINT64, %G_TYPE_UCHAR, - * %G_TYPE_STRV, %DBUS_TYPE_G_OBJECT_PATH and %TP_ARRAY_TYPE_OBJECT_PATH_LIST. - * - * Returns: the escaped form of @value + * Returns: (transfer full): a newly allocated string, free with g_free() */ gchar * -mcp_account_manager_escape_value_for_keyfile (const McpAccountManager *mcpa, - const GValue *value) +mcp_account_manager_identify_account_finish (McpAccountManager *mcpa, + GAsyncResult *res, + GError **error) { McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); g_return_val_if_fail (iface != NULL, NULL); - g_return_val_if_fail (iface->escape_value_for_keyfile != NULL, NULL); + g_return_val_if_fail (iface->identify_account_async != NULL, NULL); + g_return_val_if_fail (iface->identify_account_finish != NULL, NULL); - return iface->escape_value_for_keyfile (mcpa, value); + return iface->identify_account_finish (mcpa, res, error); } /** @@ -363,57 +180,30 @@ mcp_account_manager_escape_variant_for_keyfile (const McpAccountManager *mcpa, } /** - * mcp_account_manager_unescape_value_from_keyfile: + * mcp_account_manager_unescape_variant_from_keyfile: * @mcpa: a #McpAccountManager - * @escaped: an escaped string as returned by g_key_file_get_value() - * @value: a value to populate, with a supported #GType - * @error: used to raise an error if %FALSE is returned + * @escaped: a string that could have come from g_key_file_get_value() + * @type: the type of the variant to which to unescape * - * Attempt to interpret @escaped as a value of @value's type. - * If successful, put it in @value and return %TRUE. + * Unescape @escaped as if it had appeared in a #GKeyFile, with syntax + * appropriate for @type. * - * It is a programming error to try to escape an unsupported type. - * The supported types are currently %G_TYPE_STRING, %G_TYPE_BOOLEAN, - * %G_TYPE_INT, %G_TYPE_UINT, %G_TYPE_INT64, %G_TYPE_UINT64, %G_TYPE_UCHAR, - * %G_TYPE_STRV, %DBUS_TYPE_G_OBJECT_PATH and %TP_ARRAY_TYPE_OBJECT_PATH_LIST. + * It is a programming error to use an unsupported type. * - * Returns: %TRUE if @value was filled in + * Returns: (transfer full): the unescaped form of @escaped + * (*not* a floating reference) */ -gboolean -mcp_account_manager_unescape_value_from_keyfile (const McpAccountManager *mcpa, +GVariant * +mcp_account_manager_unescape_variant_from_keyfile ( + const McpAccountManager *mcpa, const gchar *escaped, - GValue *value, + const GVariantType *type, GError **error) { McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->unescape_value_from_keyfile != NULL, FALSE); - - return iface->unescape_value_from_keyfile (mcpa, escaped, value, error); -} - -/** - * mcp_account_manager_init_value_for_attribute: - * @mcpa: a #McpAccountManager - * @value: a zero-filled value to initialize - * @attribute: a supported Mission Control attribute - * - * If @attribute is a known Mission Control attribute, initialize @value - * with an appropriate type for @attribute and return %TRUE. Otherwise, - * return %FALSE. - * - * Returns: %TRUE if @value was initialized - */ -gboolean -mcp_account_manager_init_value_for_attribute (const McpAccountManager *mcpa, - GValue *value, - const gchar *attribute) -{ - McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); - - g_return_val_if_fail (iface != NULL, FALSE); - g_return_val_if_fail (iface->init_value_for_attribute != NULL, FALSE); + g_return_val_if_fail (iface != NULL, NULL); + g_return_val_if_fail (iface->unescape_variant_from_keyfile != NULL, NULL); - return iface->init_value_for_attribute (mcpa, value, attribute); + return iface->unescape_variant_from_keyfile (mcpa, escaped, type, error); } diff --git a/mission-control-plugins/account.h b/mission-control-plugins/account.h index 69a33d56..2a46c3ab 100644 --- a/mission-control-plugins/account.h +++ b/mission-control-plugins/account.h @@ -45,61 +45,31 @@ typedef struct _McpAccountManagerIface McpAccountManagerIface; GType mcp_account_manager_get_type (void) G_GNUC_CONST; -void mcp_account_manager_set_value (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key, - const gchar *value); - -void mcp_account_manager_set_attribute (const McpAccountManager *mcpa, - const gchar *account, - const gchar *attribute, - GVariant *value, - McpAttributeFlags flags); - -void mcp_account_manager_set_parameter (const McpAccountManager *mcpa, - const gchar *account, - const gchar *parameter, - GVariant *value, - McpParameterFlags flags); - -gchar * mcp_account_manager_get_value (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key); - -gboolean mcp_account_manager_parameter_is_secret (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key); - -void mcp_account_manager_parameter_make_secret (const McpAccountManager *mcpa, - const gchar *account, - const gchar *key); - gchar * mcp_account_manager_get_unique_name (McpAccountManager *mcpa, const gchar *manager, const gchar *protocol, - const GHashTable *params); - -GStrv mcp_account_manager_list_keys (const McpAccountManager *mcpa, - const gchar *account); - -gchar *mcp_account_manager_escape_value_for_keyfile ( - const McpAccountManager *mcpa, - const GValue *value); + const gchar *identification); gchar *mcp_account_manager_escape_variant_for_keyfile ( const McpAccountManager *mcpa, GVariant *variant); -gboolean mcp_account_manager_unescape_value_from_keyfile ( +GVariant *mcp_account_manager_unescape_variant_from_keyfile ( const McpAccountManager *mcpa, const gchar *escaped, - GValue *value, + const GVariantType *type, GError **error); -gboolean mcp_account_manager_init_value_for_attribute ( - const McpAccountManager *mcpa, - GValue *value, - const gchar *attribute); +void mcp_account_manager_identify_account_async (McpAccountManager *mcpa, + const gchar *manager, + const gchar *protocol, + GVariant *parameters, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gchar *mcp_account_manager_identify_account_finish (McpAccountManager *mcpa, + GAsyncResult *res, + GError **error); G_END_DECLS diff --git a/mission-control-plugins/dbus-acl.c b/mission-control-plugins/dbus-acl.c deleted file mode 100644 index 82ef0a27..00000000 --- a/mission-control-plugins/dbus-acl.c +++ /dev/null @@ -1,431 +0,0 @@ -/* Mission Control plugin API - DBus Caller ID. - * - * Copyright © 2010 Nokia Corporation - * Copyright © 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 - */ - -/** - * SECTION:dbus-acl - * @title: McpDBusAcl - * @short_description: DBus ACLs, implemented by plugins - * @see_also: - * @include: mission-control-plugins/mission-control-plugins.h - * - * Plugins may implement #McpDBusAcl in order to provide checks on whether - * a DBus method call or property get/set operation should be allowed. - * - * To do so, the plugin must implement a #GObject subclass that implements - * #McpDBusAcl, then return an instance of that subclass from - * mcp_plugin_ref_nth_object(). - * - * An implementation of this interface might look like this: - * - * <example><programlisting> - * G_DEFINE_TYPE_WITH_CODE (APlugin, a_plugin, - * G_TYPE_OBJECT, - * G_IMPLEMENT_INTERFACE (...); - * G_IMPLEMENT_INTERFACE (MCP_TYPE_DBUS_ACL, dbus_acl_iface_init); - * G_IMPLEMENT_INTERFACE (...)) - * /<!-- -->* ... *<!-- -->/ - * static void - * dbus_acl_iface_init (McpDBusAclIface *iface, - * gpointer unused G_GNUC_UNUSED) - * { - * iface->name = "APlugin"; - * iface->desc = "A plugin that checks some conditions"; - * iface->authorised = _authorised; - * iface->authorised_async = _authorised_async; - * } - * </programlisting></example> - * - * A single object can implement more than one interface. - */ - -#include "config.h" - -#include <mission-control-plugins/mission-control-plugins.h> -#include <mission-control-plugins/debug-internal.h> -#include <mission-control-plugins/mcp-signals-marshal.h> -#include <glib.h> -#include <telepathy-glib/telepathy-glib.h> - -#define MCP_DEBUG_TYPE MCP_DEBUG_DBUS_ACL -#define ACL_DEBUG(_p, _format, ...) \ - DEBUG("%s: " _format, \ - (_p != NULL) ? mcp_dbus_acl_name (_p) : "-", ##__VA_ARGS__) - -/** - * McpDBusAclIface: - * @parent: the parent type - * @name: the name of the plugin, or %NULL to use the GObject class name - * @desc: the description of the plugin, or %NULL - * @authorised: an implementation of part of mcp_dbus_acl_authorised() - * @authorised_async: an implementation of part of - * mcp_dbus_acl_authorised_async() - */ - -GType -mcp_dbus_acl_get_type (void) -{ - static gsize once = 0; - static GType type = 0; - - if (g_once_init_enter (&once)) - { - static const GTypeInfo info = - { - sizeof (McpDBusAclIface), - NULL, /* base_init */ - NULL, /* base_finalize */ - NULL, /* class_init */ - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, /* instance_size */ - 0, /* n_preallocs */ - NULL, /* instance_init */ - NULL /* value_table */ - }; - - type = g_type_register_static (G_TYPE_INTERFACE, - "McpDBusAcl", &info, 0); - g_type_interface_add_prerequisite (type, G_TYPE_OBJECT); - - g_once_init_leave (&once, 1); - } - - return type; -} - -static GList * -cached_acls (void) -{ - static gboolean acl_plugins_cached = FALSE; - static GList *dbus_acls = NULL; - - const GList *p; - - if (acl_plugins_cached) - return dbus_acls; - - /* insert the default storage plugin into the sorted plugin list */ - for (p = mcp_list_objects(); p != NULL; p = g_list_next (p)) - { - if (MCP_IS_DBUS_ACL (p->data)) - { - dbus_acls = g_list_prepend (dbus_acls, g_object_ref (p->data)); - } - } - - acl_plugins_cached = TRUE; - - return dbus_acls; -} - - -static DBusAclAuthData * -auth_data_new (TpDBusDaemon *dbus, const gchar *name, GHashTable *params) -{ - DBusAclAuthData *data = g_slice_new0 (DBusAclAuthData); - - data->dbus = g_object_ref (dbus); - data->params = (params != NULL) ? g_hash_table_ref (params) : NULL; - data->name = g_strdup (name); - - return data; -} - -static void -auth_data_free (DBusAclAuthData *data) -{ - data->cleanup (data->data); /* free the callback data */ - - tp_clear_pointer (&data->params, g_hash_table_unref); - tp_clear_object (&data->dbus); - g_free (data->name); - - g_slice_free (DBusAclAuthData, data); -} -/** - * mcp_dbus_acl_iface_set_name: - * @iface: an instance implementing McpDBusAclIface - * @name: the plugin's name (used in debugging and some return values) - * - * Sets the name of the plugin. Intended for use by the plugin implementor. - * - * This is no longer necessary: just use "iface->name = name". - **/ -void -mcp_dbus_acl_iface_set_name (McpDBusAclIface *iface, - const gchar *name) -{ - iface->name = name; -} - -/** - * mcp_dbus_acl_iface_set_desc: - * @iface: an instance implementing McpDBusAclIface - * @desc: the plugin's description - * - * Sets the plugin's description. Intended for use by the plugin implementor. - * - * This is no longer necessary: just use "iface->desc = desc". - **/ -void -mcp_dbus_acl_iface_set_desc (McpDBusAclIface *iface, - const gchar *desc) -{ - iface->desc = desc; -} - -/** - * mcp_dbus_acl_iface_implement_authorised: - * @iface: an instance implementing McpDBusAclIface - * @method: the plugin's description - * - * Implements this plugin's part of the mcp_dbus_acl_authorised() method. - * - * This is no longer necessary: just use "iface->authorised = method". - **/ -void -mcp_dbus_acl_iface_implement_authorised (McpDBusAclIface *iface, - DBusAclAuthoriser method) -{ - iface->authorised = method; -} - -/** - * mcp_dbus_acl_iface_implement_authorised_async: - * @iface: an instance implementing McpDBusAclIface - * @method: the plugin's description - * - * Implements this plugin's part of the mcp_dbus_acl_authorised_async() method. - * - * This is no longer necessary: just use "iface->authorised_async = method". - **/ -void -mcp_dbus_acl_iface_implement_authorised_async (McpDBusAclIface *iface, - DBusAclAsyncAuthoriser method) -{ - iface->authorised_async = method; -} - -/* FIXME: when we break ABI, this should move to src/ under a different name, - * and mcp_dbus_acl_authorised() should be a trivial wrapper around - * iface->authorised() */ -/** - * mcp_dbus_acl_authorised: - * @dbus: a #TpDBusDaemon instance - * @context: a #DBusGMethodInvocation corresponding to the DBus call - * @type: a #DBusAclType value (method call, get or set property) - * @name: the name of the method or property in question - * @params: A #GHashTable of #gchar * / #GValue parameters relating to the - * call which are deemed to be "of interest" for ACL plugins, or %NULL - * - * This method calls each #DBusAcl plugin's authorised method, set by - * mcp_dbus_acl_iface_implement_authorised() - * - * How a plugin deals with @params is entirely plugin dependent. - * - * If any plugin returns %FALSE, the call is considered to be forbidden. - * - * Returns: a #gboolean - %TRUE for permitted, %FALSE for forbidden. - **/ -gboolean -mcp_dbus_acl_authorised (const TpDBusDaemon *dbus, - DBusGMethodInvocation *context, - DBusAclType type, - const gchar *name, - const GHashTable *params) -{ - GList *p; - GList *acls = cached_acls (); - gboolean permitted = TRUE; - - for (p = acls; permitted && p != NULL; p = g_list_next (p)) - { - McpDBusAcl *plugin = MCP_DBUS_ACL (p->data); - McpDBusAclIface *iface = MCP_DBUS_ACL_GET_IFACE (p->data); - - ACL_DEBUG (plugin, "checking ACL for %s", name); - - if (iface->authorised != NULL) - permitted = iface->authorised (plugin, dbus, context, type, name, params); - - if (!permitted) - break; - } - - if (!permitted) - { - GError *denied = NULL; - const gchar *denier = mcp_dbus_acl_name (p->data); - - denied = g_error_new (DBUS_GERROR, DBUS_GERROR_ACCESS_DENIED, - "permission denied by DBus ACL plugin '%s'", denier); - - dbus_g_method_return_error (context, denied); - - g_error_free (denied); - } - - return permitted; -} - -/** - * mcp_dbus_acl_authorised_async_step: - * @ad: a #DBusAclAuthData pointer - * @permitted: whether the last plugin permitted the call being inspected - * - * This call is intended for use in the authorised_async mehod of a - * #DBusAcl plugin - it allows the plugin to hand control back to the - * overall ACL infrastructure, informing it of its decision as it does. - **/ -void -mcp_dbus_acl_authorised_async_step (DBusAclAuthData *ad, - gboolean permitted) -{ - if (permitted) - { - while (ad->next_acl != NULL && ad->next_acl->data != NULL) - { - McpDBusAcl *plugin = MCP_DBUS_ACL (ad->next_acl->data); - McpDBusAclIface *iface = MCP_DBUS_ACL_GET_IFACE (plugin); - - if (ad->acl != NULL) - ACL_DEBUG (ad->acl, "passed ACL for %s", ad->name); - - /* take the next plugin off the next_acl list */ - ad->next_acl = g_list_next (ad->next_acl); - ad->acl = plugin; - - if (iface->authorised_async != NULL) - { - /* kick off the next async authoriser in the chain */ - iface->authorised_async (plugin, ad); - - /* don't clean up, the next async acl will call us when it's - * done: */ - return; - } - } - - if (ad->acl != NULL) - ACL_DEBUG (ad->acl, "passed final ACL for %s", ad->name); - - ad->handler (ad->context, ad->data); - } - else - { - const gchar *who = (ad->acl != NULL) ? mcp_dbus_acl_name (ad->acl) : NULL; - GError *denied = g_error_new (DBUS_GERROR, DBUS_GERROR_ACCESS_DENIED, - "%s permission denied by DBus ACL plugin '%s'", - ad->name, - (who != NULL) ? who : "*unknown*"); - - dbus_g_method_return_error (ad->context, denied); - - g_error_free (denied); - } - - auth_data_free (ad); /* done with internal bookkeeping */ -} - -/* FIXME: when we break ABI, this should move to src/ under a different name, - * and mcp_dbus_acl_authorised_async() should be a trivial wrapper around - * iface->authorised_async(); it should also use GIO-style asynchronicity */ -/** - * mcp_dbus_acl_authorised_async: - * @dbus: a #TpDBusDaemon instance - * @context: a #DBusGMethodInvocation corresponding to the DBus call - * @type: a #DBusAclType value (method call, get or set property) - * @name: the name of the method or property in question - * @params: A #GHashTable of #gchar * / #GValue parameters relating to the - * call which are deemed to be "of interest" for ACL plugins, or %NULL - * @handler: callback to call if the ACL decides the call is permitted - * @data: a #gpointer to pass to the @handler - * @cleanup: a #GDestroyNotify to use to deallocate @data - * - * This method calls each #DBusAcl plugin's authorised_async method, set by - * mcp_dbus_acl_iface_implement_authorised_async() - * - * How a plugin deals with @parameters is entirely plugin dependent. - * - * The plugin should implement an async (or at least non-blocking) - * check, which should signal that it has finished by calling - * mcp_dbus_acl_authorised_async_step() - * - * If all the plugins permit this call, then @handler will be invoked - * with @context and @data as its arguments. - * - * @cleanup wll be called if the call is forbidden, or after @handler is - * invoked. If the call is forbidden, a DBus error will be returned to the - * caller automatically. - * - **/ -void -mcp_dbus_acl_authorised_async (TpDBusDaemon *dbus, - DBusGMethodInvocation *context, - DBusAclType type, - const gchar *name, - GHashTable *params, - DBusAclAuthorised handler, - gpointer data, - GDestroyNotify cleanup) -{ - GList *acls = cached_acls (); - DBusAclAuthData *ad = auth_data_new (dbus, name, params); - - ad->acl = NULL; /* first step, there's no current ACL yet */ - ad->type = type; - ad->data = data; - ad->cleanup = cleanup; - ad->context = context; - ad->handler = handler; - ad->next_acl = acls; - - ACL_DEBUG (NULL, "DBus access ACL verification: %u rules for %s", - g_list_length (acls), - name); - mcp_dbus_acl_authorised_async_step (ad, TRUE); -} - -/* plugin meta-data */ -const gchar * -mcp_dbus_acl_name (const McpDBusAcl *self) -{ - McpDBusAclIface *iface = MCP_DBUS_ACL_GET_IFACE (self); - - g_return_val_if_fail (iface != NULL, FALSE); - - if (iface->name == NULL) - return G_OBJECT_TYPE_NAME (self); - - return iface->name; -} - -const gchar * -mcp_dbus_acl_description (const McpDBusAcl *self) -{ - McpDBusAclIface *iface = MCP_DBUS_ACL_GET_IFACE (self); - - g_return_val_if_fail (iface != NULL, FALSE); - - if (iface->desc == NULL) - return "(no description)"; - - return iface->desc; -} diff --git a/mission-control-plugins/dbus-acl.h b/mission-control-plugins/dbus-acl.h deleted file mode 100644 index deba7b72..00000000 --- a/mission-control-plugins/dbus-acl.h +++ /dev/null @@ -1,134 +0,0 @@ -/* Mission Control plugin API - DBus Caller ID. - * - * Copyright © 2010 Nokia Corporation - * Copyright © 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 MCP_DBUS_ACL_H -#define MCP_DBUS_ACL_H - -#ifndef _MCP_IN_MISSION_CONTROL_PLUGINS_H -#error Use <mission-control-plugins/mission-control-plugins.h> instead -#endif - -#include <dbus/dbus-glib-lowlevel.h> -#include <telepathy-glib/telepathy-glib.h> - -G_BEGIN_DECLS - -/* API for plugins to implement */ -typedef struct _McpDBusAcl McpDBusAcl; -typedef struct _McpDBusAclIface McpDBusAclIface; - -#define MCP_TYPE_DBUS_ACL (mcp_dbus_acl_get_type ()) - -#define MCP_DBUS_ACL(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MCP_TYPE_DBUS_ACL, McpDBusAcl)) - -#define MCP_IS_DBUS_ACL(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCP_TYPE_DBUS_ACL)) - -#define MCP_DBUS_ACL_GET_IFACE(o) \ - (G_TYPE_INSTANCE_GET_INTERFACE ((o), MCP_TYPE_DBUS_ACL, \ - McpDBusAclIface)) - -GType mcp_dbus_acl_get_type (void) G_GNUC_CONST; - -/* FIXME: when we break API, rename all types to start with Mcp */ - -typedef void (*DBusAclAuthorised) (DBusGMethodInvocation *call, gpointer data); - -typedef enum { - DBUS_ACL_TYPE_UNKNOWN = 0, - DBUS_ACL_TYPE_METHOD, - DBUS_ACL_TYPE_GET_PROPERTY, - DBUS_ACL_TYPE_SET_PROPERTY, -} DBusAclType; - -typedef struct { - McpDBusAcl *acl; - const GList *next_acl; - DBusGMethodInvocation *context; - DBusAclType type; - gchar *name; - GHashTable *params; - TpDBusDaemon *dbus; - DBusAclAuthorised handler; - gpointer data; - GDestroyNotify cleanup; -} DBusAclAuthData; - -typedef gboolean (*DBusAclAuthoriser) (const McpDBusAcl *, - const TpDBusDaemon *, - const DBusGMethodInvocation *, - DBusAclType type, - const gchar *name, - const GHashTable *); - -typedef void (*DBusAclAsyncAuthoriser) (const McpDBusAcl *, - DBusAclAuthData *); - -/* real functions that handle multi-plugin acl logic */ -void mcp_dbus_acl_authorised_async_step (DBusAclAuthData *ad, - gboolean permitted); - -void mcp_dbus_acl_authorised_async (TpDBusDaemon *dbus, - DBusGMethodInvocation *context, - DBusAclType type, - const gchar *name, - GHashTable *params, - DBusAclAuthorised handler, - gpointer data, - GDestroyNotify cleanup); - -gboolean mcp_dbus_acl_authorised (const TpDBusDaemon *dbus, - DBusGMethodInvocation *context, - DBusAclType type, - const gchar *name, - const GHashTable *params); - -/* virtual methods */ -void mcp_dbus_acl_iface_set_name (McpDBusAclIface *iface, - const gchar *name); - -void mcp_dbus_acl_iface_set_desc (McpDBusAclIface *iface, - const gchar *desc); - -void mcp_dbus_acl_iface_implement_authorised (McpDBusAclIface *iface, - DBusAclAuthoriser method); - -void mcp_dbus_acl_iface_implement_authorised_async (McpDBusAclIface *iface, - DBusAclAsyncAuthoriser method); - -const gchar *mcp_dbus_acl_name (const McpDBusAcl *acl); - -const gchar *mcp_dbus_acl_description (const McpDBusAcl *acl); - -struct _McpDBusAclIface -{ - GTypeInterface parent; - - const gchar *name; - const gchar *desc; - - DBusAclAuthoriser authorised; - DBusAclAsyncAuthoriser authorised_async; -}; - -G_END_DECLS - -#endif diff --git a/mission-control-plugins/debug.c b/mission-control-plugins/debug.c index af0f1df1..177e1d66 100644 --- a/mission-control-plugins/debug.c +++ b/mission-control-plugins/debug.c @@ -26,12 +26,11 @@ static McpDebugFlags debug_flags; static GDebugKey const keys[] = { { "account", MCP_DEBUG_ACCOUNT }, { "account-storage", MCP_DEBUG_ACCOUNT_STORAGE }, - { "dbus-acl", MCP_DEBUG_DBUS_ACL }, + { "request-policy", MCP_DEBUG_REQUEST_POLICY }, { "dispatch-operation", MCP_DEBUG_DISPATCH_OPERATION }, { "dispatch-operation-policy", MCP_DEBUG_DISPATCH_OPERATION_POLICY }, { "loader", MCP_DEBUG_LOADER }, { "request", MCP_DEBUG_REQUEST }, - { "request-policy", MCP_DEBUG_REQUEST_POLICY }, { NULL, 0 } }; diff --git a/mission-control-plugins/debug.h b/mission-control-plugins/debug.h index ced40bb8..8d3cec87 100644 --- a/mission-control-plugins/debug.h +++ b/mission-control-plugins/debug.h @@ -35,12 +35,11 @@ typedef enum { MCP_DEBUG_NONE = 0, MCP_DEBUG_ACCOUNT = 1 << 0, MCP_DEBUG_ACCOUNT_STORAGE = 1 << 1, - MCP_DEBUG_DBUS_ACL = 1 << 2, + MCP_DEBUG_REQUEST_POLICY = 1 << 2, MCP_DEBUG_DISPATCH_OPERATION = 1 << 3, MCP_DEBUG_DISPATCH_OPERATION_POLICY = 1 << 4, MCP_DEBUG_LOADER = 1 << 5, MCP_DEBUG_REQUEST = 1 << 6, - MCP_DEBUG_REQUEST_POLICY = 1 << 7, } McpDebugFlags; gboolean mcp_is_debugging (McpDebugFlags type); diff --git a/mission-control-plugins/dispatch-operation.c b/mission-control-plugins/dispatch-operation.c index 2bd2ab2b..1fced1aa 100644 --- a/mission-control-plugins/dispatch-operation.c +++ b/mission-control-plugins/dispatch-operation.c @@ -285,45 +285,6 @@ mcp_dispatch_operation_end_delay (McpDispatchOperation *self, } /** - * mcp_dispatch_operation_leave_channels: - * @self: a dispatch operation - * @wait_for_observers: if %FALSE, leave the channels immediately; if %TRUE - * (usually recommended), wait for Observers to reply first - * @reason: the reason code to give - * @message: a human-readable message provided by the user, or either the - * empty string or %NULL if no message has been provided - * - * Leave all channels in this bundle by using RemoveMembersWithReason if the - * channel has the Group interface, or Close if not. - * - * This method was intended for StreamedMedia channels, which (ab)used the - * Group interface for call control. StreamedMedia channels have been - * superseded by Call channels, which have a proper "hang up" method which - * should be used instead. - * - * Deprecated: 5.15.1: Use tp_call_channel_hangup_async() to - * hang up Call channels, mcp_dispatch_operation_close_channels() to close - * generic channels, or mcp_dispatch_operation_destroy_channels() to - * terminate the channel destructively. - */ -void -mcp_dispatch_operation_leave_channels (McpDispatchOperation *self, - gboolean wait_for_observers, - TpChannelGroupChangeReason reason, - const gchar *message) -{ - McpDispatchOperationIface *iface = MCP_DISPATCH_OPERATION_GET_IFACE (self); - - g_return_if_fail (iface != NULL); - g_return_if_fail (iface->leave_channels != NULL); - - if (message == NULL) - message = ""; - - iface->leave_channels (self, wait_for_observers, reason, message); -} - -/** * mcp_dispatch_operation_close_channels: * @self: a dispatch operation * @wait_for_observers: if %FALSE, close the channels immediately; if %TRUE @@ -371,6 +332,9 @@ mcp_dispatch_operation_destroy_channels (McpDispatchOperation *self, /** * mcp_dispatch_operation_find_channel_by_type: * @self: a dispatch operation + * @client_factory: used to construct @ret_ref_channel. In high-level + * language bindings using gobject-introspection, this must not be %NULL. + * In C, this may be %NULL, but only if @ret_ref_channel is also %NULL. * @start_from: index at which to start searching, usually 0 * @handle_type: the handle type to match * @channel_type: the channel type to match @@ -397,6 +361,7 @@ mcp_dispatch_operation_destroy_channels (McpDispatchOperation *self, */ gboolean mcp_dispatch_operation_find_channel_by_type (McpDispatchOperation *self, + TpSimpleClientFactory *client_factory, guint start_from, TpHandleType handle_type, GQuark channel_type, @@ -411,6 +376,8 @@ mcp_dispatch_operation_find_channel_by_type (McpDispatchOperation *self, g_return_val_if_fail (MCP_IS_DISPATCH_OPERATION (self), FALSE); g_return_val_if_fail (channel_type != 0, FALSE); + g_return_val_if_fail (client_factory != NULL || ret_ref_channel == NULL, + FALSE); for (i = start_from; i < mcp_dispatch_operation_get_n_channels (self); i++) { @@ -441,14 +408,11 @@ mcp_dispatch_operation_find_channel_by_type (McpDispatchOperation *self, if (ret_ref_channel != NULL) { - /* FIXME: in next, this method should take a TpClientFactory - * argument, and pass it on here */ TpConnection *connection = - mcp_dispatch_operation_ref_connection (self); + mcp_dispatch_operation_ref_connection (self, client_factory); *ret_ref_channel = tp_simple_client_factory_ensure_channel ( - tp_proxy_get_factory (connection), connection, - channel_path, properties, NULL); + client_factory, connection, channel_path, properties, NULL); g_object_unref (connection); } @@ -465,35 +429,31 @@ mcp_dispatch_operation_find_channel_by_type (McpDispatchOperation *self, /** * mcp_dispatch_operation_ref_connection: * @self: a dispatch operation + * @client_factory: the client factory to use to construct the #TpConnection * - * Return a #TpConnection object. It is not guaranteed to be ready immediately; - * use tp_connection_call_when_ready(). + * Return a #TpConnection object. It is not guaranteed to be prepared; + * use tp_proxy_prepare_async(). * * Returns: a reference to a #TpConnection, which must be released with * g_object_unref() by the caller */ TpConnection * -mcp_dispatch_operation_ref_connection (McpDispatchOperation *self) +mcp_dispatch_operation_ref_connection (McpDispatchOperation *self, + TpSimpleClientFactory *client_factory) { - TpDBusDaemon *dbus = tp_dbus_daemon_dup (NULL); - TpConnection *connection = NULL; const gchar *conn_path; + g_return_val_if_fail (client_factory != NULL, NULL); + conn_path = mcp_dispatch_operation_get_connection_path (self); - if (conn_path != NULL && dbus != NULL) + if (conn_path != NULL) { - /* FIXME: in next, this method should take a TpClientFactory argument - * instead of making a new one here */ - TpSimpleClientFactory *factory = tp_simple_client_factory_new (dbus); - - connection = tp_simple_client_factory_ensure_connection (factory, + return tp_simple_client_factory_ensure_connection (client_factory, conn_path, NULL, NULL); - g_object_unref (factory); } - g_object_unref (dbus); - return connection; + return NULL; } /** @@ -501,23 +461,26 @@ mcp_dispatch_operation_ref_connection (McpDispatchOperation *self) * @self: a dispatch operation * @n: index of the channel to inspect * - * Return a #TpChannel object. It is not guaranteed to be ready immediately; - * use tp_channel_call_when_ready(). + * Return a #TpChannel object. It is not guaranteed to be prepared; + * use tp_proxy_prepare_async(). * * Returns: a reference to a #TpChannel, which must be released with * g_object_unref() by the caller, or %NULL if @n is too large */ TpChannel * mcp_dispatch_operation_ref_nth_channel (McpDispatchOperation *self, + TpSimpleClientFactory *client_factory, guint n) { - /* FIXME: in next, this method should take a TpClientFactory argument, - * and pass it on here */ - TpConnection *connection = mcp_dispatch_operation_ref_connection (self); + TpConnection *connection; GHashTable *channel_properties = NULL; const gchar *channel_path = NULL; TpChannel *channel = NULL; + g_return_val_if_fail (client_factory != NULL, NULL); + + connection = mcp_dispatch_operation_ref_connection (self, client_factory); + if (connection == NULL) goto finally; @@ -532,8 +495,7 @@ mcp_dispatch_operation_ref_nth_channel (McpDispatchOperation *self, if (channel_properties == NULL) goto finally; - channel = tp_simple_client_factory_ensure_channel ( - tp_proxy_get_factory (connection), + channel = tp_simple_client_factory_ensure_channel (client_factory, connection, channel_path, channel_properties, NULL); finally: diff --git a/mission-control-plugins/dispatch-operation.h b/mission-control-plugins/dispatch-operation.h index 5ad12d9b..889f5fd2 100644 --- a/mission-control-plugins/dispatch-operation.h +++ b/mission-control-plugins/dispatch-operation.h @@ -53,13 +53,15 @@ GType mcp_dispatch_operation_get_type (void) G_GNUC_CONST; gboolean mcp_dispatch_operation_find_channel_by_type ( McpDispatchOperation *self, + TpSimpleClientFactory *client_factory, guint start_from, TpHandleType handle_type, GQuark channel_type, guint *ret_index, gchar **ret_dup_path, GHashTable **ret_ref_immutable_properties, TpChannel **ret_ref_channel); TpConnection *mcp_dispatch_operation_ref_connection ( - McpDispatchOperation *self); + McpDispatchOperation *self, TpSimpleClientFactory *client_factory); TpChannel *mcp_dispatch_operation_ref_nth_channel (McpDispatchOperation *self, + TpSimpleClientFactory *client_factory, guint n); /* virtual methods */ @@ -85,13 +87,6 @@ McpDispatchOperationDelay *mcp_dispatch_operation_start_delay ( void mcp_dispatch_operation_end_delay (McpDispatchOperation *self, McpDispatchOperationDelay *delay); -#ifndef MC_DISABLE_DEPRECATED -G_DEPRECATED_FOR (mcp_dispatch_operation_close_channels or tp_call_channel_hangup_async) -void mcp_dispatch_operation_leave_channels (McpDispatchOperation *self, - gboolean wait_for_observers, TpChannelGroupChangeReason reason, - const gchar *message); -#endif - void mcp_dispatch_operation_close_channels (McpDispatchOperation *self, gboolean wait_for_observers); void mcp_dispatch_operation_destroy_channels (McpDispatchOperation *self, diff --git a/mission-control-plugins/implementation.h b/mission-control-plugins/implementation.h index 20c5fdf4..59096f93 100644 --- a/mission-control-plugins/implementation.h +++ b/mission-control-plugins/implementation.h @@ -68,9 +68,6 @@ struct _McpDispatchOperationIface { McpDispatchOperationDelay *delay); /* Close */ - void (*leave_channels) (McpDispatchOperation *self, - gboolean wait_for_observers, TpChannelGroupChangeReason reason, - const gchar *message); void (*close_channels) (McpDispatchOperation *self, gboolean wait_for_observers); void (*destroy_channels) (McpDispatchOperation *self, @@ -80,57 +77,28 @@ struct _McpDispatchOperationIface { struct _McpAccountManagerIface { GTypeInterface parent; - void (*set_value) (const McpAccountManager *ma, - const gchar *acct, - const gchar *key, - const gchar *value); - - gchar * (*get_value) (const McpAccountManager *ma, - const gchar *acct, - const gchar *key); - - gboolean (*is_secret) (const McpAccountManager *ma, - const gchar *acct, - const gchar *key); - - void (* make_secret) (const McpAccountManager *ma, - const gchar *acct, - const gchar *key); - gchar * (* unique_name) (const McpAccountManager *ma, const gchar *manager, const gchar *protocol, - const GHashTable *params); - - GStrv (* list_keys) (const McpAccountManager *ma, - const gchar *acct); - - gchar * (* escape_value_for_keyfile) (const McpAccountManager *mcpa, - const GValue *value); - - gboolean (* unescape_value_from_keyfile) (const McpAccountManager *mcpa, - const gchar *escaped, - GValue *value, - GError **error); - - gboolean (* init_value_for_attribute) (const McpAccountManager *mcpa, - GValue *value, - const gchar *attribute); + const gchar *identification); gchar * (* escape_variant_for_keyfile) (const McpAccountManager *mcpa, GVariant *variant); + GVariant *(* unescape_variant_from_keyfile) (const McpAccountManager *mcpa, + const gchar *escaped, + const GVariantType *type, + GError **error); - void (* set_attribute) (const McpAccountManager *mcpa, - const gchar *account, - const gchar *attribute, - GVariant *value, - McpAttributeFlags flags); - - void (* set_parameter) (const McpAccountManager *mcpa, - const gchar *account, - const gchar *parameter, - GVariant *value, - McpParameterFlags flags); + void (* identify_account_async) (McpAccountManager *mcpa, + const gchar *manager, + const gchar *protocol, + GVariant *parameters, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gchar * (* identify_account_finish) (McpAccountManager *mcpa, + GAsyncResult *res, + GError **error); }; G_END_DECLS diff --git a/mission-control-plugins/loader.c b/mission-control-plugins/loader.c index 640d1d87..a23028b3 100644 --- a/mission-control-plugins/loader.c +++ b/mission-control-plugins/loader.c @@ -62,6 +62,21 @@ static gboolean debugging = FALSE; #endif /** + * MCP_API_VERSION_5_18: + * + * Defined to allow simple plugin implementations to support both Mission + * Control 5.16 and 5.18 plugin APIs: + * + * |[ + * #ifdef MCP_API_VERSION_5_18 + * ... // implement MC 5.18 API + * #else + * ... // implement MC 5.16 API + * #endif + * ]| + */ + +/** * mcp_set_debug: * @debug: whether to log debug output * diff --git a/mission-control-plugins/mission-control-plugins.h b/mission-control-plugins/mission-control-plugins.h index d875199c..186da91d 100644 --- a/mission-control-plugins/mission-control-plugins.h +++ b/mission-control-plugins/mission-control-plugins.h @@ -25,19 +25,11 @@ #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> -typedef enum { - MCP_PARAMETER_FLAG_NONE = 0, - MCP_PARAMETER_FLAG_SECRET = TP_CONN_MGR_PARAM_FLAG_SECRET -} McpParameterFlags; - -typedef enum { - MCP_ATTRIBUTE_FLAG_NONE = 0 -} McpAttributeFlags; +#define MCP_API_VERSION_5_18 (518) #define _MCP_IN_MISSION_CONTROL_PLUGINS_H #include <mission-control-plugins/account.h> #include <mission-control-plugins/account-storage.h> -#include <mission-control-plugins/dbus-acl.h> #include <mission-control-plugins/dispatch-operation.h> #include <mission-control-plugins/dispatch-operation-policy.h> #include <mission-control-plugins/loader.h> diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 23ccc6c0..42a5a3e4 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = \ +AM_CPPFLAGS = \ $(TELEPATHY_CFLAGS) \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ @@ -8,15 +8,3 @@ INCLUDES = \ -DLIBVERSION="@MCP_ABI_VERSION@" noinst_LTLIBRARIES = - -if ENABLE_AEGIS - -noinst_LTLIBRARIES += libmcp-aegis.la -libmcp_aegis_la_SOURCES = mcp-dbus-aegis-acl.c -libmcp_aegis_la_LIBADD = \ - $(top_builddir)/mission-control-plugins/libmission-control-plugins.la \ - $(TELEPATHY_LIBS) \ - $(DBUS_LIBS) \ - $(AEGIS_LIBS) - -endif diff --git a/plugins/mcp-dbus-aegis-acl.c b/plugins/mcp-dbus-aegis-acl.c deleted file mode 100644 index 1eda98e6..00000000 --- a/plugins/mcp-dbus-aegis-acl.c +++ /dev/null @@ -1,441 +0,0 @@ -/* - * A pseudo-plugin that checks the caller's Aegis permission tokens - * - * Copyright © 2010-2011 Nokia Corporation - * Copyright © 2010-2011 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 "config.h" - -#ifdef G_LOG_DOMAIN -#undef G_LOG_DOMAIN -#endif -#define G_LOG_DOMAIN "mission-control-DBus-Access-ACL" - -#define DEBUG(_f, ...) MCP_DEBUG (MCP_DEBUG_DBUS_ACL, _f, ##__VA_ARGS__) - -#include <dbus/dbus-glib.h> - -#include <telepathy-glib/telepathy-glib.h> -#include <telepathy-glib/telepathy-glib-dbus.h> - -#include <mission-control-plugins/mission-control-plugins.h> - -#include <sys/types.h> -#include <sys/creds.h> - -typedef struct _AegisAcl AegisAcl; -typedef struct _AegisAclClass AegisAclClass; - -struct _AegisAcl { - GObject parent; -}; - -struct _AegisAclClass { - GObjectClass parent_class; -}; - -#define CREATE_CHANNEL TP_IFACE_CONNECTION_INTERFACE_REQUESTS ".CreateChannel" -#define ENSURE_CHANNEL TP_IFACE_CONNECTION_INTERFACE_REQUESTS ".EnsureChannel" -#define SEND_MESSAGE \ - TP_IFACE_CHANNEL_DISPATCHER ".Interface.Messages.DRAFT.SendMessage" - -#define AEGIS_CALL_TOKEN "Cellular" - -/* implemented by the Aegis-patched dbus-daemon */ -#define AEGIS_INTERFACE "com.meego.DBus.Creds" - -#define PLUGIN_NAME "dbus-aegis-acl" -#define PLUGIN_DESCRIPTION \ - "This plugin uses libcreds to check the aegis security tokens " \ - "associated with the calling process ID and determine whether " \ - "the DBus call or property access should be allowed" - -static creds_value_t aegis_token = CREDS_BAD; -static creds_type_t aegis_type = CREDS_BAD; - -static void aegis_acl_iface_init (McpDBusAclIface *, - gpointer); -static void aegis_cdo_policy_iface_init (McpDispatchOperationPolicyIface *, - gpointer); - -static GType aegis_acl_get_type (void); - -G_DEFINE_TYPE_WITH_CODE (AegisAcl, aegis_acl, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MCP_TYPE_DBUS_ACL, aegis_acl_iface_init); - G_IMPLEMENT_INTERFACE (MCP_TYPE_DISPATCH_OPERATION_POLICY, - aegis_cdo_policy_iface_init)) - -static void -aegis_acl_init (AegisAcl *self) -{ -} - -static void -aegis_acl_class_init (AegisAclClass *cls) -{ - if (aegis_type != CREDS_BAD) - return; - - aegis_type = creds_str2creds (AEGIS_CALL_TOKEN, &aegis_token); -} - -static gchar *restricted_methods[] = - { - CREATE_CHANNEL, - ENSURE_CHANNEL, - SEND_MESSAGE, - NULL - }; - -static gboolean -method_is_filtered (const gchar *method) -{ - guint i; - - for (i = 0; restricted_methods[i] != NULL; i++) - { - if (!tp_strdiff (method, restricted_methods[i])) - return TRUE; - } - - return FALSE; -} - -static gboolean -is_filtered (DBusAclType type, - const gchar *name, - const GHashTable *params) -{ - const GValue *account = NULL; - const gchar *path = NULL; - - /* only bothered with method calls */ - if (type != DBUS_ACL_TYPE_METHOD) - return FALSE; - - /* only create/ensure channel concern us (and send message, now): */ - if (!method_is_filtered (name)) - return FALSE; - - /* must have at least the account-path to check */ - if (params == NULL) - return FALSE; - - account = g_hash_table_lookup ((GHashTable *) params, "account-path"); - - if (account == NULL) - return FALSE; - - path = g_value_get_string (account); - - DEBUG ("should we check account %s?", path); - /* account must belong to the ring or MMS connection manager: */ - if (g_str_has_prefix (path, TP_ACCOUNT_OBJECT_PATH_BASE "ring/") || - g_str_has_prefix (path, TP_ACCOUNT_OBJECT_PATH_BASE "mmscm/")) - return TRUE; - - return FALSE; -} - -/* For simplicity we don't implement non-trivial conversion between - * dbus-glib's arrays of guint, and libcreds' arrays of uint32_t. - * If this assertion fails on your platform, you'll need to implement it. */ -G_STATIC_ASSERT (sizeof (guint) == sizeof (uint32_t)); - -static gboolean -caller_creds_are_enough (const gchar *name, - const GArray *au) -{ - creds_t caller_creds = creds_import ((const uint32_t *) au->data, au->len); - gboolean ok = creds_have_p (caller_creds, aegis_type, aegis_token); - -#ifdef ENABLE_DEBUG - if (ok) - { - DEBUG ("Caller %s is appropriately privileged", name); - } - else - { - char buf[1024]; - creds_type_t debug_type; - creds_value_t debug_value; - int i = 0; - - DEBUG ("Caller %s has these credentials:", name); - - while ((debug_type = creds_list (caller_creds, i++, &debug_value)) - != CREDS_BAD) - { - creds_creds2str (debug_type, debug_value, buf, sizeof (buf)); - DEBUG ("- %s", buf); - } - - DEBUG ("but they are insufficient"); - } -#endif - - creds_free (caller_creds); - return ok; -} - -static gboolean -check_peer_creds_sync (DBusGConnection *dgc, - const gchar *bus_name, - gboolean activate) -{ - DBusGProxy *proxy = dbus_g_proxy_new_for_name (dgc, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - AEGIS_INTERFACE); - GArray *au = NULL; - GError *error = NULL; - gboolean ok = FALSE; - - if (dbus_g_proxy_call (proxy, "GetConnectionCredentials", &error, - G_TYPE_STRING, bus_name, - G_TYPE_INVALID, - DBUS_TYPE_G_UINT_ARRAY, &au, - G_TYPE_INVALID)) - { - ok = caller_creds_are_enough (bus_name, au); - g_array_unref (au); - } - else if (activate && error->code == DBUS_GERROR_NAME_HAS_NO_OWNER) - { - guint status; - GError *start_error = NULL; - DBusGProxy *dbus = dbus_g_proxy_new_for_name (dgc, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS); - - DEBUG ("Trying to activate %s for aegis credentials check", bus_name); - if (dbus_g_proxy_call (dbus, "StartServiceByName", &start_error, - G_TYPE_STRING, bus_name, - G_TYPE_UINT, 0, - G_TYPE_INVALID, - G_TYPE_UINT, &status, - G_TYPE_INVALID)) - { - ok = check_peer_creds_sync (dgc, bus_name, FALSE); - } - else - { - DEBUG ("GetConnectionCredentials failed: %s", start_error->message); - g_clear_error (&start_error); - } - - g_object_unref (dbus); - g_clear_error (&error); - } - else - { - DEBUG ("GetConnectionCredentials failed: %s", error->message); - g_clear_error (&error); - ok = FALSE; - } - - g_object_unref (proxy); - return ok; -} - -static gboolean -caller_authorised (const McpDBusAcl *self, - const TpDBusDaemon *dbus, - const DBusGMethodInvocation *call, - DBusAclType type, - const gchar *name, - const GHashTable *params) -{ - DBusGConnection *dgc = tp_proxy_get_dbus_connection ((TpDBusDaemon *)dbus); - gboolean ok = TRUE; - - if (is_filtered (type, name, params)) - { - gchar *caller = dbus_g_method_get_sender ((DBusGMethodInvocation *) call); - - ok = check_peer_creds_sync (dgc, caller, FALSE); - - g_free (caller); - } - - DEBUG ("sync Aegis ACL check [%s]", ok ? "Allowed" : "Forbidden"); - - return ok; -} - -static void -async_authorised_cb (DBusGProxy *proxy, - DBusGProxyCall *call, - gpointer data) -{ - GError *error = NULL; - DBusAclAuthData *ad = data; - GArray *au = NULL; - const McpDBusAcl *self = ad->acl; - gboolean permitted = FALSE; - - /* if this returns FALSE, there are no credentials, which means something - * untrustworthy is going on, which in turn means we must deny: can't - * authorise without first authenticating */ - permitted = dbus_g_proxy_end_call (proxy, call, &error, - DBUS_TYPE_G_UINT_ARRAY, &au, - G_TYPE_INVALID); - - if (permitted) - { - permitted = caller_creds_are_enough (ad->name, au); - g_array_unref (au); - } - else - { - DEBUG ("GetConnectionCredentials failed: %s", error->message); - g_clear_error (&error); - } - - DEBUG ("finished async Aegis ACL check [%s]", - permitted ? "Allowed" : "Forbidden"); - - mcp_dbus_acl_authorised_async_step (ad, permitted); - - g_object_unref (proxy); -} - -static void -caller_async_authorised (const McpDBusAcl *self, - DBusAclAuthData *data) -{ - DEBUG ("starting async caller-permission ACL check"); - - if (is_filtered (data->type, data->name, data->params)) - { - DBusGConnection *dgc; - DBusGProxy *proxy; - gchar *caller = dbus_g_method_get_sender (data->context); - - dgc = tp_proxy_get_dbus_connection (data->dbus); - proxy = dbus_g_proxy_new_for_name (dgc, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - AEGIS_INTERFACE); - - dbus_g_proxy_begin_call (proxy, "GetConnectionCredentials", - async_authorised_cb, - data, - NULL, - G_TYPE_STRING, caller, - G_TYPE_INVALID); - - g_free (caller); - } - else - { - mcp_dbus_acl_authorised_async_step (data, TRUE); - } -} - - -static void -aegis_acl_iface_init (McpDBusAclIface *iface, - gpointer unused G_GNUC_UNUSED) -{ - mcp_dbus_acl_iface_set_name (iface, PLUGIN_NAME); - mcp_dbus_acl_iface_set_desc (iface, PLUGIN_DESCRIPTION); - - mcp_dbus_acl_iface_implement_authorised (iface, caller_authorised); - mcp_dbus_acl_iface_implement_authorised_async (iface, caller_async_authorised); -} - -static gchar *restricted_cms[] = { "ring", "mmscm", NULL }; - -static inline gboolean -cm_is_restricted (const gchar *cm_name) -{ - guint i; - - for (i = 0; restricted_cms[i] != NULL; i++) - { - if (!tp_strdiff (restricted_cms[i], cm_name)) - return TRUE; - } - - return FALSE; -} - -static void -handler_is_suitable_async (McpDispatchOperationPolicy *self, - TpClient *recipient, - const gchar *unique_name, - McpDispatchOperation *dispatch_op, - GAsyncReadyCallback callback, - gpointer user_data) -{ - const gchar *manager = mcp_dispatch_operation_get_cm_name (dispatch_op); - GSimpleAsyncResult *simple = g_simple_async_result_new ((GObject *) self, - callback, user_data, handler_is_suitable_async); - gboolean ok = TRUE; - - if (cm_is_restricted (manager)) - { - TpDBusDaemon *dbus = tp_dbus_daemon_dup (NULL); - - /* if MC started successfully, we ought to have one */ - g_assert (dbus != NULL); - - if (!tp_str_empty (unique_name)) - { - ok = check_peer_creds_sync (tp_proxy_get_dbus_connection (dbus), - unique_name, TRUE); - } - else - { - g_assert (recipient != NULL); - - ok = check_peer_creds_sync (tp_proxy_get_dbus_connection (dbus), - tp_proxy_get_bus_name (recipient), TRUE); - } - - if (!ok) - { - g_simple_async_result_set_error (simple, TP_ERROR, - TP_ERROR_PERMISSION_DENIED, "insufficient Aegis credentials"); - } - - g_object_unref (dbus); - } - - DEBUG ("sync Aegis CDO policy check [%s]", ok ? "Allowed" : "Forbidden"); - - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); -} - -static void -aegis_cdo_policy_iface_init (McpDispatchOperationPolicyIface *iface, - gpointer unused G_GNUC_UNUSED) -{ - iface->handler_is_suitable_async = handler_is_suitable_async; - /* the default finish function accepts our GSimpleAsyncResult */ -} - -GObject * -aegis_acl_new (void) -{ - return g_object_new (aegis_acl_get_type (), NULL); -} diff --git a/server/Makefile.am b/server/Makefile.am index 5be1cdc1..b18a442b 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -47,7 +47,7 @@ Android.mk: Makefile.am -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ -:SOURCES $(mission_control_5_SOURCES) \ -:CFLAGS $(DEFAULT_INCLUDES) $(DEFS) $(CFLAGS) $(AM_CFLAGS) \ - -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) $(INCLUDES) \ + -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) \ -DBUILD_AS_ANDROID_SERVICE \ -:LDFLAGS $(mission_control_5_LDADD) \ -:LIBFILTER_STATIC mcd-convenience mission-control-plugins \ diff --git a/src/Makefile.am b/src/Makefile.am index ae1de3e4..17c22f22 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,10 +1,8 @@ -INCLUDES = \ +AM_CPPFLAGS = \ $(TELEPATHY_CFLAGS) \ $(DBUS_CFLAGS) \ $(GLIB_CFLAGS) \ $(NETWORK_MANAGER_CFLAGS) \ - $(CONNMAN_CFLAGS) \ - $(UPOWER_GLIB_CFLAGS) \ -DG_LOG_DOMAIN=\"mcd\" \ -DMCD_PLUGIN_LOADER_DIR=\"@libdir@/mission-control-plugins.@MCP_ABI_VERSION@\" \ -DMC_DISABLE_DEPRECATED -I$(top_srcdir) @@ -13,7 +11,6 @@ AM_CFLAGS = $(ERROR_CFLAGS) mc_headers = \ mcd-account.h \ - mcd-account-conditions.h \ mcd-account-manager.h \ mcd-account-manager-default.h \ mcd-debug.h \ @@ -28,58 +25,17 @@ mc_headers = \ mcd-service.h \ mcd-storage.h -if ENABLE_LIBACCOUNTS_SSO -mc_headers += \ - mcd-account-manager-sso.h \ - $(NULL) - -if ENABLE_LIBACCOUNTS_GLIB_HIDDEN -mc_headers += \ - mcd-storage-ag-hidden.h \ - $(NULL) -endif - -endif - -mc_gen_headers = \ - _gen/cli-Connection_Manager_Interface_Account_Storage.h \ - _gen/enums.h \ - _gen/gtypes.h \ - _gen/interfaces.h \ - _gen/svc-Account_Interface_Conditions.h \ - _gen/svc-Account_Interface_External_Password_Storage.h \ - _gen/svc-Account_Interface_Hidden.h \ - _gen/svc-Account_Manager_Interface_Hidden.h \ - _gen/svc-dispatcher.h - nodist_libmcd_convenience_la_SOURCES = \ - _gen/cli-Connection_Manager_Interface_Account_Storage-body.h \ - _gen/gtypes-body.h \ - _gen/interfaces-body.h \ - _gen/register-dbus-glib-marshallers-body.h \ - _gen/svc-Account_Interface_Conditions.c \ - _gen/svc-Account_Interface_External_Password_Storage.c \ - _gen/svc-Account_Interface_Hidden.c \ - _gen/svc-Account_Manager_Interface_Hidden.c \ - _gen/svc-dispatcher.c \ mcd-enum-types.c \ mcd-enum-types.h \ - $(mc_gen_headers) + $(NULL) BUILT_SOURCES = \ - _gen/mcd.xml \ stamp-mcd-enum-types.h \ $(nodist_libmcd_convenience_la_SOURCES) CLEANFILES = \ $(BUILT_SOURCES) \ - _gen/cli-Connection_Manager_Interface_Account_Storage-gtk-doc.h \ - _gen/svc-dispatcher-gtk-doc.h \ - _gen/svc-Account_Interface_Hidden-gtk-doc.h \ - _gen/svc-Account_Interface_External_Password_Storage-gtk-doc.h \ - _gen/svc-Account_Interface_Conditions-gtk-doc.h \ - _gen/svc-Account_Manager_Interface_Hidden-gtk-doc.h \ - _gen/gtypes-gtk-doc.h \ $(NULL) libmcd_convenience_la_LIBADD = \ @@ -88,27 +44,14 @@ libmcd_convenience_la_LIBADD = \ $(DBUS_LIBS) \ $(GLIB_LIBS) \ $(NETWORK_MANAGER_LIBS) \ - $(CONNMAN_LIBS) \ - $(UPOWER_GLIB_LIBS) \ $(NULL) -if ENABLE_LIBACCOUNTS_SSO -libmcd_convenience_la_LIBADD += $(LIBACCOUNTS_SSO_LIBS) -INCLUDES += $(LIBACCOUNTS_SSO_CFLAGS) -endif - -if ENABLE_AEGIS -libmcd_convenience_la_LIBADD += $(top_builddir)/plugins/libmcp-aegis.la -endif - noinst_LTLIBRARIES = libmcd-convenience.la libmcd_convenience_la_SOURCES = \ mcd-account.c \ mcd-account-addressing.h \ - mcd-account-conditions.c \ mcd-account-config.h \ - mcd-account-connection.c \ mcd-account-requests.c \ mcd-account-addressing.c \ mcd-account-manager.c \ @@ -123,7 +66,6 @@ libmcd_convenience_la_SOURCES = \ client-registry.h \ connectivity-monitor.c \ connectivity-monitor.h \ - gtypes.c \ mcd-dbusprop.c \ mcd-dbusprop.h \ mcd-debug.c \ @@ -163,19 +105,6 @@ libmcd_convenience_la_SOURCES = \ sp_timestamp.h \ $(mc_headers) -if ENABLE_LIBACCOUNTS_SSO -libmcd_convenience_la_SOURCES += \ - mcd-account-manager-sso.c \ - $(NULL) - -if ENABLE_LIBACCOUNTS_GLIB_HIDDEN -libmcd_convenience_la_SOURCES += \ - mcd-storage-ag-hidden.c \ - $(NULL) -endif - -endif - mcd-enum-types.h: stamp-mcd-enum-types.h $(AM_V_GEN)true stamp-mcd-enum-types.h: Makefile $(mc_headers) mcd-enum-types.c @@ -202,8 +131,6 @@ mcd-enum-types.c: Makefile $(mc_headers) && rm -f xgen-getc EXTRA_DIST = \ - mcd.xml \ - dispatcher.xml \ stamp-mcd-enum-types.h Android.mk: Makefile.am $(nodist_libmcd_convenience_la_SOURCES) @@ -212,111 +139,11 @@ Android.mk: Makefile.am $(nodist_libmcd_convenience_la_SOURCES) -:TAGS eng debug \ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ -:SOURCES $(libmcd_convenience_la_SOURCES) $(nodist_libmcd_convenience_la_SOURCES) \ - -:CFLAGS $(DEFAULT_INCLUDES) $(INCLUDES) $(DEFS) $(CFLAGS) $(AM_CFLAGS) \ + -:CFLAGS $(DEFAULT_INCLUDES) $(DEFS) $(CFLAGS) $(AM_CFLAGS) \ -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) \ -:LDFLAGS $(libmcd_convenience_la_LIBADD) \ > $@ -# ---- telepathy-glib-style code generation ---- - -tools_dir = $(top_srcdir)/tools - -_gen/mcd.xml: mcd.xml $(wildcard $(top_srcdir)/xml/*.xml) - $(AM_V_at)$(mkdir_p) _gen - $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) --xinclude $(tools_dir)/identity.xsl \ - $< > $@ - -_gen/%.xml: $(top_srcdir)/xml/%.xml $(wildcard $(top_srcdir)/xml/*.xml) - $(AM_V_at)$(mkdir_p) _gen - $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) --xinclude $(tools_dir)/identity.xsl \ - $< > $@ - -# Generated files which can be done for all "classes" at once - -_gen/register-dbus-glib-marshallers-body.h: _gen/mcd.xml \ - $(tools_dir)/glib-client-marshaller-gen.py Makefile.am - $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-marshaller-gen.py $< \ - _mcd_ext > $@ - -_gen/enums.h: _gen/mcd.xml $(tools_dir)/c-constants-generator.xsl - $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) \ - --stringparam mixed-case-prefix mc \ - $(tools_dir)/c-constants-generator.xsl \ - $< > $@ - -_gen/interfaces.h: _gen/mcd.xml \ - $(tools_dir)/glib-interfaces-generator.xsl \ - $(tools_dir)/c-interfaces-generator.xsl - $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) \ - --stringparam mixed-case-prefix mc \ - $(tools_dir)/glib-interfaces-generator.xsl \ - $< > $@ - -_gen/interfaces-body.h: _gen/mcd.xml \ - $(tools_dir)/glib-interfaces-body-generator.xsl \ - $(tools_dir)/c-interfaces-generator.xsl - $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) \ - --stringparam mixed-case-prefix mc \ - $(tools_dir)/glib-interfaces-body-generator.xsl \ - $< > $@ - -_gen/gtypes.h: _gen/gtypes-gtk-doc.h - @: # generated as a side-effect - -_gen/gtypes-body.h: _gen/gtypes-gtk-doc.h - @: # generated as a side-effect - -_gen/gtypes-gtk-doc.h: _gen/mcd.xml \ - $(top_srcdir)/tools/glib-gtypes-generator.py - $(AM_V_GEN)$(PYTHON) $(top_srcdir)/tools/glib-gtypes-generator.py \ - $< _gen/gtypes mc - - - -# Generated files which must be generated per "class". -# (Currently the only "class" is nmc4, but the new API will need "classes" -# like account, account-manager, ...) - -_gen/%.xml: $(top_srcdir)/xml/%.xml $(wildcard $(top_srcdir)/xml/*.xml) Makefile.am - $(AM_V_at)$(mkdir_p) _gen - $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) --xinclude $(tools_dir)/identity.xsl \ - $< > $@ - -_gen/%.xml: %.xml $(wildcard $(top_srcdir)/xml/*.xml) Makefile.am - $(AM_V_at)$(mkdir_p) _gen - $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) --xinclude $(tools_dir)/identity.xsl \ - $< > $@ - -_gen/cli-%-body.h: _gen/cli-%-gtk-doc.h - @: # generated as a side-effect - -_gen/cli-%.h: _gen/cli-%-gtk-doc.h - @: # generated as a side-effect - -_gen/cli-%-gtk-doc.h: _gen/%.xml \ - $(tools_dir)/glib-client-gen.py Makefile.am - $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-gen.py \ - --group=`echo $* | tr x- x_` \ - --iface-quark-prefix=MC_IFACE_QUARK \ - --tp-proxy-api=0.7.6 \ - $< Mc_Cli _gen/cli-$* - -_gen/svc-%.c: _gen/svc-%-gtk-doc.h - @: # generated as a side-effect - -_gen/svc-%.h: _gen/svc-%-gtk-doc.h - @: # generated as a side-effect - -_gen/svc-%-gtk-doc.h: _gen/%.xml \ - $(tools_dir)/glib-ginterface-gen.py Makefile.am - $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \ - --filename=_gen/svc-$* \ - --signal-marshal-prefix=_mcd_ext \ - --include='<telepathy-glib/telepathy-glib.h>' \ - --not-implemented-func='tp_dbus_g_method_return_not_implemented' \ - --allow-unstable \ - $< Mc_Svc_ - include ../tools/header-checks.am check-local: header-decl-macro-check diff --git a/src/connectivity-monitor.c b/src/connectivity-monitor.c index 6d699723..e4af960a 100644 --- a/src/connectivity-monitor.c +++ b/src/connectivity-monitor.c @@ -35,10 +35,6 @@ #include <NetworkManager.h> #endif -#ifdef HAVE_UPOWER -#include <upower.h> -#endif - #include <telepathy-glib/telepathy-glib.h> #include "mcd-debug.h" @@ -96,10 +92,6 @@ struct _McdConnectivityMonitorPrivate { gulong state_change_signal_id; #endif -#ifdef HAVE_UPOWER - UpClient *upower_client; -#endif - #ifdef ENABLE_CONN_SETTING /* Application settings we steal from under Empathy's nose. */ GSettings *settings; @@ -258,43 +250,6 @@ connectivity_monitor_network_changed (GNetworkMonitor *monitor, } } -#ifdef HAVE_UPOWER -static void -connectivity_monitor_set_awake ( - McdConnectivityMonitor *self, - gboolean awake) -{ - if (awake) - connectivity_monitor_add_states (self, CONNECTIVITY_AWAKE, NULL); - else - connectivity_monitor_remove_states (self, CONNECTIVITY_AWAKE, NULL); -} - -static void -notify_sleep_cb ( - UpClient *upower_client, - UpSleepKind sleep_kind, - gpointer user_data) -{ - McdConnectivityMonitor *self = MCD_CONNECTIVITY_MONITOR (user_data); - - DEBUG ("about to sleep! sleep_kind=%s", up_sleep_kind_to_string (sleep_kind)); - connectivity_monitor_set_awake (self, FALSE); -} - -static void -notify_resume_cb ( - UpClient *upower_client, - UpSleepKind sleep_kind, - gpointer user_data) -{ - McdConnectivityMonitor *self = MCD_CONNECTIVITY_MONITOR (user_data); - - DEBUG ("woke up! sleep_kind=%s", up_sleep_kind_to_string (sleep_kind)); - connectivity_monitor_set_awake (self, TRUE); -} -#endif - #ifdef HAVE_GIO_UNIX static void login1_inhibit_cb (GObject *source G_GNUC_UNUSED, @@ -546,16 +501,6 @@ mcd_connectivity_monitor_init (McdConnectivityMonitor *connectivity_monitor) } #endif -#ifdef HAVE_UPOWER - priv->upower_client = up_client_new (); - tp_g_signal_connect_object (priv->upower_client, - "notify-sleep", G_CALLBACK (notify_sleep_cb), connectivity_monitor, - G_CONNECT_AFTER); - tp_g_signal_connect_object (priv->upower_client, - "notify-resume", G_CALLBACK (notify_resume_cb), connectivity_monitor, - G_CONNECT_AFTER); -#endif - g_bus_get (G_BUS_TYPE_SYSTEM, NULL, got_system_bus_cb, g_object_ref (connectivity_monitor)); } @@ -574,12 +519,10 @@ connectivity_monitor_constructed (GObject *object) static void connectivity_monitor_finalize (GObject *object) { -#if defined(HAVE_NM) || defined(HAVE_UPOWER) +#if defined(HAVE_NM) McdConnectivityMonitor *connectivity_monitor = MCD_CONNECTIVITY_MONITOR (object); McdConnectivityMonitorPrivate *priv = connectivity_monitor->priv; -#endif -#ifdef HAVE_NM if (priv->nm_client != NULL) { g_signal_handler_disconnect (priv->nm_client, @@ -590,10 +533,6 @@ connectivity_monitor_finalize (GObject *object) } #endif -#ifdef HAVE_UPOWER - tp_clear_object (&priv->upower_client); -#endif - G_OBJECT_CLASS (mcd_connectivity_monitor_parent_class)->finalize (object); } diff --git a/src/dispatcher.xml b/src/dispatcher.xml deleted file mode 100644 index 19535a6d..00000000 --- a/src/dispatcher.xml +++ /dev/null @@ -1,7 +0,0 @@ -<tp:spec - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - xmlns:xi="http://www.w3.org/2001/XInclude"> - -<xi:include href="../xml/Channel_Dispatcher_Interface_Messages_DRAFT.xml"/> - -</tp:spec> diff --git a/src/gtypes.c b/src/gtypes.c deleted file mode 100644 index 90c4fb18..00000000 --- a/src/gtypes.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "config.h" - -#include <dbus/dbus-glib.h> - -#include "_gen/gtypes.h" -#include "_gen/gtypes-body.h" - -/* Because I'm too lazy to have a separate file for these. */ -#include "_gen/interfaces.h" -#include "_gen/interfaces-body.h" diff --git a/src/mcd-account-addressing.c b/src/mcd-account-addressing.c index ec3d8f5e..e1a0fb77 100644 --- a/src/mcd-account-addressing.c +++ b/src/mcd-account-addressing.c @@ -30,7 +30,6 @@ #include "mcd-account.h" #include "mcd-account-priv.h" -#include "_gen/interfaces.h" static void addressing_set_uri_scheme_association (TpSvcAccountInterfaceAddressing *iface, @@ -48,7 +47,7 @@ addressing_set_uri_scheme_association (TpSvcAccountInterfaceAddressing *iface, g_value_init (&value, G_TYPE_STRV); if (mcd_storage_get_attribute (storage, account, MC_ACCOUNTS_KEY_URI_SCHEMES, - &value, NULL)) + G_VARIANT_TYPE_STRING_ARRAY, &value, NULL)) { schemes = g_value_get_boxed (&value); old_association = tp_strv_contains ((const gchar * const *) schemes, @@ -109,7 +108,7 @@ addressing_get_uri_schemes (TpSvcDBusProperties *iface, g_value_init (value, G_TYPE_STRV); if (!mcd_storage_get_attribute (storage, account, MC_ACCOUNTS_KEY_URI_SCHEMES, - value, NULL)) + G_VARIANT_TYPE_STRING_ARRAY, value, NULL)) { g_value_set_boxed (value, NULL); } diff --git a/src/mcd-account-conditions.c b/src/mcd-account-conditions.c deleted file mode 100644 index e40dccf9..00000000 --- a/src/mcd-account-conditions.c +++ /dev/null @@ -1,160 +0,0 @@ -/* vi: set et sw=4 ts=8 cino=t0,(0: */ -/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */ -/* - * This file is part of mission-control - * - * Copyright (C) 2008-2009 Nokia Corporation. - * Copyright (C) 2009 Collabora Ltd. - * - * Contact: Alberto Mardegan <alberto.mardegan@nokia.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * version 2.1 as published by the Free Software Foundation. - * - * 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 "config.h" - -#include <stdio.h> -#include <string.h> -#include <glib/gstdio.h> - -#include <telepathy-glib/telepathy-glib.h> -#include <telepathy-glib/telepathy-glib-dbus.h> - -#include "mcd-account.h" -#include "mcd-account-priv.h" -#include "mcd-account-conditions.h" -#include "mcd-account-manager.h" - - -static void -store_condition (gpointer key, gpointer value, gpointer userdata) -{ - McdAccount *account = MCD_ACCOUNT (userdata); - McdStorage *storage = _mcd_account_get_storage (account); - const gchar *account_name = mcd_account_get_unique_name (account); - const gchar *name = key, *condition = value; - gchar condition_key[256]; - - g_snprintf (condition_key, sizeof (condition_key), "condition-%s", name); - mcd_storage_set_string (storage, account_name, condition_key, condition); -} - -static gboolean -set_condition (TpSvcDBusProperties *self, - const gchar *name, - const GValue *value, - McdDBusPropSetFlags flags, - GError **error) -{ - McdAccount *account = MCD_ACCOUNT (self); - McdStorage *storage = _mcd_account_get_storage (account); - const gchar *account_name = mcd_account_get_unique_name (account); - gchar **keys, **key; - GHashTable *conditions; - - /* FIXME: some sort of validation beyond just the type? */ - - if (!G_VALUE_HOLDS (value, TP_HASH_TYPE_STRING_STRING_MAP)) - { - g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "Expected a{s:s} for Condition, but got %s", - G_VALUE_TYPE_NAME (value)); - return FALSE; - } - - if (_mcd_account_get_always_on (account)) - { - g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, - "Account %s conditions cannot be changed", - mcd_account_get_unique_name (account)); - return FALSE; - } - - conditions = g_value_get_boxed (value); - - /* first, delete existing conditions */ - keys = mcd_storage_dup_attributes (storage, account_name, NULL); - - for (key = keys; *key != NULL; key++) - { - if (strncmp (*key, "condition-", 10) != 0) - continue; - - mcd_storage_set_attribute (storage, account_name, *key, NULL); - } - - g_strfreev (keys); - - if (!(flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE)) - { - g_hash_table_foreach (conditions, store_condition, account); - - mcd_storage_commit (storage, account_name); - } - - return TRUE; -} - -static void -get_condition (TpSvcDBusProperties *self, const gchar *name, GValue *value) -{ - GHashTable *conditions; - - conditions = mcd_account_get_conditions (MCD_ACCOUNT (self)); - - g_value_init (value, DBUS_TYPE_G_STRING_STRING_HASHTABLE); - g_value_take_boxed (value, conditions); -} - - -const McdDBusProp account_conditions_properties[] = { - { "Condition", set_condition, get_condition }, - { 0 }, -}; - -void -account_conditions_iface_init (McSvcAccountInterfaceConditionsClass *iface, - gpointer iface_data) -{ -} - -GHashTable *mcd_account_get_conditions (McdAccount *account) -{ - gchar **keys, **key, *condition; - GHashTable *conditions; - McdStorage *storage = _mcd_account_get_storage (account); - const gchar *account_name = mcd_account_get_unique_name (account); - - conditions = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_free); - - keys = mcd_storage_dup_attributes (storage, account_name, NULL); - - for (key = keys; *key != NULL; key++) - { - if (strncmp (*key, "condition-", 10) != 0) - continue; - - condition = mcd_storage_dup_string (storage, account_name, *key); - DEBUG ("Condition: %s = %s", *key, condition); - g_hash_table_insert (conditions, g_strdup (*key + 10), condition); - } - - g_strfreev (keys); - - return conditions; -} - diff --git a/src/mcd-account-conditions.h b/src/mcd-account-conditions.h deleted file mode 100644 index cd8f75b6..00000000 --- a/src/mcd-account-conditions.h +++ /dev/null @@ -1,34 +0,0 @@ -/* vi: set et sw=4 ts=8 cino=t0,(0: */ -/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */ -/* - * mcd-account.h - the Telepathy Account D-Bus interface (service side) - * - * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/> - * Copyright (C) 2008 Nokia Corporation - * - * 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 __MCD_ACCOUNT_CONDITIONS_H__ -#define __MCD_ACCOUNT_CONDITIONS_H__ - -#include "mcd-account.h" - -G_BEGIN_DECLS - -GHashTable *mcd_account_get_conditions (McdAccount *account); - -G_END_DECLS -#endif diff --git a/src/mcd-account-config.h b/src/mcd-account-config.h index fc7a9eae..8abfbb87 100644 --- a/src/mcd-account-config.h +++ b/src/mcd-account-config.h @@ -46,7 +46,6 @@ #define MC_ACCOUNTS_KEY_ICON "Icon" #define MC_ACCOUNTS_KEY_NICKNAME "Nickname" #define MC_ACCOUNTS_KEY_SERVICE "Service" -/* ... also "condition-*" reserved by mcd-account-conditions.c */ /* unsigned 32-bit integer, 'u' */ /* obsoleted by MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE */ @@ -57,7 +56,6 @@ #define MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY "ConnectAutomatically" #define MC_ACCOUNTS_KEY_ENABLED "Enabled" #define MC_ACCOUNTS_KEY_HAS_BEEN_ONLINE "HasBeenOnline" -#define MC_ACCOUNTS_KEY_HIDDEN "Hidden" /* string array, 'as' */ #define MC_ACCOUNTS_KEY_URI_SCHEMES \ @@ -74,9 +72,11 @@ #define MC_OLD_ACCOUNTS_KEY_DATA_DIR "data_dir" #define MC_OLD_ACCOUNTS_KEY_DELETED "deleted" #define MC_OLD_ACCOUNTS_KEY_GROUPS "groups" +#define MC_OLD_ACCOUNTS_KEY_HIDDEN "Hidden" #define MC_OLD_ACCOUNTS_KEY_ICON_NAME "icon_name" #define MC_OLD_ACCOUNTS_KEY_PRESETS "presets" #define MC_OLD_ACCOUNTS_KEY_PROFILE "profile" #define MC_OLD_ACCOUNTS_KEY_SECONDARY_VCARD_FIELDS "secondary_vcard_fields" +/* ... also "condition-*" formerly reserved by Nokia Conditions interface */ #endif /* __MCD_ACCOUNT_CONFIG_H__ */ diff --git a/src/mcd-account-connection.c b/src/mcd-account-connection.c index 327514de..a532f351 100644 --- a/src/mcd-account-connection.c +++ b/src/mcd-account-connection.c @@ -36,118 +36,3 @@ #include "mcd-account-priv.h" #include "mcd-account-manager.h" #include "mcd-connection-priv.h" - -struct _McdAccountConnectionContext { - GHashTable *params; - gboolean user_initiated; -}; - -void -_mcd_account_connection_context_free (McdAccountConnectionContext *c) -{ - g_hash_table_unref (c->params); - g_free (c); -} - -void -_mcd_account_connection_begin (McdAccount *account, - gboolean user_initiated) -{ - McdAccountConnectionContext *ctx; - - /* check whether a connection process is already ongoing */ - if (_mcd_account_get_connection_context (account) != NULL) - { - DEBUG ("already trying to connect"); - return; - } - - /* get account params */ - /* create dynamic params HT */ - /* run the handlers */ - ctx = g_malloc (sizeof (McdAccountConnectionContext)); - ctx->user_initiated = user_initiated; - - /* If we get this far, the account should be valid, so getting the - * parameters should succeed. - */ - ctx->params = _mcd_account_dup_parameters (account); - g_assert (ctx->params != NULL); - - _mcd_account_set_connection_status (account, - TP_CONNECTION_STATUS_CONNECTING, - TP_CONNECTION_STATUS_REASON_REQUESTED, - NULL, NULL, NULL); - _mcd_account_set_connection_context (account, ctx); - mcd_account_connection_proceed (account, TRUE); -} - -void -mcd_account_connection_proceed_with_reason (McdAccount *account, - gboolean success, - TpConnectionStatusReason reason) -{ - McdAccountConnectionContext *ctx; - gboolean delayed; - - /* call next handler, or terminate the chain (emitting proper signal). - * if everything is fine, call mcd_manager_create_connection() and - * _mcd_connection_connect () with the dynamic parameters. Remove that call - * from mcd_manager_create_connection() */ - ctx = _mcd_account_get_connection_context (account); - g_return_if_fail (ctx != NULL); - g_return_if_fail (ctx->params != NULL); - - if (success) - { - if (mcd_connectivity_monitor_is_online ( - mcd_account_get_connectivity_monitor (account)) || _mcd_account_needs_dispatch (account)) - { - DEBUG ("%s wants to connect and we're online - go for it", - mcd_account_get_unique_name (account)); - delayed = FALSE; - } - else if (!mcd_account_get_waiting_for_connectivity (account)) - { - DEBUG ("%s wants to connect, but we're offline; queuing it up", - mcd_account_get_unique_name (account)); - delayed = TRUE; - mcd_account_set_waiting_for_connectivity (account, TRUE); - } - else - { - DEBUG ("%s wants to connect, but is already waiting for " - "connectivity?", mcd_account_get_unique_name (account)); - delayed = TRUE; - } - } - else - { - DEBUG ("%s failed to connect: reason code %d", - mcd_account_get_unique_name (account), reason); - delayed = FALSE; - } - - if (!delayed) - { - /* end of the chain */ - if (success) - { - _mcd_account_connect (account, ctx->params); - } - else - { - _mcd_account_set_connection_status - (account, TP_CONNECTION_STATUS_DISCONNECTED, reason, NULL, - TP_ERROR_STR_DISCONNECTED, NULL); - } - _mcd_account_set_connection_context (account, NULL); - } -} - -void -mcd_account_connection_proceed (McdAccount *account, gboolean success) -{ - mcd_account_connection_proceed_with_reason - (account, success, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED); -} diff --git a/src/mcd-account-manager-default.c b/src/mcd-account-manager-default.c index 1403f843..e6fc14fc 100644 --- a/src/mcd-account-manager-default.c +++ b/src/mcd-account-manager-default.c @@ -1,8 +1,8 @@ /* - * The default account manager keyfile storage pseudo-plugin + * The default account manager storage pseudo-plugin * * Copyright © 2010 Nokia Corporation - * Copyright © 2010 Collabora Ltd. + * Copyright © 2010-2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,12 +30,75 @@ #include "mcd-account-manager-default.h" #include "mcd-debug.h" +#include "mcd-storage.h" #include "mcd-misc.h" -#define PLUGIN_NAME "default-gkeyfile" +#define PLUGIN_NAME "default" #define PLUGIN_PRIORITY MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_DEFAULT -#define PLUGIN_DESCRIPTION "GKeyFile (default) account storage backend" -#define INITIAL_CONFIG "# Telepathy accounts\n" +#define PLUGIN_DESCRIPTION "Default account storage backend" + +typedef struct { + /* owned string, attribute => owned GVariant, value + * attributes to be stored in the variant-file */ + GHashTable *attributes; + /* owned string, parameter (without "param-") => owned GVariant, value + * parameters of known type to be stored in the variant-file */ + GHashTable *parameters; + /* owned string, parameter (without "param-") => owned string, value + * parameters of unknwn type to be stored in the variant-file */ + GHashTable *untyped_parameters; + /* TRUE if the account doesn't really exist, but is here to stop us + * loading it from a lower-priority file */ + gboolean absent; + /* TRUE if this account needs saving */ + gboolean dirty; +} McdDefaultStoredAccount; + +static GVariant * +variant_ref0 (GVariant *v) +{ + return (v == NULL ? NULL : g_variant_ref (v)); +} + +static McdDefaultStoredAccount * +lookup_stored_account (McdAccountManagerDefault *self, + const gchar *account) +{ + return g_hash_table_lookup (self->accounts, account); +} + +static McdDefaultStoredAccount * +ensure_stored_account (McdAccountManagerDefault *self, + const gchar *account) +{ + McdDefaultStoredAccount *sa = lookup_stored_account (self, account); + + if (sa == NULL) + { + sa = g_slice_new0 (McdDefaultStoredAccount); + sa->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_variant_unref); + sa->parameters = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_variant_unref); + sa->untyped_parameters = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + g_hash_table_insert (self->accounts, g_strdup (account), sa); + } + + sa->absent = FALSE; + return sa; +} + +static void +stored_account_free (gpointer p) +{ + McdDefaultStoredAccount *sa = p; + + g_hash_table_unref (sa->attributes); + g_hash_table_unref (sa->parameters); + g_hash_table_unref (sa->untyped_parameters); + g_slice_free (McdDefaultStoredAccount, sa); +} static void account_storage_iface_init (McpAccountStorageIface *, gpointer); @@ -65,22 +128,39 @@ get_old_filename (void) } static gchar * -account_filename_in (const gchar *dir) +accounts_cfg_in (const gchar *dir) { return g_build_filename (dir, "telepathy", "mission-control", "accounts.cfg", NULL); } +static gchar * +account_directory_in (const gchar *dir) +{ + return g_build_filename (dir, "telepathy", "mission-control", NULL); +} + +static gchar * +account_file_in (const gchar *dir, + const gchar *account) +{ + gchar *basename = g_strdup_printf ("%s.account", account); + gchar *ret; + + g_strdelimit (basename, "/", '-'); + ret = g_build_filename (dir, "telepathy", "mission-control", + basename, NULL); + g_free (basename); + return ret; +} + static void mcd_account_manager_default_init (McdAccountManagerDefault *self) { DEBUG ("mcd_account_manager_default_init"); - self->filename = account_filename_in (g_get_user_data_dir ()); - self->keyfile = g_key_file_new (); - self->removed = g_key_file_new (); - self->removed_accounts = - g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - self->save = FALSE; + self->directory = account_directory_in (g_get_user_data_dir ()); + self->accounts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + stored_account_free); self->loaded = FALSE; } @@ -90,184 +170,433 @@ mcd_account_manager_default_class_init (McdAccountManagerDefaultClass *cls) DEBUG ("mcd_account_manager_default_class_init"); } -/* We happen to know that the string MC gave us is "sufficiently escaped" to - * put it in the keyfile as-is. */ -static gboolean -_set (const McpAccountStorage *self, - const McpAccountManager *am, +static McpAccountStorageSetResult +set_parameter (McpAccountStorage *self, + McpAccountManager *am, const gchar *account, - const gchar *key, - const gchar *val) + const gchar *parameter, + GVariant *val, + McpParameterFlags flags) { McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); + McdDefaultStoredAccount *sa; - amd->save = TRUE; + sa = lookup_stored_account (amd, account); + g_return_val_if_fail (sa != NULL, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (!sa->absent, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); - if (val != NULL) - g_key_file_set_value (amd->keyfile, account, key, val); + if (val == NULL) + { + gboolean changed = FALSE; + + changed = g_hash_table_remove (sa->parameters, parameter); + /* deliberately not ||= - if we removed it from parameters, we + * still want to remove it from untyped_parameters if it was there */ + changed |= g_hash_table_remove (sa->untyped_parameters, parameter); + + if (!changed) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + } else - g_key_file_remove_key (amd->keyfile, account, key, NULL); + { + GVariant *old; + + old = g_hash_table_lookup (sa->parameters, parameter); + + if (old != NULL && g_variant_equal (old, val)) + { + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + } + + /* We haven't checked whether it's in untyped_parameters with the + * same value - but if it is, we want to migrate it to parameters + * anyway (in order to record its type), so treat it as having + * actually changed. */ - return TRUE; + g_hash_table_remove (sa->untyped_parameters, parameter); + g_hash_table_insert (sa->parameters, g_strdup (parameter), + g_variant_ref (val)); + } + + sa->dirty = TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } -static gboolean -_get (const McpAccountStorage *self, - const McpAccountManager *am, +static McpAccountStorageSetResult +set_attribute (McpAccountStorage *self, + McpAccountManager *am, const gchar *account, - const gchar *key) + const gchar *attribute, + GVariant *val, + McpAttributeFlags flags) { McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); + McdDefaultStoredAccount *sa; - if (key != NULL) + sa = lookup_stored_account (amd, account); + g_return_val_if_fail (sa != NULL, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (!sa->absent, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + + if (val == NULL) + { + if (!g_hash_table_remove (sa->attributes, attribute)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + } + else { - gchar *v = NULL; + GVariant *old; - v = g_key_file_get_value (amd->keyfile, account, key, NULL); + old = g_hash_table_lookup (sa->attributes, attribute); - if (v == NULL) - return FALSE; + if (old != NULL && g_variant_equal (old, val)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; - mcp_account_manager_set_value (am, account, key, v); - g_free (v); + g_hash_table_insert (sa->attributes, g_strdup (attribute), + g_variant_ref (val)); } - else - { - gsize i; - gsize n; - GStrv keys = g_key_file_get_keys (amd->keyfile, account, &n, NULL); - if (keys == NULL) - n = 0; + sa->dirty = TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; +} - for (i = 0; i < n; i++) - { - gchar *v = g_key_file_get_value (amd->keyfile, account, keys[i], NULL); +static GVariant * +get_attribute (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) +{ + McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); + McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); - if (v != NULL) - mcp_account_manager_set_value (am, account, keys[i], v); + if (flags != NULL) + *flags = 0; - g_free (v); - } + g_return_val_if_fail (sa != NULL, NULL); + g_return_val_if_fail (!sa->absent, NULL); - g_strfreev (keys); - } + /* ignore @type, we store every attribute with its type anyway; MC will + * coerce values to an appropriate type if needed */ + return variant_ref0 (g_hash_table_lookup (sa->attributes, attribute)); +} - return TRUE; +static GVariant * +get_parameter (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) +{ + McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); + McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); + GVariant *variant; + gchar *str; + + if (flags != NULL) + *flags = 0; + + g_return_val_if_fail (sa != NULL, NULL); + g_return_val_if_fail (!sa->absent, NULL); + + variant = g_hash_table_lookup (sa->parameters, parameter); + + if (variant != NULL) + return g_variant_ref (variant); + + if (type == NULL) + return NULL; + + str = g_hash_table_lookup (sa->untyped_parameters, parameter); + + if (str == NULL) + return NULL; + + return mcp_account_manager_unescape_variant_from_keyfile (am, + str, type, NULL); +} + +static gchar ** +list_typed_parameters (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account) +{ + McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); + McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); + GPtrArray *arr; + GHashTableIter iter; + gpointer k; + + g_return_val_if_fail (sa != NULL, NULL); + g_return_val_if_fail (!sa->absent, NULL); + + arr = g_ptr_array_sized_new (g_hash_table_size (sa->parameters) + 1); + + g_hash_table_iter_init (&iter, sa->parameters); + + while (g_hash_table_iter_next (&iter, &k, NULL)) + g_ptr_array_add (arr, g_strdup (k)); + + g_ptr_array_add (arr, NULL); + + return (gchar **) g_ptr_array_free (arr, FALSE); +} + +static gchar ** +list_untyped_parameters (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account) +{ + McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); + McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); + GPtrArray *arr; + GHashTableIter iter; + gpointer k; + + g_return_val_if_fail (sa != NULL, NULL); + g_return_val_if_fail (!sa->absent, NULL); + + arr = g_ptr_array_sized_new (g_hash_table_size (sa->untyped_parameters) + 1); + + g_hash_table_iter_init (&iter, sa->untyped_parameters); + + while (g_hash_table_iter_next (&iter, &k, NULL)) + g_ptr_array_add (arr, g_strdup (k)); + + g_ptr_array_add (arr, NULL); + + return (gchar **) g_ptr_array_free (arr, FALSE); } static gchar * -_create (const McpAccountStorage *self, - const McpAccountManager *am, +_create (McpAccountStorage *self, + McpAccountManager *am, const gchar *manager, const gchar *protocol, - GHashTable *params, + const gchar *identification, GError **error) { + McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); gchar *unique_name; - /* See comment in plugin-account.c::_storage_create_account() before changing - * this implementation, it's more subtle than it looks */ unique_name = mcp_account_manager_get_unique_name (MCP_ACCOUNT_MANAGER (am), - manager, protocol, params); + manager, protocol, + identification); g_return_val_if_fail (unique_name != NULL, NULL); + ensure_stored_account (amd, unique_name); return unique_name; } -static gboolean -_delete (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *key) +static void +delete_async (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); + McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); + GTask *task; + gchar *filename = NULL; + const gchar * const *iter; + + task = g_task_new (amd, cancellable, callback, user_data); + + g_return_if_fail (sa != NULL); + g_return_if_fail (!sa->absent); - if (key == NULL) + filename = account_file_in (g_get_user_data_dir (), account); + + DEBUG ("Deleting account %s from %s", account, filename); + + if (g_unlink (filename) != 0) { - if (g_key_file_remove_group (amd->keyfile, account, NULL)) - amd->save = TRUE; + int e = errno; + + /* ENOENT is OK, anything else is more upsetting */ + if (e != ENOENT) + { + WARNING ("Unable to delete %s: %s", filename, + g_strerror (e)); + g_task_return_new_error (task, G_IO_ERROR, g_io_error_from_errno (e), + "Unable to delete %s: %s", filename, g_strerror (e)); + goto finally; + } } - else + + for (iter = g_get_system_data_dirs (); + iter != NULL && *iter != NULL; + iter++) { - gsize n; - GStrv keys; - gboolean save = FALSE; + gchar *other = account_file_in (*iter, account); + gboolean other_exists = g_file_test (other, G_FILE_TEST_EXISTS); - save = g_key_file_remove_key (amd->keyfile, account, key, NULL); + g_free (other); - if (save) - amd->save = TRUE; + if (other_exists) + { + GError *error = NULL; - keys = g_key_file_get_keys (amd->keyfile, account, &n, NULL); + /* There is a lower-priority file that would provide this + * account. We can't delete a file from XDG_DATA_DIRS which + * are conceptually read-only, but we can mask it with an + * empty file (prior art: systemd) */ + if (!g_file_set_contents (filename, "", 0, &error)) + { + g_prefix_error (&error, + "Unable to save empty account file to %s: ", filename); + WARNING ("%s", error->message); + g_task_return_error (task, error); + g_free (filename); + goto finally; + } - /* if that was the last parameter, the account is gone too */ - if (keys == NULL || n == 0) - { - g_key_file_remove_group (amd->keyfile, account, NULL); + break; } - - g_strfreev (keys); } - return TRUE; + /* clean up the mess */ + g_hash_table_remove (amd->accounts, account); + mcp_account_storage_emit_deleted (self, account); + + g_task_return_boolean (task, TRUE); + +finally: + g_free (filename); + g_object_unref (task); } +static gboolean +delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} static gboolean -_commit (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account) +am_default_commit_one (McdAccountManagerDefault *self, + const gchar *account_name, + McdDefaultStoredAccount *sa) { - gsize n; - gchar *data; - McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); - gboolean rval = FALSE; - gchar *dir; + gchar *filename; + GHashTableIter inner; + gpointer k, v; + GVariantBuilder params_builder; + GVariantBuilder attrs_builder; + GVariant *content; + gchar *content_text; + gboolean ret; GError *error = NULL; - if (!amd->save) + g_return_val_if_fail (sa != NULL, FALSE); + g_return_val_if_fail (!sa->absent, FALSE); + + if (!sa->dirty) return TRUE; - dir = g_path_get_dirname (amd->filename); + if (!mcd_ensure_directory (self->directory, &error)) + { + g_warning ("%s", error->message); + g_error_free (error); + return FALSE; + } + + filename = account_file_in (g_get_user_data_dir (), account_name); + + DEBUG ("Saving account %s to %s", account_name, filename); + + g_variant_builder_init (&attrs_builder, G_VARIANT_TYPE_VARDICT); - DEBUG ("Saving accounts to %s", amd->filename); + g_hash_table_iter_init (&inner, sa->attributes); - if (!mcd_ensure_directory (dir, &error)) + while (g_hash_table_iter_next (&inner, &k, &v)) { - g_warning ("%s", error->message); - g_clear_error (&error); - /* fall through anyway: writing to the file will fail, but it does - * give us a chance to commit to the keyring too */ + g_variant_builder_add (&attrs_builder, "{sv}", k, v); + } + + g_variant_builder_init (¶ms_builder, G_VARIANT_TYPE ("a{sv}")); + g_hash_table_iter_init (&inner, sa->parameters); + + while (g_hash_table_iter_next (&inner, &k, &v)) + { + g_variant_builder_add (¶ms_builder, "{sv}", k, v); + } + + g_variant_builder_add (&attrs_builder, "{sv}", + "Parameters", g_variant_builder_end (¶ms_builder)); + + g_variant_builder_init (¶ms_builder, G_VARIANT_TYPE ("a{ss}")); + g_hash_table_iter_init (&inner, sa->untyped_parameters); + + while (g_hash_table_iter_next (&inner, &k, &v)) + { + g_variant_builder_add (¶ms_builder, "{ss}", k, v); } - g_free (dir); + g_variant_builder_add (&attrs_builder, "{sv}", + "KeyFileParameters", g_variant_builder_end (¶ms_builder)); - data = g_key_file_to_data (amd->keyfile, &n, NULL); - rval = g_file_set_contents (amd->filename, data, n, &error); + content = g_variant_ref_sink (g_variant_builder_end (&attrs_builder)); + content_text = g_variant_print (content, TRUE); + DEBUG ("%s", content_text); + g_variant_unref (content); - if (rval) + if (g_file_set_contents (filename, content_text, -1, &error)) { - amd->save = FALSE; + sa->dirty = FALSE; + ret = TRUE; } else { - g_warning ("%s", error->message); - g_error_free (error); + WARNING ("Unable to save account to %s: %s", filename, + error->message); + g_clear_error (&error); + ret = FALSE; } - g_free (data); + g_free (filename); + g_free (content_text); + return ret; +} - return rval; +static gboolean +_commit (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account) +{ + McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); + McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); + + g_return_val_if_fail (sa != NULL, FALSE); + g_return_val_if_fail (!sa->absent, FALSE); + + DEBUG ("Saving account %s to %s", account, amd->directory); + + return am_default_commit_one (amd, account, sa); } -static void +static gboolean am_default_load_keyfile (McdAccountManagerDefault *self, const gchar *filename) { GError *error = NULL; + GKeyFile *keyfile = g_key_file_new (); + gsize i; + gsize n = 0; + GStrv account_tails; + gboolean all_ok = TRUE; - if (g_key_file_load_from_file (self->keyfile, filename, + /* We shouldn't call this function without modification if we think we've + * migrated to a newer storage format, because it doesn't check whether + * each account has already been loaded. */ + g_assert (!self->loaded); + g_assert (g_hash_table_size (self->accounts) == 0); + + if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_KEEP_COMMENTS, &error)) { DEBUG ("Loaded accounts from %s", filename); @@ -277,33 +606,311 @@ am_default_load_keyfile (McdAccountManagerDefault *self, DEBUG ("Failed to load accounts from %s: %s", filename, error->message); g_error_free (error); - /* Start with a blank configuration, but do not save straight away; - * we don't want to overwrite a corrupt-but-maybe-recoverable - * configuration file with an empty one until given a reason to - * do so. */ - g_key_file_load_from_data (self->keyfile, INITIAL_CONFIG, -1, - G_KEY_FILE_KEEP_COMMENTS, NULL); + /* Start with a blank configuration. */ + g_key_file_load_from_data (keyfile, "", -1, 0, NULL); + /* Don't delete the old file, which might be recoverable. */ + all_ok = FALSE; + } + + account_tails = g_key_file_get_groups (keyfile, &n); + + for (i = 0; i < n; i++) + { + const gchar *account = account_tails[i]; + McdDefaultStoredAccount *sa = ensure_stored_account (self, account); + gsize j; + gsize m = 0; + GStrv keys = g_key_file_get_keys (keyfile, account, &m, NULL); + + /* We're going to need to migrate this account. */ + sa->dirty = TRUE; + + for (j = 0; j < m; j++) + { + gchar *key = keys[j]; + + if (g_str_has_prefix (key, "param-")) + { + gchar *raw = g_key_file_get_value (keyfile, account, key, NULL); + + /* steals ownership of raw */ + g_hash_table_insert (sa->untyped_parameters, g_strdup (key + 6), + raw); + } + else + { + const GVariantType *type = mcd_storage_get_attribute_type (key); + GVariant *variant = NULL; + + if (type == NULL) + { + /* go to the error code path */ + g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "unknown attribute"); + } + else + { + variant = mcd_keyfile_get_variant (keyfile, + account, key, type, &error); + } + + if (variant == NULL) + { + WARNING ("Unable to migrate %s.%s from keyfile: %s", + account, key, error->message); + g_clear_error (&error); + /* Don't delete the old file, which might be recoverable. */ + all_ok = FALSE; + } + else + { + g_hash_table_insert (sa->attributes, g_strdup (key), + g_variant_ref_sink (variant)); + } + } + } + + g_strfreev (keys); + } + + g_strfreev (account_tails); + g_key_file_unref (keyfile); + return all_ok; +} + +static void +am_default_load_variant_file (McdAccountManagerDefault *self, + const gchar *account_tail, + const gchar *full_name) +{ + McdDefaultStoredAccount *sa; + gchar *text = NULL; + gsize len; + GVariant *contents = NULL; + GVariantIter iter; + const gchar *k; + GVariant *v; + GError *error = NULL; + + DEBUG ("%s from %s", account_tail, full_name); + + sa = lookup_stored_account (self, account_tail); + + if (sa != NULL) + { + DEBUG ("Ignoring %s: account %s already %s", + full_name, account_tail, sa->absent ? "masked" : "loaded"); + goto finally; + } + + if (!g_file_get_contents (full_name, &text, &len, &error)) + { + WARNING ("Unable to read account %s from %s: %s", + account_tail, full_name, error->message); + g_error_free (error); + goto finally; + } + + if (len == 0) + { + DEBUG ("Empty file %s masks account %s", full_name, account_tail); + ensure_stored_account (self, account_tail)->absent = TRUE; + goto finally; + } + + contents = g_variant_parse (G_VARIANT_TYPE_VARDICT, + text, text + len, NULL, &error); + + if (contents == NULL) + { + WARNING ("Unable to parse account %s from %s: %s", + account_tail, full_name, error->message); + g_error_free (error); + goto finally; + } + + sa = ensure_stored_account (self, account_tail); + + g_variant_iter_init (&iter, contents); + + while (g_variant_iter_loop (&iter, "{sv}", &k, &v)) + { + if (!tp_strdiff (k, "KeyFileParameters")) + { + GVariantIter param_iter; + gchar *parameter; + gchar *param_value; + + if (!g_variant_is_of_type (v, G_VARIANT_TYPE ("a{ss}"))) + { + gchar *repr = g_variant_print (v, TRUE); + + WARNING ("invalid KeyFileParameters found in %s, " + "ignoring: %s", full_name, repr); + g_free (repr); + continue; + } + + g_variant_iter_init (¶m_iter, v); + + while (g_variant_iter_next (¶m_iter, "{ss}", ¶meter, + ¶m_value)) + { + /* steals parameter, param_value */ + g_hash_table_insert (sa->untyped_parameters, parameter, + param_value); + } + } + else if (!tp_strdiff (k, "Parameters")) + { + GVariantIter param_iter; + gchar *parameter; + GVariant *param_value; + + if (!g_variant_is_of_type (v, G_VARIANT_TYPE ("a{sv}"))) + { + gchar *repr = g_variant_print (v, TRUE); + + WARNING ("invalid Parameters found in %s, " + "ignoring: %s", full_name, repr); + g_free (repr); + continue; + } + + g_variant_iter_init (¶m_iter, v); + + while (g_variant_iter_next (¶m_iter, "{sv}", ¶meter, + ¶m_value)) + { + /* steals parameter, param_value */ + g_hash_table_insert (sa->parameters, parameter, param_value); + } + } + else + { + /* an ordinary attribute */ + g_hash_table_insert (sa->attributes, + g_strdup (k), g_variant_ref (v)); + } + } + +finally: + tp_clear_pointer (&contents, g_variant_unref); + g_free (text); +} + +static void +am_default_load_directory (McdAccountManagerDefault *self, + const gchar *directory) +{ + GDir *dir_handle; + const gchar *basename; + GRegex *regex; + GError *error = NULL; + + dir_handle = g_dir_open (directory, 0, &error); + + if (dir_handle == NULL) + { + /* We expect ENOENT. Anything else is a cause for (minor) concern. */ + if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + DEBUG ("%s", error->message); + else + WARNING ("%s", error->message); + + g_error_free (error); + return; + } + + DEBUG ("Looking for accounts in %s", directory); + + regex = g_regex_new ("^[A-Za-z][A-Za-z0-9_]*-" /* CM name */ + "[A-Za-z][A-Za-z0-9_]*-" /* protocol with s/-/_/ */ + "[A-Za-z_][A-Za-z0-9_]*\\.account$", /* account-specific part */ + G_REGEX_DOLLAR_ENDONLY, 0, &error); + g_assert_no_error (error); + + while ((basename = g_dir_read_name (dir_handle)) != NULL) + { + gchar *full_name; + gchar *account_tail; + + /* skip it silently if it's obviously not an account */ + if (!g_str_has_suffix (basename, ".account")) + continue; + + /* We consider ourselves to have migrated to the new storage format + * as soon as we find something that looks as though it ought to be an + * account. */ + self->loaded = TRUE; + + if (!g_regex_match (regex, basename, 0, NULL)) + { + WARNING ("Ignoring %s/%s: not a syntactically valid account", + directory, basename); + } + + full_name = g_build_filename (directory, basename, NULL); + account_tail = g_strdup (basename); + g_strdelimit (account_tail, "-", '/'); + g_strdelimit (account_tail, ".", '\0'); + + am_default_load_variant_file (self, account_tail, full_name); + + g_free (account_tail); + g_free (full_name); } } static GList * -_list (const McpAccountStorage *self, - const McpAccountManager *am) +_list (McpAccountStorage *self, + McpAccountManager *am) { - gsize i; - gsize n; - GStrv accounts; GList *rval = NULL; McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); + GHashTableIter hash_iter; + gchar *migrate_from = NULL; + gpointer k, v; + gboolean save = FALSE; - if (!amd->loaded && g_file_test (amd->filename, G_FILE_TEST_EXISTS)) + if (!amd->loaded) { - /* If the file exists, but loading it fails, we deliberately - * do not fall through to the "initial configuration" case, - * because we don't want to overwrite a corrupted file - * with an empty one until an actual write takes place. */ - am_default_load_keyfile (amd, amd->filename); - amd->loaded = TRUE; + const gchar * const *iter; + + am_default_load_directory (amd, amd->directory); + + /* We do this even if am_default_load_directory() succeeded, and + * do not stop when amd->loaded becomes true. If XDG_DATA_HOME + * contains gabble-jabber-example_2eexample_40com.account, that doesn't + * mean a directory in XDG_DATA_DIRS doesn't also contain + * haze-msn-example_2ehotmail_40com.account or something, which + * should also be loaded. */ + for (iter = g_get_system_data_dirs (); + iter != NULL && *iter != NULL; + iter++) + { + gchar *dir = account_directory_in (*iter); + + am_default_load_directory (amd, dir); + g_free (dir); + } + } + + if (!amd->loaded) + { + migrate_from = accounts_cfg_in (g_get_user_data_dir ()); + + if (g_file_test (migrate_from, G_FILE_TEST_EXISTS)) + { + if (!am_default_load_keyfile (amd, migrate_from)) + tp_clear_pointer (&migrate_from, g_free); + + amd->loaded = TRUE; + } + else + { + tp_clear_pointer (&migrate_from, g_free); + } } if (!amd->loaded) @@ -314,14 +921,14 @@ _list (const McpAccountStorage *self, iter != NULL && *iter != NULL; iter++) { - gchar *filename = account_filename_in (*iter); + /* not setting migrate_from here - XDG_DATA_DIRS are conceptually + * read-only, so we don't want to delete these files */ + gchar *filename = accounts_cfg_in (*iter); if (g_file_test (filename, G_FILE_TEST_EXISTS)) { am_default_load_keyfile (amd, filename); amd->loaded = TRUE; - /* Do not set amd->save: we don't need to write it to a - * higher-priority directory until it actually changes. */ } g_free (filename); @@ -333,49 +940,99 @@ _list (const McpAccountStorage *self, if (!amd->loaded) { - gchar *old_filename = get_old_filename (); + migrate_from = get_old_filename (); - if (g_file_test (old_filename, G_FILE_TEST_EXISTS)) + if (g_file_test (migrate_from, G_FILE_TEST_EXISTS)) { - am_default_load_keyfile (amd, old_filename); + if (!am_default_load_keyfile (amd, migrate_from)) + tp_clear_pointer (&migrate_from, g_free); amd->loaded = TRUE; - amd->save = TRUE; - - if (_commit (self, am, NULL)) - { - DEBUG ("Migrated %s to new location: deleting old copy", - old_filename); - if (g_unlink (old_filename) != 0) - g_warning ("Unable to delete %s: %s", old_filename, - g_strerror (errno)); - } + save = TRUE; + } + else + { + tp_clear_pointer (&migrate_from, g_free); } - - g_free (old_filename); } if (!amd->loaded) { DEBUG ("Creating initial account data"); - g_key_file_load_from_data (amd->keyfile, INITIAL_CONFIG, -1, - G_KEY_FILE_KEEP_COMMENTS, NULL); amd->loaded = TRUE; - amd->save = TRUE; - _commit (self, am, NULL); + save = TRUE; } - accounts = g_key_file_get_groups (amd->keyfile, &n); + if (!save) + { + g_hash_table_iter_init (&hash_iter, amd->accounts); - for (i = 0; i < n; i++) + while (g_hash_table_iter_next (&hash_iter, NULL, &v)) + { + McdDefaultStoredAccount *sa = v; + + if (sa->dirty) + { + save = TRUE; + break; + } + } + } + + if (save) { - rval = g_list_prepend (rval, g_strdup (accounts[i])); + gboolean all_succeeded = TRUE; + + DEBUG ("Saving initial or migrated account data"); + + g_hash_table_iter_init (&hash_iter, amd->accounts); + + while (g_hash_table_iter_next (&hash_iter, &k, &v)) + { + McdDefaultStoredAccount *sa = v; + + if (sa->absent) + continue; + + if (!am_default_commit_one (amd, k, v)) + all_succeeded = FALSE; + } + + if (all_succeeded) + { + if (migrate_from != NULL) + { + DEBUG ("Migrated %s to new location: deleting old copy", + migrate_from); + + if (g_unlink (migrate_from) != 0) + WARNING ("Unable to delete %s: %s", migrate_from, + g_strerror (errno)); + } + } } - g_strfreev (accounts); + tp_clear_pointer (&migrate_from, g_free); + + g_hash_table_iter_init (&hash_iter, amd->accounts); + + while (g_hash_table_iter_next (&hash_iter, &k, &v)) + { + McdDefaultStoredAccount *sa = v; + + if (!sa->absent) + rval = g_list_prepend (rval, g_strdup (k)); + } return rval; } +static McpAccountStorageFlags +get_flags (McpAccountStorage *storage, + const gchar *account) +{ + return MCP_ACCOUNT_STORAGE_FLAG_STORES_TYPES; +} + static void account_storage_iface_init (McpAccountStorageIface *iface, gpointer unused G_GNUC_UNUSED) @@ -384,11 +1041,17 @@ account_storage_iface_init (McpAccountStorageIface *iface, iface->desc = PLUGIN_DESCRIPTION; iface->priority = PLUGIN_PRIORITY; - iface->get = _get; - iface->set = _set; + iface->get_flags = get_flags; + iface->get_attribute = get_attribute; + iface->get_parameter = get_parameter; + iface->list_typed_parameters = list_typed_parameters; + iface->list_untyped_parameters = list_untyped_parameters; + iface->set_attribute = set_attribute; + iface->set_parameter = set_parameter; iface->create = _create; - iface->delete = _delete; - iface->commit_one = _commit; + iface->delete_async = delete_async; + iface->delete_finish = delete_finish; + iface->commit = _commit; iface->list = _list; } diff --git a/src/mcd-account-manager-default.h b/src/mcd-account-manager-default.h index b16bc4f9..fb2cb75e 100644 --- a/src/mcd-account-manager-default.h +++ b/src/mcd-account-manager-default.h @@ -49,11 +49,8 @@ G_BEGIN_DECLS typedef struct { GObject parent; - GKeyFile *keyfile; - GKeyFile *removed; - GHashTable *removed_accounts; - gchar *filename; - gboolean save; + GHashTable *accounts; + gchar *directory; gboolean loaded; } _McdAccountManagerDefault; diff --git a/src/mcd-account-manager-priv.h b/src/mcd-account-manager-priv.h index 80a1e0fa..e89b1d33 100644 --- a/src/mcd-account-manager-priv.h +++ b/src/mcd-account-manager-priv.h @@ -28,9 +28,6 @@ #include "mcd-dbusprop.h" -/* auto-generated stubs */ -#include "_gen/svc-Account_Manager_Interface_Hidden.h" - G_BEGIN_DECLS G_GNUC_INTERNAL void _mcd_account_manager_setup diff --git a/src/mcd-account-manager-sso.c b/src/mcd-account-manager-sso.c deleted file mode 100644 index 186c6368..00000000 --- a/src/mcd-account-manager-sso.c +++ /dev/null @@ -1,1745 +0,0 @@ -/* - * A pseudo-plugin that stores/fetches accounts in/from the SSO via libaccounts - * - * Copyright © 2010-2011 Nokia Corporation - * Copyright © 2010-2011 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 "config.h" -#include "mcd-account-manager-sso.h" -#include "mcd-debug.h" - -#include <telepathy-glib/telepathy-glib.h> - -#include <libaccounts-glib/ag-account.h> -#include <libaccounts-glib/ag-service.h> - -#include <string.h> -#include <ctype.h> - -#define PLUGIN_PRIORITY (MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING + 10) -#define PLUGIN_NAME "maemo-libaccounts" -#define PLUGIN_DESCRIPTION \ - "Account storage in the Maemo SSO store via libaccounts-glib API" -#define PLUGIN_PROVIDER "org.maemo.Telepathy.Account.Storage.LibAccounts" - -#define MCPP "param-" -#define AGPP "parameters/" -#define LIBACCT_ID_KEY "libacct-uid" - -#define MC_ENABLED_KEY "Enabled" -#define AG_ENABLED_KEY "enabled" - -#define AG_LABEL_KEY "name" -#define MC_LABEL_KEY "DisplayName" - -#define AG_ACCOUNT_KEY "username" -#define MC_ACCOUNT_KEY "account" -#define PASSWORD_KEY "password" -#define AG_ACCOUNT_ALT_KEY AGPP "account" - -#define MC_CMANAGER_KEY "manager" -#define MC_PROTOCOL_KEY "protocol" -#define MC_IDENTITY_KEY "tmc-uid" - -#define SERVICES_KEY "sso-services" - -#define MC_SERVICE_KEY "Service" - -#define AG_ACCOUNT_WRITE_INTERVAL 5 - -static const gchar *exported_settings[] = { "CredentialsId", NULL }; - -typedef enum { - DELAYED_CREATE, - DELAYED_DELETE, -} DelayedSignal; - -typedef struct { - gchar *mc_name; - gchar *ag_name; - gboolean global; /* global ag setting or service specific? */ - gboolean readable; /* does the _standard_ read method copy this into MC? */ - gboolean writable; /* does the _standard_ write method copy this into AG? */ - gboolean freeable; /* should clear_setting_data deallocate the names? */ -} Setting; - -#define GLOBAL TRUE -#define SERVICE FALSE -#define READABLE TRUE -#define UNREADABLE FALSE -#define WRITABLE TRUE -#define UNWRITABLE FALSE - -typedef enum { - SETTING_MC, - SETTING_AG, -} SettingType; - -/* IMPORTANT IMPLEMENTATION NOTE: - * - * The mapping between telepathy settings and parameter names - * and ag account (libaccounts) settings, and whether those settings - * are stored in the global or service specific ag section is a - * finicky beast - the mapping below has been arrived at empirically - * Take care when altering it. - * - * Settings not mentioned explicitly are: - * • given the same name on both MC and AG sides - * • assigned to the service specific section - * • automatically prefixed (param- vs parameters/) for each side if necessary - * - * So if your setting fits these criteria, you do not need to add it at all. - */ -Setting setting_map[] = { - { MC_ENABLED_KEY , AG_ENABLED_KEY , GLOBAL , UNREADABLE, UNWRITABLE }, - { MCPP MC_ACCOUNT_KEY, AG_ACCOUNT_KEY , GLOBAL , READABLE , UNWRITABLE }, - { MCPP PASSWORD_KEY , PASSWORD_KEY , GLOBAL , READABLE , WRITABLE }, - { MC_LABEL_KEY , AG_LABEL_KEY , GLOBAL , READABLE , WRITABLE }, - { LIBACCT_ID_KEY , LIBACCT_ID_KEY , GLOBAL , UNREADABLE, UNWRITABLE }, - { MC_IDENTITY_KEY , MC_IDENTITY_KEY, SERVICE, READABLE , WRITABLE }, - { MC_CMANAGER_KEY , MC_CMANAGER_KEY, SERVICE, READABLE , UNWRITABLE }, - { MC_PROTOCOL_KEY , MC_PROTOCOL_KEY, SERVICE, READABLE , UNWRITABLE }, - { MC_SERVICE_KEY , MC_SERVICE_KEY , SERVICE, UNREADABLE, UNWRITABLE }, - { SERVICES_KEY , SERVICES_KEY , GLOBAL , UNREADABLE, UNWRITABLE }, - { NULL } -}; - -typedef struct { - DelayedSignal signal; - AgAccountId account_id; -} DelayedSignalData; - -typedef struct { - McdAccountManagerSso *sso; - struct { - AgAccountWatch service; - AgAccountWatch global; - } watch; -} WatchData; - -static Setting * -setting_data (const gchar *name, SettingType type) -{ - guint i = 0; - static Setting parameter = { NULL, NULL, SERVICE, READABLE, WRITABLE, TRUE }; - const gchar *prefix; - - for (; setting_map[i].mc_name != NULL; i++) - { - const gchar *setting_name = NULL; - - if (type == SETTING_MC) - setting_name = setting_map[i].mc_name; - else - setting_name = setting_map[i].ag_name; - - if (g_strcmp0 (name, setting_name) == 0) - return &setting_map[i]; - } - - prefix = (type == SETTING_MC) ? MCPP : AGPP; - - if (!g_str_has_prefix (name, prefix)) - { /* a non-parameter setting */ - parameter.mc_name = g_strdup (name); - parameter.ag_name = g_strdup (name); - } - else - { /* a setting that is a parameter on both sides (AG & MC) */ - const guint plength = strlen (prefix); - - parameter.mc_name = g_strdup_printf ("%s%s", MCPP, name + plength); - parameter.ag_name = g_strdup_printf ("%s%s", AGPP, name + plength); - } - - return ¶meter; -} - -static void -clear_setting_data (Setting *setting) -{ - if (setting == NULL) - return; - - if (!setting->freeable) - return; - - g_free (setting->mc_name); - g_free (setting->ag_name); - setting->mc_name = NULL; - setting->ag_name = NULL; -} - -static gboolean _sso_account_enabled ( - McdAccountManagerSso *self, - AgAccount *account, - AgService *service); - -static void account_storage_iface_init (McpAccountStorageIface *, - gpointer); - -static gchar * -_ag_accountid_to_mc_key (McdAccountManagerSso *sso, - AgAccountId id, - gboolean create); - -static void _ag_account_stored_cb (AgAccount *acct, - const GError *err, - gpointer ignore); - -static void _sso_created (GObject *object, - AgAccountId id, - gpointer user_data); - -static void _sso_toggled (GObject *object, - AgAccountId id, - gpointer data); - -static gboolean save_setting ( - McdAccountManagerSso *self, - AgAccount *account, - const Setting *setting, - const gchar *val); - -G_DEFINE_TYPE_WITH_CODE (McdAccountManagerSso, mcd_account_manager_sso, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_STORAGE, - account_storage_iface_init)); - -static gchar * -_gvalue_to_string (const GValue *val) -{ - switch (G_VALUE_TYPE (val)) - { - case G_TYPE_STRING: - return g_value_dup_string (val); - case G_TYPE_BOOLEAN: - return g_strdup (g_value_get_boolean (val) ? "true" : "false"); - case G_TYPE_CHAR: - return g_strdup_printf ("%c", g_value_get_uchar (val)); - case G_TYPE_UCHAR: - return g_strdup_printf ("%c", g_value_get_char (val)); - case G_TYPE_INT: - return g_strdup_printf ("%i", g_value_get_int (val)); - case G_TYPE_UINT: - return g_strdup_printf ("%u", g_value_get_uint (val)); - case G_TYPE_LONG: - return g_strdup_printf ("%ld", g_value_get_long (val)); - case G_TYPE_ULONG: - return g_strdup_printf ("%lu", g_value_get_ulong (val)); - case G_TYPE_INT64: - return g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (val)); - case G_TYPE_UINT64: - return g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (val)); - case G_TYPE_ENUM: - return g_strdup_printf ("%d" , g_value_get_enum (val)); - case G_TYPE_FLAGS: - return g_strdup_printf ("%u", g_value_get_flags (val)); - case G_TYPE_FLOAT: - return g_strdup_printf ("%f", g_value_get_float (val)); - case G_TYPE_DOUBLE: - return g_strdup_printf ("%g", g_value_get_double (val)); - default: - DEBUG ("Unsupported type %s", G_VALUE_TYPE_NAME (val)); - return NULL; - } -} - -static const gchar * -account_manager_sso_get_service_type (McdAccountManagerSso *self) -{ - McdAccountManagerSsoClass *klass = MCD_ACCOUNT_MANAGER_SSO_GET_CLASS (self); - - g_assert (klass->service_type != NULL); - - return klass->service_type; -} - -static gboolean -_ag_account_select_default_im_service ( - McdAccountManagerSso *self, - AgAccount *account) -{ - const gchar *service_type = account_manager_sso_get_service_type (self); - gboolean have_service = FALSE; - GList *first = ag_account_list_services_by_type (account, service_type); - - if (first != NULL && first->data != NULL) - { - have_service = TRUE; - DEBUG ("default %s service %s", service_type, - ag_service_get_name (first->data)); - ag_account_select_service (account, first->data); - } - - ag_service_list_free (first); - - return have_service; -} - -static AgSettingSource -_ag_account_global_value (AgAccount *account, - const gchar *key, - GValue *value) -{ - AgSettingSource src = AG_SETTING_SOURCE_NONE; - AgService *service = ag_account_get_selected_service (account); - - if (service != NULL) - { - ag_account_select_service (account, NULL); - src = ag_account_get_value (account, key, value); - ag_account_select_service (account, service); - } - else - { - src = ag_account_get_value (account, key, value); - } - - return src; -} - -static AgSettingSource -_ag_account_local_value ( - McdAccountManagerSso *self, - AgAccount *account, - const gchar *key, - GValue *value) -{ - AgSettingSource src = AG_SETTING_SOURCE_NONE; - AgService *service = ag_account_get_selected_service (account); - - if (service != NULL) - { - src = ag_account_get_value (account, key, value); - } - else - { - _ag_account_select_default_im_service (self, account); - src = ag_account_get_value (account, key, value); - ag_account_select_service (account, NULL); - } - - return src; -} - -/* AG_ACCOUNT_ALT_KEY from service overrides global AG_ACCOUNT_KEY if set */ -static void -_maybe_set_account_param_from_service ( - McdAccountManagerSso *self, - const McpAccountManager *am, - AgAccount *ag_account, - const gchar *mc_account) -{ - Setting *setting = setting_data (AG_ACCOUNT_KEY, SETTING_AG); - AgSettingSource source = AG_SETTING_SOURCE_NONE; - GValue ag_value = G_VALUE_INIT; - - g_return_if_fail (setting != NULL); - g_return_if_fail (ag_account != NULL); - - g_value_init (&ag_value, G_TYPE_STRING); - - source = _ag_account_local_value (self, ag_account, AG_ACCOUNT_ALT_KEY, - &ag_value); - - if (source != AG_SETTING_SOURCE_NONE) - { - gchar *value = _gvalue_to_string (&ag_value); - - DEBUG ("overriding global %s param with %s: %s", - AG_ACCOUNT_KEY, AG_ACCOUNT_ALT_KEY, value); - mcp_account_manager_set_value (am, mc_account, setting->mc_name, value); - g_free (value); - } - - g_value_unset (&ag_value); - clear_setting_data (setting); -} - -static WatchData * -make_watch_data (McdAccountManagerSso *sso) -{ - WatchData *data = g_slice_new0 (WatchData); - - data->sso = g_object_ref (sso); - - return data; -} - -static void -free_watch_data (gpointer data) -{ - WatchData *wd = data; - - if (wd == NULL) - return; - - tp_clear_object (&wd->sso); - g_slice_free (WatchData, wd); -} - -static void unwatch_account_keys (McdAccountManagerSso *sso, - AgAccountId id) -{ - gpointer watch_key = GUINT_TO_POINTER (id); - WatchData *wd = g_hash_table_lookup (sso->watches, watch_key); - AgAccount *account = ag_manager_get_account (sso->ag_manager, id); - - if (wd != NULL && account != NULL) - { - ag_account_remove_watch (account, wd->watch.global); - ag_account_remove_watch (account, wd->watch.service); - } - - g_hash_table_remove (sso->watches, watch_key); -} - -/* There are two types of ag watch: ag_account_watch_key and * - * ag_account_watch_dir. _key passees us the watched key when invoking this * - * callback, dir watches only a prefix, and passes the watched prefix * - * (not the actual updated setting) - we now watch with _dir since _key * - * doesn't allow us to watch for keys-that-are-not-set at creation time * - * (since those cannot be known in advance): This means that in this * - * callback we must compare what we have in MC with what's in AG and issue * - * update notices accordingly (and remember to handle deleted keys). * - * It also means the const gchar *what-was-updated parameter is not useful */ -static void _sso_updated (AgAccount *account, - const gchar *unused, - gpointer data) -{ - WatchData *wd = data; - McdAccountManagerSso *sso = wd->sso; - McpAccountManager *am = sso->manager_interface; - McpAccountStorage *mcpa = MCP_ACCOUNT_STORAGE (sso); - gpointer id = GUINT_TO_POINTER (account->id); - const gchar *name = g_hash_table_lookup (sso->id_name_map, id); - AgService *service = ag_account_get_selected_service (account); - GStrv keys = NULL; - GHashTable *unseen = NULL; - GHashTableIter deleted_iter = { 0 }; - const gchar *deleted_key; - guint i; - gboolean params_updated = FALSE; - const gchar *immutables[] = { MC_SERVICE_KEY, SERVICES_KEY, NULL }; - - /* account has no name yet: might be time to create it */ - if (name == NULL) - return _sso_created (G_OBJECT (sso->ag_manager), account->id, sso); - - DEBUG ("update for account %s", name); - - /* list the keys we know about so we can tell if one has been deleted */ - keys = mcp_account_manager_list_keys (am, name); - unseen = g_hash_table_new (g_str_hash, g_str_equal); - - for (i = 0; keys != NULL && keys[i] != NULL; i++) - g_hash_table_insert (unseen, keys[i], GUINT_TO_POINTER (TRUE)); - - /* now iterate over ag settings, global then service specific: */ - ag_account_select_service (account, NULL); - - for (i = 0; i < 2; i++) - { - AgAccountSettingIter iter = { 0 }; - const gchar *ag_key = NULL; - const GValue *ag_val = NULL; - - if (i == 1) - _ag_account_select_default_im_service (sso, account); - - ag_account_settings_iter_init (account, &iter, NULL); - - while (ag_account_settings_iter_next (&iter, &ag_key, &ag_val)) - { - Setting *setting = setting_data (ag_key, SETTING_AG); - const gchar *mc_key; - gchar *ag_str; - gchar *mc_str; - - if (setting == NULL) - continue; - - mc_key = setting->mc_name; - mc_str = mcp_account_manager_get_value (am, name, mc_key); - ag_str = _gvalue_to_string (ag_val); - g_hash_table_remove (unseen, mc_key); - - if (tp_strdiff (ag_str, mc_str)) - { - mcp_account_manager_set_value (am, name, mc_key, ag_str); - - if (sso->ready) - { - if (g_str_has_prefix (mc_key, MCPP)) - params_updated = TRUE; - else - mcp_account_storage_emit_altered_one (mcpa, name, mc_key); - } - } - - g_free (mc_str); - g_free (ag_str); - clear_setting_data (setting); - } - } - - /* special case values always exist and therefore cannot be deleted */ - for (i = 0; immutables[i] != NULL; i++) - { - Setting *immutable = setting_data (immutables[i], SETTING_AG); - - g_hash_table_remove (unseen, immutable->ag_name); - clear_setting_data (immutable); - } - - /* signal (and update) deleted settings: */ - g_hash_table_iter_init (&deleted_iter, unseen); - - while (g_hash_table_iter_next (&deleted_iter, (gpointer *)&deleted_key, NULL)) - { - mcp_account_manager_set_value (am, name, deleted_key, NULL); - - if (g_str_has_prefix (deleted_key, MCPP)) - params_updated = TRUE; - else - mcp_account_storage_emit_altered_one (mcpa, name, deleted_key); - } - - g_hash_table_unref (unseen); - g_strfreev (keys); - - if (params_updated) - mcp_account_storage_emit_altered_one (mcpa, name, "Parameters"); - - /* put the selected service back the way it was when we found it */ - ag_account_select_service (account, service); -} - -static void watch_for_updates (McdAccountManagerSso *sso, - AgAccount *account) -{ - WatchData *data; - gpointer id = GUINT_TO_POINTER (account->id); - AgService *service; - - /* already watching account? let's be idempotent */ - if (g_hash_table_lookup (sso->watches, id) != NULL) - return; - - DEBUG ("watching AG ID %u for updates", account->id); - - service = ag_account_get_selected_service (account); - - data = make_watch_data (sso); - - ag_account_select_service (account, NULL); - data->watch.global = ag_account_watch_dir (account, "", _sso_updated, data); - - _ag_account_select_default_im_service (sso, account); - data->watch.service = ag_account_watch_dir (account, "", _sso_updated, data); - - g_hash_table_insert (sso->watches, id, data); - ag_account_select_service (account, service); -} - -static void _sso_toggled (GObject *object, - AgAccountId id, - gpointer data) -{ - AgManager *manager = AG_MANAGER (object); - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (data); - McpAccountStorage *mcpa = MCP_ACCOUNT_STORAGE (sso); - AgAccount *account = NULL; - gboolean on = FALSE; - const gchar *name = NULL; - - /* If the account manager isn't ready, account state changes are of no * - * interest to us: it will pick up the then-current state of the account * - * when it does become ready, and anything that happens between now and * - * then is not important: */ - if (!sso->ready) - return; - - account = ag_manager_get_account (manager, id); - - if (account != NULL) - { - on = _sso_account_enabled (sso, account, NULL); - name = g_hash_table_lookup (sso->id_name_map, GUINT_TO_POINTER (id)); - } - - if (name != NULL) - { - const gchar *value = on ? "true" : "false"; - McpAccountManager *am = sso->manager_interface; - - mcp_account_manager_set_value (am, name, "Enabled", value); - mcp_account_storage_emit_toggled (mcpa, name, on); - } - else - { - DEBUG ("received enabled=%u signal for unknown SSO account %u", on, id); - } -} - -static void _sso_deleted (GObject *object, - AgAccountId id, - gpointer user_data) -{ - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (user_data); - - if (sso->ready) - { - const gchar *name = - g_hash_table_lookup (sso->id_name_map, GUINT_TO_POINTER (id)); - - /* if the account was in our cache, then this was a 3rd party delete * - * op that someone did behind our back: fire the signal and clean up */ - if (name != NULL) - { - McpAccountStorage *mcpa = MCP_ACCOUNT_STORAGE (sso); - gchar *signalled_name = g_strdup (name); - - /* forget id->name map first, so the signal can't start a loop */ - g_hash_table_remove (sso->id_name_map, GUINT_TO_POINTER (id)); - g_hash_table_remove (sso->accounts, signalled_name); - - /* stop watching for updates */ - unwatch_account_keys (sso, id); - - mcp_account_storage_emit_deleted (mcpa, signalled_name); - - g_free (signalled_name); - } - } - else - { - DelayedSignalData *sig_data = g_slice_new0 (DelayedSignalData); - - sig_data->signal = DELAYED_DELETE; - sig_data->account_id = id; - g_queue_push_tail (sso->pending_signals, sig_data); - } -} - -/* return TRUE if we actually changed any state, FALSE otherwise */ -static gboolean _sso_account_enable ( - McdAccountManagerSso *self, - AgAccount *account, - AgService *service, - gboolean on) -{ - AgService *original = ag_account_get_selected_service (account); - - /* the setting account is already in one of the global+service - configurations that corresponds to our current state: don't touch it */ - if (_sso_account_enabled (self, account, service) == on) - return FALSE; - - /* turn the local enabled flag on/off as required */ - if (service != NULL) - ag_account_select_service (account, service); - else - _ag_account_select_default_im_service (self, account); - - ag_account_set_enabled (account, on); - - /* if we are turning the account on, the global flag must also be set * - * NOTE: this isn't needed when turning the account off */ - if (on) - { - ag_account_select_service (account, NULL); - ag_account_set_enabled (account, on); - } - - ag_account_select_service (account, original); - - return TRUE; -} - -static gboolean _sso_account_enabled ( - McdAccountManagerSso *self, - AgAccount *account, - AgService *service) -{ - gboolean local = FALSE; - gboolean global = FALSE; - AgService *original = ag_account_get_selected_service (account); - - if (service == NULL) - { - _ag_account_select_default_im_service (self, account); - local = ag_account_get_enabled (account); - } - else - { - if (original != service) - ag_account_select_service (account, service); - - local = ag_account_get_enabled (account); - } - - ag_account_select_service (account, NULL); - global = ag_account_get_enabled (account); - - ag_account_select_service (account, original); - - DEBUG ("_sso_account_enabled: global:%d && local:%d", global, local); - - return local && global; -} - -static void _sso_created (GObject *object, - AgAccountId id, - gpointer user_data) -{ - AgManager *ag_manager = AG_MANAGER (object); - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (user_data); - gchar *name = - g_hash_table_lookup (sso->id_name_map, GUINT_TO_POINTER (id)); - - if (sso->ready) - { - /* if we already know the account's name, we shouldn't fire the new * - * account signal as it is one we (and our superiors) already have * - * This could happen as a result of multiple updates being set off * - * before we are ready, for example */ - if (name == NULL) - { - McpAccountStorage *mcpa = MCP_ACCOUNT_STORAGE (sso); - AgAccount *account = ag_manager_get_account (ag_manager, id); - - if (account != NULL) - { - /* this will be owned by the ag account hash, do not free it */ - name = _ag_accountid_to_mc_key (sso, id, TRUE); - - if (name != NULL) - { - Setting *setting = setting_data (MC_IDENTITY_KEY, SETTING_MC); - - g_hash_table_insert (sso->accounts, name, account); - g_hash_table_insert (sso->id_name_map, GUINT_TO_POINTER (id), - g_strdup (name)); - - save_setting (sso, account, setting, name); - - ag_account_store (account, _ag_account_stored_cb, sso); - - mcp_account_storage_emit_created (mcpa, name); - - clear_setting_data (setting); - } - else - { - /* not enough data to name the account: wait for an update */ - DEBUG ("SSO account #%u is currently unnameable", id); - } - - /* in either case, add the account to the watched list */ - watch_for_updates (sso, account); - } - } - } - else - { - DelayedSignalData *sig_data = g_slice_new0 (DelayedSignalData); - - sig_data->signal = DELAYED_CREATE; - sig_data->account_id = id; - g_queue_push_tail (sso->pending_signals, sig_data); - } -} - -static void -mcd_account_manager_sso_init (McdAccountManagerSso *self) -{ - self->accounts = - g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - self->id_name_map = - g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); - self->watches = - g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, - (GDestroyNotify) free_watch_data); - self->pending_signals = g_queue_new (); - -} - -static void -mcd_account_manager_sso_constructed (GObject *object) -{ - McdAccountManagerSso *self = MCD_ACCOUNT_MANAGER_SSO (object); - GObjectClass *parent_class = - G_OBJECT_CLASS (mcd_account_manager_sso_parent_class); - const gchar *service_type = account_manager_sso_get_service_type (self); - - if (parent_class->constructed != NULL) - parent_class->constructed (object); - - DEBUG ("Watching for services of type '%s'", service_type); - self->ag_manager = ag_manager_new_for_service_type (service_type); - - g_signal_connect(self->ag_manager, "enabled-event", - G_CALLBACK (_sso_toggled), self); - g_signal_connect(self->ag_manager, "account-deleted", - G_CALLBACK (_sso_deleted), self); - g_signal_connect(self->ag_manager, "account-created", - G_CALLBACK (_sso_created), self); -} - -static void -mcd_account_manager_sso_class_init (McdAccountManagerSsoClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = mcd_account_manager_sso_constructed; - - klass->service_type = "IM"; -} - -static void -_ag_account_stored_cb ( - AgAccount *account, - const GError *err, - gpointer user_data) -{ - McdAccountManagerSso *self = MCD_ACCOUNT_MANAGER_SSO (user_data); - GValue uid = G_VALUE_INIT; - const gchar *name = NULL; - AgSettingSource src = AG_SETTING_SOURCE_NONE; - - g_value_init (&uid, G_TYPE_STRING); - - src = _ag_account_local_value (self, account, MC_IDENTITY_KEY, &uid); - - if (src != AG_SETTING_SOURCE_NONE && G_VALUE_HOLDS_STRING (&uid)) - { - name = g_value_get_string (&uid); - DEBUG ("%p:%s stored: %s", account, name, err ? err->message : "-"); - g_value_unset (&uid); - } - else - { - DEBUG ("%p:%s not stored? %s", acct, - ag_account_get_display_name (account), err ? err->message : "-"); - } -} - -static gchar * -_ag_accountid_to_mc_key (McdAccountManagerSso *sso, - AgAccountId id, - gboolean create) -{ - AgAccount *account = ag_manager_get_account (sso->ag_manager, id); - AgSettingSource src = AG_SETTING_SOURCE_NONE; - AgService *service = NULL; - GValue value = G_VALUE_INIT; - - if (account == NULL) - { - DEBUG ("AG Account ID %u invalid", id); - return NULL; - } - - service = ag_account_get_selected_service (account); - - DEBUG ("AG Account ID: %u", id); - - g_value_init (&value, G_TYPE_STRING); - - /* first look for the stored TMC uid */ - src = _ag_account_local_value (sso, account, MC_IDENTITY_KEY, &value); - - /* if we found something, our work here is done: */ - if (src != AG_SETTING_SOURCE_NONE) - { - gchar *uid = g_value_dup_string (&value); - g_value_unset (&value); - return uid; - } - - if (!create) - { - g_value_unset (&value); - return NULL; - } - - DEBUG ("no " MC_IDENTITY_KEY " found, synthesising one:"); - - src = _ag_account_global_value (account, AG_ACCOUNT_KEY, &value); - - /* fall back to the alernative account-naming setting if necessary: */ - if (src == AG_SETTING_SOURCE_NONE) - { - _ag_account_select_default_im_service (sso, account); - src = _ag_account_local_value (sso, account, AG_ACCOUNT_ALT_KEY, &value); - } - - if (src != AG_SETTING_SOURCE_NONE && G_VALUE_HOLDS_STRING (&value)) - { - AgAccountSettingIter iter; - const gchar *k; - const GValue *v; - GValue cmanager = G_VALUE_INIT; - GValue protocol = G_VALUE_INIT; - const gchar *cman, *proto; - McpAccountManager *am = sso->manager_interface; - GHashTable *params = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - gchar *name = NULL; - - g_value_init (&cmanager, G_TYPE_STRING); - g_value_init (&protocol, G_TYPE_STRING); - - /* if we weren't on a service when got here, pick the most likely one: */ - if (service == NULL) - _ag_account_select_default_im_service (sso, account); - - ag_account_get_value (account, MC_CMANAGER_KEY, &cmanager); - cman = g_value_get_string (&cmanager); - - if (cman == NULL) - goto cleanup; - - ag_account_get_value (account, MC_PROTOCOL_KEY, &protocol); - proto = g_value_get_string (&protocol); - - if (proto == NULL) - goto cleanup; - - /* prepare the hash of MC param keys -> GValue */ - /* NOTE: some AG bare settings map to MC parameters, * - * so we must iterate over all AG settings, parameters * - * and bare settings included */ - - /* first any matching global values: */ - ag_account_select_service (account, NULL); - ag_account_settings_iter_init (account, &iter, NULL); - - while (ag_account_settings_iter_next (&iter, &k, &v)) - { - Setting *setting = setting_data (k, SETTING_AG); - - if (setting != NULL && g_str_has_prefix (setting->mc_name, MCPP)) - { - gchar *param_key = g_strdup (setting->mc_name + strlen (MCPP)); - - g_hash_table_insert (params, param_key, (gpointer) v); - } - - clear_setting_data (setting); - } - - /* then any service specific settings */ - if (service != NULL) - ag_account_select_service (account, service); - else - _ag_account_select_default_im_service (sso, account); - - ag_account_settings_iter_init (account, &iter, NULL); - while (ag_account_settings_iter_next (&iter, &k, &v)) - { - Setting *setting = setting_data (k, SETTING_AG); - - if (setting != NULL && g_str_has_prefix (setting->mc_name, MCPP)) - { - gchar *param_key = g_strdup (setting->mc_name + strlen (MCPP)); - - g_hash_table_insert (params, param_key, (gpointer) v); - } - - clear_setting_data (setting); - } - - /* we want this to override any other settings for uid generation */ - g_hash_table_insert (params, g_strdup (MC_ACCOUNT_KEY), &value); - - name = mcp_account_manager_get_unique_name (am, cman, proto, params); - - cleanup: - ag_account_select_service (account, service); - g_hash_table_unref (params); - g_value_unset (&value); - g_value_unset (&cmanager); - g_value_unset (&protocol); - - DEBUG (MC_IDENTITY_KEY " value %p:%s synthesised", name, name); - return name; - } - else - { - g_value_unset (&value); - } - - DEBUG (MC_IDENTITY_KEY " not synthesised, returning NULL"); - return NULL; -} - -static AgAccount * -get_ag_account (const McdAccountManagerSso *sso, - const McpAccountManager *am, - const gchar *name, - AgAccountId *id) -{ - AgAccount *account; - - g_return_val_if_fail (id != NULL, NULL); - - /* we have a cached account, just return that */ - account = g_hash_table_lookup (sso->accounts, name); - if (account != NULL) - { - *id = account->id; - return account; - } - - *id = 0; - - return NULL; -} - -/* returns true if it actually changed an account's state */ -static gboolean -save_setting ( - McdAccountManagerSso *self, - AgAccount *account, - const Setting *setting, - const gchar *val) -{ - gboolean changed = FALSE; - AgService *service = ag_account_get_selected_service (account); - - if (!setting->writable) - return FALSE; - - if (setting->global) - ag_account_select_service (account, NULL); - else if (service == NULL) - _ag_account_select_default_im_service (self, account); - - if (setting->readable) - { - GValue old = G_VALUE_INIT; - AgSettingSource src = AG_SETTING_SOURCE_NONE; - - g_value_init (&old, G_TYPE_STRING); - - if (setting->global) - src = _ag_account_global_value (account, setting->ag_name, &old); - else - src = _ag_account_local_value (self, account, setting->ag_name, &old); - - /* unsetting an already unset value, bail out */ - if (val == NULL && src == AG_SETTING_SOURCE_NONE) - goto done; - - /* assigning a value to one which _is_ set: check it actually changed */ - if (val != NULL && src != AG_SETTING_SOURCE_NONE) - { - gchar *str = _gvalue_to_string (&old); - gboolean noop = g_strcmp0 (str, val) == 0; - - g_value_unset (&old); - g_free (str); - - if (noop) - goto done; - } - } - - /* if we got this far, we're changing the stored state: */ - changed = TRUE; - - if (val != NULL) - { - GValue value = G_VALUE_INIT; - - g_value_init (&value, G_TYPE_STRING); - g_value_set_string (&value, val); - ag_account_set_value (account, setting->ag_name, &value); - g_value_unset (&value); - } - else - { - ag_account_set_value (account, setting->ag_name, NULL); - } - - /* leave the selected service as we found it: */ - done: - ag_account_select_service (account, service); - - return changed; -} - -static gboolean -_set (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account_suffix, - const gchar *key, - const gchar *val) -{ - AgAccountId id; - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (self); - AgAccount *account = get_ag_account (sso, am, account_suffix, &id); - Setting *setting = NULL; - gboolean updated = FALSE; - - /* can't store a setting with no name */ - g_return_val_if_fail (key != NULL, FALSE); - - /* we no longer create accounts in libaccount: either an account exists * - * in libaccount as a result of some 3rd party intervention, or it is * - * not an account that this plugin should ever concern itself with */ - - if (account != NULL) - setting = setting_data (key, SETTING_MC); - else - return FALSE; - - if (setting != NULL) - { - /* Enabled is both a global and a local value, for extra fun: */ - if (g_str_equal (setting->mc_name, MC_ENABLED_KEY)) - { - gboolean on = g_str_equal (val, "true"); - - DEBUG ("setting enabled flag: '%d'", on); - updated = _sso_account_enable (sso, account, NULL, on); - } - else - { - updated = save_setting (sso, account, setting, val); - } - - if (updated) - sso->save = TRUE; - - clear_setting_data (setting); - } - - /* whether or not we stored this value, if we got this far it's our * - * setting and no-one else is allowed to claim it: so return TRUE */ - return TRUE; -} - -/* Implements the half of the _get method where key is not NULL. */ -static void -account_manager_sso_get_one ( - McdAccountManagerSso *sso, - const McpAccountManager *am, - const gchar *account_suffix, - const gchar *key, - AgAccount *account, - AgService *service) -{ - if (g_str_equal (key, MC_ENABLED_KEY)) - { - const gchar *v = NULL; - - v = _sso_account_enabled (sso, account, service) ? "true" : "false"; - mcp_account_manager_set_value (am, account_suffix, key, v); - } - else if (g_str_equal (key, SERVICES_KEY)) - { - GString *result = g_string_new (""); - AgManager * agm = ag_account_get_manager (account); - GList *services = ag_manager_list_services (agm); - GList *item = NULL; - - for (item = services; item != NULL; item = g_list_next (item)) - { - const gchar *name = ag_service_get_name (item->data); - - g_string_append_printf (result, "%s;", name); - } - - mcp_account_manager_set_value (am, account_suffix, key, result->str); - - ag_service_list_free (services); - g_string_free (result, TRUE); - } - else if (g_str_equal (key, MC_SERVICE_KEY)) - { - const gchar *service_name = NULL; - AgService *im_service = NULL; - - _ag_account_select_default_im_service (sso, account); - im_service = ag_account_get_selected_service (account); - service_name = ag_service_get_name (im_service); - mcp_account_manager_set_value (am, account_suffix, key, service_name); - } - else - { - GValue v = G_VALUE_INIT; - AgSettingSource src = AG_SETTING_SOURCE_NONE; - Setting *setting = setting_data (key, SETTING_MC); - - if (setting == NULL) - return; - - g_value_init (&v, G_TYPE_STRING); - - if (setting->global) - src = _ag_account_global_value (account, setting->ag_name, &v); - else - src = _ag_account_local_value (sso, account, setting->ag_name, &v); - - if (src != AG_SETTING_SOURCE_NONE) - { - gchar *val = _gvalue_to_string (&v); - - mcp_account_manager_set_value (am, account_suffix, key, val); - - g_free (val); - } - - if (g_str_equal (key, MCPP MC_ACCOUNT_KEY)) - _maybe_set_account_param_from_service (sso, am, account, - account_suffix); - - g_value_unset (&v); - clear_setting_data (setting); - } -} - -/* Implements the half of the _get method where key == NULL, which is an - * instruction from MC that we should look up all of this account's properties - * and stash them with mcp_account_manager_set_value(). - */ -static void -account_manager_sso_get_all ( - McdAccountManagerSso *sso, - const McpAccountManager *am, - const gchar *account_suffix, - AgAccount *account, - AgService *service) -{ - AgAccountSettingIter ag_setting; - const gchar *k; - const GValue *v; - const gchar *on = NULL; - AgService *im_service = NULL; - - /* pick the IM service if we haven't got one set */ - if (service == NULL) - _ag_account_select_default_im_service (sso, account); - - /* special case, not stored as a normal setting */ - im_service = ag_account_get_selected_service (account); - mcp_account_manager_set_value (am, account_suffix, MC_SERVICE_KEY, - ag_service_get_name (im_service)); - - ag_account_settings_iter_init (account, &ag_setting, NULL); - while (ag_account_settings_iter_next (&ag_setting, &k, &v)) - { - Setting *setting = setting_data (k, SETTING_AG); - - if (setting != NULL && setting->readable && !setting->global) - { - gchar *value = _gvalue_to_string (v); - - mcp_account_manager_set_value (am, account_suffix, - setting->mc_name, value); - - g_free (value); - } - - clear_setting_data (setting); - } - - /* deselect any service we may have to get global settings */ - ag_account_select_service (account, NULL); - ag_account_settings_iter_init (account, &ag_setting, NULL); - - while (ag_account_settings_iter_next (&ag_setting, &k, &v)) - { - Setting *setting = setting_data (k, SETTING_AG); - - if (setting != NULL && setting->readable && setting->global) - { - gchar *value = _gvalue_to_string (v); - - mcp_account_manager_set_value (am, account_suffix, - setting->mc_name, value); - - g_free (value); - } - - clear_setting_data (setting); - } - - /* special case, actually two separate but related flags in SSO */ - on = _sso_account_enabled (sso, account, NULL) ? "true" : "false"; - mcp_account_manager_set_value (am, account_suffix, MC_ENABLED_KEY, on); - - _maybe_set_account_param_from_service (sso, am, account, account_suffix); -} - -gboolean -_mcd_account_manager_sso_get ( - const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account_suffix, - const gchar *key) -{ - AgAccountId id; - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (self); - AgAccount *account = get_ag_account (sso, am, account_suffix, &id); - AgService *service = ag_account_get_selected_service (account); - - if (account == NULL) - return FALSE; - - /* Delegate to one of the two relatively-orthogonal meanings of this - * method... */ - if (key != NULL) - account_manager_sso_get_one (sso, am, account_suffix, key, account, - service); - else - account_manager_sso_get_all (sso, am, account_suffix, account, service); - - /* leave the selected service as we found it */ - ag_account_select_service (account, service); - return TRUE; -} - -static gboolean -_delete (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account_suffix, - const gchar *key) -{ - AgAccountId id; - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (self); - AgAccount *account = get_ag_account (sso, am, account_suffix, &id); - gboolean updated = FALSE; - - /* have no values for this account, nothing to do here: */ - if (account == NULL) - return TRUE; - - if (key == NULL) - { - ag_account_delete (account); - g_hash_table_remove (sso->accounts, account_suffix); - g_hash_table_remove (sso->id_name_map, GUINT_TO_POINTER (id)); - - /* stop watching for updates */ - unwatch_account_keys (sso, id); - updated = TRUE; - } - else - { - Setting *setting = setting_data (key, SETTING_MC); - - if (setting != NULL) - updated = save_setting (sso, account, setting, NULL); - - clear_setting_data (setting); - } - - if (updated) - sso->save = TRUE; - - return TRUE; -} - -static gboolean -_commit_real (gpointer user_data) -{ - McpAccountStorage *self = MCP_ACCOUNT_STORAGE (user_data); - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (self); - GHashTableIter iter; - gchar *key; - AgAccount *account; - - g_hash_table_iter_init (&iter, sso->accounts); - - /* for each account, set its telepathy uid MC_IDENTITY_KEY in the * - * AgAccount structure, and then flush any changes to said account * - * to long term storage with ag_account_store() * - * The actual changes are those pushed into the AgAccount in _set * - * and _delete */ - while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &account)) - { - Setting *setting = setting_data (MC_IDENTITY_KEY, SETTING_MC); - /* this value ties MC accounts to SSO accounts */ - save_setting (sso, account, setting, key); - ag_account_store (account, _ag_account_stored_cb, sso); - } - - sso->commit_source = 0; - - /* any pending changes should now have been pushed, clear the save-me flag */ - sso->save = FALSE; - - return FALSE; -} - -static gboolean -_commit (const McpAccountStorage *self, - const McpAccountManager *am) -{ - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (self); - - if (!sso->save) - return TRUE; - - if (sso->commit_source == 0) - { - DEBUG ("Deferring commit for %d seconds", AG_ACCOUNT_WRITE_INTERVAL); - sso->commit_source = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, - AG_ACCOUNT_WRITE_INTERVAL, - _commit_real, g_object_ref (sso), g_object_unref); - } - else - { - DEBUG ("Already deferred commit"); - } - - return TRUE; -} - -static void -_load_from_libaccounts (McdAccountManagerSso *sso, - const McpAccountManager *am) -{ - GList *ag_ids = ag_manager_list_by_service_type (sso->ag_manager, - account_manager_sso_get_service_type (sso)); - GList *ag_id; - - for (ag_id = ag_ids; ag_id != NULL; ag_id = g_list_next (ag_id)) - { - const gchar *key; - const GValue *val; - AgAccountSettingIter iter; - AgAccountId id = GPOINTER_TO_UINT (ag_id->data); - AgAccount *account = ag_manager_get_account (sso->ag_manager, id); - - if (account != NULL) - { - AgService *service = ag_account_get_selected_service (account); - gchar *name = _ag_accountid_to_mc_key (sso, id, FALSE); - - if (name != NULL) - { - AgService *im_service = NULL; - gchar *ident = g_strdup_printf ("%u", id); - GStrv mc_id = g_strsplit (name, "/", 3); - gboolean enabled; - - /* cache the account object, and the ID->name maping: the * - * latter is required because we might receive an async * - * delete signal with the ID after libaccounts-glib has * - * purged all its account data, so we couldn't rely on the * - * MC_IDENTITY_KEY setting. */ - g_hash_table_insert (sso->accounts, name, account); - g_hash_table_insert (sso->id_name_map, GUINT_TO_POINTER (id), - g_strdup (name)); - - if (service == NULL) - _ag_account_select_default_im_service (sso, account); - - /* special case, not stored as a normal setting */ - im_service = ag_account_get_selected_service (account); - mcp_account_manager_set_value (am, name, MC_SERVICE_KEY, - ag_service_get_name (im_service)); - - ag_account_settings_iter_init (account, &iter, NULL); - - while (ag_account_settings_iter_next (&iter, &key, &val)) - { - Setting *setting = setting_data (key, SETTING_AG); - - if (setting != NULL && !setting->global && setting->readable) - { - gchar *value = _gvalue_to_string (val); - - mcp_account_manager_set_value (am, name, setting->mc_name, - value); - - g_free (value); - } - - clear_setting_data (setting); - } - - ag_account_select_service (account, NULL); - ag_account_settings_iter_init (account, &iter, NULL); - - while (ag_account_settings_iter_next (&iter, &key, &val)) - { - Setting *setting = setting_data (key, SETTING_AG); - - if (setting != NULL && setting->global && setting->readable) - { - gchar *value = _gvalue_to_string (val); - - mcp_account_manager_set_value (am, name, setting->mc_name, - value); - - g_free (value); - } - - clear_setting_data (setting); - } - - /* special case, actually two separate but related flags in SSO */ - enabled = _sso_account_enabled (sso, account, NULL); - - mcp_account_manager_set_value (am, name, MC_ENABLED_KEY, - enabled ? "true" : "false"); - mcp_account_manager_set_value (am, name, LIBACCT_ID_KEY, ident); - mcp_account_manager_set_value (am, name, MC_CMANAGER_KEY, mc_id[0]); - mcp_account_manager_set_value (am, name, MC_PROTOCOL_KEY, mc_id[1]); - mcp_account_manager_set_value (am, name, MC_IDENTITY_KEY, name); - _maybe_set_account_param_from_service (sso, am, account, name); - - /* force the services value to be synthesised + cached */ - _mcd_account_manager_sso_get (MCP_ACCOUNT_STORAGE (sso), am, - name, SERVICES_KEY); - - ag_account_select_service (account, service); - - watch_for_updates (sso, account); - - g_strfreev (mc_id); - g_free (ident); - } - } - } - - sso->loaded = TRUE; - ag_manager_list_free (ag_ids); -} - -static GList * -_list (const McpAccountStorage *self, - const McpAccountManager *am) -{ - GList *rval = NULL; - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (self); - GList *ag_ids = NULL; - GList *ag_id; - - if (!sso->loaded) - _load_from_libaccounts (sso, am); - - ag_ids = ag_manager_list_by_service_type (sso->ag_manager, - account_manager_sso_get_service_type (sso)); - - for (ag_id = ag_ids; ag_id != NULL; ag_id = g_list_next (ag_id)) - { - AgAccountId id = GPOINTER_TO_UINT (ag_id->data); - gchar *name = NULL; - - name = _ag_accountid_to_mc_key (sso, id, FALSE); - - if (name != NULL) - { - DEBUG ("account %s listed", name); - rval = g_list_prepend (rval, name); - } - else - { - DelayedSignalData *data = g_slice_new0 (DelayedSignalData); - - DEBUG ("account %u delayed", id); - data->signal = DELAYED_CREATE; - data->account_id = id; - g_queue_push_tail (sso->pending_signals, data); - } - } - - ag_manager_list_free (ag_ids); - - return rval; -} - -static void -_ready (const McpAccountStorage *self, - const McpAccountManager *am) -{ - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (self); - - if (sso->ready) - return; - - g_assert (sso->manager_interface == NULL); - sso->manager_interface = g_object_ref (G_OBJECT (am)); - sso->ready = TRUE; - - while (g_queue_get_length (sso->pending_signals) > 0) - { - DelayedSignalData *data = g_queue_pop_head (sso->pending_signals); - GObject *signal_source = G_OBJECT (sso->ag_manager); - - switch (data->signal) - { - case DELAYED_CREATE: - _sso_created (signal_source, data->account_id, sso); - break; - case DELAYED_DELETE: - _sso_deleted (signal_source, data->account_id, sso); - break; - default: - g_assert_not_reached (); - } - - g_slice_free (DelayedSignalData, data); - } - - g_queue_free (sso->pending_signals); - sso->pending_signals = NULL; -} - -static gboolean -_find_account (McdAccountManagerSso *sso, - const gchar *account_name, - AgAccountId *account_id) -{ - GList *ag_ids = NULL; - GList *ag_id; - gboolean found = FALSE; - - g_return_val_if_fail (account_id != NULL, found); - - ag_ids = ag_manager_list_by_service_type (sso->ag_manager, - account_manager_sso_get_service_type (sso)); - - for (ag_id = ag_ids; ag_id != NULL; ag_id = g_list_next (ag_id)) - { - AgAccountId id = GPOINTER_TO_UINT (ag_id->data); - gchar *name = NULL; - - name = _ag_accountid_to_mc_key (sso, id, FALSE); - - if (g_strcmp0 (name, account_name) == 0) - { - found = TRUE; - *account_id = id; - } - - g_free (name); - - if (found) - break; - } - - ag_manager_list_free (ag_ids); - - return found; -} - -static void -_get_identifier (const McpAccountStorage *self, - const gchar *account, - GValue *identifier) -{ - AgAccountId account_id = 0; - - if (!_find_account (MCD_ACCOUNT_MANAGER_SSO (self), account, &account_id)) - g_warning ("Didn't find account %s in %s", account, PLUGIN_NAME); - - g_value_init (identifier, G_TYPE_UINT); - - g_value_set_uint (identifier, account_id); -} - -static GHashTable * -_get_additional_info (const McpAccountStorage *self, - const gchar *account_suffix) -{ - AgAccountId account_id = 0; - McdAccountManagerSso *sso = MCD_ACCOUNT_MANAGER_SSO (self); - GHashTable *additional_info = NULL; - AgAccount *account; - AgService *service; - AgAccountSettingIter iter; - const GValue *val; - const gchar *key; - - if (!_find_account (sso, account_suffix, &account_id)) - { - g_warning ("Didn't find account %s in %s", account_suffix, PLUGIN_NAME); - return NULL; - } - - account = ag_manager_get_account (sso->ag_manager, account_id); - - g_return_val_if_fail (account != NULL, NULL); - - service = ag_account_get_selected_service (account); - - additional_info = g_hash_table_new_full (g_str_hash, g_str_equal, - (GDestroyNotify) g_free, (GDestroyNotify) tp_g_value_slice_free); - - if (service == NULL) - _ag_account_select_default_im_service (sso, account); - - ag_account_settings_iter_init (account, &iter, NULL); - - while (ag_account_settings_iter_next (&iter, &key, &val)) - { - if (tp_strv_contains (exported_settings, key)) - g_hash_table_insert (additional_info, g_strdup (key), - tp_g_value_slice_dup (val)); - } - - ag_account_select_service (account, NULL); - ag_account_settings_iter_init (account, &iter, NULL); - - while (ag_account_settings_iter_next (&iter, &key, &val)) - { - if (tp_strv_contains (exported_settings, key)) - g_hash_table_insert (additional_info, g_strdup (key), - tp_g_value_slice_dup (val)); - } - - ag_account_select_service (account, service); - - g_object_unref (account); - - return additional_info; -} - -static void -account_storage_iface_init (McpAccountStorageIface *iface, - gpointer unused G_GNUC_UNUSED) -{ - iface->name = PLUGIN_NAME; - iface->desc = PLUGIN_DESCRIPTION; - iface->priority = PLUGIN_PRIORITY; - iface->provider = PLUGIN_PROVIDER; - - iface->get = _mcd_account_manager_sso_get; - iface->set = _set; - iface->delete = _delete; - iface->commit = _commit; - iface->list = _list; - iface->ready = _ready; - iface->get_identifier = _get_identifier; - iface->get_additional_info = _get_additional_info; -} - -McdAccountManagerSso * -mcd_account_manager_sso_new (void) -{ - return g_object_new (MCD_TYPE_ACCOUNT_MANAGER_SSO, NULL); -} diff --git a/src/mcd-account-manager-sso.h b/src/mcd-account-manager-sso.h deleted file mode 100644 index 12e616f8..00000000 --- a/src/mcd-account-manager-sso.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * The SSO/libaccounts-glib manager keyfile storage pseudo-plugin - * - * Copyright © 2010 Nokia Corporation - * Copyright © 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 <mission-control-plugins/mission-control-plugins.h> -#include <libaccounts-glib/ag-manager.h> - -#ifndef __MCD_ACCOUNT_MANAGER_SSO_H__ -#define __MCD_ACCOUNT_MANAGER_SSO_H__ - -G_BEGIN_DECLS - -#define MCD_TYPE_ACCOUNT_MANAGER_SSO \ - (mcd_account_manager_sso_get_type ()) - -#define MCD_ACCOUNT_MANAGER_SSO(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_ACCOUNT_MANAGER_SSO, \ - McdAccountManagerSso)) - -#define MCD_ACCOUNT_MANAGER_SSO_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_ACCOUNT_MANAGER_SSO, \ - McdAccountManagerSsoClass)) - -#define MCD_IS_ACCOUNT_MANAGER_SSO(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_ACCOUNT_MANAGER_SSO)) - -#define MCD_IS_ACCOUNT_MANAGER_SSO_CLASS(k) \ - (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_ACCOUNT_MANAGER_SSO)) - -#define MCD_ACCOUNT_MANAGER_SSO_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_ACCOUNT_MANAGER_SSO, \ - McdAccountManagerSsoClass)) - -typedef struct { - GObject parent; - GHashTable *accounts; - GHashTable *id_name_map; - GHashTable *watches; - GQueue *pending_signals; - AgManager *ag_manager; - McpAccountManager *manager_interface; - gboolean ready; - gboolean save; - gboolean loaded; - guint commit_source; -} _McdAccountManagerSso; - -typedef struct { - GObjectClass parent_class; - - /* In the libaccounts model, each account has a number of associated - * 'services'; for example, you might have a Google account with Google Talk, - * Google Mail, Google Calendar, etc. services. Each service is of a - * particular service type; for instance, the service named "google-talk" is - * of type "IM". - * - * Typically we care about the "IM" service type for Telepathy purposes; but - * we allow for the possibility of a subclass which cares about some other - * service type. - */ - const gchar *service_type; -} _McdAccountManagerSsoClass; - -typedef _McdAccountManagerSso McdAccountManagerSso; -typedef _McdAccountManagerSsoClass McdAccountManagerSsoClass; - -GType mcd_account_manager_sso_get_type (void) G_GNUC_CONST; - -McdAccountManagerSso *mcd_account_manager_sso_new (void); - -/* FIXME: we shouldn't need to expose this. Subclasses should be able to chain - * up to the parent class's implementation of the interface method, but they - * can't because McpAccountStorageIface isn't exposed. See - * <https://bugs.freedesktop.org//show_bug.cgi?id=32914>. - */ -gboolean _mcd_account_manager_sso_get ( - const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account_suffix, - const gchar *key); - -G_END_DECLS - -#endif diff --git a/src/mcd-account-manager.c b/src/mcd-account-manager.c index 8584308d..ec06908d 100644 --- a/src/mcd-account-manager.c +++ b/src/mcd-account-manager.c @@ -49,8 +49,6 @@ #include "mission-control-plugins/implementation.h" #include "plugin-loader.h" -#include "_gen/interfaces.h" - #define PARAM_PREFIX "param-" #define WRITE_CONF_DELAY 500 @@ -59,24 +57,17 @@ static void account_manager_iface_init (TpSvcAccountManagerClass *iface, gpointer iface_data); -static void account_manager_hidden_iface_init ( - McSvcAccountManagerInterfaceHiddenClass *iface, - gpointer iface_data); static void properties_iface_init (TpSvcDBusPropertiesClass *iface, gpointer iface_data); static void _mcd_account_manager_constructed (GObject *obj); static const McdDBusProp account_manager_properties[]; -static const McdDBusProp account_manager_hidden_properties[]; static const McdInterfaceData account_manager_interfaces[] = { MCD_IMPLEMENT_IFACE (tp_svc_account_manager_get_type, account_manager, TP_IFACE_ACCOUNT_MANAGER), - MCD_IMPLEMENT_IFACE (mc_svc_account_manager_interface_hidden_get_type, - account_manager_hidden, - MC_IFACE_ACCOUNT_MANAGER_INTERFACE_HIDDEN), { NULL, } }; @@ -99,6 +90,8 @@ struct _McdAccountManagerPrivate gchar *account_connections_file; /* in account_connections_dir */ gboolean dbus_registered; + /* 1 per thing we need to do before we can take the AccountManager name */ + gint setup_lock; }; typedef struct @@ -107,11 +100,16 @@ typedef struct McpAccountStorage *storage_plugin; McdAccount *account; gint account_lock; + gboolean holds_setup_lock; } McdLoadAccountsData; typedef struct { McdAccountManager *account_manager; + gchar *cm_name; + gchar *protocol_name; + gchar *display_name; + gchar *provider; GHashTable *parameters; GHashTable *properties; McdGetAccountCb callback; @@ -135,80 +133,18 @@ enum PROP_CLIENT_FACTORY }; -static guint write_conf_id = 0; - static void register_dbus_service (McdAccountManager *account_manager); +static void release_setup_lock (McdAccountManager *account_manager); +static void setup_account_loaded (McdAccount *account, + const GError *error, + gpointer user_data); static void release_load_accounts_lock (McdLoadAccountsData *lad); static void add_account (McdAccountManager *manager, McdAccount *account, const gchar *source); -static void account_loaded (McdAccount *account, - const GError *error, - gpointer user_data); - -/* calback chain for asynchronously updates from backends: */ -static void -async_altered_validity_cb (McdAccount *account, const GError *invalid_reason, gpointer data) -{ - DEBUG ("asynchronously altered account %s is %svalid", - mcd_account_get_unique_name (account), (invalid_reason == NULL) ? "" : "in"); - - g_object_unref (account); -} - -static void -async_altered_manager_cb (McdManager *cm, const GError *error, gpointer data) -{ - McdAccount *account = data; - const gchar *name = NULL; - - if (cm != NULL) - name = mcd_manager_get_name (cm); - - if (error != NULL) - DEBUG ("manager %s not ready: %s", name, error->message); - else - DEBUG ("manager %s is ready", name); - - /* this triggers the final parameter check which results in dbus signals * - * being fired and (potentially) the account going online automatically */ - mcd_account_check_validity (account, async_altered_validity_cb, NULL); - - g_object_unref (cm); -} - -/* account has been updated by a third party, and the McpAccountStorage * - * plugin has just informed us of this fact */ -static void -altered_cb (GObject *storage, const gchar *name, gpointer data) -{ - McdAccountManager *am = MCD_ACCOUNT_MANAGER (data); - McdMaster *master = mcd_master_get_default (); - McdAccount *account = NULL; - McdManager *cm = NULL; - const gchar *cm_name = NULL; - - account = mcd_account_manager_lookup_account (am, name); - - if (G_UNLIKELY (!account)) - { - g_warning ("%s: account %s does not exist", G_STRFUNC, name); - return; - } - - /* in theory, the CM is already ready by this point, but make sure: */ - cm_name = mcd_account_get_manager_name (account); - - if (cm_name != NULL) - cm = _mcd_master_lookup_manager (master, cm_name); - - if (cm != NULL) - { - g_object_ref (cm); - g_object_ref (account); - mcd_manager_call_when_ready (cm, async_altered_manager_cb, account); - } -} +static void async_account_loaded (McdAccount *account, + const GError *error, + gpointer user_data); static void async_altered_one_manager_cb (McdManager *cm, @@ -238,7 +174,8 @@ async_altered_one_manager_cb (McdManager *cm, static void -altered_one_cb (GObject *storage, +altered_one_cb (McdStorage *storage, + McpAccountStorage *plugin, const gchar *account_name, const gchar *key, gpointer data) @@ -275,17 +212,6 @@ altered_one_cb (GObject *storage, } } -/* callbacks for the various stages in an backend-driven account creation */ -static void -async_created_validity_cb (McdAccount *account, const GError *invalid_reason, gpointer data) -{ - DEBUG ("asynchronously created account %s is %svalid", - mcd_account_get_unique_name (account), (invalid_reason == NULL) ? "" : "in"); - - /* safely cached in the accounts hash by now */ - g_object_unref (account); -} - static void async_created_manager_cb (McdManager *cm, const GError *error, gpointer data) { @@ -294,6 +220,12 @@ async_created_manager_cb (McdManager *cm, const GError *error, gpointer data) McdAccountManager *am = lad->account_manager; McpAccountStorage *plugin = lad->storage_plugin; const gchar *name = NULL; + gboolean ok; + + g_assert (lad->account_lock > 0); + g_assert (MCD_IS_ACCOUNT (lad->account)); + g_assert (MCD_IS_ACCOUNT_MANAGER (lad->account_manager)); + g_assert (MCP_IS_ACCOUNT_STORAGE (lad->storage_plugin)); if (cm != NULL) name = mcd_manager_get_name (cm); @@ -307,13 +239,19 @@ async_created_manager_cb (McdManager *cm, const GError *error, gpointer data) add_account (am, account, mcp_account_storage_name (plugin)); /* this will free the McdLoadAccountsData, don't use it after this */ - _mcd_account_load (account, account_loaded, lad); + _mcd_account_load (account, async_account_loaded, lad); /* this triggers the final parameter check which results in dbus signals * * being fired and (potentially) the account going online automatically */ - mcd_account_check_validity (account, async_created_validity_cb, NULL); + ok = mcd_account_check_validity (account, NULL); + DEBUG ("asynchronously created account %s is %svalid", + mcd_account_get_unique_name (account), + ok ? "" : "in"); g_object_unref (cm); + + /* safely cached in the accounts hash by now */ + g_object_unref (account); } /* account created by an McpAccountStorage plugin after the initial setup * @@ -321,40 +259,35 @@ async_created_manager_cb (McdManager *cm, const GError *error, gpointer data) * to fetch the named account explicitly at this point (ie it's a read, not * * not a write, from the plugin's POV: */ static void -created_cb (GObject *storage_plugin_obj, +created_cb (McdStorage *storage, + GObject *storage_plugin_obj, const gchar *name, gpointer data) { McpAccountStorage *plugin = MCP_ACCOUNT_STORAGE (storage_plugin_obj); McdAccountManager *am = MCD_ACCOUNT_MANAGER (data); McdAccountManagerPrivate *priv = MCD_ACCOUNT_MANAGER_PRIV (am); - McdLoadAccountsData *lad = g_slice_new (McdLoadAccountsData); + McdLoadAccountsData *lad = NULL; McdAccount *account = NULL; - McdStorage *storage = priv->storage; McdMaster *master = mcd_master_get_default (); McdManager *cm = NULL; const gchar *cm_name = NULL; - lad->account_manager = am; - lad->storage_plugin = plugin; - lad->account_lock = 1; /* will be released at the end of this function */ + g_return_if_fail (storage == priv->storage); - /* actually fetch the data into our cache from the plugin: */ - if (mcd_storage_add_account_from_plugin (storage, plugin, name)) - { - account = mcd_account_new (am, name, priv->minotaur); - lad->account = account; - } - else - { - /* that function already warned about it */ - goto finish; - } + account = mcd_account_new (am, name, priv->minotaur, plugin); + g_assert (MCD_IS_ACCOUNT (account)); - if (G_UNLIKELY (!account)) + lad = g_slice_new0 (McdLoadAccountsData); + lad->account_manager = g_object_ref (am); + lad->storage_plugin = g_object_ref (plugin); + lad->account_lock = 1; /* released at the end of this function */ + lad->account = g_object_ref (account); + + if (am->priv->setup_lock > 0) { - g_warning ("%s: account %s failed to instantiate", G_STRFUNC, name); - goto finish; + lad->holds_setup_lock = TRUE; + am->priv->setup_lock++; } cm_name = mcd_account_get_manager_name (account); @@ -376,12 +309,15 @@ created_cb (GObject *storage_plugin_obj, g_object_unref (account); } -finish: release_load_accounts_lock (lad); } static void -toggled_cb (GObject *plugin, const gchar *name, gboolean on, gpointer data) +toggled_cb (McdStorage *storage, + GObject *plugin, + const gchar *name, + gboolean on, + gpointer data) { McpAccountStorage *storage_plugin = MCP_ACCOUNT_STORAGE (plugin); McdAccountManager *manager = MCD_ACCOUNT_MANAGER (data); @@ -401,7 +337,7 @@ toggled_cb (GObject *plugin, const gchar *name, gboolean on, gpointer data) } _mcd_account_set_enabled (account, on, FALSE, - MCD_DBUS_PROP_SET_FLAG_NONE, &error); + MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE, &error); if (error != NULL) { @@ -411,7 +347,10 @@ toggled_cb (GObject *plugin, const gchar *name, gboolean on, gpointer data) } static void -reconnect_cb (GObject *plugin, const gchar *name, gpointer data) +reconnect_cb (McdStorage *storage, + GObject *plugin, + const gchar *name, + gpointer data) { McpAccountStorage *storage_plugin = MCP_ACCOUNT_STORAGE (plugin); McdAccountManager *manager = MCD_ACCOUNT_MANAGER (data); @@ -435,16 +374,35 @@ reconnect_cb (GObject *plugin, const gchar *name, gpointer data) } static void -_mcd_account_delete_cb (McdAccount *account, const GError *error, gpointer data) +mcd_account_delete_debug_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { - /* no need to do anything other than release the account ref, which * - * should be the last ref we hold by the time this rolls arouns: */ - g_object_unref (account); + McdAccount *account = MCD_ACCOUNT (source); + GError *error = NULL; + + if (mcd_account_delete_finish (account, res, &error)) + { + DEBUG ("successfully deleted account %s (%s)", + mcd_account_get_unique_name (account), + (const gchar *) user_data); + } + else + { + WARNING ("could not delete account %s (%s): %s #%d: %s", + mcd_account_get_unique_name (account), + (const gchar *) user_data, + g_quark_to_string (error->domain), error->code, error->message); + g_clear_error (&error); + } } /* a backend plugin notified us that an account was vaporised: remove it */ static void -deleted_cb (GObject *plugin, const gchar *name, gpointer data) +deleted_cb (McdStorage *storage, + GObject *plugin, + const gchar *name, + gpointer data) { McpAccountStorage *storage_plugin = MCP_ACCOUNT_STORAGE (plugin); McdAccountManager *manager = MCD_ACCOUNT_MANAGER (data); @@ -463,7 +421,11 @@ deleted_cb (GObject *plugin, const gchar *name, gpointer data) /* this unhooks the account's signal handlers */ g_hash_table_remove (manager->priv->accounts, name); tp_svc_account_manager_emit_account_removed (manager, object_path); - mcd_account_delete (account, _mcd_account_delete_cb, NULL); + mcd_account_delete_async (account, + MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE, + mcd_account_delete_debug_cb, + "in response to McpAccountStorage::deleted"); + g_object_unref (account); } } @@ -622,45 +584,24 @@ on_account_validity_changed (McdAccount *account, gboolean valid, object_path = mcd_account_get_object_path (account); - if (_mcd_account_is_hidden (account)) - { - mc_svc_account_manager_interface_hidden_emit_hidden_account_validity_changed ( - account_manager, object_path, valid); - } - else - { - tp_svc_account_manager_emit_account_validity_changed (account_manager, - object_path, - valid); - } + tp_svc_account_manager_emit_account_validity_changed (account_manager, + object_path, + valid); } static void on_account_removed (McdAccount *account, McdAccountManager *account_manager) { McdAccountManagerPrivate *priv = account_manager->priv; - McdStorage *storage = priv->storage; const gchar *name, *object_path; object_path = mcd_account_get_object_path (account); - if (_mcd_account_is_hidden (account)) - { - mc_svc_account_manager_interface_hidden_emit_hidden_account_removed ( - account_manager, object_path); - } - else - { - tp_svc_account_manager_emit_account_removed (account_manager, - object_path); - } + tp_svc_account_manager_emit_account_removed (account_manager, + object_path); name = mcd_account_get_unique_name (account); g_hash_table_remove (priv->accounts, name); - - mcd_storage_delete_account (storage, name); - mcd_account_manager_write_conf_async (account_manager, account, NULL, - NULL); } static inline void @@ -737,6 +678,14 @@ mcd_create_account_data_free (McdCreateAccountData *cad) if (G_UNLIKELY (cad->error)) g_error_free (cad->error); + if (cad->destroy != NULL) + cad->destroy (cad->user_data); + + g_free (cad->provider); + g_free (cad->cm_name); + g_free (cad->protocol_name); + g_free (cad->display_name); + g_slice_free (McdCreateAccountData, cad); } @@ -783,12 +732,18 @@ complete_account_creation_finish (McdAccount *account, if (!cad->ok) { - mcd_account_delete (account, NULL, NULL); + mcd_account_delete_async (account, + MCD_DBUS_PROP_SET_FLAG_NONE, + mcd_account_delete_debug_cb, + "while recovering from failure to create"); tp_clear_object (&account); } - mcd_account_manager_write_conf_async (account_manager, account, NULL, - NULL); + if (account != NULL) + { + mcd_storage_commit (account_manager->priv->storage, + mcd_account_get_unique_name (account)); + } if (cad->callback != NULL) cad->callback (account_manager, account, cad->error, cad->user_data); @@ -798,23 +753,6 @@ complete_account_creation_finish (McdAccount *account, } static void -complete_account_creation_check_validity_cb (McdAccount *account, - const GError *invalid_reason, - gpointer user_data) -{ - McdCreateAccountData *cad = user_data; - - if (invalid_reason != NULL) - { - cad->ok = FALSE; - g_set_error_literal (&cad->error, invalid_reason->domain, - invalid_reason->code, invalid_reason->message); - } - - complete_account_creation_finish (account, cad); -} - -static void complete_account_creation_set_cb (McdAccount *account, GPtrArray *not_yet, const GError *set_error, gpointer user_data) { @@ -839,12 +777,12 @@ complete_account_creation_set_cb (McdAccount *account, GPtrArray *not_yet, if (cad->ok) { add_account (account_manager, account, G_STRFUNC); - mcd_account_check_validity (account, complete_account_creation_check_validity_cb, cad); - } - else - { - complete_account_creation_finish (account, cad); + + if (!mcd_account_check_validity (account, &cad->error)) + cad->ok = FALSE; } + + complete_account_creation_finish (account, cad); } static void @@ -868,85 +806,134 @@ complete_account_creation (McdAccount *account, cad); } -void -_mcd_account_manager_create_account (McdAccountManager *account_manager, - const gchar *manager, - const gchar *protocol, - const gchar *display_name, - GHashTable *params, - GHashTable *properties, - McdGetAccountCb callback, - gpointer user_data, - GDestroyNotify destroy) +static void +identify_account_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - McdAccountManagerPrivate *priv = account_manager->priv; - McdStorage *storage = priv->storage; - McdCreateAccountData *cad; + McdStorage *storage = MCD_STORAGE (source_object); + McdCreateAccountData *cad = user_data; + gchar *id; + gchar *unique_name; McdAccount *account; - gchar *unique_name = NULL; - const gchar *provider; - GError *e = NULL; + McpAccountStorage *plugin; - DEBUG ("called"); - if (G_UNLIKELY (manager == NULL || manager[0] == 0 || - protocol == NULL || protocol[0] == 0)) + id = mcp_account_manager_identify_account_finish ( + MCP_ACCOUNT_MANAGER (storage), result, &cad->error); + + if (id == NULL) { - GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "Invalid parameters"}; - callback (account_manager, NULL, &error, user_data); - if (destroy) - destroy (user_data); + cad->callback (cad->account_manager, NULL, cad->error, cad->user_data); + mcd_create_account_data_free (cad); return; } - provider = tp_asv_get_string (properties, - TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER); - - unique_name = mcd_storage_create_account (storage, provider, - manager, protocol, params, - &e); + unique_name = mcd_storage_create_account (storage, cad->provider, + cad->cm_name, cad->protocol_name, + id, &plugin, &cad->error); if (unique_name == NULL) { - callback (account_manager, NULL, e, user_data); - g_clear_error (&e); - if (destroy) - destroy (user_data); + g_free (id); + cad->callback (cad->account_manager, NULL, cad->error, cad->user_data); + mcd_create_account_data_free (cad); return; } /* create the basic account keys */ mcd_storage_set_string (storage, unique_name, - MC_ACCOUNTS_KEY_MANAGER, manager); + MC_ACCOUNTS_KEY_MANAGER, cad->cm_name); mcd_storage_set_string (storage, unique_name, - MC_ACCOUNTS_KEY_PROTOCOL, protocol); + MC_ACCOUNTS_KEY_PROTOCOL, cad->protocol_name); + g_free (id); - if (display_name != NULL) + if (cad->display_name != NULL) mcd_storage_set_string (storage, unique_name, - MC_ACCOUNTS_KEY_DISPLAY_NAME, display_name); + MC_ACCOUNTS_KEY_DISPLAY_NAME, + cad->display_name); - account = mcd_account_new (account_manager, unique_name, priv->minotaur); + account = mcd_account_new (cad->account_manager, unique_name, + cad->account_manager->priv->minotaur, + plugin); g_free (unique_name); + g_object_unref (plugin); if (G_LIKELY (account)) { - cad = g_slice_new (McdCreateAccountData); - cad->account_manager = account_manager; - cad->parameters = g_hash_table_ref (params); - cad->properties = (properties ? g_hash_table_ref (properties) : NULL); - cad->callback = callback; - cad->user_data = user_data; - cad->destroy = destroy; - cad->error = NULL; _mcd_account_load (account, complete_account_creation, cad); } else { GError error = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "" }; + cad->callback (cad->account_manager, NULL, &error, cad->user_data); + mcd_create_account_data_free (cad); + } +} + +void +_mcd_account_manager_create_account (McdAccountManager *account_manager, + const gchar *manager, + const gchar *protocol, + const gchar *display_name, + GHashTable *params, + GHashTable *properties, + McdGetAccountCb callback, + gpointer user_data, + GDestroyNotify destroy) +{ + McdAccountManagerPrivate *priv = account_manager->priv; + McdStorage *storage = priv->storage; + McdCreateAccountData *cad; + GValue value = G_VALUE_INIT; + GVariant *variant_params; + + DEBUG ("called"); + if (G_UNLIKELY (manager == NULL || manager[0] == 0 || + protocol == NULL || protocol[0] == 0)) + { + GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Invalid parameters"}; callback (account_manager, NULL, &error, user_data); if (destroy) destroy (user_data); + return; } + + cad = g_slice_new0 (McdCreateAccountData); + cad->account_manager = account_manager; + cad->cm_name = g_strdup (manager); + cad->protocol_name = g_strdup (protocol); + cad->display_name = g_strdup (display_name); + cad->parameters = g_hash_table_ref (params); + cad->callback = callback; + cad->user_data = user_data; + cad->destroy = destroy; + cad->error = NULL; + + if (properties != NULL) + { + cad->properties = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) tp_g_value_slice_free); + + tp_g_hash_table_update (cad->properties, properties, + (GBoxedCopyFunc) g_strdup, + (GBoxedCopyFunc) tp_g_value_slice_dup); + + /* special case: "construct-only" */ + cad->provider = g_strdup (tp_asv_get_string (cad->properties, + TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER)); + g_hash_table_remove (cad->properties, + TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER); + } + + g_value_init (&value, TP_HASH_TYPE_STRING_VARIANT_MAP); + g_value_set_static_boxed (&value, params); + variant_params = dbus_g_value_build_g_variant (&value); + g_value_unset (&value); + + mcp_account_manager_identify_account_async (MCP_ACCOUNT_MANAGER (storage), + manager, protocol, variant_params, NULL, identify_account_cb, cad); + g_variant_unref (variant_params); } static void @@ -993,15 +980,7 @@ account_manager_iface_init (TpSvcAccountManagerClass *iface, } static void -account_manager_hidden_iface_init ( - McSvcAccountManagerInterfaceHiddenClass *iface, - gpointer iface_data) -{ -} - -static void -accounts_to_gvalue (GHashTable *accounts, gboolean valid, gboolean hidden, - GValue *value) +accounts_to_gvalue (GHashTable *accounts, gboolean valid, GValue *value) { static GType ao_type = G_TYPE_INVALID; GPtrArray *account_array; @@ -1019,8 +998,7 @@ accounts_to_gvalue (GHashTable *accounts, gboolean valid, gboolean hidden, while (g_hash_table_iter_next (&iter, &k, (gpointer)&account)) { - if (mcd_account_is_valid (account) == valid && - _mcd_account_is_hidden (account) == hidden) + if (mcd_account_is_valid (account) == valid) { g_ptr_array_add (account_array, g_strdup (mcd_account_get_object_path (account))); @@ -1039,7 +1017,7 @@ get_valid_accounts (TpSvcDBusProperties *self, const gchar *name, McdAccountManagerPrivate *priv = account_manager->priv; DEBUG ("called"); - accounts_to_gvalue (priv->accounts, TRUE, FALSE, value); + accounts_to_gvalue (priv->accounts, TRUE, value); } static void @@ -1050,7 +1028,7 @@ get_invalid_accounts (TpSvcDBusProperties *self, const gchar *name, McdAccountManagerPrivate *priv = account_manager->priv; DEBUG ("called"); - accounts_to_gvalue (priv->accounts, FALSE, FALSE, value); + accounts_to_gvalue (priv->accounts, FALSE, value); } static void @@ -1068,9 +1046,7 @@ get_supported_account_properties (TpSvcDBusProperties *svc, TP_IFACE_ACCOUNT ".Supersedes", TP_PROP_ACCOUNT_SERVICE, TP_IFACE_ACCOUNT_INTERFACE_AVATAR ".Avatar", - MC_IFACE_ACCOUNT_INTERFACE_CONDITIONS ".Condition", TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER, - MC_IFACE_ACCOUNT_INTERFACE_HIDDEN ".Hidden", NULL }; @@ -1087,32 +1063,6 @@ static const McdDBusProp account_manager_properties[] = { }; static void -get_valid_hidden_accounts (TpSvcDBusProperties *self, const gchar *name, - GValue *value) -{ - McdAccountManager *account_manager = MCD_ACCOUNT_MANAGER (self); - McdAccountManagerPrivate *priv = account_manager->priv; - - accounts_to_gvalue (priv->accounts, TRUE, TRUE, value); -} - -static void -get_invalid_hidden_accounts (TpSvcDBusProperties *self, const gchar *name, - GValue *value) -{ - McdAccountManager *account_manager = MCD_ACCOUNT_MANAGER (self); - McdAccountManagerPrivate *priv = account_manager->priv; - - accounts_to_gvalue (priv->accounts, FALSE, TRUE, value); -} - -static const McdDBusProp account_manager_hidden_properties[] = { - { "ValidHiddenAccounts", NULL, get_valid_hidden_accounts }, - { "InvalidHiddenAccounts", NULL, get_invalid_hidden_accounts }, - { 0 }, -}; - -static void properties_iface_init (TpSvcDBusPropertiesClass *iface, gpointer iface_data) { #define IMPLEMENT(x) tp_svc_dbus_properties_implement_##x (\ @@ -1123,18 +1073,17 @@ properties_iface_init (TpSvcDBusPropertiesClass *iface, gpointer iface_data) #undef IMPLEMENT } -static gboolean -write_conf (gpointer userdata) +static void +release_setup_lock (McdAccountManager *self) { - McdStorage *storage = MCD_STORAGE (userdata); + g_return_if_fail (self->priv->setup_lock > 0); + self->priv->setup_lock--; + DEBUG ("called, count is now %d", self->priv->setup_lock); - DEBUG ("called"); - g_source_remove (write_conf_id); - write_conf_id = 0; - - mcd_storage_commit (storage, NULL); - - return TRUE; + if (self->priv->setup_lock == 0) + { + register_dbus_service (self); + } } static void @@ -1146,13 +1095,21 @@ release_load_accounts_lock (McdLoadAccountsData *lad) if (lad->account_lock == 0) { - register_dbus_service (lad->account_manager); + if (lad->holds_setup_lock) + release_setup_lock (lad->account_manager); + + g_object_unref (lad->account_manager); + g_object_unref (lad->storage_plugin); + g_object_unref (lad->account); + g_slice_free (McdLoadAccountsData, lad); } } static void -account_loaded (McdAccount *account, const GError *error, gpointer user_data) +async_account_loaded (McdAccount *account, + const GError *error, + gpointer user_data) { McdLoadAccountsData *lad = user_data; @@ -1167,54 +1124,61 @@ account_loaded (McdAccount *account, const GError *error, gpointer user_data) } static void -uncork_storage_plugins (McdAccountManager *account_manager) +setup_account_loaded (McdAccount *account, + const GError *error, + gpointer user_data) { - McdAccountManagerPrivate *priv = MCD_ACCOUNT_MANAGER_PRIV (account_manager); + McdAccountManager *self = MCD_ACCOUNT_MANAGER (user_data); - mcd_account_manager_write_conf_async (account_manager, NULL, NULL, NULL); - mcd_storage_ready (priv->storage); + if (error) + { + g_warning ("%s: got error: %s", G_STRFUNC, error->message); + g_hash_table_remove (self->priv->accounts, + mcd_account_get_unique_name (account)); + } + + release_setup_lock (self); + g_object_unref (self); } typedef struct { McdAccountManager *self; McdAccount *account; - McdLoadAccountsData *lad; } MigrateCtx; static MigrateCtx * migrate_ctx_new (McdAccountManager *self, - McdAccount *account, - McdLoadAccountsData *lad) + McdAccount *account) { MigrateCtx *ctx = g_slice_new (MigrateCtx); ctx->self = g_object_ref (self); ctx->account = g_object_ref (account); - ctx->lad = lad; /* Lock attempting to migrate the account */ - lad->account_lock++; + self->priv->setup_lock++; return ctx; } static void migrate_ctx_free (MigrateCtx *ctx) { + release_setup_lock (ctx->self); g_object_unref (ctx->self); g_object_unref (ctx->account); - release_load_accounts_lock (ctx->lad); g_slice_free (MigrateCtx, ctx); } static void -migrate_delete_account_cb (McdAccount *account, - const GError *error, - gpointer user_data) +migrate_delete_account_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { MigrateCtx *ctx = user_data; + mcd_account_delete_debug_cb (source, res, "after migrating it"); migrate_ctx_free (ctx); } @@ -1238,7 +1202,8 @@ migrate_create_account_cb (McdAccountManager *account_manager, DEBUG ("Account %s migrated, removing it", mcd_account_get_unique_name (ctx->account)); - mcd_account_delete (ctx->account, migrate_delete_account_cb, ctx); + mcd_account_delete_async (ctx->account, MCD_DBUS_PROP_SET_FLAG_NONE, + migrate_delete_account_cb, ctx); } static void @@ -1265,7 +1230,9 @@ migrate_butterfly_haze_ready (McdManager *manager, /* Parameters; the only mandatory one is 'account' */ if (!mcd_account_get_parameter_of_known_type (ctx->account, - "account", G_TYPE_STRING, + "account", + G_VARIANT_TYPE_STRING, + G_TYPE_STRING, &v, NULL)) { _mcd_account_set_enabled (ctx->account, FALSE, TRUE, @@ -1279,7 +1246,9 @@ migrate_butterfly_haze_ready (McdManager *manager, /* If MC is storing the password, let's copy that too, so Empathy * can migrate it somewhere better. */ if (mcd_account_get_parameter_of_known_type (ctx->account, - "password", G_TYPE_STRING, + "password", + G_VARIANT_TYPE_STRING, + G_TYPE_STRING, &password_v, NULL)) { g_hash_table_insert (parameters, "password", &password_v); @@ -1370,12 +1339,11 @@ error: static void migrate_butterfly_account (McdAccountManager *self, - McdAccount *account, - McdLoadAccountsData *lad) + McdAccount *account) { MigrateCtx *ctx; - ctx = migrate_ctx_new (self, account, lad); + ctx = migrate_ctx_new (self, account); _mcd_account_load (account, butterfly_account_loaded, ctx); } @@ -1383,8 +1351,7 @@ migrate_butterfly_account (McdAccountManager *self, /* Migrate some specific type of account. If something went wrong during the * migration we disable it. */ static void -migrate_accounts (McdAccountManager *self, - McdLoadAccountsData *lad) +migrate_accounts (McdAccountManager *self) { McdAccountManagerPrivate *priv = self->priv; GHashTableIter iter; @@ -1402,7 +1369,7 @@ migrate_accounts (McdAccountManager *self, continue; if (!tp_strdiff (tp_connection_manager_get_name (cm), "butterfly")) - migrate_butterfly_account (self, account, lad); + migrate_butterfly_account (self, account); } } @@ -1418,42 +1385,62 @@ _mcd_account_manager_setup (McdAccountManager *account_manager) { McdAccountManagerPrivate *priv = account_manager->priv; McdStorage *storage = priv->storage; - McdLoadAccountsData *lad; - gchar **accounts, **name; + GHashTable *accounts; GHashTableIter iter; - gpointer v; + gpointer k, v; + + /* for simplicity we don't support re-entrant setup */ + g_return_if_fail (priv->setup_lock == 0); + + priv->setup_lock = 1; /* will be released at the end of this function */ tp_list_connection_names (priv->dbus_daemon, list_connection_names_cb, NULL, NULL, (GObject *)account_manager); - lad = g_slice_new (McdLoadAccountsData); - lad->account_manager = account_manager; - lad->account_lock = 1; /* will be released at the end of this function */ + accounts = mcd_storage_get_accounts (storage); + + /* as soon as we've listed the initial set, connect to signals + * for any subsequently-added accounts */ + g_signal_connect_object (priv->storage, "altered-one", + G_CALLBACK (altered_one_cb), account_manager, 0); + g_signal_connect_object (priv->storage, "created", + G_CALLBACK (created_cb), account_manager, 0); + g_signal_connect_object (priv->storage, "toggled", + G_CALLBACK (toggled_cb), account_manager, 0); + g_signal_connect_object (priv->storage, "deleted", + G_CALLBACK (deleted_cb), account_manager, 0); + g_signal_connect_object (priv->storage, "reconnect", + G_CALLBACK (reconnect_cb), account_manager, 0); - accounts = mcd_storage_dup_accounts (storage, NULL); + g_hash_table_iter_init (&iter, accounts); - for (name = accounts; *name != NULL; name++) + while (g_hash_table_iter_next (&iter, &k, &v)) { gboolean plausible = FALSE; const gchar *manager = NULL; const gchar *protocol = NULL; + const gchar *account_name = k; + McpAccountStorage *plugin = v; McdAccount *account = mcd_account_manager_lookup_account ( - account_manager, *name); + account_manager, account_name); if (account != NULL) { - /* FIXME: this shouldn't really happen */ - DEBUG ("already have account %p called '%s'; skipping", account, *name); + /* FIXME: can't happen? We shouldn't create any accounts before + * we got here, and there can't be any duplicates in @accounts */ + DEBUG ("already have account %p called '%s'; skipping", + account, account_name); continue; } - account = mcd_account_new (account_manager, *name, priv->minotaur); + account = mcd_account_new (account_manager, account_name, + priv->minotaur, plugin); if (G_UNLIKELY (!account)) { g_warning ("%s: account %s failed to instantiate", G_STRFUNC, - *name); + account_name); continue; } @@ -1468,23 +1455,30 @@ _mcd_account_manager_setup (McdAccountManager *account_manager) const gchar *dbg_protocol = (protocol == NULL) ? "(nil)" : protocol; g_warning ("%s: account %s has implausible manager/protocol: %s/%s", - G_STRFUNC, *name, dbg_manager, dbg_protocol); + G_STRFUNC, account_name, dbg_manager, dbg_protocol); g_object_unref (account); continue; } - lad->account_lock++; - add_account (lad->account_manager, account, "keyfile"); - _mcd_account_load (account, account_loaded, lad); + priv->setup_lock++; + add_account (account_manager, account, "keyfile"); + _mcd_account_load (account, setup_account_loaded, + g_object_ref (account_manager)); g_object_unref (account); } - g_strfreev (accounts); - uncork_storage_plugins (account_manager); + /* FIXME: why do we need to commit the accounts at this point? + * It was added to uncork_storage_plugins() in 3d5b5e7a248d + * without explanation */ + g_hash_table_iter_init (&iter, account_manager->priv->accounts); + while (g_hash_table_iter_next (&iter, &k, NULL)) + { + mcd_storage_commit (storage, k); + } - migrate_accounts (account_manager, lad); + migrate_accounts (account_manager); - release_load_accounts_lock (lad); + release_setup_lock (account_manager); g_hash_table_iter_init (&iter, account_manager->priv->accounts); @@ -1566,12 +1560,6 @@ _mcd_account_manager_finalize (GObject *object) { McdAccountManagerPrivate *priv = MCD_ACCOUNT_MANAGER_PRIV (object); - if (write_conf_id) - { - write_conf (priv->storage); - g_assert (write_conf_id == 0); - } - tp_clear_object (&priv->storage); g_free (priv->account_connections_dir); remove (priv->account_connections_file); @@ -1655,15 +1643,6 @@ _mcd_account_manager_constructed (GObject *obj) { McdAccountManager *account_manager = MCD_ACCOUNT_MANAGER (obj); McdAccountManagerPrivate *priv = account_manager->priv; - guint i = 0; - static struct { const gchar *name; GCallback handler; } sig[] = - { { "created", G_CALLBACK (created_cb) }, - { "altered", G_CALLBACK (altered_cb) }, - { "toggled", G_CALLBACK (toggled_cb) }, - { "deleted", G_CALLBACK (deleted_cb) }, - { "altered-one", G_CALLBACK (altered_one_cb) }, - { "reconnect", G_CALLBACK (reconnect_cb) }, - { NULL, NULL } }; DEBUG (""); @@ -1681,13 +1660,6 @@ _mcd_account_manager_constructed (GObject *obj) DEBUG ("loading plugins"); mcd_storage_load (priv->storage); - /* hook up all the storage plugin signals to their handlers: */ - for (i = 0; sig[i].name != NULL; i++) - { - mcd_storage_connect_signal (sig[i].name, sig[i].handler, - account_manager); - } - /* initializes the interfaces */ mcd_dbus_init_interfaces_instances (account_manager); } @@ -1724,63 +1696,6 @@ mcd_account_manager_get_connectivity_monitor (McdAccountManager *self) return self->priv->minotaur; } -/** - * McdAccountManagerWriteConfCb: - * @account_manager: the #McdAccountManager - * @error: a set #GError on failure or %NULL if there was no error - * @user_data: user data - * - * The callback from mcd_account_manager_write_conf_async(). If the config - * writing was successful, @error will be %NULL, otherwise it will be set - * with the appropriate error. - */ - -/** - * mcd_account_manager_write_conf_async: - * @account_manager: the #McdAccountManager - * @account: the account to be written, or %NULL to flush all accounts - * @callback: a callback to be called on write success or failure - * @user_data: data to be passed to @callback - * - * Write the account manager configuration to disk. - */ -void -mcd_account_manager_write_conf_async (McdAccountManager *account_manager, - McdAccount *account, - McdAccountManagerWriteConfCb callback, - gpointer user_data) -{ - McdStorage *storage = NULL; - const gchar *account_name = NULL; - - g_return_if_fail (MCD_IS_ACCOUNT_MANAGER (account_manager)); - - storage = account_manager->priv->storage; - - if (account != NULL) - { - account_name = mcd_account_get_unique_name (account); - - DEBUG ("updating %s", account_name); - mcd_storage_commit (storage, account_name); - } - else - { - GStrv groups; - gsize n_accounts = 0; - - groups = mcd_storage_dup_accounts (storage, &n_accounts); - DEBUG ("updating all %" G_GSIZE_FORMAT " accounts", n_accounts); - - mcd_storage_commit (storage, NULL); - - g_strfreev (groups); - } - - if (callback != NULL) - callback (account_manager, NULL, user_data); -} - GHashTable * _mcd_account_manager_get_accounts (McdAccountManager *account_manager) { diff --git a/src/mcd-account-manager.h b/src/mcd-account-manager.h index 922f4896..f33c2949 100644 --- a/src/mcd-account-manager.h +++ b/src/mcd-account-manager.h @@ -66,15 +66,6 @@ McdAccountManager *mcd_account_manager_new ( TpDBusDaemon *mcd_account_manager_get_dbus_daemon (McdAccountManager *account_manager); -typedef void (McdAccountManagerWriteConfCb) (McdAccountManager *account_manager, - const GError *error, - gpointer user_data); - -void mcd_account_manager_write_conf_async (McdAccountManager *account_manager, - McdAccount *account, - McdAccountManagerWriteConfCb callback, - gpointer user_data); - McdAccount *mcd_account_manager_lookup_account (McdAccountManager *account_manager, const gchar *name); McdAccount *mcd_account_manager_lookup_account_by_path (McdAccountManager *account_manager, diff --git a/src/mcd-account-priv.h b/src/mcd-account-priv.h index 7225d3e5..c4991325 100644 --- a/src/mcd-account-priv.h +++ b/src/mcd-account-priv.h @@ -35,13 +35,6 @@ #include <telepathy-glib/proxy-subclass.h> -/* auto-generated stubs */ -#include "_gen/svc-Account_Interface_Conditions.h" -#include "_gen/svc-Account_Interface_External_Password_Storage.h" -#include "_gen/svc-Account_Interface_Hidden.h" - -#include "_gen/cli-Connection_Manager_Interface_Account_Storage.h" - G_GNUC_INTERNAL void _mcd_account_maybe_autoconnect (McdAccount *account); G_GNUC_INTERNAL void _mcd_account_connect (McdAccount *account, GHashTable *params); @@ -130,19 +123,6 @@ G_GNUC_INTERNAL McdChannel *_mcd_account_create_request ( gboolean use_existing, McdRequest **request_out, GError **error); -typedef struct _McdAccountConnectionContext McdAccountConnectionContext; - -G_GNUC_INTERNAL -McdAccountConnectionContext *_mcd_account_get_connection_context - (McdAccount *self); - -G_GNUC_INTERNAL -void _mcd_account_set_connection_context (McdAccount *self, - McdAccountConnectionContext *c); - -G_GNUC_INTERNAL void _mcd_account_connection_context_free - (McdAccountConnectionContext *c); - typedef void (*McdAccountDupParametersCb) (McdAccount *account, GHashTable *params, gpointer user_data); @@ -150,17 +130,10 @@ typedef void (*McdAccountDupParametersCb) (McdAccount *account, G_GNUC_INTERNAL G_GNUC_WARN_UNUSED_RESULT GHashTable *_mcd_account_dup_parameters (McdAccount *account); -extern const McdDBusProp account_conditions_properties[]; - -void account_conditions_iface_init (McSvcAccountInterfaceConditionsClass *iface, - gpointer iface_data); - G_GNUC_INTERNAL gboolean _mcd_account_check_request_real (McdAccount *account, GHashTable *request, GError **error); -G_GNUC_INTERNAL gboolean _mcd_account_get_always_on (McdAccount *self); - G_GNUC_INTERNAL void _mcd_account_set_changing_presence (McdAccount *self, gboolean value); G_GNUC_INTERNAL gboolean _mcd_account_set_enabled (McdAccount *account, @@ -172,8 +145,6 @@ G_GNUC_INTERNAL gboolean _mcd_account_set_enabled (McdAccount *account, G_GNUC_INTERNAL gboolean _mcd_account_presence_type_is_settable ( TpConnectionPresenceType type); -gboolean _mcd_account_is_hidden (McdAccount *account); - G_GNUC_INTERNAL gboolean _mcd_account_needs_dispatch (McdAccount *account); G_GNUC_INTERNAL void _mcd_account_reconnect (McdAccount *self, diff --git a/src/mcd-account.c b/src/mcd-account.c index f02c80d2..8009110e 100644 --- a/src/mcd-account.c +++ b/src/mcd-account.c @@ -34,7 +34,6 @@ #include <telepathy-glib/telepathy-glib-dbus.h> #include "mcd-account-priv.h" -#include "mcd-account-conditions.h" #include "mcd-account-manager-priv.h" #include "mcd-account-addressing.h" #include "mcd-connection-priv.h" @@ -45,11 +44,6 @@ #include "mcd-master-priv.h" #include "mcd-dbusprop.h" -#include "_gen/interfaces.h" -#include "_gen/enums.h" -#include "_gen/gtypes.h" -#include "_gen/cli-Connection_Manager_Interface_Account_Storage-body.h" - #define MC_OLD_AVATAR_FILENAME "avatar.bin" #define MCD_ACCOUNT_PRIV(account) (MCD_ACCOUNT (account)->priv) @@ -63,40 +57,22 @@ static void account_avatar_iface_init (TpSvcAccountInterfaceAvatarClass *iface, static void account_storage_iface_init ( TpSvcAccountInterfaceStorageClass *iface, gpointer iface_data); -static void account_hidden_iface_init ( - McSvcAccountInterfaceHiddenClass *iface, - gpointer iface_data); -static void account_external_password_storage_iface_init ( - McSvcAccountInterfaceExternalPasswordStorageClass *iface, - gpointer iface_data); static const McdDBusProp account_properties[]; static const McdDBusProp account_avatar_properties[]; static const McdDBusProp account_storage_properties[]; -static const McdDBusProp account_hidden_properties[]; -static const McdDBusProp account_external_password_storage_properties[]; static const McdInterfaceData account_interfaces[] = { MCD_IMPLEMENT_IFACE (tp_svc_account_get_type, account, TP_IFACE_ACCOUNT), MCD_IMPLEMENT_IFACE (tp_svc_account_interface_avatar_get_type, account_avatar, TP_IFACE_ACCOUNT_INTERFACE_AVATAR), - MCD_IMPLEMENT_IFACE (mc_svc_account_interface_conditions_get_type, - account_conditions, - MC_IFACE_ACCOUNT_INTERFACE_CONDITIONS), MCD_IMPLEMENT_IFACE (tp_svc_account_interface_storage_get_type, account_storage, TP_IFACE_ACCOUNT_INTERFACE_STORAGE), MCD_IMPLEMENT_IFACE (tp_svc_account_interface_addressing_get_type, account_addressing, TP_IFACE_ACCOUNT_INTERFACE_ADDRESSING), - MCD_IMPLEMENT_IFACE (mc_svc_account_interface_hidden_get_type, - account_hidden, - MC_IFACE_ACCOUNT_INTERFACE_HIDDEN), - MCD_IMPLEMENT_OPTIONAL_IFACE ( - mc_svc_account_interface_external_password_storage_get_type, - account_external_password_storage, - MC_IFACE_ACCOUNT_INTERFACE_EXTERNAL_PASSWORD_STORAGE), { NULL, } }; @@ -112,6 +88,11 @@ typedef struct { gpointer user_data; } McdOnlineRequestData; +typedef struct { + GHashTable *params; + gboolean user_initiated; +} McdAccountConnectionContext; + struct _McdAccountPrivate { gchar *unique_name; @@ -170,13 +151,11 @@ struct _McdAccountPrivate gboolean loaded; gboolean has_been_online; gboolean removed; - gboolean always_on; gboolean changing_presence; gboolean setting_avatar; gboolean waiting_for_initial_avatar; gboolean waiting_for_connectivity; - gboolean hidden; /* In addition to affecting dispatching, this flag also makes this * account bypass connectivity checks. */ gboolean always_dispatch; @@ -195,9 +174,8 @@ enum PROP_DBUS_DAEMON, PROP_CONNECTIVITY_MONITOR, PROP_STORAGE, + PROP_STORAGE_PLUGIN, PROP_NAME, - PROP_ALWAYS_ON, - PROP_HIDDEN, }; enum @@ -207,9 +185,19 @@ enum LAST_SIGNAL }; +static void +_mcd_account_connection_context_free (McdAccountConnectionContext *c) +{ + g_hash_table_unref (c->params); + g_free (c); +} + static guint _mcd_account_signals[LAST_SIGNAL] = { 0 }; static GQuark account_ready_quark = 0; +static void mcd_account_changed_property (McdAccount *account, + const gchar *key, const GValue *value); + GQuark mcd_account_error_quark (void) { @@ -330,6 +318,20 @@ mcd_account_loaded (McdAccount *account) g_return_if_fail (!account->priv->loaded); account->priv->loaded = TRUE; + if (account->priv->invalid_reason == NULL) + { + DEBUG ("account %s is now loaded and valid", + account->priv->unique_name); + } + else + { + DEBUG ("account %s is now loaded, but not valid: %s #%d: %s", + account->priv->unique_name, + g_quark_to_string (account->priv->invalid_reason->domain), + account->priv->invalid_reason->code, + account->priv->invalid_reason->message); + } + /* invoke all the callbacks */ g_object_ref (account); @@ -394,17 +396,17 @@ _mcd_account_set_parameter (McdAccount *account, const gchar *name, McdAccountPrivate *priv = account->priv; McdStorage *storage = priv->storage; const gchar *account_name = mcd_account_get_unique_name (account); - gboolean secret = mcd_account_parameter_is_secret (account, name); - mcd_storage_set_parameter (storage, account_name, name, value, secret); + mcd_storage_set_parameter (storage, account_name, name, value); } -static GType mc_param_type (const TpConnectionManagerParam *param); +static GType mc_param_type (const TpConnectionManagerParam *param, + const GVariantType **variant_type_out); /** * mcd_account_get_parameter: * @account: the #McdAccount. - * @name: the parameter name. + * @param: a connection manager parameter * @parameter: location at which to store the parameter's current value, or * %NULL if you don't actually care about the parameter's value. * @error: location at which to store an error if the parameter cannot be @@ -414,26 +416,28 @@ static GType mc_param_type (const TpConnectionManagerParam *param); * * Returns: %TRUE if the parameter could be retrieved; %FALSE otherwise */ -gboolean -mcd_account_get_parameter (McdAccount *account, const gchar *name, +static gboolean +mcd_account_get_parameter (McdAccount *account, + const TpConnectionManagerParam *param, GValue *parameter, GError **error) { - McdAccountPrivate *priv = account->priv; - const TpConnectionManagerParam *param; GType type; + const GVariantType *variant_type; + gboolean ret; + const gchar *name = tp_connection_manager_param_get_name (param); - param = mcd_manager_get_protocol_param (priv->manager, - priv->protocol_name, name); - type = mc_param_type (param); + type = mc_param_type (param, &variant_type); - return mcd_account_get_parameter_of_known_type (account, name, - type, parameter, error); + ret = mcd_account_get_parameter_of_known_type (account, name, + variant_type, type, parameter, error); + return ret; } gboolean mcd_account_get_parameter_of_known_type (McdAccount *account, const gchar *name, + const GVariantType *variant_type, GType type, GValue *parameter, GError **error) @@ -444,7 +448,8 @@ mcd_account_get_parameter_of_known_type (McdAccount *account, g_value_init (&tmp, type); - if (mcd_storage_get_parameter (storage, account_name, name, &tmp, error)) + if (mcd_storage_get_parameter (storage, account_name, name, variant_type, + &tmp, error)) { if (parameter != NULL) { @@ -462,169 +467,63 @@ mcd_account_get_parameter_of_known_type (McdAccount *account, return FALSE; } -typedef void (*CheckParametersCb) ( - McdAccount *account, - const GError *invalid_reason, - gpointer user_data); -static void mcd_account_check_parameters (McdAccount *account, - CheckParametersCb callback, gpointer user_data); - -static void -manager_ready_check_params_cb (McdAccount *account, - const GError *invalid_reason, - gpointer user_data) -{ - McdAccountPrivate *priv = account->priv; - - g_clear_error (&priv->invalid_reason); - if (invalid_reason != NULL) - { - priv->invalid_reason = g_error_copy (invalid_reason); - } - - mcd_account_loaded (account); -} - -static void -account_external_password_storage_get_accounts_cb (TpProxy *cm, - const GValue *value, - const GError *in_error, - gpointer user_data, - GObject *self) -{ - McdAccount *account = MCD_ACCOUNT (self); - const char *account_id = user_data; - GHashTable *map, *props; - - if (in_error != NULL) - { - DEBUG ("Failed to get Account property: %s", in_error->message); - return; - } - - g_return_if_fail (G_VALUE_HOLDS (value, MC_HASH_TYPE_ACCOUNT_FLAGS_MAP)); - - map = g_value_get_boxed (value); - - account->priv->password_saved = - GPOINTER_TO_UINT (g_hash_table_lookup (map, account_id)) & - MC_ACCOUNT_FLAG_CREDENTIALS_STORED; - - DEBUG ("PasswordSaved = %u", account->priv->password_saved); - - /* emit the changed signal */ - props = tp_asv_new ( - "PasswordSaved", G_TYPE_BOOLEAN, account->priv->password_saved, - NULL); - - tp_svc_dbus_properties_emit_properties_changed (account, - MC_IFACE_ACCOUNT_INTERFACE_EXTERNAL_PASSWORD_STORAGE, - props, - NULL); - - g_hash_table_unref (props); -} - -static void -account_setup_identify_account_cb (TpProxy *protocol, - const char *account_id, - const GError *in_error, - gpointer user_data, - GObject *self) -{ - McdAccount *account = MCD_ACCOUNT (self); - TpConnectionManager *cm = mcd_account_get_cm (account); - - if (in_error != NULL) - { - DEBUG ("Error identifying account: %s", in_error->message); - return; - } - - DEBUG ("Identified account as %s", account_id); - - /* look up the current value of the CM.I.AS.Accounts property - * and monitor future changes */ - tp_cli_dbus_properties_call_get (cm, -1, - MC_IFACE_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE, - "Accounts", - account_external_password_storage_get_accounts_cb, - g_strdup (account_id), g_free, G_OBJECT (account)); -} - -static void -account_external_password_storage_properties_changed_cb (TpProxy *cm, - const char *iface, - GHashTable *changed_properties, - const char **invalidated_properties, - gpointer user_data, - GObject *self) -{ - McdAccount *account = MCD_ACCOUNT (self); - TpProtocol *protocol = tp_connection_manager_get_protocol_object ( - TP_CONNECTION_MANAGER (cm), account->priv->protocol_name); - GHashTable *params; - - if (tp_strdiff (iface, - MC_IFACE_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE)) - return; - - /* look up account identity so we can look up our value in - * the Accounts map */ - params = _mcd_account_dup_parameters (account); - tp_cli_protocol_call_identify_account (protocol, -1, params, - account_setup_identify_account_cb, - NULL, NULL, G_OBJECT (account)); - - g_hash_table_unref (params); -} +static gboolean mcd_account_check_parameters (McdAccount *account, + GError **invalid_reason); static void on_manager_ready (McdManager *manager, const GError *error, gpointer user_data) { McdAccount *account = MCD_ACCOUNT (user_data); + GError *invalid_reason = NULL; + TpProtocol *protocol; if (error) { DEBUG ("got error: %s", error->message); - mcd_account_loaded (account); + } + else if (!mcd_account_check_parameters (account, &invalid_reason)) + { + g_clear_error (&account->priv->invalid_reason); + account->priv->invalid_reason = invalid_reason; } else { - TpConnectionManager *cm = mcd_manager_get_tp_proxy (manager); + g_clear_error (&account->priv->invalid_reason); + } - mcd_account_check_parameters (account, manager_ready_check_params_cb, - NULL); + protocol = _mcd_manager_dup_protocol (account->priv->manager, + account->priv->protocol_name); - /* determine if we support Acct.I.ExternalPasswordStorage */ - if (tp_proxy_has_interface_by_id (cm, - MC_IFACE_QUARK_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE)) + if (protocol != NULL) + { + if (mcd_storage_maybe_migrate_parameters ( + account->priv->storage, + account->priv->unique_name, + protocol)) { - TpProtocol *protocol = tp_connection_manager_get_protocol_object ( - cm, account->priv->protocol_name); - GHashTable *params; - - DEBUG ("CM %s has CM.I.AccountStorage iface", - mcd_manager_get_name (manager)); + GHashTable *params = _mcd_account_dup_parameters (account); - mcd_dbus_activate_optional_interface ( - TP_SVC_DBUS_PROPERTIES (account), - MC_TYPE_SVC_ACCOUNT_INTERFACE_EXTERNAL_PASSWORD_STORAGE); + if (params != NULL) + { + GValue value = G_VALUE_INIT; - /* look up account identity so we can look up our value in - * the Accounts map */ - params = _mcd_account_dup_parameters (account); - tp_cli_protocol_call_identify_account (protocol, -1, params, - account_setup_identify_account_cb, - NULL, NULL, G_OBJECT (account)); + g_value_init (&value, TP_HASH_TYPE_STRING_VARIANT_MAP); + g_value_take_boxed (&value, params); + mcd_account_changed_property (account, "Parameters", &value); + g_value_unset (&value); + } + else + { + WARNING ("somehow managed to migrate parameters without " + "being able to emit change notification"); + } + } - tp_cli_dbus_properties_connect_to_properties_changed (cm, - account_external_password_storage_properties_changed_cb, - NULL, NULL, G_OBJECT (account), NULL); - g_hash_table_unref (params); - } + g_object_unref (protocol); } + + mcd_account_loaded (account); } static gboolean @@ -666,31 +565,8 @@ get_old_account_data_path (McdAccountPrivate *priv) return g_build_filename (base, priv->unique_name, NULL); } -static void -account_delete_identify_account_cb (TpProxy *protocol, - const char *account_id, - const GError *in_error, - gpointer user_data, - GObject *self) -{ - McdAccount *account = MCD_ACCOUNT (self); - TpConnectionManager *cm = mcd_account_get_cm (account); - - if (in_error != NULL) - { - DEBUG ("Error identifying account: %s", in_error->message); - } - else - { - DEBUG ("Identified account as %s", account_id); - - mc_cli_connection_manager_interface_account_storage_call_remove_account ( - cm, -1, account_id, - NULL, NULL, NULL, NULL); - } - - g_object_unref (account); -} +static TpStorageRestrictionFlags mcd_account_get_storage_restrictions ( + McdAccount *account); static void unregister_dbus_service (McdAccount *self) @@ -709,48 +585,48 @@ unregister_dbus_service (McdAccount *self) } void -mcd_account_delete (McdAccount *account, - McdAccountDeleteCb callback, - gpointer user_data) +mcd_account_delete_async (McdAccount *account, + McdDBusPropSetFlags flags, + GAsyncReadyCallback callback, + gpointer user_data) { McdAccountPrivate *priv = account->priv; gchar *data_dir_str; GError *error = NULL; const gchar *name = mcd_account_get_unique_name (account); - TpConnectionManager *cm = mcd_account_get_cm (account); - - /* if the CM implements CM.I.AccountStorage, we need to tell the CM - * to forget any account credentials it knows */ - if (tp_proxy_has_interface_by_id (cm, - MC_IFACE_QUARK_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE)) - { - TpProtocol *protocol; - GHashTable *params; - - /* identify the account */ - protocol = tp_connection_manager_get_protocol_object (cm, - account->priv->protocol_name); - params = _mcd_account_dup_parameters (account); - - tp_cli_protocol_call_identify_account (protocol, -1, params, - account_delete_identify_account_cb, - NULL, NULL, g_object_ref (account)); - - g_hash_table_unref (params); + GTask *task; + + task = g_task_new (account, NULL, callback, user_data); + + /* We don't really have a flag for "cannot delete accounts" yet, but + * it seems reasonable that if you can't disable it or put it + * offline, you shouldn't be able to delete it. Also, we're going + * to try to disable it in a moment anyway. */ + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0 && + (mcd_account_get_storage_restrictions (account) & + (TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_ENABLED | + TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PRESENCE)) != 0) + { + g_task_return_new_error (task, TP_ERROR, TP_ERROR_PERMISSION_DENIED, + "Storage plugin for %s does not allow deleting it", + name); + g_object_unref (task); + return; } /* got to turn the account off before removing it, otherwise we can * * end up with an orphaned CM holding the account online */ if (!_mcd_account_set_enabled (account, FALSE, FALSE, - MCD_DBUS_PROP_SET_FLAG_NONE, &error)) + flags, &error)) { g_warning ("could not disable account %s (%s)", name, error->message); - callback (account, error, user_data); - g_error_free (error); + g_task_return_error (task, error); + g_object_unref (task); return; } - mcd_storage_delete_account (priv->storage, name); + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0) + mcd_storage_delete_account (priv->storage, name); data_dir_str = get_old_account_data_path (priv); @@ -777,27 +653,27 @@ mcd_account_delete (McdAccount *account, g_free (data_dir_str); } - mcd_storage_commit (priv->storage, name); - - /* The callback may drop the latest ref on @account so make sure it stays - * alive while we still need it. */ - g_object_ref (account); - - if (callback != NULL) - callback (account, NULL, user_data); - - /* If the account was not removed via the DBus Account interface code * - * path and something is holding a ref to it so it does not get disposed, * - * then this signal may not get fired, so we make sure it _does_ here */ if (!priv->removed) { - DEBUG ("Forcing Account.Removed for %s", name); + DEBUG ("emitting Account.Removed for %s", name); priv->removed = TRUE; tp_svc_account_emit_removed (account); } unregister_dbus_service (account); - g_object_unref (account); + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +gboolean +mcd_account_delete_finish (McdAccount *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); } void @@ -805,10 +681,29 @@ _mcd_account_load (McdAccount *account, McdAccountLoadCb callback, gpointer user_data) { if (account->priv->loaded) + { + if (account->priv->invalid_reason == NULL) + { + DEBUG ("account %s already loaded and valid", + account->priv->unique_name); + } + else + { + DEBUG ("account %s already loaded, but not valid: %s #%d: %s", + account->priv->unique_name, + g_quark_to_string (account->priv->invalid_reason->domain), + account->priv->invalid_reason->code, + account->priv->invalid_reason->message); + } + callback (account, NULL, user_data); + } else + { + DEBUG ("account %s not yet loaded", account->priv->unique_name); _mcd_object_call_when_ready (account, account_ready_quark, (McdReadyCb)callback, user_data); + } } static void @@ -820,9 +715,6 @@ on_connection_abort (McdConnection *connection, McdAccount *account) _mcd_account_set_connection (account, NULL); } -static void mcd_account_changed_property (McdAccount *account, - const gchar *key, const GValue *value); - static void mcd_account_request_presence_int (McdAccount *account, TpConnectionPresenceType type, @@ -1104,7 +996,8 @@ mcd_account_get_string_val (McdAccount *account, const gchar *key, g_value_init (value, G_TYPE_STRING); - if (!mcd_storage_get_attribute (priv->storage, name, key, value, NULL)) + if (!mcd_storage_get_attribute (priv->storage, name, key, + G_VARIANT_TYPE_STRING, value, NULL)) { g_value_set_static_string (value, NULL); } @@ -1195,19 +1088,21 @@ _mcd_account_set_enabled (McdAccount *account, { McdAccountPrivate *priv = account->priv; - if (priv->always_on && !enabled) - { - g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, - "Account %s cannot be disabled", - priv->unique_name); - return FALSE; - } - if (priv->enabled != enabled) { GValue value = G_VALUE_INIT; const gchar *name = mcd_account_get_unique_name (account); + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0 && + (mcd_account_get_storage_restrictions (account) & + TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_ENABLED) != 0) + { + g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, + "Storage plugin for %s does not allow changing " + "its Enabled property", name); + return FALSE; + } + if (!enabled && priv->connection != NULL) _mcd_connection_request_presence (priv->connection, TP_CONNECTION_PRESENCE_TYPE_OFFLINE, @@ -1735,6 +1630,16 @@ set_automatic_presence (TpSvcDBusProperties *self, return FALSE; } + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0 && + (mcd_account_get_storage_restrictions (account) & + TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PRESENCE) != 0) + { + g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, + "Storage plugin for %s does not allow changing " + "its presence", priv->unique_name); + return FALSE; + } + DEBUG ("setting automatic presence: %d, %s, %s", type, status, message); if (priv->auto_presence_type != type) @@ -1816,18 +1721,24 @@ set_connect_automatically (TpSvcDBusProperties *self, connect_automatically = g_value_get_boolean (value); - if (priv->always_on && !connect_automatically) - { - g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, - "Account %s always connects automatically", - priv->unique_name); - return FALSE; - } - if (priv->connect_automatically != connect_automatically) { const gchar *account_name = mcd_account_get_unique_name (account); + /* We use CANNOT_SET_PRESENCE to control access to + * ConnectAutomatically, because RequestedPresence is not stored, + * but it can be derived from ConnectAutomatically and + * AutomaticPresence */ + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0 && + (mcd_account_get_storage_restrictions (account) & + TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PRESENCE) != 0) + { + g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, + "Storage plugin for %s does not allow changing " + "its ConnectAutomatically property", account_name); + return FALSE; + } + if (!(flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE)) { mcd_storage_set_attribute (priv->storage, account_name, @@ -1969,10 +1880,13 @@ set_requested_presence (TpSvcDBusProperties *self, status = g_value_get_string (va->values + 1); message = g_value_get_string (va->values + 2); - if (priv->always_on && !_presence_type_is_online (type)) + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0 && + (mcd_account_get_storage_restrictions (account) & + TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PRESENCE) != 0) { g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, - "Account %s cannot be taken offline", priv->unique_name); + "Storage plugin for %s does not allow changing " + "its presence", priv->unique_name); return FALSE; } @@ -2078,58 +1992,16 @@ get_supersedes (TpSvcDBusProperties *svc, g_value_set_boxed (value, self->priv->supersedes); } -static McpAccountStorage * -get_storage_plugin (McdAccount *account) -{ - McdAccountPrivate *priv = account->priv; - const gchar *account_name = mcd_account_get_unique_name (account); - - if (priv->storage_plugin != NULL) - return priv->storage_plugin; - - priv->storage_plugin = mcd_storage_get_plugin (priv->storage, account_name); - - if (priv->storage_plugin != NULL) - g_object_ref (priv->storage_plugin); - - return priv->storage_plugin; -} - static void get_storage_provider (TpSvcDBusProperties *self, const gchar *name, GValue *value) { McdAccount *account = MCD_ACCOUNT (self); - McpAccountStorage *storage_plugin = get_storage_plugin (account); g_value_init (value, G_TYPE_STRING); - if (storage_plugin != NULL) - g_value_set_string (value, mcp_account_storage_provider (storage_plugin)); - else - g_value_set_static_string (value, ""); -} - -static gboolean -set_storage_provider (TpSvcDBusProperties *self, - const gchar *name, - const GValue *value, - McdDBusPropSetFlags flags, - GError **error) -{ - McdAccount *account = MCD_ACCOUNT (self); - McpAccountStorage *storage_plugin = get_storage_plugin (account); - const gchar *current_provider = mcp_account_storage_provider (storage_plugin); - - if (!G_VALUE_HOLDS_STRING (value) || - tp_strdiff (g_value_get_string (value), current_provider)) - { - g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "Cannot change provider, it is defined at account creation only"); - return FALSE; - } - - return TRUE; + g_value_set_string (value, + mcp_account_storage_provider (account->priv->storage_plugin)); } static void @@ -2138,22 +2010,13 @@ get_storage_identifier (TpSvcDBusProperties *self, { McdAccount *account = MCD_ACCOUNT (self); - McpAccountStorage *storage_plugin = get_storage_plugin (account); GValue identifier = G_VALUE_INIT; g_value_init (value, G_TYPE_VALUE); - if (storage_plugin != NULL) - { - mcp_account_storage_get_identifier ( - storage_plugin, account->priv->unique_name, &identifier); - } - else - { - g_value_init (&identifier, G_TYPE_UINT); - - g_value_set_uint (&identifier, 0); - } + mcp_account_storage_get_identifier ( + account->priv->storage_plugin, account->priv->unique_name, + &identifier); g_value_set_boxed (value, &identifier); @@ -2166,35 +2029,29 @@ get_storage_specific_info (TpSvcDBusProperties *self, { GHashTable *storage_specific_info; McdAccount *account = MCD_ACCOUNT (self); - McpAccountStorage *storage_plugin = get_storage_plugin (account); g_value_init (value, TP_HASH_TYPE_STRING_VARIANT_MAP); - if (storage_plugin != NULL) - storage_specific_info = mcp_account_storage_get_additional_info ( - storage_plugin, account->priv->unique_name); - else - storage_specific_info = g_hash_table_new (g_str_hash, g_str_equal); + storage_specific_info = mcp_account_storage_get_additional_info ( + account->priv->storage_plugin, account->priv->unique_name); g_value_take_boxed (value, storage_specific_info); } +static TpStorageRestrictionFlags +mcd_account_get_storage_restrictions (McdAccount *self) +{ + return mcp_account_storage_get_restrictions (self->priv->storage_plugin, + self->priv->unique_name); +} + static void get_storage_restrictions (TpSvcDBusProperties *self, const gchar *name, GValue *value) { - TpStorageRestrictionFlags flags; - McdAccount *account = MCD_ACCOUNT (self); - McpAccountStorage *storage_plugin = get_storage_plugin (account); - g_value_init (value, G_TYPE_UINT); - - g_return_if_fail (storage_plugin != NULL); - - flags = mcp_account_storage_get_restrictions (storage_plugin, - account->priv->unique_name); - - g_value_set_uint (value, flags); + g_value_set_uint (value, + mcd_account_get_storage_restrictions (MCD_ACCOUNT (self))); } static const McdDBusProp account_properties[] = { @@ -2228,7 +2085,7 @@ static const McdDBusProp account_avatar_properties[] = { }; static const McdDBusProp account_storage_properties[] = { - { "StorageProvider", set_storage_provider, get_storage_provider }, + { "StorageProvider", NULL, get_storage_provider }, { "StorageIdentifier", NULL, get_storage_identifier }, { "StorageSpecificInformation", NULL, get_storage_specific_info }, { "StorageRestrictions", NULL, get_storage_restrictions }, @@ -2248,173 +2105,6 @@ account_storage_iface_init (TpSvcAccountInterfaceStorageClass *iface, } static void -get_hidden (TpSvcDBusProperties *self, - const gchar *name, GValue *value) -{ - g_value_init (value, G_TYPE_BOOLEAN); - g_object_get_property (G_OBJECT (self), "hidden", value); -} - -static gboolean -set_hidden (TpSvcDBusProperties *self, - const gchar *name, - const GValue *value, - McdDBusPropSetFlags flags, - GError **error) -{ - McdAccount *account = MCD_ACCOUNT (self); - McdAccountPrivate *priv = account->priv; - const gchar *account_name = mcd_account_get_unique_name (account); - - if (!G_VALUE_HOLDS_BOOLEAN (value)) - { - g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "Hidden must be set to a boolean, not a %s", - G_VALUE_TYPE_NAME (value)); - return FALSE; - } - - /* Technically this property is immutable after the account's been created, - * but currently it's not easy for this code to tell whether or not this is - * a create-time property. It would probably be better if the create-time - * properties were passed into us as a construct-time GObject property. But - * that's a job for another month. - * - * So for now we check whether the value has changed, and violate the spec - * by making this property mutable (at least with the keyfile backend). - */ - if (mcd_storage_set_attribute (priv->storage, account_name, - MC_ACCOUNTS_KEY_HIDDEN, value)) - { - mcd_storage_commit (priv->storage, account_name); - mcd_account_changed_property (account, MC_ACCOUNTS_KEY_HIDDEN, value); - g_object_set_property (G_OBJECT (self), "hidden", value); - } - - return TRUE; -} - -static const McdDBusProp account_hidden_properties[] = { - { "Hidden", set_hidden, get_hidden }, - { 0 }, -}; - -static void -account_hidden_iface_init ( - McSvcAccountInterfaceHiddenClass *iface, - gpointer iface_data) -{ - /* wow, it's pretty crap that I need this. */ -} - -static void -get_password_saved (TpSvcDBusProperties *self, - const gchar *name, - GValue *value) -{ - McdAccount *account = MCD_ACCOUNT (self); - - g_assert_cmpstr (name, ==, "PasswordSaved"); - - g_value_init (value, G_TYPE_BOOLEAN); - g_value_set_boolean (value, account->priv->password_saved); -} - -static const McdDBusProp account_external_password_storage_properties[] = { - { "PasswordSaved", NULL, get_password_saved }, - { 0 }, -}; - -static void -account_external_password_storage_forget_credentials_cb (TpProxy *cm, - const GError *in_error, - gpointer user_data, - GObject *self) -{ - DBusGMethodInvocation *context = user_data; - - if (in_error != NULL) - { - dbus_g_method_return_error (context, in_error); - return; - } - - mc_svc_account_interface_external_password_storage_return_from_forget_password (context); -} - -static void -account_external_password_storage_identify_account_cb (TpProxy *protocol, - const char *account_id, - const GError *in_error, - gpointer user_data, - GObject *self) -{ - McdAccount *account = MCD_ACCOUNT (self); - DBusGMethodInvocation *context = user_data; - TpConnectionManager *cm = mcd_account_get_cm (account); - - if (in_error != NULL) - { - dbus_g_method_return_error (context, in_error); - return; - } - - DEBUG ("Identified account as %s", account_id); - - mc_cli_connection_manager_interface_account_storage_call_forget_credentials ( - cm, -1, account_id, - account_external_password_storage_forget_credentials_cb, - context, NULL, self); -} - -static void -account_external_password_storage_forget_password ( - McSvcAccountInterfaceExternalPasswordStorage *self, - DBusGMethodInvocation *context) -{ - McdAccount *account = MCD_ACCOUNT (self); - TpConnectionManager *cm = mcd_account_get_cm (account); - TpProtocol *protocol; - GHashTable *params; - - /* do we support the interface */ - if (!tp_proxy_has_interface_by_id (cm, - MC_IFACE_QUARK_CONNECTION_MANAGER_INTERFACE_ACCOUNT_STORAGE)) - { - GError *error = g_error_new (TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, - "CM for this Account does not implement AccountStorage iface"); - - dbus_g_method_return_error (context, error); - g_error_free (error); - - return; - } - - /* identify the account */ - protocol = tp_connection_manager_get_protocol_object (cm, - account->priv->protocol_name); - params = _mcd_account_dup_parameters (account); - - tp_cli_protocol_call_identify_account (protocol, -1, params, - account_external_password_storage_identify_account_cb, - context, NULL, G_OBJECT (self)); - - g_hash_table_unref (params); -} - -static void -account_external_password_storage_iface_init ( - McSvcAccountInterfaceExternalPasswordStorageClass *iface, - gpointer iface_data) -{ -#define IMPLEMENT(x) \ - mc_svc_account_interface_external_password_storage_implement_##x (\ - iface, account_external_password_storage_##x) - IMPLEMENT (forget_password); -#undef IMPLEMENT -} - -static void properties_iface_init (TpSvcDBusPropertiesClass *iface, gpointer iface_data) { #define IMPLEMENT(x) tp_svc_dbus_properties_implement_##x (\ @@ -2426,10 +2116,13 @@ properties_iface_init (TpSvcDBusPropertiesClass *iface, gpointer iface_data) } static GType -mc_param_type (const TpConnectionManagerParam *param) +mc_param_type (const TpConnectionManagerParam *param, + const GVariantType **variant_type_out) { const gchar *dbus_signature; + *variant_type_out = NULL; + if (G_UNLIKELY (param == NULL)) return G_TYPE_INVALID; @@ -2441,37 +2134,49 @@ mc_param_type (const TpConnectionManagerParam *param) switch (dbus_signature[0]) { case DBUS_TYPE_STRING: + *variant_type_out = G_VARIANT_TYPE_STRING; return G_TYPE_STRING; case DBUS_TYPE_BYTE: + *variant_type_out = G_VARIANT_TYPE_BYTE; return G_TYPE_UCHAR; case DBUS_TYPE_INT16: case DBUS_TYPE_INT32: + *variant_type_out = G_VARIANT_TYPE_INT32; return G_TYPE_INT; case DBUS_TYPE_UINT16: case DBUS_TYPE_UINT32: + *variant_type_out = G_VARIANT_TYPE_UINT32; return G_TYPE_UINT; case DBUS_TYPE_BOOLEAN: + *variant_type_out = G_VARIANT_TYPE_BOOLEAN; return G_TYPE_BOOLEAN; case DBUS_TYPE_DOUBLE: + *variant_type_out = G_VARIANT_TYPE_DOUBLE; return G_TYPE_DOUBLE; case DBUS_TYPE_OBJECT_PATH: + *variant_type_out = G_VARIANT_TYPE_OBJECT_PATH; return DBUS_TYPE_G_OBJECT_PATH; case DBUS_TYPE_INT64: + *variant_type_out = G_VARIANT_TYPE_INT64; return G_TYPE_INT64; case DBUS_TYPE_UINT64: + *variant_type_out = G_VARIANT_TYPE_UINT64; return G_TYPE_UINT64; case DBUS_TYPE_ARRAY: if (dbus_signature[1] == DBUS_TYPE_STRING) + { + *variant_type_out = G_VARIANT_TYPE_STRING_ARRAY; return G_TYPE_STRV; + } /* other array types are not supported: * fall through the default case */ default: @@ -2488,25 +2193,24 @@ typedef struct } RemoveMethodData; static void -account_remove_delete_cb (McdAccount *account, const GError *error, +account_remove_delete_cb (GObject *source, + GAsyncResult *res, gpointer user_data) { RemoveMethodData *data = (RemoveMethodData *) user_data; + GError *error = NULL; - if (error != NULL) + if (!mcd_account_delete_finish (MCD_ACCOUNT (source), res, &error)) { dbus_g_method_return_error (data->context, (GError *) error); + g_error_free (error); return; } - if (!data->self->priv->removed) - { - data->self->priv->removed = TRUE; - tp_svc_account_emit_removed (data->self); - } + /* mcd_account_delete() is meant to have deleted it */ + g_warn_if_fail (data->self->priv->removed); tp_svc_account_return_from_remove (data->context); - g_slice_free (RemoveMethodData, data); } @@ -2521,7 +2225,8 @@ account_remove (TpSvcAccount *svc, DBusGMethodInvocation *context) data->context = context; DEBUG ("called"); - mcd_account_delete (self, account_remove_delete_cb, data); + mcd_account_delete_async (self, MCD_DBUS_PROP_SET_FLAG_NONE, + account_remove_delete_cb, data); } /* @@ -2548,11 +2253,13 @@ mcd_account_altered_by_plugin (McdAccount *account, const McdDBusProp *prop = NULL; GValue value = G_VALUE_INIT; GError *error = NULL; + const GVariantType *variant_type = NULL; DEBUG ("%s", name); if (tp_strdiff (name, "Parameters") && - !mcd_storage_init_value_for_attribute (&value, name)) + !mcd_storage_init_value_for_attribute (&value, name, + &variant_type)) { WARNING ("plugin wants to alter %s but I don't know what " "type that ought to be", name); @@ -2565,7 +2272,8 @@ mcd_account_altered_by_plugin (McdAccount *account, } else if (!mcd_storage_get_attribute (account->priv->storage, account->priv->unique_name, - name, &value, &error)) + name, variant_type, &value, + &error)) { WARNING ("cannot get new value of %s: %s", name, error->message); g_error_free (error); @@ -2615,25 +2323,22 @@ mcd_account_altered_by_plugin (McdAccount *account, } -static void +static gboolean mcd_account_check_parameters (McdAccount *account, - CheckParametersCb callback, - gpointer user_data) + GError **error) { McdAccountPrivate *priv = account->priv; TpProtocol *protocol; GList *params = NULL; GList *iter; - GError *error = NULL; - - g_return_if_fail (callback != NULL); + GError *inner_error = NULL; DEBUG ("called for %s", priv->unique_name); protocol = _mcd_manager_dup_protocol (priv->manager, priv->protocol_name); if (protocol == NULL) { - g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + g_set_error (&inner_error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "CM '%s' doesn't implement protocol '%s'", priv->manager_name, priv->protocol_name); goto out; @@ -2644,40 +2349,36 @@ mcd_account_check_parameters (McdAccount *account, for (iter = params; iter != NULL; iter = iter->next) { TpConnectionManagerParam *param = iter->data; - const gchar *param_name = tp_connection_manager_param_get_name (param); if (!tp_connection_manager_param_is_required ((param))) continue; - if (!mcd_account_get_parameter (account, param_name, NULL, NULL)) + if (!mcd_account_get_parameter (account, param, NULL, NULL)) { - g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "missing required parameter '%s'", param_name); + g_set_error (&inner_error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "missing required parameter '%s'", + tp_connection_manager_param_get_name (param)); goto out; } } out: - if (error != NULL) + if (inner_error != NULL) { - DEBUG ("%s", error->message); + DEBUG ("%s", inner_error->message); } - callback (account, error, user_data); - g_clear_error (&error); g_list_free_full (params, (GDestroyNotify) tp_connection_manager_param_free); g_clear_object (&protocol); -} -static void -set_parameters_maybe_autoconnect_cb (McdAccount *account, - const GError *invalid_reason, - gpointer user_data G_GNUC_UNUSED) -{ - /* Strictly speaking this doesn't need to be called unless invalid_reason - * is NULL, but calling it in all cases gives us clearer debug output */ - _mcd_account_maybe_autoconnect (account); + if (inner_error != NULL) + { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; } static void @@ -2715,8 +2416,11 @@ apply_parameter_updates (McdAccount *account, } } - mcd_account_check_validity (account, - set_parameters_maybe_autoconnect_cb, NULL); + mcd_account_check_validity (account, NULL); + + /* Strictly speaking this doesn't need to be called if not valid, + * but calling it in all cases gives us clearer debug output */ + _mcd_account_maybe_autoconnect (account); } static void @@ -2754,6 +2458,7 @@ check_one_parameter_update (McdAccount *account, const TpConnectionManagerParam *param = tp_protocol_get_param (protocol, name); GType type; + const GVariantType *variant_type; if (param == NULL) { @@ -2763,15 +2468,18 @@ check_one_parameter_update (McdAccount *account, return FALSE; } - type = mc_param_type (param); + type = mc_param_type (param, &variant_type); if (G_VALUE_TYPE (new_value) != type) { /* FIXME: use D-Bus type names, not GType names. */ g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "parameter '%s' must be of type %s, not %s", + "parameter '%s' must be of type %s ('%.*s'), not %s", tp_connection_manager_param_get_name (param), - g_type_name (type), G_VALUE_TYPE_NAME (new_value)); + g_type_name (type), + (int) g_variant_type_get_string_length (variant_type), + g_variant_type_peek_string (variant_type), + G_VALUE_TYPE_NAME (new_value)); return FALSE; } @@ -2783,7 +2491,7 @@ check_one_parameter_update (McdAccount *account, /* Check if the parameter's current value (or its default, if it has * one and it's not set to anything) matches the new value. */ - if (mcd_account_get_parameter (account, tp_connection_manager_param_get_name (param), + if (mcd_account_get_parameter (account, param, ¤t_value, NULL) || tp_connection_manager_param_get_default (param, ¤t_value)) { @@ -2826,7 +2534,7 @@ check_one_parameter_unset (McdAccount *account, { GValue current_value = G_VALUE_INIT; - if (mcd_account_get_parameter (account, tp_connection_manager_param_get_name (param), + if (mcd_account_get_parameter (account, param, ¤t_value, NULL)) { /* There's an existing value; let's see if it's the same as the @@ -3013,6 +2721,11 @@ void _mcd_account_reconnect (McdAccount *self, gboolean user_initiated) { + DEBUG ("%s", mcd_account_get_unique_name (self)); + + /* If the account is disabled, invalid or has offline requested presence, + * disconnecting should be a no-op, so we keep this before checking + * whether we want to. */ /* FIXME: this isn't quite right. If we've just called RequestConnection * (possibly with out of date parameters) but we haven't got a Connection * back from the CM yet, the old parameters will still be used, I think @@ -3020,32 +2733,28 @@ _mcd_account_reconnect (McdAccount *self, if (self->priv->connection) mcd_connection_close (self->priv->connection, NULL); - _mcd_account_connection_begin (self, user_initiated); -} - -static void -account_reconnect (TpSvcAccount *service, - DBusGMethodInvocation *context) -{ - McdAccount *self = MCD_ACCOUNT (service); - McdAccountPrivate *priv = self->priv; - - DEBUG ("%s", mcd_account_get_unique_name (self)); - /* if we can't, or don't want to, connect this method is a no-op */ - if (!priv->enabled || + if (!self->priv->enabled || !mcd_account_is_valid (self) || - priv->req_presence_type == TP_CONNECTION_PRESENCE_TYPE_OFFLINE) + self->priv->req_presence_type == TP_CONNECTION_PRESENCE_TYPE_OFFLINE) { DEBUG ("doing nothing (enabled=%c, valid=%c and " "combined presence=%i)", self->priv->enabled ? 'T' : 'F', mcd_account_is_valid (self) ? 'T' : 'F', self->priv->req_presence_type); - tp_svc_account_return_from_reconnect (context); return; } + _mcd_account_connection_begin (self, user_initiated); +} + +static void +account_reconnect (TpSvcAccount *service, + DBusGMethodInvocation *context) +{ + McdAccount *self = MCD_ACCOUNT (service); + /* Reconnect() counts as user-initiated */ _mcd_account_reconnect (self, TRUE); @@ -3341,20 +3050,14 @@ mcd_account_setup (McdAccount *account) priv->object_path = g_strconcat (TP_ACCOUNT_OBJECT_PATH_BASE, name, NULL); - if (!priv->always_on) - { - priv->enabled = - mcd_storage_get_boolean (storage, name, MC_ACCOUNTS_KEY_ENABLED); + priv->enabled = mcd_storage_get_boolean (storage, name, + MC_ACCOUNTS_KEY_ENABLED); - priv->connect_automatically = - mcd_storage_get_boolean (storage, name, - MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY); - } + priv->connect_automatically = mcd_storage_get_boolean (storage, name, + MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY); priv->has_been_online = mcd_storage_get_boolean (storage, name, MC_ACCOUNTS_KEY_HAS_BEEN_ONLINE); - priv->hidden = - mcd_storage_get_boolean (storage, name, MC_ACCOUNTS_KEY_HIDDEN); /* special case flag (for ring accounts, so far) */ priv->always_dispatch = @@ -3366,7 +3069,8 @@ mcd_account_setup (McdAccount *account) g_free (priv->auto_presence_message); if (mcd_storage_get_attribute (storage, name, - MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE, &value, + MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE, + G_VARIANT_TYPE ("(uss)"), &value, NULL)) { GValueArray *va = g_value_get_boxed (&value); @@ -3438,7 +3142,9 @@ mcd_account_setup (McdAccount *account) g_ptr_array_unref (priv->supersedes); if (mcd_storage_get_attribute (storage, name, - MC_ACCOUNTS_KEY_SUPERSEDES, &value, NULL)) + MC_ACCOUNTS_KEY_SUPERSEDES, + G_VARIANT_TYPE_OBJECT_PATH_ARRAY, + &value, NULL)) { priv->supersedes = g_value_dup_boxed (&value); } @@ -3486,6 +3192,11 @@ set_property (GObject *obj, guint prop_id, priv->storage = g_value_dup_object (val); break; + case PROP_STORAGE_PLUGIN: + g_assert (priv->storage_plugin == NULL); + priv->storage_plugin = g_value_dup_object (val); + break; + case PROP_DBUS_DAEMON: g_assert (priv->dbus_daemon == NULL); priv->dbus_daemon = g_value_dup_object (val); @@ -3501,22 +3212,6 @@ set_property (GObject *obj, guint prop_id, priv->unique_name = g_value_dup_string (val); break; - case PROP_ALWAYS_ON: - priv->always_on = g_value_get_boolean (val); - - if (priv->always_on) - { - priv->enabled = TRUE; - priv->connect_automatically = TRUE; - priv->req_presence_type = priv->auto_presence_type; - priv->req_presence_status = g_strdup (priv->auto_presence_status); - priv->req_presence_message = g_strdup (priv->auto_presence_message); - } - - break; - case PROP_HIDDEN: - priv->hidden = g_value_get_boolean (val); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; @@ -3542,9 +3237,6 @@ get_property (GObject *obj, guint prop_id, case PROP_NAME: g_value_set_string (val, priv->unique_name); break; - case PROP_HIDDEN: - g_value_set_boolean (val, priv->hidden); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; @@ -3591,6 +3283,9 @@ _mcd_account_dispose (GObject *object) if (!self->priv->removed) { + /* this can happen in certain account-creation error paths, + * as far as I can see */ + DEBUG ("Account never emitted Removed, emitting it now"); self->priv->removed = TRUE; tp_svc_account_emit_removed (self); } @@ -3621,7 +3316,8 @@ _mcd_account_dispose (GObject *object) tp_clear_object (&priv->self_contact); tp_clear_object (&priv->connectivity); - _mcd_account_set_connection_context (self, NULL); + tp_clear_pointer (&self->priv->connection_context, + _mcd_account_connection_context_free); _mcd_account_set_connection (self, NULL); G_OBJECT_CLASS (mcd_account_parent_class)->dispose (object); @@ -3649,6 +3345,9 @@ _mcd_account_constructor (GType type, guint n_params, return (GObject *) account; } +static void mcd_account_connection_proceed_with_reason + (McdAccount *account, gboolean success, TpConnectionStatusReason reason); + static void monitor_state_changed_cb ( McdConnectivityMonitor *monitor, @@ -3721,16 +3420,6 @@ _mcd_account_constructed (GObject *object) } static void -mcd_account_add_signals (TpProxy *self, - guint quark, - DBusGProxy *proxy, - gpointer data) -{ - mc_cli_Connection_Manager_Interface_Account_Storage_add_signals (self, - quark, proxy, data); -} - -static void mcd_account_class_init (McdAccountClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); @@ -3766,23 +3455,17 @@ mcd_account_class_init (McdAccountClass * klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property + (object_class, PROP_STORAGE_PLUGIN, + g_param_spec_object ("storage-plugin", "storage-plugin", + "Storage plugin", MCP_TYPE_ACCOUNT_STORAGE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", "Unique name", "Unique name", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - g_object_class_install_property - (object_class, PROP_ALWAYS_ON, - g_param_spec_boolean ("always-on", "Always on?", "Always on?", - FALSE, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property - (object_class, PROP_HIDDEN, - g_param_spec_boolean ("hidden", "Hidden?", "Is this account hidden?", - FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /* Signals */ _mcd_account_signals[VALIDITY_CHANGED] = g_signal_new ("validity-changed", @@ -3801,9 +3484,6 @@ mcd_account_class_init (McdAccountClass * klass) G_TYPE_NONE, 1, G_TYPE_STRING); account_ready_quark = g_quark_from_static_string ("mcd_account_load"); - - tp_proxy_or_subclass_hook_on_interface_add (TP_TYPE_CONNECTION_MANAGER, - mcd_account_add_signals); } static void @@ -3824,7 +3504,6 @@ mcd_account_init (McdAccount *account) priv->curr_presence_status = g_strdup ("offline"); priv->curr_presence_message = g_strdup (""); - priv->always_on = FALSE; priv->always_dispatch = FALSE; priv->enabled = FALSE; priv->connect_automatically = FALSE; @@ -3854,7 +3533,8 @@ mcd_account_init (McdAccount *account) McdAccount * mcd_account_new (McdAccountManager *account_manager, const gchar *name, - McdConnectivityMonitor *connectivity) + McdConnectivityMonitor *connectivity, + McpAccountStorage *storage_plugin) { gpointer *obj; McdStorage *storage = mcd_account_manager_get_storage (account_manager); @@ -3862,6 +3542,7 @@ mcd_account_new (McdAccountManager *account_manager, obj = g_object_new (MCD_TYPE_ACCOUNT, "storage", storage, + "storage-plugin", storage_plugin, "dbus-daemon", dbus, "connectivity-monitor", connectivity, "name", name, @@ -3875,6 +3556,12 @@ _mcd_account_get_storage (McdAccount *account) return account->priv->storage; } +McpAccountStorage * +mcd_account_get_storage_plugin (McdAccount *account) +{ + return account->priv->storage_plugin; +} + /* * mcd_account_is_valid: * @account: the #McdAccount. @@ -3892,6 +3579,39 @@ mcd_account_is_valid (McdAccount *account) return priv->invalid_reason == NULL; } +/* + * mcd_account_dup_protocol: + * @self: the account + * + * Returns: (transfer full): the account's connection manager's protocol, + * possibly %NULL if "not valid" + */ +static TpProtocol * +mcd_account_dup_protocol (McdAccount *self) +{ + TpProtocol *protocol; + + if (!self->priv->manager && !load_manager (self)) + { + DEBUG ("unable to load manager for account %s", + self->priv->unique_name); + return NULL; + } + + protocol = _mcd_manager_dup_protocol (self->priv->manager, + self->priv->protocol_name); + + if (G_UNLIKELY (protocol == NULL)) + { + DEBUG ("unable to get protocol for %s account %s", + self->priv->protocol_name, + self->priv->unique_name); + return NULL; + } + + return protocol; +} + /** * mcd_account_is_enabled: * @account: the #McdAccount. @@ -3907,14 +3627,6 @@ mcd_account_is_enabled (McdAccount *account) return priv->enabled; } -gboolean -_mcd_account_is_hidden (McdAccount *account) -{ - g_return_val_if_fail (MCD_IS_ACCOUNT (account), FALSE); - - return account->priv->hidden; -} - const gchar * mcd_account_get_unique_name (McdAccount *account) { @@ -3927,54 +3639,25 @@ mcd_account_get_object_path (McdAccount *account) return account->priv->object_path; } -/** - * _mcd_account_dup_parameters: - * @account: the #McdAccount. - * - * Get the parameters set for this account. The resulting #GHashTable will be - * newly allocated and must be g_hash_table_unref()'d after use. +/* + * Like _mcd_account_dup_parameters(), but return the parameters as they + * would be passed to RequestConnection for the given protocol. * - * Returns: @account's current parameters, or %NULL if they could not be - * retrieved. + * Returns: A GHashTable with g_strdup'ed keys and tp_g_value_slice_dup'ed + * values. Be careful: callers rely on that memory allocation model. */ -GHashTable * -_mcd_account_dup_parameters (McdAccount *account) +static GHashTable * +mcd_account_coerce_parameters (McdAccount *account, + TpProtocol *protocol) { - McdAccountPrivate *priv; - TpProtocol *protocol; GList *protocol_params; GList *iter; GHashTable *params; g_return_val_if_fail (MCD_IS_ACCOUNT (account), NULL); - priv = account->priv; - DEBUG ("called"); - /* FIXME: this is ridiculous. MC stores the parameters for the account, so - * it should be able to expose them on D-Bus even if the CM is uninstalled. - * It shouldn't need to iterate across the parameters supported by the CM. - * But it does, because MC doesn't store the types of parameters. So it - * needs the CM (or .manager file) to be around to tell it whether "true" - * is a string or a boolean… - */ - if (!priv->manager && !load_manager (account)) - { - DEBUG ("unable to load manager for account %s", priv->unique_name); - return NULL; - } - - protocol = _mcd_manager_dup_protocol (priv->manager, - priv->protocol_name); - - if (G_UNLIKELY (protocol == NULL)) - { - DEBUG ("unable to get protocol for %s account %s", priv->protocol_name, - priv->unique_name); - return NULL; - } - params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); @@ -3984,11 +3667,11 @@ _mcd_account_dup_parameters (McdAccount *account) for (iter = protocol_params; iter != NULL; iter = iter->next) { TpConnectionManagerParam *param = iter->data; - const gchar *name = tp_connection_manager_param_get_name (param); GValue v = G_VALUE_INIT; - if (mcd_account_get_parameter (account, name, &v, NULL)) + if (mcd_account_get_parameter (account, param, &v, NULL)) { + const gchar *name = tp_connection_manager_param_get_name (param); g_hash_table_insert (params, g_strdup (name), tp_g_value_slice_dup (&v)); g_value_unset (&v); @@ -3997,11 +3680,67 @@ _mcd_account_dup_parameters (McdAccount *account) g_list_free_full (protocol_params, (GDestroyNotify) tp_connection_manager_param_free); - g_object_unref (protocol); return params; } /** + * _mcd_account_dup_parameters: + * @account: the #McdAccount. + * + * Get the parameters set for this account. The resulting #GHashTable will be + * newly allocated and must be g_hash_table_unref()'d after use. + * + * Returns: @account's current parameters, or %NULL if they could not be + * retrieved. + */ +GHashTable * +_mcd_account_dup_parameters (McdAccount *self) +{ + McpAccountManager *api; + gchar **untyped_parameters; + GHashTable *params = NULL; + TpProtocol *protocol; + + g_return_val_if_fail (MCD_IS_ACCOUNT (self), NULL); + + DEBUG ("called"); + + /* Maybe our storage plugin knows the types of the parameters? */ + + api = MCP_ACCOUNT_MANAGER (self->priv->storage); + untyped_parameters = mcp_account_storage_list_untyped_parameters ( + self->priv->storage_plugin, api, self->priv->unique_name); + + if (untyped_parameters == NULL || *untyped_parameters == NULL) + { + /* Happy path: there are no parameters that lack types. */ + params = mcd_storage_dup_typed_parameters (self->priv->storage, + self->priv->unique_name); + goto finally; + } + + /* MC didn't always know parameters' types, so it might need the CM + * (or .manager file) to be around to tell it whether "true" + * is a string or a boolean… this is ridiculous, but backwards-compatible. + */ + protocol = mcd_account_dup_protocol (self); + + if (protocol != NULL) + { + params = mcd_account_coerce_parameters (self, protocol); + g_object_unref (protocol); + + if (params != NULL) + goto finally; + } + +finally: + g_strfreev (untyped_parameters); + + return params; +} + +/** * mcd_account_request_presence: * @account: the #McdAccount. * @presence: a #TpConnectionPresenceType. @@ -4675,23 +4414,22 @@ mcd_account_get_connection (McdAccount *account) return priv->connection; } -typedef struct -{ - McdAccountCheckValidityCb callback; - gpointer user_data; -} CheckValidityData; - -static void -check_validity_check_parameters_cb (McdAccount *account, - const GError *invalid_reason, - gpointer user_data) +gboolean +mcd_account_check_validity (McdAccount *account, + GError **error) { - CheckValidityData *data = (CheckValidityData *) user_data; McdAccountPrivate *priv = account->priv; - gboolean now_valid = (invalid_reason == NULL); - gboolean was_valid = (priv->invalid_reason == NULL); + GError *invalid_reason = NULL; + gboolean now_valid; + gboolean was_valid; + + g_return_val_if_fail (MCD_IS_ACCOUNT (account), FALSE); + + was_valid = (priv->invalid_reason == NULL); + now_valid = mcd_account_check_parameters (account, &invalid_reason); g_clear_error (&priv->invalid_reason); + if (invalid_reason != NULL) { priv->invalid_reason = g_error_copy (invalid_reason); @@ -4717,27 +4455,13 @@ check_validity_check_parameters_cb (McdAccount *account, } } - if (data->callback != NULL) - data->callback (account, invalid_reason, data->user_data); - - g_slice_free (CheckValidityData, data); -} - -void -mcd_account_check_validity (McdAccount *account, - McdAccountCheckValidityCb callback, - gpointer user_data) -{ - CheckValidityData *data; - - g_return_if_fail (MCD_IS_ACCOUNT (account)); - - data = g_slice_new0 (CheckValidityData); - data->callback = callback; - data->user_data = user_data; + if (invalid_reason != NULL) + { + g_propagate_error (error, invalid_reason); + return FALSE; + } - mcd_account_check_parameters (account, check_validity_check_parameters_cb, - data); + return TRUE; } /* @@ -5235,36 +4959,6 @@ _mcd_account_set_has_been_online (McdAccount *account) } } -McdAccountConnectionContext * -_mcd_account_get_connection_context (McdAccount *self) -{ - g_return_val_if_fail (MCD_IS_ACCOUNT (self), NULL); - - return self->priv->connection_context; -} - -void -_mcd_account_set_connection_context (McdAccount *self, - McdAccountConnectionContext *c) -{ - g_return_if_fail (MCD_IS_ACCOUNT (self)); - - if (self->priv->connection_context != NULL) - { - _mcd_account_connection_context_free (self->priv->connection_context); - } - - self->priv->connection_context = c; -} - -gboolean -_mcd_account_get_always_on (McdAccount *self) -{ - g_return_val_if_fail (MCD_IS_ACCOUNT (self), FALSE); - - return self->priv->always_on; -} - gboolean _mcd_account_needs_dispatch (McdAccount *self) { @@ -5273,19 +4967,6 @@ _mcd_account_needs_dispatch (McdAccount *self) return self->priv->always_dispatch; } -gboolean -mcd_account_parameter_is_secret (McdAccount *self, const gchar *name) -{ - McdAccountPrivate *priv = self->priv; - const TpConnectionManagerParam *param; - - param = mcd_manager_get_protocol_param (priv->manager, - priv->protocol_name, name); - - return (param != NULL && - tp_connection_manager_param_is_secret (param)); -} - void _mcd_account_set_changing_presence (McdAccount *self, gboolean value) { @@ -5345,3 +5026,114 @@ mcd_account_set_waiting_for_connectivity (McdAccount *self, { self->priv->waiting_for_connectivity = waiting; } + +void +_mcd_account_connection_begin (McdAccount *account, + gboolean user_initiated) +{ + McdAccountConnectionContext *ctx; + TpProtocol *protocol; + + /* check whether a connection process is already ongoing */ + if (account->priv->connection_context != NULL) + { + DEBUG ("already trying to connect"); + return; + } + + /* get account params */ + /* create dynamic params HT */ + /* run the handlers */ + ctx = g_malloc (sizeof (McdAccountConnectionContext)); + ctx->user_initiated = user_initiated; + + /* If we get this far, the account should be valid, so getting the + * protocol should succeed. + */ + protocol = mcd_account_dup_protocol (account); + g_assert (protocol != NULL); + + ctx->params = mcd_account_coerce_parameters (account, protocol); + g_assert (ctx->params != NULL); + + /* Inject "account-path-suffix" parameter if supported by the protocol */ + if (tp_protocol_has_param (protocol, "account-path-suffix")) + { + g_hash_table_insert (ctx->params, g_strdup ("account-path-suffix"), + tp_g_value_slice_new_string (account->priv->unique_name)); + } + g_object_unref (protocol); + + _mcd_account_set_connection_status (account, + TP_CONNECTION_STATUS_CONNECTING, + TP_CONNECTION_STATUS_REASON_REQUESTED, + NULL, NULL, NULL); + account->priv->connection_context = ctx; + + mcd_account_connection_proceed_with_reason + (account, TRUE, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED); +} + +void +mcd_account_connection_proceed_with_reason (McdAccount *account, + gboolean success, + TpConnectionStatusReason reason) +{ + McdAccountConnectionContext *ctx; + gboolean delayed; + + /* call next handler, or terminate the chain (emitting proper signal). + * if everything is fine, call mcd_manager_create_connection() and + * _mcd_connection_connect () with the dynamic parameters. Remove that call + * from mcd_manager_create_connection() */ + ctx = account->priv->connection_context; + g_return_if_fail (ctx != NULL); + g_return_if_fail (ctx->params != NULL); + + if (success) + { + if (mcd_connectivity_monitor_is_online ( + mcd_account_get_connectivity_monitor (account)) || _mcd_account_needs_dispatch (account)) + { + DEBUG ("%s wants to connect and we're online - go for it", + mcd_account_get_unique_name (account)); + delayed = FALSE; + } + else if (!mcd_account_get_waiting_for_connectivity (account)) + { + DEBUG ("%s wants to connect, but we're offline; queuing it up", + mcd_account_get_unique_name (account)); + delayed = TRUE; + mcd_account_set_waiting_for_connectivity (account, TRUE); + } + else + { + DEBUG ("%s wants to connect, but is already waiting for " + "connectivity?", mcd_account_get_unique_name (account)); + delayed = TRUE; + } + } + else + { + DEBUG ("%s failed to connect: reason code %d", + mcd_account_get_unique_name (account), reason); + delayed = FALSE; + } + + if (!delayed) + { + /* end of the chain */ + if (success) + { + _mcd_account_connect (account, ctx->params); + } + else + { + _mcd_account_set_connection_status + (account, TP_CONNECTION_STATUS_DISCONNECTED, reason, NULL, + TP_ERROR_STR_DISCONNECTED, NULL); + } + tp_clear_pointer (&account->priv->connection_context, + _mcd_account_connection_context_free); + } +} diff --git a/src/mcd-account.h b/src/mcd-account.h index 7843c279..85a56cce 100644 --- a/src/mcd-account.h +++ b/src/mcd-account.h @@ -40,6 +40,7 @@ typedef struct _McdAccountClass McdAccountClass; #include "mcd-connection.h" #include "mcd-account-manager.h" +#include "mcd-dbusprop.h" struct _McdAccount { @@ -60,9 +61,6 @@ GQuark mcd_account_error_quark (void); typedef void (*McdAccountLoadCb) (McdAccount *account, const GError *error, gpointer user_data); -typedef void (*McdAccountDeleteCb) (McdAccount *account, - const GError *error, - gpointer user_data); struct _McdAccountClass { @@ -82,22 +80,24 @@ GType mcd_account_get_type (void); McdAccount *mcd_account_new (McdAccountManager *account_manager, const gchar *name, - McdConnectivityMonitor *minotaur); + McdConnectivityMonitor *minotaur, + McpAccountStorage *storage_plugin); -void mcd_account_delete (McdAccount *account, McdAccountDeleteCb callback, - gpointer user_data); +void mcd_account_delete_async (McdAccount *account, + McdDBusPropSetFlags flags, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mcd_account_delete_finish (McdAccount *account, + GAsyncResult *result, + GError **error); const gchar *mcd_account_get_unique_name (McdAccount *account); const gchar *mcd_account_get_object_path (McdAccount *account); gboolean mcd_account_is_valid (McdAccount *account); -typedef void (*McdAccountCheckValidityCb) (McdAccount *account, - const GError *invalid_reason, - gpointer user_data); -void mcd_account_check_validity (McdAccount *account, - McdAccountCheckValidityCb callback, - gpointer user_data); +gboolean mcd_account_check_validity (McdAccount *account, + GError **error); gboolean mcd_account_is_enabled (McdAccount *account); @@ -126,19 +126,13 @@ McdConnection *mcd_account_get_connection (McdAccount *account); gboolean mcd_account_check_request (McdAccount *account, GHashTable *request, GError **error); -gboolean mcd_account_parameter_is_secret (McdAccount *self, - const gchar *name); - void mcd_account_altered_by_plugin (McdAccount *account, const gchar *name); gchar * mcd_account_dup_display_name (McdAccount *self); -gboolean mcd_account_get_parameter (McdAccount *account, const gchar *name, - GValue *parameter, - GError **error); - gboolean mcd_account_get_parameter_of_known_type (McdAccount *account, const gchar *name, + const GVariantType *variant_type, GType type, GValue *parameter, GError **error); @@ -154,9 +148,7 @@ gboolean mcd_account_get_waiting_for_connectivity (McdAccount *self); void mcd_account_set_waiting_for_connectivity (McdAccount *self, gboolean waiting); -void mcd_account_connection_proceed (McdAccount *account, gboolean success); -void mcd_account_connection_proceed_with_reason - (McdAccount *account, gboolean success, TpConnectionStatusReason reason); +McpAccountStorage *mcd_account_get_storage_plugin (McdAccount *account); G_END_DECLS diff --git a/src/mcd-channel-priv.h b/src/mcd-channel-priv.h index b45ad313..9e045837 100644 --- a/src/mcd-channel-priv.h +++ b/src/mcd-channel-priv.h @@ -67,10 +67,6 @@ void _mcd_channel_set_request_proxy (McdChannel *channel, McdChannel *source); void _mcd_channel_close (McdChannel *channel); -G_GNUC_INTERNAL void _mcd_channel_depart (McdChannel *channel, - TpChannelGroupChangeReason reason, - const gchar *message); - G_GNUC_INTERNAL gboolean _mcd_channel_is_primary_for_path (McdChannel *self, const gchar *channel_path); diff --git a/src/mcd-channel.c b/src/mcd-channel.c index fe2b0323..a0d0a95c 100644 --- a/src/mcd-channel.c +++ b/src/mcd-channel.c @@ -48,8 +48,6 @@ #include "mcd-enum-types.h" #include "request.h" -#include "_gen/interfaces.h" - #define MCD_CHANNEL_PRIV(channel) (MCD_CHANNEL (channel)->priv) G_DEFINE_TYPE (McdChannel, mcd_channel, MCD_TYPE_MISSION) @@ -1123,95 +1121,6 @@ mcd_channel_get_tp_channel (McdChannel *channel) return channel->priv->tp_chan; } -static void -mcd_channel_depart_cb (GObject *source_object, - GAsyncResult *result, - gpointer data G_GNUC_UNUSED) -{ - GError *error = NULL; - - /* By this point, TpChannel has already called Close() for us; - * we only get an error if that failed. If Close() fails, there's - * not a whole lot we can do about it. */ - - if (!tp_channel_leave_finish (TP_CHANNEL (source_object), result, &error)) - { - WARNING ("failed to depart, even via Close(): %s %d: %s", - g_quark_to_string (error->domain), error->code, error->message); - g_error_free (error); - } -} - -typedef struct { - TpChannelGroupChangeReason reason; - gchar *message; -} DepartData; - -static void -mcd_channel_ready_to_depart_cb (GObject *source_object, - GAsyncResult *result, - gpointer data) -{ - TpChannel *channel = TP_CHANNEL (source_object); - DepartData *d = data; - GError *error = NULL; - - if (!tp_proxy_prepare_finish (channel, result, &error)) - { - DEBUG ("%s %d: %s", g_quark_to_string (error->domain), error->code, - error->message); - g_free (d->message); - g_slice_free (DepartData, d); - g_clear_error (&error); - return; - } - - /* If it's a Group, this will leave gracefully. - * If not, it will just close it. Either's good. */ - tp_channel_leave_async (channel, d->reason, d->message, - mcd_channel_depart_cb, NULL); -} - -void -_mcd_channel_depart (McdChannel *channel, - TpChannelGroupChangeReason reason, - const gchar *message) -{ - DepartData *d; - const GError *invalidated; - GQuark just_group_feature[] = { TP_CHANNEL_FEATURE_GROUP, 0 }; - - g_return_if_fail (MCD_IS_CHANNEL (channel)); - - g_return_if_fail (channel->priv->tp_chan != NULL); - g_return_if_fail (message != NULL); - - invalidated = tp_proxy_get_invalidated (channel->priv->tp_chan); - - if (invalidated != NULL) - { - DEBUG ("%s %d: %s", g_quark_to_string (invalidated->domain), - invalidated->code, invalidated->message); - return; - } - - if (message[0] == '\0' && reason == TP_CHANNEL_GROUP_CHANGE_REASON_NONE) - { - /* exactly equivalent to Close(), so skip the Group interface */ - tp_channel_close_async (channel->priv->tp_chan, NULL, NULL); - return; - } - - d = g_slice_new (DepartData); - d->reason = reason; - d->message = g_strdup (message); - - /* tp_channel_leave_async documents calling it without first preparing - * GROUP as deprecated. */ - tp_proxy_prepare_async (channel->priv->tp_chan, just_group_feature, - mcd_channel_ready_to_depart_cb, d); -} - /* * _mcd_channel_is_primary_for_path: * @self: an McdChannel diff --git a/src/mcd-client-priv.h b/src/mcd-client-priv.h index c00ce13a..069d2a67 100644 --- a/src/mcd-client-priv.h +++ b/src/mcd-client-priv.h @@ -97,8 +97,6 @@ G_GNUC_INTERNAL const GList *_mcd_client_proxy_get_handler_filters (McdClientProxy *self); G_GNUC_INTERNAL gboolean _mcd_client_proxy_get_bypass_approval (McdClientProxy *self); -G_GNUC_INTERNAL gboolean _mcd_client_proxy_get_bypass_observers - (McdClientProxy *self); G_GNUC_INTERNAL gboolean _mcd_client_proxy_get_delay_approvers (McdClientProxy *self); diff --git a/src/mcd-client.c b/src/mcd-client.c index 455cfd77..ec5c504b 100644 --- a/src/mcd-client.c +++ b/src/mcd-client.c @@ -68,7 +68,6 @@ struct _McdClientProxyPrivate gboolean introspect_started; gboolean ready; gboolean bypass_approval; - gboolean bypass_observers; gboolean delay_approvers; gboolean recover; @@ -400,10 +399,6 @@ parse_client_file (McdClientProxy *client, g_key_file_get_boolean (file, TP_IFACE_CLIENT_HANDLER, "BypassApproval", NULL); - client->priv->bypass_observers = - g_key_file_get_boolean (file, TP_IFACE_CLIENT_HANDLER, - "BypassObservers", NULL); - client->priv->delay_approvers = g_key_file_get_boolean (file, TP_IFACE_CLIENT_OBSERVER, "DelayApprovers", NULL); @@ -664,10 +659,6 @@ _mcd_client_proxy_handler_get_all_cb (TpProxy *proxy, self->priv->bypass_approval = bypass; DEBUG ("%s has BypassApproval=%c", bus_name, bypass ? 'T' : 'F'); - bypass = tp_asv_get_boolean (properties, "BypassObservers", NULL); - self->priv->bypass_observers = bypass; - DEBUG ("%s has BypassObservers=%c", bus_name, bypass ? 'T' : 'F'); - /* don't emit handler-capabilities-changed if we're not actually available * any more - if that's the case, then we already signalled our loss of * any capabilities */ @@ -1369,14 +1360,6 @@ _mcd_client_proxy_get_bypass_approval (McdClientProxy *self) } gboolean -_mcd_client_proxy_get_bypass_observers (McdClientProxy *self) -{ - g_return_val_if_fail (MCD_IS_CLIENT_PROXY (self), FALSE); - - return self->priv->bypass_observers; -} - -gboolean _mcd_client_proxy_get_delay_approvers (McdClientProxy *self) { g_return_val_if_fail (MCD_IS_CLIENT_PROXY (self), FALSE); diff --git a/src/mcd-connection-service-points.c b/src/mcd-connection-service-points.c index 58b65f19..b52abe30 100644 --- a/src/mcd-connection-service-points.c +++ b/src/mcd-connection-service-points.c @@ -31,25 +31,6 @@ #include <telepathy-glib/telepathy-glib-dbus.h> static void -service_point_contact_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - McdConnection *connection = MCD_CONNECTION (user_data); - TpContact *contact = tp_connection_dup_contact_by_id_finish ( - TP_CONNECTION (source), result, NULL); - - if (contact != NULL) - { - mcd_connection_add_emergency_handle (connection, - tp_contact_get_handle (contact)); - g_object_unref (contact); - } - - g_object_unref (connection); -} - -static void parse_services_list (McdConnection *connection, const GPtrArray *services) { @@ -76,22 +57,6 @@ parse_services_list (McdConnection *connection, if (e_numbers != NULL) { - GSList *service; - TpConnection *tp_conn = mcd_connection_get_tp_connection (connection); - - /* FIXME: in 1.0, drop this and spec that when calling a service point, - * you should use TargetID. See - * https://bugs.freedesktop.org/show_bug.cgi?id=59162#c3 */ - for (service = e_numbers; service != NULL; service =g_slist_next (service)) - { - const gchar * const *iter; - - for (iter = service->data; iter != NULL && *iter != NULL; iter++) - tp_connection_dup_contact_by_id_async (tp_conn, - *iter, 0, NULL, service_point_contact_cb, - g_object_ref (connection)); - } - _mcd_connection_take_emergency_numbers (connection, e_numbers); } } @@ -125,31 +90,16 @@ service_points_fetched_cb (TpProxy *proxy, parse_services_list (connection, g_value_get_boxed (value)); } -static void -service_point_interface_check (TpConnection *tp_conn, - const gchar **interfaces, - const GError *error, - gpointer data, - GObject *connection) +void +mcd_connection_service_point_setup (McdConnection *connection, + gboolean watch) { - const gchar *interface; - gboolean found = FALSE; - gboolean watch = GPOINTER_TO_UINT (data); - guint i = 0; - - if (interfaces == NULL) - return; - - for (interface = interfaces[0]; - !found && !tp_str_empty (interface); - interface = interfaces[++i]) - { - if (!tp_strdiff (interface, TP_IFACE_CONNECTION_INTERFACE_SERVICE_POINT)) - found = TRUE; - } + TpConnection *tp_conn = mcd_connection_get_tp_connection (connection); - if (!found) - return; + g_return_if_fail (MCD_IS_CONNECTION (connection)); + g_return_if_fail (TP_IS_PROXY (tp_conn)); + g_return_if_fail (tp_proxy_is_prepared (tp_conn, + TP_CONNECTION_FEATURE_CONNECTED)); /* so we know if/when the service points change (eg the SIM might not be * accessible yet, in which case the call below won't return any entries) @@ -157,25 +107,12 @@ service_point_interface_check (TpConnection *tp_conn, */ if (watch) tp_cli_connection_interface_service_point_connect_to_service_points_changed - (tp_conn, service_points_changed_cb, NULL, NULL, connection, NULL); + (tp_conn, service_points_changed_cb, NULL, NULL, + (GObject *) connection, NULL); /* fetch the current list to initialise our state */ tp_cli_dbus_properties_call_get (tp_conn, -1, TP_IFACE_CONNECTION_INTERFACE_SERVICE_POINT, "KnownServicePoints", service_points_fetched_cb, - NULL, NULL, connection); -} - -void -mcd_connection_service_point_setup (McdConnection *connection, gboolean watch) -{ - TpConnection *tp_conn = mcd_connection_get_tp_connection (connection); - - if (G_UNLIKELY (!tp_conn)) - return; - - /* see if the connection supports the service point interface */ - tp_cli_connection_call_get_interfaces (tp_conn, -1, - service_point_interface_check, - GUINT_TO_POINTER (watch), NULL, G_OBJECT (connection)); + NULL, NULL, (GObject *) connection); } diff --git a/src/mcd-connection.c b/src/mcd-connection.c index fe7029fd..066deba2 100644 --- a/src/mcd-connection.c +++ b/src/mcd-connection.c @@ -50,6 +50,7 @@ #include <stdlib.h> #include <dlfcn.h> +#include <dbus/dbus.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> @@ -104,7 +105,6 @@ struct _McdConnectionPrivate TpConnectionStatusReason abort_reason; guint got_contact_capabilities : 1; guint has_presence_if : 1; - guint has_capabilities_if : 1; guint has_contact_capabilities_if : 1; guint has_power_saving_if : 1; @@ -173,7 +173,8 @@ static const gchar * const *presence_fallbacks[] = { }; static void _mcd_connection_release_tp_connection (McdConnection *connection, - McdInhibit *inhibit); + McdInhibit *inhibit, + gboolean already_signalled); static gboolean request_channel_new_iface (McdConnection *connection, McdChannel *channel); @@ -487,40 +488,6 @@ _mcd_connection_request_presence (McdConnection *self, } static void -on_new_channel (TpConnection *proxy, const gchar *chan_obj_path, - const gchar *chan_type, guint handle_type, guint handle, - gboolean suppress_handler, gpointer user_data, - GObject *weak_object) -{ - McdConnection *connection = MCD_CONNECTION (weak_object); - McdConnectionPrivate *priv = user_data; - McdChannel *channel; - - DEBUG ("%s (t=%s, ht=%u, h=%u, suppress=%c)", - chan_obj_path, chan_type, handle_type, handle, - suppress_handler ? 'T' : 'F'); - - if (priv->dispatched_initial_channels) - { - channel = mcd_channel_new_from_path (proxy, - chan_obj_path, - chan_type, handle, handle_type); - if (G_UNLIKELY (!channel)) return; - mcd_operation_take_mission (MCD_OPERATION (connection), - MCD_MISSION (channel)); - - /* MC no longer calls RequestChannel. As a result, if suppress_handler - * is TRUE, we know that this channel was requested "behind our back", - * therefore we should call ObserveChannels, but refrain from calling - * AddDispatchOperation or HandleChannels. - * - * We assume that channels without suppress_handler are incoming. */ - _mcd_dispatcher_add_channel (priv->dispatcher, channel, - suppress_handler, suppress_handler); - } -} - -static void _foreach_channel_remove (McdMission * mission, McdOperation * operation) { g_assert (MCD_IS_MISSION (mission)); @@ -624,10 +591,6 @@ on_connection_status_changed (TpConnection *tp_conn, GParamSpec *pspec, priv->probation_drop_count = 0; } - mcd_connection_service_point_setup (connection, - !priv->service_points_watched); - priv->service_points_watched = TRUE; - priv->connected = TRUE; } break; @@ -738,7 +701,7 @@ mcd_connection_invalidated_cb (TpConnection *tp_conn, DEBUG ("Proxy destroyed (%s)!", message); - _mcd_connection_release_tp_connection (connection, NULL); + _mcd_connection_release_tp_connection (connection, NULL, FALSE); if (priv->connected && priv->abort_reason != TP_CONNECTION_STATUS_REASON_REQUESTED && @@ -1083,65 +1046,6 @@ mcd_connection_setup_requests (McdConnection *connection) } static void -list_channels_cb (TpConnection *connection, - const GPtrArray *structs, - const GError *error, - gpointer user_data, - GObject *weak_object) -{ - McdConnection *self = MCD_CONNECTION (weak_object); - guint i; - - if (error) - { - g_warning ("ListChannels got error: %s", error->message); - return; - } - - for (i = 0; i < structs->len; i++) - { - GValueArray *va = g_ptr_array_index (structs, i); - const gchar *object_path; - GHashTable *channel_props; - - object_path = g_value_get_boxed (va->values + 0); - - DEBUG ("%s (t=%s, ht=%u, h=%u)", - object_path, - g_value_get_string (va->values + 1), - g_value_get_uint (va->values + 2), - g_value_get_uint (va->values + 3)); - - /* this is not the most efficient thing we could possibly do, but - * we're on a fallback path so it's OK to be a bit slow */ - channel_props = g_hash_table_new (g_str_hash, g_str_equal); - g_hash_table_insert (channel_props, TP_IFACE_CHANNEL ".ChannelType", - va->values + 1); - g_hash_table_insert (channel_props, TP_IFACE_CHANNEL ".TargetHandleType", - va->values + 2); - g_hash_table_insert (channel_props, TP_IFACE_CHANNEL ".TargetHandle", - va->values + 3); - mcd_connection_found_channel (self, object_path, channel_props); - g_hash_table_unref (channel_props); - } - - self->priv->dispatched_initial_channels = TRUE; -} - -static void -mcd_connection_setup_pre_requests (McdConnection *connection) -{ - McdConnectionPrivate *priv = connection->priv; - - tp_cli_connection_connect_to_new_channel - (priv->tp_conn, on_new_channel, priv, NULL, - (GObject *)connection, NULL); - - tp_cli_connection_call_list_channels (priv->tp_conn, -1, - list_channels_cb, priv, NULL, (GObject *) connection); -} - -static void on_connection_ready (GObject *source_object, GAsyncResult *result, gpointer user_data) { @@ -1161,13 +1065,41 @@ on_connection_ready (GObject *source_object, GAsyncResult *result, if (!connection) goto finally; + if (!tp_proxy_has_interface_by_id (tp_conn, + TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS)) + { + GHashTable *asv; + + DEBUG ("%s: connection manager is too old", + tp_proxy_get_object_path (tp_conn)); + connection->priv->abort_reason = + TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED; + asv = tp_asv_new ( + "debug-message", G_TYPE_STRING, + "Connection manager does not implement Requests interface", + NULL); + g_signal_emit (connection, signals[CONNECTION_STATUS_CHANGED], 0, + TP_CONNECTION_STATUS_DISCONNECTED, + connection->priv->abort_reason, + tp_conn, TP_ERROR_STR_SOFTWARE_UPGRADE_REQUIRED, asv); + g_hash_table_unref (asv); + _mcd_connection_release_tp_connection (connection, NULL, TRUE); + goto finally; + } + DEBUG ("connection is ready"); priv = MCD_CONNECTION_PRIV (connection); + if (tp_proxy_has_interface_by_id (tp_conn, + TP_IFACE_QUARK_CONNECTION_INTERFACE_SERVICE_POINT)) + { + mcd_connection_service_point_setup (connection, + !priv->service_points_watched); + priv->service_points_watched = TRUE; + } + priv->has_presence_if = tp_proxy_has_interface_by_id (tp_conn, TP_IFACE_QUARK_CONNECTION_INTERFACE_SIMPLE_PRESENCE); - priv->has_capabilities_if = tp_proxy_has_interface_by_id (tp_conn, - TP_IFACE_QUARK_CONNECTION_INTERFACE_CAPABILITIES); priv->has_contact_capabilities_if = tp_proxy_has_interface_by_id (tp_conn, TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_CAPABILITIES); priv->has_power_saving_if = tp_proxy_has_interface_by_id (tp_conn, @@ -1202,11 +1134,9 @@ _mcd_connection_start_dispatching (McdConnection *self, self->priv->dispatching_started = TRUE; - if (tp_proxy_has_interface_by_id (self->priv->tp_conn, - TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS)) - mcd_connection_setup_requests (self); - else - mcd_connection_setup_pre_requests (self); + g_return_if_fail (tp_proxy_has_interface_by_id (self->priv->tp_conn, + TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS)); + mcd_connection_setup_requests (self); /* FIXME: why is this here? if we need to update caps before and after * * connected, it should be in the call_when_ready callback. */ @@ -1294,12 +1224,13 @@ mcd_connection_early_get_statuses_cb (TpProxy *proxy, } static void -mcd_connection_early_get_interfaces_cb (TpConnection *tp_conn, - const gchar **interfaces, +mcd_connection_early_get_interfaces_cb (TpProxy *proxy, + const GValue *value, const GError *error, gpointer user_data, GObject *weak_object) { + TpConnection *tp_conn = TP_CONNECTION (proxy); McdConnection *self = MCD_CONNECTION (weak_object); const gchar **iter; @@ -1317,9 +1248,11 @@ mcd_connection_early_get_interfaces_cb (TpConnection *tp_conn, tp_proxy_get_object_path (tp_conn), g_quark_to_string (error->domain), error->code, error->message); } - else + else if (G_VALUE_HOLDS (value, G_TYPE_STRV)) { - for (iter = interfaces; *iter != NULL; iter++) + for (iter = g_value_get_boxed (value); + iter != NULL && *iter != NULL; + iter++) { GQuark q = g_quark_try_string (*iter); @@ -1373,6 +1306,12 @@ mcd_connection_early_get_interfaces_cb (TpConnection *tp_conn, } } } + else + { + DEBUG ("%s: Early GetInterfaces returned unexpected type %s", + tp_proxy_get_object_path (tp_conn), + G_VALUE_TYPE_NAME (value)); + } mcd_connection_done_task_before_connect (self); } @@ -1501,7 +1440,8 @@ request_connection_cb (TpConnectionManager *proxy, const gchar *bus_name, /* TpConnection doesn't yet know how to get this information before * the Connection goes to CONNECTED, so we'll have to do it ourselves */ - tp_cli_connection_call_get_interfaces (priv->tp_conn, -1, + tp_cli_dbus_properties_call_get (priv->tp_conn, -1, + TP_IFACE_CONNECTION, "Interfaces", mcd_connection_early_get_interfaces_cb, NULL, NULL, (GObject *) connection); @@ -1556,13 +1496,18 @@ _mcd_connection_finalize (GObject * object) static void _mcd_connection_release_tp_connection (McdConnection *connection, - McdInhibit *inhibit) + McdInhibit *inhibit, + gboolean already_signalled) { McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); DEBUG ("%p", connection); - if (priv->abort_reason == TP_CONNECTION_STATUS_REASON_REQUESTED) + if (already_signalled) + { + DEBUG ("already emitted connection-status-changed"); + } + else if (priv->abort_reason == TP_CONNECTION_STATUS_REASON_REQUESTED) { g_signal_emit (connection, signals[CONNECTION_STATUS_CHANGED], 0, TP_CONNECTION_STATUS_DISCONNECTED, @@ -1675,7 +1620,7 @@ _mcd_connection_dispose (GObject * object) mcd_operation_foreach (MCD_OPERATION (connection), (GFunc) _foreach_channel_remove, connection); - _mcd_connection_release_tp_connection (connection, NULL); + _mcd_connection_release_tp_connection (connection, NULL, FALSE); g_assert (priv->tp_conn == NULL); if (priv->account) @@ -1892,8 +1837,6 @@ mcd_connection_class_init (McdConnectionClass * klass) object_class->set_property = _mcd_connection_set_property; object_class->get_property = _mcd_connection_get_property; - _mcd_ext_register_dbus_glib_marshallers (); - tp_connection_init_known_interfaces (); /* Properties */ @@ -1934,7 +1877,7 @@ mcd_connection_class_init (McdConnectionClass * klass) g_object_class_install_property (object_class, PROP_SLACKER, g_param_spec_object ("slacker", - "MCE slacker", + "Slacker", "Slacker object notifies us of user inactivity", MCD_TYPE_SLACKER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); @@ -2123,7 +2066,7 @@ mcd_connection_close (McdConnection *connection, connection->priv->closed = TRUE; connection->priv->abort_reason = TP_CONNECTION_STATUS_REASON_REQUESTED; - _mcd_connection_release_tp_connection (connection, inhibit); + _mcd_connection_release_tp_connection (connection, inhibit, FALSE); mcd_mission_abort (MCD_MISSION (connection)); } @@ -2257,7 +2200,7 @@ _mcd_connection_set_tp_connection (McdConnection *connection, } DEBUG ("releasing old connection first"); - _mcd_connection_release_tp_connection (connection, NULL); + _mcd_connection_release_tp_connection (connection, NULL, FALSE); } g_assert (priv->tp_conn == NULL); diff --git a/src/mcd-dispatch-operation-priv.h b/src/mcd-dispatch-operation-priv.h index ce168fc3..a61bb7be 100644 --- a/src/mcd-dispatch-operation-priv.h +++ b/src/mcd-dispatch-operation-priv.h @@ -108,9 +108,6 @@ G_GNUC_INTERNAL void _mcd_dispatch_operation_start_plugin_delay ( G_GNUC_INTERNAL void _mcd_dispatch_operation_end_plugin_delay ( McdDispatchOperation *self); -G_GNUC_INTERNAL void _mcd_dispatch_operation_leave_channels ( - McdDispatchOperation *self, TpChannelGroupChangeReason reason, - const gchar *message); G_GNUC_INTERNAL void _mcd_dispatch_operation_close_channels ( McdDispatchOperation *self); G_GNUC_INTERNAL void _mcd_dispatch_operation_destroy_channels ( diff --git a/src/mcd-dispatch-operation.c b/src/mcd-dispatch-operation.c index 4948b6be..4abaf078 100644 --- a/src/mcd-dispatch-operation.c +++ b/src/mcd-dispatch-operation.c @@ -388,8 +388,6 @@ static void mcd_dispatch_operation_set_channel_handled_by ( const gchar *well_known_name); static gboolean _mcd_dispatch_operation_handlers_can_bypass_approval ( McdDispatchOperation *self); -static gboolean _mcd_dispatch_operation_handlers_can_bypass_observers ( - McdDispatchOperation *self); static void _mcd_dispatch_operation_check_client_locks (McdDispatchOperation *self) @@ -1828,34 +1826,6 @@ _mcd_dispatch_operation_handlers_can_bypass_approval ( return FALSE; } -/* this is analogous to *_can_bypass_handlers() method above */ -static gboolean -_mcd_dispatch_operation_handlers_can_bypass_observers ( - McdDispatchOperation *self) -{ - gchar **iter; - - for (iter = self->priv->possible_handlers; - iter != NULL && *iter != NULL; - iter++) - { - McdClientProxy *handler = _mcd_client_registry_lookup ( - self->priv->client_registry, *iter); - - if (handler != NULL) - { - gboolean bypass = _mcd_client_proxy_get_bypass_observers ( - handler); - - DEBUG ("%s has BypassObservers=%c", *iter, bypass ? 'T' : 'F'); - return bypass; - } - } - - return FALSE; -} - - gboolean _mcd_dispatch_operation_has_channel (McdDispatchOperation *self, McdChannel *channel) @@ -2240,15 +2210,8 @@ _mcd_dispatch_operation_run_clients (McdDispatchOperation *self) { const GList *mini_plugins; - if (_mcd_dispatch_operation_handlers_can_bypass_observers (self)) - { - DEBUG ("Bypassing observers"); - } - else - { - DEBUG ("Running observers"); - _mcd_dispatch_operation_run_observers (self); - } + DEBUG ("Running observers"); + _mcd_dispatch_operation_run_observers (self); for (mini_plugins = mcp_list_objects (); mini_plugins != NULL; @@ -2544,29 +2507,6 @@ _mcd_dispatch_operation_forget_channels (McdDispatchOperation *self) } void -_mcd_dispatch_operation_leave_channels (McdDispatchOperation *self, - TpChannelGroupChangeReason reason, - const gchar *message) -{ - if (message == NULL) - { - message = ""; - } - - if (self->priv->channel != NULL) - { - /* Take a temporary copy, because self->priv->channels could - * be modified as a result */ - McdChannel *channel = g_object_ref (self->priv->channel); - - _mcd_channel_depart (channel, reason, message); - g_object_unref (channel); - } - - _mcd_dispatch_operation_forget_channels (self); -} - -void _mcd_dispatch_operation_close_channels (McdDispatchOperation *self) { if (self->priv->channel != NULL) diff --git a/src/mcd-dispatcher.c b/src/mcd-dispatcher.c index b004b096..3e6e1c16 100644 --- a/src/mcd-dispatcher.c +++ b/src/mcd-dispatcher.c @@ -59,8 +59,6 @@ #include "mcd-misc.h" #include "plugin-loader.h" -#include "_gen/svc-dispatcher.h" - #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> @@ -70,11 +68,6 @@ #include <string.h> #include "sp_timestamp.h" -#define CREATE_CHANNEL TP_IFACE_CONNECTION_INTERFACE_REQUESTS ".CreateChannel" -#define ENSURE_CHANNEL TP_IFACE_CONNECTION_INTERFACE_REQUESTS ".EnsureChannel" -#define SEND_MESSAGE \ - TP_IFACE_CHANNEL_DISPATCHER ".Interface.Messages.DRAFT.SendMessage" - #define MCD_DISPATCHER_PRIV(dispatcher) (MCD_DISPATCHER (dispatcher)->priv) static void dispatcher_iface_init (gpointer, gpointer); @@ -84,7 +77,7 @@ static void messages_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (McdDispatcher, mcd_dispatcher, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_DISPATCHER, dispatcher_iface_init); - G_IMPLEMENT_INTERFACE (MC_TYPE_SVC_CHANNEL_DISPATCHER_INTERFACE_MESSAGES_DRAFT, + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_DISPATCHER_INTERFACE_MESSAGES1, messages_iface_init); G_IMPLEMENT_INTERFACE ( TP_TYPE_SVC_CHANNEL_DISPATCHER_INTERFACE_OPERATION_LIST, @@ -357,6 +350,7 @@ _mcd_dispatcher_set_property (GObject * obj, guint prop_id, static const char * const interfaces[] = { TP_IFACE_CHANNEL_DISPATCHER_INTERFACE_OPERATION_LIST, + TP_IFACE_CHANNEL_DISPATCHER_INTERFACE_MESSAGES1, NULL }; @@ -595,17 +589,7 @@ mcd_dispatcher_client_needs_recovery_cb (McdClientProxy *client, for (list = channels; list; list = list->next) { TpChannel *channel = list->data; - const gchar *object_path = tp_proxy_get_object_path (channel); GVariant *properties; - McdClientProxy *handler; - - /* FIXME: This is not exactly the right behaviour, see fd.o#40305 */ - handler = _mcd_dispatcher_lookup_handler (self, channel, NULL); - if (handler && _mcd_client_proxy_get_bypass_observers (handler)) - { - DEBUG ("skipping unobservable channel %s", object_path); - continue; - } properties = tp_channel_dup_immutable_properties (channel); @@ -1317,92 +1301,6 @@ finally: } static void -dispatcher_channel_request_acl_cleanup (gpointer data) -{ - McdChannelRequestACL *crd = data; - - DEBUG ("cleanup acl (%p)", data); - - g_free (crd->account_path); - g_free (crd->preferred_handler); - g_hash_table_unref (crd->properties); - g_object_unref (crd->dispatcher); - tp_clear_pointer (&crd->request_metadata, g_hash_table_unref); - - g_slice_free (McdChannelRequestACL, crd); -} - -static void -dispatcher_channel_request_acl_success (DBusGMethodInvocation *context, - gpointer data) -{ - McdChannelRequestACL *crd = data; - - DEBUG ("complete acl (%p)", crd); - - dispatcher_request_channel (MCD_DISPATCHER (crd->dispatcher), - crd->account_path, - crd->properties, - crd->user_action_time, - crd->preferred_handler, - crd->request_metadata, - context, - crd->ensure); -} - -static void -free_gvalue (gpointer gvalue) -{ - GValue *gv = gvalue; - - g_value_unset (gv); - g_slice_free (GValue, gv); -} - -static void -dispatcher_channel_request_acl_start (McdDispatcher *dispatcher, - const gchar *method, - const gchar *account_path, - GHashTable *requested_properties, - gint64 user_action_time, - const gchar *preferred_handler, - GHashTable *request_metadata, - DBusGMethodInvocation *context, - gboolean ensure) -{ - McdChannelRequestACL *crd = g_slice_new0 (McdChannelRequestACL); - GValue *account = g_slice_new0 (GValue); - GHashTable *params = - g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_gvalue); - - g_value_init (account, G_TYPE_STRING); - g_value_set_string (account, account_path); - g_hash_table_insert (params, "account-path", account); - - crd->dispatcher = g_object_ref (dispatcher); - crd->account_path = g_strdup (account_path); - crd->preferred_handler = g_strdup (preferred_handler); - crd->properties = g_hash_table_ref (requested_properties); - crd->user_action_time = user_action_time; - crd->ensure = ensure; - crd->request_metadata = request_metadata != NULL ? - g_hash_table_ref (request_metadata) : NULL; - - DEBUG ("start %s.%s acl (%p)", account_path, method, crd); - - mcp_dbus_acl_authorised_async (dispatcher->priv->dbus_daemon, - context, - DBUS_ACL_TYPE_METHOD, - method, - params, - dispatcher_channel_request_acl_success, - crd, - dispatcher_channel_request_acl_cleanup); - - g_hash_table_unref (params); -} - -static void dispatcher_create_channel (TpSvcChannelDispatcher *iface, const gchar *account_path, GHashTable *requested_properties, @@ -1410,15 +1308,14 @@ dispatcher_create_channel (TpSvcChannelDispatcher *iface, const gchar *preferred_handler, DBusGMethodInvocation *context) { - dispatcher_channel_request_acl_start (MCD_DISPATCHER (iface), - CREATE_CHANNEL, - account_path, - requested_properties, - user_action_time, - preferred_handler, - NULL, - context, - FALSE); + dispatcher_request_channel (MCD_DISPATCHER (iface), + account_path, + requested_properties, + user_action_time, + preferred_handler, + NULL, + context, + FALSE); } static void @@ -1429,15 +1326,14 @@ dispatcher_ensure_channel (TpSvcChannelDispatcher *iface, const gchar *preferred_handler, DBusGMethodInvocation *context) { - dispatcher_channel_request_acl_start (MCD_DISPATCHER (iface), - ENSURE_CHANNEL, - account_path, - requested_properties, - user_action_time, - preferred_handler, - NULL, - context, - TRUE); + dispatcher_request_channel (MCD_DISPATCHER (iface), + account_path, + requested_properties, + user_action_time, + preferred_handler, + NULL, + context, + TRUE); } static void @@ -1449,15 +1345,14 @@ dispatcher_create_channel_with_hints (TpSvcChannelDispatcher *iface, GHashTable *hints, DBusGMethodInvocation *context) { - dispatcher_channel_request_acl_start (MCD_DISPATCHER (iface), - CREATE_CHANNEL, - account_path, - requested_properties, - user_action_time, - preferred_handler, - hints, - context, - FALSE); + dispatcher_request_channel (MCD_DISPATCHER (iface), + account_path, + requested_properties, + user_action_time, + preferred_handler, + hints, + context, + FALSE); } static void @@ -1469,15 +1364,14 @@ dispatcher_ensure_channel_with_hints (TpSvcChannelDispatcher *iface, GHashTable *hints, DBusGMethodInvocation *context) { - dispatcher_channel_request_acl_start (MCD_DISPATCHER (iface), - ENSURE_CHANNEL, - account_path, - requested_properties, - user_action_time, - preferred_handler, - hints, - context, - TRUE); + dispatcher_request_channel (MCD_DISPATCHER (iface), + account_path, + requested_properties, + user_action_time, + preferred_handler, + hints, + context, + TRUE); } @@ -1552,17 +1446,6 @@ typedef struct } MessageContext; static MessageContext * -message_context_steal (MessageContext *from) -{ - MessageContext *stolen = g_slice_new0 (MessageContext); - - g_memmove (stolen, from, sizeof (MessageContext)); - memset (from, 0, sizeof (MessageContext)); - - return stolen; -} - -static MessageContext * message_context_new (McdDispatcher *dispatcher, const gchar *account_path, const gchar *target_id, @@ -1650,7 +1533,7 @@ send_message_submitted (TpChannel *proxy, /* this frees the dbus context, so clear it from our cache afterwards */ if (error == NULL) { - mc_svc_channel_dispatcher_interface_messages_draft_return_from_send_message (context, token); + tp_svc_channel_dispatcher_interface_messages1_return_from_send_message (context, token); message_context_set_return_context (message, NULL); } else @@ -1717,16 +1600,6 @@ send_message_got_channel (McdRequest *request, } static void -messages_send_message_acl_success (DBusGMethodInvocation *dbus_context, - gpointer data) -{ - /* steal the contents of the message context from the ACL framework: * - * this avoids a nasty double-free (and means we don't have to dup * - * the message payload memory twice) */ - messages_send_message_start (dbus_context, message_context_steal (data)); -} - -static void messages_send_message_start (DBusGMethodInvocation *dbus_context, MessageContext *message) { @@ -1820,18 +1693,7 @@ finished: } static void -messages_send_message_acl_cleanup (gpointer data) -{ - MessageContext *message = data; - - /* At this point either the messages framework or the ACL framework * - * is expected to have handled the DBus return, so we must not try to */ - message_context_set_return_context (message, NULL); - message_context_free (message); -} - -static void -messages_send_message (McSvcChannelDispatcherInterfaceMessagesDraft *iface, +messages_send_message (TpSvcChannelDispatcherInterfaceMessages1 *iface, const gchar *account_path, const gchar *target_id, const GPtrArray *payload, @@ -1842,30 +1704,14 @@ messages_send_message (McSvcChannelDispatcherInterfaceMessagesDraft *iface, MessageContext *message = message_context_new (self, account_path, target_id, payload, flags); - /* these are for the ACL itself */ - GValue *account = g_slice_new0 (GValue); - GHashTable *params = - g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_gvalue); - - g_value_init (account, G_TYPE_STRING); - g_value_set_string (account, account_path); - g_hash_table_insert (params, "account-path", account); - - mcp_dbus_acl_authorised_async (self->priv->dbus_daemon, - context, - DBUS_ACL_TYPE_METHOD, - SEND_MESSAGE, - params, - messages_send_message_acl_success, - message, - messages_send_message_acl_cleanup); + messages_send_message_start (context, message); } static void messages_iface_init (gpointer iface, gpointer data G_GNUC_UNUSED) { #define IMPLEMENT(x) \ - mc_svc_channel_dispatcher_interface_messages_draft_implement_##x (iface, messages_##x) + tp_svc_channel_dispatcher_interface_messages1_implement_##x (iface, messages_##x) IMPLEMENT (send_message); #undef IMPLEMENT } diff --git a/src/mcd-master.c b/src/mcd-master.c index 37c127bd..d4b53cfc 100644 --- a/src/mcd-master.c +++ b/src/mcd-master.c @@ -71,7 +71,6 @@ #include "mcd-dispatcher.h" #include "mcd-account-manager.h" #include "mcd-account-manager-priv.h" -#include "mcd-account-conditions.h" #include "mcd-account-priv.h" #include "plugin-loader.h" diff --git a/src/mcd-misc.c b/src/mcd-misc.c index 3a3f1541..14fb905b 100644 --- a/src/mcd-misc.c +++ b/src/mcd-misc.c @@ -37,8 +37,6 @@ #include "mcd-debug.h" -#include "_gen/register-dbus-glib-marshallers-body.h" - #include <dbus/dbus.h> #include <dbus/dbus-glib-lowlevel.h> diff --git a/src/mcd-service.c b/src/mcd-service.c index 17c94b04..cf3d05e7 100644 --- a/src/mcd-service.c +++ b/src/mcd-service.c @@ -133,6 +133,10 @@ mcd_service_constructed (GObject *obj) { DEBUG ("called"); + /* It's actually quite important that we request *a* bus name here, + * so we can use it as a mutex for loading and migrating accounts, + * all of which we want to have finished before we open up our main + * D-Bus API for business. (See also fd.o #24000) */ mcd_service_obtain_bus_name (MCD_OBJECT (obj)); mcd_debug_print_tree (obj); diff --git a/src/mcd-storage-ag-hidden.c b/src/mcd-storage-ag-hidden.c deleted file mode 100644 index ee857a74..00000000 --- a/src/mcd-storage-ag-hidden.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * storage-ag-hidden.c - account backend for "magic" hidden accounts using - * accounts-glib - * Copyright ©2011 Collabora Ltd. - * Copyright ©2011 Nokia Corporation - * - * 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 "config.h" - -#include "mcd-storage-ag-hidden.h" - -#include <telepathy-glib/telepathy-glib.h> - -#include "mcd-debug.h" -/* FIXME: if we weren't in-tree, we wouldn't be able to include this header and - * we'd have to re-hardcode magic strings like "Hidden". - */ -#include "mcd-account-config.h" - -static void account_storage_iface_init ( - McpAccountStorageIface *iface, - gpointer unused G_GNUC_UNUSED); - -G_DEFINE_TYPE_WITH_CODE (McdStorageAgHidden, mcd_storage_ag_hidden, - MCD_TYPE_ACCOUNT_MANAGER_SSO, - G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_STORAGE, - account_storage_iface_init); - ); - -static void -mcd_storage_ag_hidden_init (McdStorageAgHidden *self) -{ -} - -static void -mcd_storage_ag_hidden_class_init (McdStorageAgHiddenClass *klass) -{ - McdAccountManagerSsoClass *super = MCD_ACCOUNT_MANAGER_SSO_CLASS (klass); - - super->service_type = ACCOUNTS_GLIB_HIDDEN_SERVICE_TYPE; -} - -static gboolean -_mcd_storage_ag_hidden_get ( - const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account_suffix, - const gchar *key) -{ - /* Chain up to the real implementation, checking whether this is an account - * we care about in the process. - */ - if (!_mcd_account_manager_sso_get (self, am, account_suffix, key)) - return FALSE; - - /* If the caller is looking for the "Hidden" key (or NULL, which means - * everything), let's fill it in. (Every account this plugin cares about - * should be hidden.) - */ - if (key == NULL || !tp_strdiff (key, MC_ACCOUNTS_KEY_HIDDEN)) - mcp_account_manager_set_value (am, account_suffix, MC_ACCOUNTS_KEY_HIDDEN, - "true"); - - return TRUE; -} - -static void -account_storage_iface_init ( - McpAccountStorageIface *iface, - gpointer unused G_GNUC_UNUSED) -{ - mcp_account_storage_iface_set_name (iface, - "maemo-libaccounts-hidden"); - mcp_account_storage_iface_set_desc (iface, - "Loads accounts with service type '" ACCOUNTS_GLIB_HIDDEN_SERVICE_TYPE - "' from accounts-glib, and marks them as Hidden"); - mcp_account_storage_iface_implement_get (iface, - _mcd_storage_ag_hidden_get); -} - -McdStorageAgHidden * -mcd_storage_ag_hidden_new () -{ - return g_object_new (MCD_TYPE_STORAGE_AG_HIDDEN, NULL); -} diff --git a/src/mcd-storage-ag-hidden.h b/src/mcd-storage-ag-hidden.h deleted file mode 100644 index 2d9311f4..00000000 --- a/src/mcd-storage-ag-hidden.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * storage-ag-hidden.h - account backend for "magic" hidden accounts using - * accounts-glib - * Copyright ©2011 Collabora Ltd. - * Copyright ©2011 Nokia Corporation - * - * 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 MCD_STORAGE_AG_HIDDEN_H -#define MCD_STORAGE_AG_HIDDEN_H - -#include <glib-object.h> -#include "mcd-account-manager-sso.h" - -typedef struct _McdStorageAgHidden McdStorageAgHidden; -typedef struct _McdStorageAgHiddenClass McdStorageAgHiddenClass; - -struct _McdStorageAgHiddenClass { - McdAccountManagerSsoClass parent_class; -}; - -struct _McdStorageAgHidden { - McdAccountManagerSso parent; -}; - -GType mcd_storage_ag_hidden_get_type (void); - -McdStorageAgHidden *mcd_storage_ag_hidden_new (void); - -/* TYPE MACROS */ -#define MCD_TYPE_STORAGE_AG_HIDDEN \ - (mcd_storage_ag_hidden_get_type ()) -#define MCD_STORAGE_AG_HIDDEN(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), MCD_TYPE_STORAGE_AG_HIDDEN, McdStorageAgHidden)) -#define MCD_STORAGE_AG_HIDDEN_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), MCD_TYPE_STORAGE_AG_HIDDEN,\ - McdStorageAgHiddenClass)) -#define MCD_IS_STORAGE_AG_HIDDEN(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), MCD_TYPE_STORAGE_AG_HIDDEN)) -#define MCD_IS_STORAGE_AG_HIDDEN_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), MCD_TYPE_STORAGE_AG_HIDDEN)) -#define MCD_STORAGE_AG_HIDDEN_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), MCD_TYPE_STORAGE_AG_HIDDEN, \ - McdStorageAgHiddenClass)) - -#endif /* MCD_STORAGE_AG_HIDDEN_H */ diff --git a/src/mcd-storage.c b/src/mcd-storage.c index 1a0ebbf5..fea76c2a 100644 --- a/src/mcd-storage.c +++ b/src/mcd-storage.c @@ -32,6 +32,7 @@ #include <errno.h> #include <string.h> +#include <dbus/dbus.h> #include <telepathy-glib/telepathy-glib.h> #include <telepathy-glib/telepathy-glib-dbus.h> @@ -40,13 +41,6 @@ /* these pseudo-plugins take care of the actual account storage/retrieval */ #include "mcd-account-manager-default.h" -#if ENABLE_LIBACCOUNTS_SSO -#include "mcd-account-manager-sso.h" -# ifdef ACCOUNTS_GLIB_HIDDEN_SERVICE_TYPE -# include "mcd-storage-ag-hidden.h" -# endif -#endif - #define MAX_KEY_LENGTH (DBUS_MAXIMUM_NAME_LENGTH + 6) static GList *stores = NULL; @@ -56,6 +50,17 @@ enum { PROP_DBUS_DAEMON = 1, }; +enum { + SIGNAL_CREATED, + SIGNAL_TOGGLED, + SIGNAL_DELETED, + SIGNAL_ALTERED_ONE, + SIGNAL_RECONNECT, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0 }; + struct _McdStorageClass { GObjectClass parent; }; @@ -67,39 +72,11 @@ G_DEFINE_TYPE_WITH_CODE (McdStorage, mcd_storage, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_MANAGER, plugin_iface_init)) -typedef struct { - /* owned string => GVariant - * e.g. { 'DisplayName': <'Frederick Bloggs'> } */ - GHashTable *attributes; - /* owned string => owned GVariant - * e.g. { 'account': <'fred@example.com'>, 'password': <'foo'> } */ - GHashTable *parameters; - /* owned string => owned string escaped as if for a keyfile - * e.g. { 'account': 'fred@example.com', 'password': 'foo' } - * keys of @parameters and @escaped_parameters are disjoint */ - GHashTable *escaped_parameters; - /* set of owned strings - * e.g. { 'password': 'password' } */ - GHashTable *secrets; -} McdStorageAccount; - -static void -mcd_storage_account_free (gpointer p) -{ - McdStorageAccount *sa = p; - - g_hash_table_unref (sa->attributes); - g_hash_table_unref (sa->parameters); - g_hash_table_unref (sa->escaped_parameters); - g_hash_table_unref (sa->secrets); - g_slice_free (McdStorageAccount, sa); -} - static void mcd_storage_init (McdStorage *self) { self->accounts = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, mcd_storage_account_free); + g_free, g_object_unref); } static void @@ -181,6 +158,31 @@ mcd_storage_class_init (McdStorageClass *cls) object_class->finalize = storage_finalize; g_object_class_install_property (object_class, PROP_DBUS_DAEMON, spec); + + signals[SIGNAL_CREATED] = g_signal_new ("created", + MCD_TYPE_STORAGE, G_SIGNAL_RUN_LAST, 0, NULL, NULL, + NULL, G_TYPE_NONE, + 2, MCP_TYPE_ACCOUNT_STORAGE, G_TYPE_STRING); + + signals[SIGNAL_ALTERED_ONE] = g_signal_new ("altered-one", + MCD_TYPE_STORAGE, G_SIGNAL_RUN_LAST, 0, NULL, NULL, + NULL, G_TYPE_NONE, + 3, MCP_TYPE_ACCOUNT_STORAGE, G_TYPE_STRING, G_TYPE_STRING); + + signals[SIGNAL_DELETED] = g_signal_new ("deleted", + MCD_TYPE_STORAGE, G_SIGNAL_RUN_LAST, 0, NULL, NULL, + NULL, G_TYPE_NONE, + 2, MCP_TYPE_ACCOUNT_STORAGE, G_TYPE_STRING); + + signals[SIGNAL_TOGGLED] = g_signal_new ("toggled", + MCD_TYPE_STORAGE, G_SIGNAL_RUN_LAST, 0, NULL, NULL, + NULL, G_TYPE_NONE, + 3, MCP_TYPE_ACCOUNT_STORAGE, G_TYPE_STRING, G_TYPE_BOOLEAN); + + signals[SIGNAL_RECONNECT] = g_signal_new ("reconnect", + MCD_TYPE_STORAGE, G_SIGNAL_RUN_LAST, 0, NULL, NULL, + NULL, G_TYPE_NONE, + 2, MCP_TYPE_ACCOUNT_STORAGE, G_TYPE_STRING); } McdStorage * @@ -194,106 +196,18 @@ mcd_storage_new (TpDBusDaemon *dbus_daemon) static gchar * mcd_keyfile_escape_variant (GVariant *variant) { - GValue value = G_VALUE_INIT; + GKeyFile *keyfile; gchar *ret; - dbus_g_value_parse_g_variant (variant, &value); - - if (G_IS_VALUE (&value)) - { - ret = mcd_keyfile_escape_value (&value); - g_value_unset (&value); - } - else - { - gchar *printed = g_variant_print (variant, TRUE); - - ret = NULL; - g_warning ("Unable to translate variant %s", printed); - g_free (printed); - } + g_return_val_if_fail (variant != NULL, NULL); + keyfile = g_key_file_new (); + mcd_keyfile_set_variant (keyfile, "g", "k", variant); + ret = g_key_file_get_value (keyfile, "g", "k", NULL); + g_key_file_free (keyfile); return ret; } -static McdStorageAccount * -lookup_account (McdStorage *self, - const gchar *account) -{ - return g_hash_table_lookup (self->accounts, account); -} - -static McdStorageAccount * -ensure_account (McdStorage *self, - const gchar *account) -{ - McdStorageAccount *sa = lookup_account (self, account); - - if (sa == NULL) - { - sa = g_slice_new (McdStorageAccount); - sa->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) g_variant_unref); - sa->parameters = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) g_variant_unref); - sa->escaped_parameters = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_free); - sa->secrets = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - g_hash_table_insert (self->accounts, g_strdup (account), sa); - } - - return sa; -} - -static gchar * -get_value (const McpAccountManager *ma, - const gchar *account, - const gchar *key) -{ - McdStorage *self = MCD_STORAGE (ma); - McdStorageAccount *sa = lookup_account (self, account); - GVariant *variant; - gchar *ret; - - if (sa == NULL) - return NULL; - - if (g_str_has_prefix (key, "param-")) - { - variant = g_hash_table_lookup (sa->parameters, key + 6); - - if (variant != NULL) - { - ret = mcd_keyfile_escape_variant (variant); - g_variant_unref (variant); - return ret; - } - else - { - /* OK, we don't have it as a variant. How about the keyfile-escaped - * version? */ - return g_strdup (g_hash_table_lookup (sa->escaped_parameters, - key + 6)); - } - } - else - { - variant = g_hash_table_lookup (sa->attributes, key); - - if (variant != NULL) - { - ret = mcd_keyfile_escape_variant (variant); - g_variant_unref (variant); - return ret; - } - else - { - return NULL; - } - } -} - static struct { const gchar *type; const gchar *name; @@ -314,7 +228,6 @@ static struct { { "b", MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY }, { "b", MC_ACCOUNTS_KEY_ENABLED }, { "b", MC_ACCOUNTS_KEY_HAS_BEEN_ONLINE }, - { "b", MC_ACCOUNTS_KEY_HIDDEN }, /* Strings */ { "s", MC_ACCOUNTS_KEY_AUTO_PRESENCE_MESSAGE }, @@ -335,7 +248,7 @@ static struct { { NULL, NULL } }; -const gchar * +const GVariantType * mcd_storage_get_attribute_type (const gchar *attribute) { guint i; @@ -343,26 +256,26 @@ mcd_storage_get_attribute_type (const gchar *attribute) for (i = 0; known_attributes[i].type != NULL; i++) { if (!tp_strdiff (attribute, known_attributes[i].name)) - return known_attributes[i].type; + return G_VARIANT_TYPE (known_attributes[i].type); } - /* special case for mcd-account-conditions.c */ - if (g_str_has_prefix (attribute, "condition-")) - return "s"; - return NULL; } gboolean mcd_storage_init_value_for_attribute (GValue *value, - const gchar *attribute) + const gchar *attribute, + const GVariantType **variant_type) { - const gchar *s = mcd_storage_get_attribute_type (attribute); + const GVariantType *s = mcd_storage_get_attribute_type (attribute); if (s == NULL) return FALSE; - switch (s[0]) + if (variant_type != NULL) + *variant_type = s; + + switch (g_variant_type_peek_string (s)[0]) { case 's': g_value_init (value, G_TYPE_STRING); @@ -379,7 +292,7 @@ mcd_storage_init_value_for_attribute (GValue *value, case 'a': { - switch (s[1]) + switch (g_variant_type_peek_string (s)[1]) { case 'o': g_value_init (value, TP_ARRAY_TYPE_OBJECT_PATH_LIST); @@ -394,7 +307,7 @@ mcd_storage_init_value_for_attribute (GValue *value, case '(': { - if (!tp_strdiff (s, "(uss)")) + if (g_variant_type_equal (s, G_VARIANT_TYPE ("(uss)"))) { g_value_init (value, TP_STRUCT_TYPE_SIMPLE_PRESENCE); return TRUE; @@ -406,230 +319,134 @@ mcd_storage_init_value_for_attribute (GValue *value, return FALSE; } -static gboolean -mcpa_init_value_for_attribute (const McpAccountManager *mcpa, - GValue *value, - const gchar *attribute) -{ - return mcd_storage_init_value_for_attribute (value, attribute); -} - -static void -mcpa_set_attribute (const McpAccountManager *ma, - const gchar *account, - const gchar *attribute, - GVariant *value, - McpAttributeFlags flags) +static gchar * +unique_name (const McpAccountManager *ma, + const gchar *manager, + const gchar *protocol, + const gchar *identification) { McdStorage *self = MCD_STORAGE (ma); - McdStorageAccount *sa = ensure_account (self, account); + gchar *esc_manager, *esc_protocol, *esc_base; + guint i; + gsize base_len = strlen (TP_ACCOUNT_OBJECT_PATH_BASE); + DBusGConnection *connection = tp_proxy_get_dbus_connection (self->dbusd); - if (value != NULL) - { - g_hash_table_insert (sa->attributes, g_strdup (attribute), - g_variant_ref_sink (value)); - } - else - { - g_hash_table_remove (sa->attributes, attribute); - } -} + esc_manager = tp_escape_as_identifier (manager); + esc_protocol = g_strdelimit (g_strdup (protocol), "-", '_'); + esc_base = tp_escape_as_identifier (identification); -static void -mcpa_set_parameter (const McpAccountManager *ma, - const gchar *account, - const gchar *parameter, - GVariant *value, - McpParameterFlags flags) -{ - McdStorage *self = MCD_STORAGE (ma); - McdStorageAccount *sa = ensure_account (self, account); + for (i = 0; i < G_MAXUINT; i++) + { + gchar *path = g_strdup_printf ( + TP_ACCOUNT_OBJECT_PATH_BASE "%s/%s/%s%u", + esc_manager, esc_protocol, esc_base, i); - g_hash_table_remove (sa->parameters, parameter); - g_hash_table_remove (sa->escaped_parameters, parameter); + if (!g_hash_table_contains (self->accounts, path + base_len) && + dbus_g_connection_lookup_g_object (connection, path) == NULL) + { + gchar *ret = g_strdup (path + base_len); - if (value != NULL) - g_hash_table_insert (sa->parameters, g_strdup (parameter), - g_variant_ref_sink (value)); + g_free (path); + return ret; + } - if (flags & MCP_PARAMETER_FLAG_SECRET) - { - DEBUG ("flagging %s parameter %s as secret", account, parameter); - g_hash_table_add (sa->secrets, g_strdup (parameter)); + g_free (path); } + + return NULL; } static void -set_value (const McpAccountManager *ma, - const gchar *account, - const gchar *key, - const gchar *value) +identify_account_cb (TpProxy *proxy, + const gchar *identification, + const GError *error, + gpointer task, + GObject *weak_object G_GNUC_UNUSED) { - McdStorage *self = MCD_STORAGE (ma); - McdStorageAccount *sa = ensure_account (self, account); - - if (g_str_has_prefix (key, "param-")) + if (error == NULL) { - g_hash_table_remove (sa->parameters, key + 6); - g_hash_table_remove (sa->escaped_parameters, key + 6); - - if (value != NULL) - g_hash_table_insert (sa->escaped_parameters, g_strdup (key + 6), - g_strdup (value)); + DEBUG ("identified account: %s", identification); + g_task_return_pointer (task, g_strdup (identification), g_free); } - else + else if (g_error_matches (error, TP_ERROR, TP_ERROR_INVALID_HANDLE) || + g_error_matches (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT)) { - if (value != NULL) - { - GValue tmp = G_VALUE_INIT; - GError *error = NULL; - - if (!mcd_storage_init_value_for_attribute (&tmp, key)) - { - g_warning ("Not sure what the type of '%s' is, assuming string", - key); - g_value_init (&tmp, G_TYPE_STRING); - } - - if (mcd_keyfile_unescape_value (value, &tmp, &error)) - { - g_hash_table_insert (sa->attributes, g_strdup (key), - g_variant_ref_sink (dbus_g_value_build_g_variant (&tmp))); - g_value_unset (&tmp); - } - else - { - g_warning ("Could not decode attribute '%s':'%s' from plugin: %s", - key, value, error->message); - g_error_free (error); - g_hash_table_remove (sa->attributes, key); - } - } - else - { - g_hash_table_remove (sa->attributes, key); - } + /* The connection manager didn't like our account parameters. + * Give up now. */ + DEBUG ("failed to identify account: %s #%d: %s", + g_quark_to_string (error->domain), error->code, error->message); + g_task_return_error (task, g_error_copy (error)); } -} - -static GStrv -list_keys (const McpAccountManager *ma, - const gchar * account) -{ - McdStorage *self = MCD_STORAGE (ma); - GPtrArray *ret = g_ptr_array_new (); - McdStorageAccount *sa = lookup_account (self, account); - - if (sa != NULL) + else { - GHashTableIter iter; - gpointer k; - - g_hash_table_iter_init (&iter, sa->attributes); - - while (g_hash_table_iter_next (&iter, &k, NULL)) - g_ptr_array_add (ret, g_strdup (k)); - - g_hash_table_iter_init (&iter, sa->parameters); - - while (g_hash_table_iter_next (&iter, &k, NULL)) - g_ptr_array_add (ret, g_strdup_printf ("param-%s", (gchar *) k)); + /* We weren't able to identify the account, but carry on and hope + * for the best... */ + DEBUG ("ignoring failure to identify account: %s #%d: %s", + g_quark_to_string (error->domain), error->code, error->message); + g_task_return_pointer (task, g_strdup (g_task_get_task_data (task)), + g_free); } - - g_ptr_array_add (ret, NULL); - return (GStrv) g_ptr_array_free (ret, FALSE); -} - -static gboolean -is_secret (const McpAccountManager *ma, - const gchar *account, - const gchar *key) -{ - McdStorage *self = MCD_STORAGE (ma); - McdStorageAccount *sa = lookup_account (self, account); - - if (sa == NULL || !g_str_has_prefix (key, "param-")) - return FALSE; - - return g_hash_table_contains (sa->secrets, key + 6); } -static void -mcd_storage_make_secret (McdStorage *self, - const gchar *account, - const gchar *key) +static gchar * +identify_account_finish (McpAccountManager *mcpa, + GAsyncResult *result, + GError **error) { - McdStorageAccount *sa; + g_return_val_if_fail (g_task_is_valid (result, mcpa), NULL); - g_return_if_fail (MCD_IS_STORAGE (self)); - g_return_if_fail (account != NULL); - g_return_if_fail (key != NULL); - - if (!g_str_has_prefix (key, "param-")) - return; - - DEBUG ("flagging %s parameter %s as secret", account, key + 6); - sa = ensure_account (self, account); - g_hash_table_add (sa->secrets, g_strdup (key + 6)); + return g_task_propagate_pointer (G_TASK (result), error); } static void -make_secret (const McpAccountManager *ma, - const gchar *account, - const gchar *key) -{ - mcd_storage_make_secret (MCD_STORAGE (ma), account, key); -} - -static gchar * -unique_name (const McpAccountManager *ma, +identify_account_async (McpAccountManager *mcpa, const gchar *manager, - const gchar *protocol, - const GHashTable *params) + const gchar *protocol_name, + GVariant *parameters, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - McdStorage *self = MCD_STORAGE (ma); - const gchar *base = NULL; - gchar *esc_manager, *esc_protocol, *esc_base; - guint i; - gsize base_len = strlen (TP_ACCOUNT_OBJECT_PATH_BASE); - DBusGConnection *connection = tp_proxy_get_dbus_connection (self->dbusd); + McdStorage *self = MCD_STORAGE (mcpa); + GError *error = NULL; + TpProtocol *protocol; + GTask *task; + GValue value = G_VALUE_INIT; + const gchar *base; - base = tp_asv_get_string (params, "account"); + task = g_task_new (self, cancellable, callback, user_data); - if (base == NULL) + /* in case IdentifyAccount fails and we need to make something up */ + if (!g_variant_lookup (parameters, "account", "&s", &base)) base = "account"; - esc_manager = tp_escape_as_identifier (manager); - esc_protocol = g_strdelimit (g_strdup (protocol), "-", '_'); - esc_base = tp_escape_as_identifier (base); - - for (i = 0; i < G_MAXUINT; i++) - { - gchar *path = g_strdup_printf ( - TP_ACCOUNT_OBJECT_PATH_BASE "%s/%s/%s%u", - esc_manager, esc_protocol, esc_base, i); + g_task_set_task_data (task, g_strdup (base), g_free); - if (!g_hash_table_contains (self->accounts, path + base_len) && - dbus_g_connection_lookup_g_object (connection, path) == NULL) - { - gchar *ret = g_strdup (path + base_len); + protocol = tp_protocol_new (self->dbusd, manager, protocol_name, + NULL, &error); - g_free (path); - return ret; - } - - g_free (path); + if (protocol == NULL) + { + g_task_return_error (task, error); + g_object_unref (task); + return; } - return NULL; + dbus_g_value_parse_g_variant (parameters, &value); + + tp_cli_protocol_call_identify_account (protocol, -1, + g_value_get_boxed (&value), identify_account_cb, task, g_object_unref, + NULL); + g_object_unref (protocol); + g_value_unset (&value); } /* sort in descending order of priority (ie higher prio => earlier in list) */ static gint account_storage_cmp (gconstpointer a, gconstpointer b) { - gint pa = mcp_account_storage_priority (a); - gint pb = mcp_account_storage_priority (b); + gint pa = mcp_account_storage_priority ((McpAccountStorage *) a); + gint pb = mcp_account_storage_priority ((McpAccountStorage *) b); if (pa > pb) return -1; if (pa < pb) return 1; @@ -644,17 +461,6 @@ add_storage_plugin (McpAccountStorage *plugin) } static void -add_libaccounts_plugins_if_enabled (void) -{ -#if ENABLE_LIBACCOUNTS_SSO - add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_account_manager_sso_new ())); -# ifdef ACCOUNTS_GLIB_HIDDEN_SERVICE_TYPE - add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_storage_ag_hidden_new ())); -# endif -#endif -} - -static void sort_and_cache_plugins () { const GList *p; @@ -668,7 +474,6 @@ sort_and_cache_plugins () /* Add compiled-in plugins */ add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_account_manager_default_new ())); - add_libaccounts_plugins_if_enabled (); for (p = mcp_list_objects(); p != NULL; p = g_list_next (p)) { @@ -694,23 +499,125 @@ sort_and_cache_plugins () plugins_cached = TRUE; } -void -mcd_storage_connect_signal (const gchar *signame, - GCallback func, - gpointer user_data) +static void +created_cb (McpAccountStorage *plugin, + const gchar *account_name, + McdStorage *self) { - GList *p; + GError *error = NULL; - for (p = stores; p != NULL; p = g_list_next (p)) + g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin)); + g_return_if_fail (MCD_IS_STORAGE (self)); + + if (mcd_storage_add_account_from_plugin (self, plugin, account_name, &error)) { - McpAccountStorage *plugin = p->data; + DEBUG ("%s", account_name); + g_signal_emit (self, signals[SIGNAL_CREATED], 0, plugin, account_name); + } + else + { + WARNING ("%s", error->message); + g_error_free (error); + } +} + +static gboolean +check_is_responsible (McdStorage *self, + McpAccountStorage *plugin, + const gchar *account_name, + const gchar *changing, + GError **error) +{ + McpAccountStorage *other = g_hash_table_lookup (self->accounts, + account_name); + + if (other == NULL) + { + WARNING ("account %s does not exist, preventing plugin '%s' from " + "%s it", account_name, mcp_account_storage_name (plugin), changing); + return FALSE; + } + + if (other != plugin) + { + WARNING ("account %s is in plugin '%s', preventing plugin '%s' from " + "%s it", account_name, mcp_account_storage_name (other), + mcp_account_storage_name (plugin), changing); + return FALSE; + } + + return TRUE; +} + +static void +toggled_cb (McpAccountStorage *plugin, + const gchar *account_name, + gboolean on, + McdStorage *self) +{ + GError *error = NULL; + + g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin)); + g_return_if_fail (MCD_IS_STORAGE (self)); - DEBUG ("connecting handler to %s plugin signal %s ", - mcp_account_storage_name (plugin), signame); - g_signal_connect (plugin, signame, func, user_data); + if (check_is_responsible (self, plugin, account_name, "toggling", &error)) + g_signal_emit (self, signals[SIGNAL_TOGGLED], 0, plugin, + account_name, on); +} + +static void +deleted_cb (McpAccountStorage *plugin, + const gchar *account_name, + McdStorage *self) +{ + GError *error = NULL; + + g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin)); + g_return_if_fail (MCD_IS_STORAGE (self)); + + if (check_is_responsible (self, plugin, account_name, "deleting", + &error)) + { + g_hash_table_remove (self->accounts, account_name); + + g_signal_emit (self, signals[SIGNAL_DELETED], 0, plugin, + account_name); } } +static void +altered_one_cb (McpAccountStorage *plugin, + const gchar *account_name, + const gchar *key, + McdStorage *self) +{ + GError *error = NULL; + + g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin)); + g_return_if_fail (MCD_IS_STORAGE (self)); + + if (check_is_responsible (self, plugin, account_name, "altering", + &error)) + g_signal_emit (self, signals[SIGNAL_ALTERED_ONE], 0, plugin, + account_name, key); +} + +static void +reconnect_cb (McpAccountStorage *plugin, + const gchar *account_name, + McdStorage *self) +{ + GError *error = NULL; + + g_return_if_fail (MCP_IS_ACCOUNT_STORAGE (plugin)); + g_return_if_fail (MCD_IS_STORAGE (self)); + + if (check_is_responsible (self, plugin, account_name, "reconnecting", + &error)) + g_signal_emit (self, signals[SIGNAL_RECONNECT], 0, plugin, + account_name); +} + /* * mcd_storage_load: * @storage: An object implementing the #McdStorage interface @@ -729,95 +636,68 @@ mcd_storage_load (McdStorage *self) sort_and_cache_plugins (); - store = g_list_last (stores); - - /* fetch accounts stored in plugins, in reverse priority so higher prio * - * plugins can overwrite lower prio ones' account data */ - while (store != NULL) + /* fetch accounts stored in plugins, highest priority first, so that + * low priority plugins can be overidden by high priority */ + for (store = stores; store != NULL; store = store->next) { GList *account; McpAccountStorage *plugin = store->data; - GList *stored = mcp_account_storage_list (plugin, ma); + GList *stored; const gchar *pname = mcp_account_storage_name (plugin); const gint prio = mcp_account_storage_priority (plugin); - DEBUG ("listing from plugin %s [prio: %d]", pname, prio); + DEBUG ("listing initial accounts from plugin %s [prio: %d]", pname, prio); + stored = mcp_account_storage_list (plugin, ma); + + /* Connect to signals for non-initial accounts. We only do this + * after we have called list(), to make sure the plugins don't need + * to queue up change-notification signals until after we've + * called the old "ready" vfunc. */ + g_signal_connect_object (plugin, "created", G_CALLBACK (created_cb), + self, 0); + g_signal_connect_object (plugin, "toggled", G_CALLBACK (toggled_cb), + self, 0); + g_signal_connect_object (plugin, "deleted", G_CALLBACK (deleted_cb), + self, 0); + g_signal_connect_object (plugin, "altered-one", + G_CALLBACK (altered_one_cb), self, 0); + g_signal_connect_object (plugin, "reconnect", G_CALLBACK (reconnect_cb), + self, 0); + for (account = stored; account != NULL; account = g_list_next (account)) { + GError *error = NULL; gchar *name = account->data; DEBUG ("fetching %s from plugin %s [prio: %d]", name, pname, prio); - mcd_storage_add_account_from_plugin (self, plugin, name); + + if (!mcd_storage_add_account_from_plugin (self, plugin, name, + &error)) + { + DEBUG ("%s", error->message); + g_clear_error (&error); + } + g_free (name); } /* already freed the contents, just need to free the list itself */ g_list_free (stored); - store = g_list_previous (store); } } /* - * mcd_storage_dup_accounts: + * mcd_storage_get_accounts: * @storage: An object implementing the #McdStorage interface * @n: place for the number of accounts to be written to (or %NULL) * - * Returns: a newly allocated GStrv containing the unique account names, - * which must be freed by the caller with g_strfreev(). + * Returns: (transfer none) (element-type utf8 Mcp.AccountStorage): a + * map from account object path tail to plugin */ -GStrv -mcd_storage_dup_accounts (McdStorage *self, - gsize *n) +GHashTable * +mcd_storage_get_accounts (McdStorage *self) { - GPtrArray *ret = g_ptr_array_new (); - GHashTableIter iter; - gpointer k, v; - - g_hash_table_iter_init (&iter, self->accounts); - - while (g_hash_table_iter_next (&iter, &k, &v)) - { - McdStorageAccount *sa = v; - - if (g_hash_table_size (sa->attributes) > 0) - g_ptr_array_add (ret, g_strdup (k)); - } - - g_ptr_array_add (ret, NULL); - return (GStrv) g_ptr_array_free (ret, FALSE); -} - -/* - * mcd_storage_dup_attributes: - * @storage: An object implementing the #McdStorage interface - * @account: unique name of the account - * @n: place for the number of attributes to be written to (or %NULL) - * - * Returns: a newly allocated GStrv containing the names of all the - * attributes or parameters currently stored for @account. Must be - * freed by the caller with g_strfreev(). - */ -GStrv -mcd_storage_dup_attributes (McdStorage *self, - const gchar *account, - gsize *n) -{ - GPtrArray *ret = g_ptr_array_new (); - McdStorageAccount *sa = lookup_account (self, account); - - if (sa != NULL) - { - GHashTableIter iter; - gpointer k; - - g_hash_table_iter_init (&iter, sa->attributes); - - while (g_hash_table_iter_next (&iter, &k, NULL)) - g_ptr_array_add (ret, g_strdup (k)); - } - - g_ptr_array_add (ret, NULL); - return (GStrv) g_ptr_array_free (ret, FALSE); + return self->accounts; } /* @@ -837,22 +717,15 @@ McpAccountStorage * mcd_storage_get_plugin (McdStorage *self, const gchar *account) { - GList *store = stores; - McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); - McpAccountStorage *owner = NULL; + McpAccountStorage *plugin; g_return_val_if_fail (MCD_IS_STORAGE (self), NULL); g_return_val_if_fail (account != NULL, NULL); - for (; store != NULL && owner == NULL; store = g_list_next (store)) - { - McpAccountStorage *plugin = store->data; - - if (mcp_account_storage_owns (plugin, ma, account)) - owner = plugin; - } + plugin = g_hash_table_lookup (self->accounts, account); + g_return_val_if_fail (plugin != NULL, NULL); - return owner; + return plugin; } /* @@ -879,7 +752,8 @@ mcd_storage_dup_string (McdStorage *self, g_value_init (&tmp, G_TYPE_STRING); - if (!mcd_storage_get_attribute (self, account, attribute, &tmp, NULL)) + if (!mcd_storage_get_attribute (self, account, attribute, + G_VARIANT_TYPE_STRING, &tmp, NULL)) return NULL; ret = g_value_dup_string (&tmp); @@ -907,10 +781,10 @@ mcd_storage_coerce_variant_to_value (GVariant *variant, /* This is really pretty stupid but it'll do for now. * FIXME: implement a better similar-type-coercion mechanism than * round-tripping through a GKeyFile. */ - escaped = mcd_keyfile_escape_value (&tmp); + g_value_unset (&tmp); + escaped = mcd_keyfile_escape_variant (variant); ret = mcd_keyfile_unescape_value (escaped, value, error); g_free (escaped); - g_value_unset (&tmp); return ret; } @@ -926,36 +800,42 @@ gboolean mcd_storage_get_attribute (McdStorage *self, const gchar *account, const gchar *attribute, + const GVariantType *type, GValue *value, GError **error) { - McdStorageAccount *sa; + McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); + McpAccountStorage *plugin; GVariant *variant; + gboolean ret; g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE); g_return_val_if_fail (account != NULL, FALSE); g_return_val_if_fail (attribute != NULL, FALSE); g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), FALSE); - sa = lookup_account (self, account); + plugin = g_hash_table_lookup (self->accounts, account); - if (sa == NULL) + if (plugin == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Account %s does not exist", account); return FALSE; } - variant = g_hash_table_lookup (sa->attributes, attribute); + variant = mcp_account_storage_get_attribute (plugin, ma, account, + attribute, type, NULL); if (variant == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, - "Setting '%s' not stored by account %s", attribute, account); + "Account %s has no attribute '%s'", account, attribute); return FALSE; } - return mcd_storage_coerce_variant_to_value (variant, value, error); + ret = mcd_storage_coerce_variant_to_value (variant, value, error); + g_variant_unref (variant); + return ret; } /* @@ -970,52 +850,42 @@ gboolean mcd_storage_get_parameter (McdStorage *self, const gchar *account, const gchar *parameter, + const GVariantType *type, GValue *value, GError **error) { - McdStorageAccount *sa; - const gchar *escaped; + McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); + McpAccountStorage *plugin; GVariant *variant; + gboolean ret; g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE); g_return_val_if_fail (account != NULL, FALSE); g_return_val_if_fail (parameter != NULL, FALSE); + g_return_val_if_fail (!g_str_has_prefix (parameter, "param-"), FALSE); - sa = lookup_account (self, account); + plugin = g_hash_table_lookup (self->accounts, account); - if (sa == NULL) + if (plugin == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Account %s does not exist", account); return FALSE; } - variant = g_hash_table_lookup (sa->parameters, parameter); + variant = mcp_account_storage_get_parameter (plugin, ma, account, + parameter, type, NULL); - if (variant != NULL) - return mcd_storage_coerce_variant_to_value (variant, value, error); - - /* OK, we don't have it as a variant. How about the keyfile-escaped - * version? */ - escaped = g_hash_table_lookup (sa->escaped_parameters, parameter); - - if (escaped == NULL) + if (variant == NULL) { g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, - "Parameter '%s' not stored by account %s", parameter, account); + "Account %s has no parameter '%s'", account, parameter); return FALSE; } - return mcd_keyfile_unescape_value (escaped, value, error); -} - -static gboolean -mcpa_unescape_value_from_keyfile (const McpAccountManager *unused G_GNUC_UNUSED, - const gchar *escaped, - GValue *value, - GError **error) -{ - return mcd_keyfile_unescape_value (escaped, value, error); + ret = mcd_storage_coerce_variant_to_value (variant, value, error); + g_variant_unref (variant); + return ret; } /* @@ -1061,8 +931,8 @@ mcd_keyfile_get_value (GKeyFile *keyfile, GValue *value, GError **error) { - gboolean ret = FALSE; GType type; + GVariant *variant = NULL; g_return_val_if_fail (keyfile != NULL, FALSE); g_return_val_if_fail (group != NULL, FALSE); @@ -1074,20 +944,155 @@ mcd_keyfile_get_value (GKeyFile *keyfile, switch (type) { case G_TYPE_STRING: + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_STRING, error); + break; + + case G_TYPE_INT: + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_INT32, error); + break; + + case G_TYPE_INT64: + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_INT64, error); + break; + + case G_TYPE_UINT: + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_UINT32, error); + break; + + case G_TYPE_UCHAR: + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_BYTE, error); + break; + + case G_TYPE_UINT64: + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_UINT64, error); + break; + + case G_TYPE_BOOLEAN: + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_BOOLEAN, error); + break; + + case G_TYPE_DOUBLE: + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_DOUBLE, error); + break; + + default: + if (type == G_TYPE_STRV) + { + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_STRING_ARRAY, error); + } + else if (type == DBUS_TYPE_G_OBJECT_PATH) + { + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_OBJECT_PATH, error); + } + else if (type == TP_ARRAY_TYPE_OBJECT_PATH_LIST) + { + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE_OBJECT_PATH_ARRAY, error); + } + else if (type == TP_STRUCT_TYPE_SIMPLE_PRESENCE) + { + variant = mcd_keyfile_get_variant (keyfile, group, key, + G_VARIANT_TYPE ("(uss)"), error); + } + else + { + gchar *message = + g_strdup_printf ("cannot get key %s from group %s: " + "unknown type %s", + key, group, g_type_name (type)); + + g_warning ("%s: %s", G_STRFUNC, message); + g_set_error (error, MCD_ACCOUNT_ERROR, + MCD_ACCOUNT_ERROR_GET_PARAMETER, + "%s", message); + g_free (message); + } + } + + if (variant == NULL) + return FALSE; + + g_variant_ref_sink (variant); + g_value_unset (value); + dbus_g_value_parse_g_variant (variant, value); + g_assert (G_VALUE_TYPE (value) == type); + g_variant_unref (variant); + return TRUE; +} + +/* + * mcd_keyfile_get_variant: + * @keyfile: A #GKeyFile + * @group: name of a group + * @key: name of a key + * @type: the desired type + * @error: a place to store any #GError<!-- -->s that occur + * + * Returns: a new floating #GVariant + */ +GVariant * +mcd_keyfile_get_variant (GKeyFile *keyfile, + const gchar *group, + const gchar *key, + const GVariantType *type, + GError **error) +{ + const gchar *type_str = g_variant_type_peek_string (type); + GVariant *ret = NULL; + + g_return_val_if_fail (keyfile != NULL, NULL); + g_return_val_if_fail (group != NULL, NULL); + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (g_variant_type_string_scan (type_str, NULL, NULL), + NULL); + + switch (type_str[0]) + { + case G_VARIANT_CLASS_STRING: { gchar *v_string = g_key_file_get_string (keyfile, group, key, error); if (v_string != NULL) + ret = g_variant_new_string (v_string); + /* else error is already set */ + } + break; + + case G_VARIANT_CLASS_INT16: + { + GError *e = NULL; + gint v_int = g_key_file_get_integer (keyfile, group, + key, &e); + + if (e != NULL) { - g_value_take_string (value, v_string); - ret = TRUE; + g_propagate_error (error, e); + } + else if (v_int < G_MININT16 || v_int > G_MAXINT16) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "integer %d out of range [%d,%d]", + v_int, G_MININT16, G_MAXINT16); + } + else + { + ret = g_variant_new_int16 (v_int); } - /* else error is already set */ } break; - case G_TYPE_INT: + case G_VARIANT_CLASS_INT32: { GError *e = NULL; gint v_int = g_key_file_get_integer (keyfile, group, @@ -1099,13 +1104,12 @@ mcd_keyfile_get_value (GKeyFile *keyfile, } else { - g_value_set_int (value, v_int); - ret = TRUE; + ret = g_variant_new_int32 (v_int); } } break; - case G_TYPE_INT64: + case G_VARIANT_CLASS_INT64: { GError *e = NULL; gint64 v_int = g_key_file_get_int64 (keyfile, group, @@ -1117,13 +1121,34 @@ mcd_keyfile_get_value (GKeyFile *keyfile, } else { - g_value_set_int64 (value, v_int); - ret = TRUE; + ret = g_variant_new_int64 (v_int); } } break; - case G_TYPE_UINT: + case G_VARIANT_CLASS_UINT16: + { + GError *e = NULL; + gint v_int = g_key_file_get_integer (keyfile, group, + key, &e); + + if (e != NULL) + { + g_propagate_error (error, e); + } + else if (v_int < 0 || (unsigned) v_int > G_MAXUINT16) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "integer %d out of range [0,%d]", v_int, G_MAXUINT16); + } + else + { + ret = g_variant_new_uint16 (v_int); + } + } + break; + + case G_VARIANT_CLASS_UINT32: { GError *e = NULL; guint64 v_uint = g_key_file_get_uint64 (keyfile, group, @@ -1142,13 +1167,12 @@ mcd_keyfile_get_value (GKeyFile *keyfile, } else { - g_value_set_uint (value, v_uint); - ret = TRUE; + ret = g_variant_new_uint32 (v_uint); } } break; - case G_TYPE_UCHAR: + case G_VARIANT_CLASS_BYTE: { GError *e = NULL; gint v_int = g_key_file_get_integer (keyfile, group, @@ -1167,13 +1191,12 @@ mcd_keyfile_get_value (GKeyFile *keyfile, } else { - g_value_set_uchar (value, v_int); - ret = TRUE; + ret = g_variant_new_byte (v_int); } } break; - case G_TYPE_UINT64: + case G_VARIANT_CLASS_UINT64: { GError *e = NULL; guint64 v_uint = g_key_file_get_uint64 (keyfile, group, @@ -1185,13 +1208,12 @@ mcd_keyfile_get_value (GKeyFile *keyfile, } else { - g_value_set_uint64 (value, v_uint); - ret = TRUE; + ret = g_variant_new_uint64 (v_uint); } } break; - case G_TYPE_BOOLEAN: + case G_VARIANT_CLASS_BOOLEAN: { GError *e = NULL; gboolean v_bool = g_key_file_get_boolean (keyfile, group, @@ -1203,13 +1225,12 @@ mcd_keyfile_get_value (GKeyFile *keyfile, } else { - g_value_set_boolean (value, v_bool); - ret = TRUE; + ret = g_variant_new_boolean (v_bool); } } break; - case G_TYPE_DOUBLE: + case G_VARIANT_CLASS_DOUBLE: { GError *e = NULL; gdouble v_double = g_key_file_get_double (keyfile, group, @@ -1221,25 +1242,24 @@ mcd_keyfile_get_value (GKeyFile *keyfile, } else { - g_value_set_double (value, v_double); - ret = TRUE; + ret = g_variant_new_double (v_double); } } break; default: - if (type == G_TYPE_STRV) + if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY)) { gchar **v = g_key_file_get_string_list (keyfile, group, key, NULL, error); if (v != NULL) { - g_value_take_boxed (value, v); - ret = TRUE; + ret = g_variant_new_strv ((const gchar **) v, -1); + g_strfreev (v); } } - else if (type == DBUS_TYPE_G_OBJECT_PATH) + else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH)) { gchar *v_string = g_key_file_get_string (keyfile, group, key, error); @@ -1253,15 +1273,15 @@ mcd_keyfile_get_value (GKeyFile *keyfile, g_set_error (error, MCD_ACCOUNT_ERROR, MCD_ACCOUNT_ERROR_GET_PARAMETER, "Invalid object path %s", v_string); - g_free (v_string); } else { - g_value_take_boxed (value, v_string); - ret = TRUE; + ret = g_variant_new_object_path (v_string); } + + g_free (v_string); } - else if (type == TP_ARRAY_TYPE_OBJECT_PATH_LIST) + else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH_ARRAY)) { gchar **v = g_key_file_get_string_list (keyfile, group, key, NULL, error); @@ -1269,7 +1289,6 @@ mcd_keyfile_get_value (GKeyFile *keyfile, if (v != NULL) { gchar **iter; - GPtrArray *arr = g_ptr_array_new (); for (iter = v; iter != NULL && *iter != NULL; iter++) { @@ -1284,21 +1303,11 @@ mcd_keyfile_get_value (GKeyFile *keyfile, } } - for (iter = v; iter != NULL && *iter != NULL; iter++) - { - /* transfer ownership from v to arr */ - g_ptr_array_add (arr, *iter); - } - - /* not g_strfreev - the strings' ownership has been - * transferred */ - g_free (v); - - g_value_take_boxed (value, arr); - ret = TRUE; + ret = g_variant_new_objv ((const gchar **) v, -1); + g_strfreev (v); } } - else if (type == TP_STRUCT_TYPE_SIMPLE_PRESENCE) + else if (g_variant_type_equal (type, G_VARIANT_TYPE ("(uss)"))) { gchar **v = g_key_file_get_string_list (keyfile, group, key, NULL, error); @@ -1328,13 +1337,8 @@ mcd_keyfile_get_value (GKeyFile *keyfile, else { /* a syntactically valid simple presence */ - g_value_take_boxed (value, - tp_value_array_build (3, - G_TYPE_UINT, (guint) u, - G_TYPE_STRING, v[1], - G_TYPE_STRING, v[2], - G_TYPE_INVALID)); - ret = TRUE; + ret = g_variant_new_parsed ("(%u, %s, %s)", + (guint32) u, v[1], v[2]); } } @@ -1344,8 +1348,9 @@ mcd_keyfile_get_value (GKeyFile *keyfile, { gchar *message = g_strdup_printf ("cannot get key %s from group %s: " - "unknown type %s", - key, group, g_type_name (type)); + "unknown type %.*s", key, group, + (int) g_variant_type_get_string_length (type), + type_str); g_warning ("%s: %s", G_STRFUNC, message); g_set_error (error, MCD_ACCOUNT_ERROR, @@ -1355,6 +1360,7 @@ mcd_keyfile_get_value (GKeyFile *keyfile, } } + g_assert (ret == NULL || g_variant_is_of_type (ret, type)); return ret; } @@ -1380,7 +1386,8 @@ mcd_storage_get_boolean (McdStorage *self, g_value_init (&tmp, G_TYPE_BOOLEAN); - if (!mcd_storage_get_attribute (self, account, attribute, &tmp, NULL)) + if (!mcd_storage_get_attribute (self, account, attribute, + G_VARIANT_TYPE_BOOLEAN, &tmp, NULL)) return FALSE; return g_value_get_boolean (&tmp); @@ -1408,65 +1415,60 @@ mcd_storage_get_integer (McdStorage *self, g_value_init (&tmp, G_TYPE_INT); - if (!mcd_storage_get_attribute (self, account, attribute, &tmp, NULL)) + if (!mcd_storage_get_attribute (self, account, attribute, + G_VARIANT_TYPE_INT32, &tmp, NULL)) return FALSE; return g_value_get_int (&tmp); } -static void +static gboolean update_storage (McdStorage *self, const gchar *account, + gboolean parameter, const gchar *key, - GVariant *variant, - const gchar *escaped, - gboolean secret) + GVariant *variant) { - GList *store; - gboolean done = FALSE; - gboolean parameter = g_str_has_prefix (key, "param-"); McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); + gboolean updated = FALSE; + McpAccountStorage *plugin; + const gchar *pn; + McpAccountStorageSetResult res; - if (secret) - mcd_storage_make_secret (self, account, key); + plugin = g_hash_table_lookup (self->accounts, account); + g_return_val_if_fail (plugin != NULL, FALSE); + pn = mcp_account_storage_name (plugin); - /* we're deleting, which is unconditional, no need to check if anyone * - * claims this setting for themselves */ - if (escaped == NULL) - done = TRUE; + if (parameter) + res = mcp_account_storage_set_parameter (plugin, ma, account, + key, variant, MCP_PARAMETER_FLAG_NONE); + else + res = mcp_account_storage_set_attribute (plugin, ma, account, + key, variant, MCP_ATTRIBUTE_FLAG_NONE); - for (store = stores; store != NULL; store = g_list_next (store)) + switch (res) { - McpAccountStorage *plugin = store->data; - const gchar *pn = mcp_account_storage_name (plugin); + case MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED: + DEBUG ("MCP:%s -> store %s %s.%s", pn, + parameter ? "parameter" : "attribute", account, key); + updated = TRUE; + break; - if (done) - { - DEBUG ("MCP:%s -> delete %s.%s", pn, account, key); - mcp_account_storage_delete (plugin, ma, account, key); - } - else if (variant != NULL && !parameter && - mcp_account_storage_set_attribute (plugin, ma, account, key, variant, - MCP_ATTRIBUTE_FLAG_NONE)) - { - done = TRUE; - DEBUG ("MCP:%s -> store attribute %s.%s", pn, account, key); - } - else if (variant != NULL && parameter && - mcp_account_storage_set_parameter (plugin, ma, account, key + 6, - variant, - secret ? MCP_PARAMETER_FLAG_SECRET : MCP_PARAMETER_FLAG_NONE)) - { - done = TRUE; - DEBUG ("MCP:%s -> store parameter %s.%s", pn, account, key); - } - else - { - done = mcp_account_storage_set (plugin, ma, account, key, escaped); - DEBUG ("MCP:%s -> %s %s.%s", - pn, done ? "store" : "ignore", account, key); - } + case MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED: + DEBUG ("MCP:%s -> failed to store %s %s.%s", + pn, parameter ? "parameter" : "attribute", account, key); + break; + + case MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED: + DEBUG ("MCP:%s -> no change to %s %s.%s", + pn, parameter ? "parameter" : "attribute", account, key); + break; + + default: + g_warn_if_reached (); } + + return updated; } /* @@ -1537,45 +1539,24 @@ mcd_storage_set_attribute (McdStorage *self, const gchar *attribute, const GValue *value) { - McdStorageAccount *sa; - GVariant *old_v; GVariant *new_v; gboolean updated = FALSE; + McpAccountStorage *plugin; g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE); g_return_val_if_fail (account != NULL, FALSE); g_return_val_if_fail (attribute != NULL, FALSE); g_return_val_if_fail (!g_str_has_prefix (attribute, "param-"), FALSE); - sa = ensure_account (self, account); + plugin = g_hash_table_lookup (self->accounts, account); + g_return_val_if_fail (plugin != NULL, FALSE); if (value != NULL) new_v = g_variant_ref_sink (dbus_g_value_build_g_variant (value)); else new_v = NULL; - old_v = g_hash_table_lookup (sa->attributes, attribute); - - if (!mcd_nullable_variant_equal (old_v, new_v)) - { - gchar *escaped = NULL; - - /* First put it in the attributes hash table. (Watch out, this might - * invalidate old_v.) */ - if (new_v == NULL) - g_hash_table_remove (sa->attributes, attribute); - else - g_hash_table_insert (sa->attributes, g_strdup (attribute), - g_variant_ref (new_v)); - - /* OK now we have to escape it in a stupid way for plugins */ - if (value != NULL) - escaped = mcd_keyfile_escape_value (value); - - update_storage (self, account, attribute, new_v, escaped, FALSE); - g_free (escaped); - updated = TRUE; - } + updated = update_storage (self, account, FALSE, attribute, new_v); tp_clear_pointer (&new_v, g_variant_unref); return updated; @@ -1587,8 +1568,6 @@ mcd_storage_set_attribute (McdStorage *self, * @account: the unique name of an account * @parameter: the name of the parameter, e.g. "account" * @value: the value to be stored (or %NULL to erase it) - * @secret: whether the value is confidential (might get stored in the - * keyring, for example) * * Copies and stores the supplied @value (or removes it if %NULL) in the * internal cache. @@ -1603,66 +1582,30 @@ gboolean mcd_storage_set_parameter (McdStorage *self, const gchar *account, const gchar *parameter, - const GValue *value, - gboolean secret) + const GValue *value) { - GVariant *old_v; GVariant *new_v = NULL; - const gchar *old_escaped; - gchar *new_escaped = NULL; - McdStorageAccount *sa; gboolean updated = FALSE; + McpAccountStorage *plugin; g_return_val_if_fail (MCD_IS_STORAGE (self), FALSE); g_return_val_if_fail (account != NULL, FALSE); g_return_val_if_fail (parameter != NULL, FALSE); - sa = ensure_account (self, account); + plugin = g_hash_table_lookup (self->accounts, account); + g_return_val_if_fail (plugin != NULL, FALSE); if (value != NULL) { - new_escaped = mcd_keyfile_escape_value (value); new_v = g_variant_ref_sink (dbus_g_value_build_g_variant (value)); } - old_v = g_hash_table_lookup (sa->parameters, parameter); - old_escaped = g_hash_table_lookup (sa->escaped_parameters, parameter); - - if (old_v != NULL) - updated = !mcd_nullable_variant_equal (old_v, new_v); - else if (old_escaped != NULL) - updated = tp_strdiff (old_escaped, new_escaped); - else - updated = (value != NULL); + updated = update_storage (self, account, TRUE, parameter, new_v); - if (updated) - { - gchar key[MAX_KEY_LENGTH]; - - g_hash_table_remove (sa->parameters, parameter); - g_hash_table_remove (sa->escaped_parameters, parameter); - - if (new_v != NULL) - g_hash_table_insert (sa->parameters, g_strdup (parameter), - g_variant_ref (new_v)); - - g_snprintf (key, sizeof (key), "param-%s", parameter); - update_storage (self, account, key, new_v, new_escaped, secret); - return TRUE; - } - - g_free (new_escaped); tp_clear_pointer (&new_v, g_variant_unref); return updated; } -static gchar * -mcpa_escape_value_for_keyfile (const McpAccountManager *unused G_GNUC_UNUSED, - const GValue *value) -{ - return mcd_keyfile_escape_value (value); -} - /* * @value: a populated #GValue of a supported #GType * @@ -1675,15 +1618,23 @@ mcpa_escape_value_for_keyfile (const McpAccountManager *unused G_GNUC_UNUSED, gchar * mcd_keyfile_escape_value (const GValue *value) { - GKeyFile *keyfile; + GVariant *variant; gchar *ret; g_return_val_if_fail (G_IS_VALUE (value), NULL); - keyfile = g_key_file_new (); - mcd_keyfile_set_value (keyfile, "g", "k", value); - ret = g_key_file_get_value (keyfile, "g", "k", NULL); - g_key_file_free (keyfile); + variant = dbus_g_value_build_g_variant (value); + + if (variant == NULL) + { + g_warning ("Unable to convert %s to GVariant", + G_VALUE_TYPE_NAME (value)); + return NULL; + } + + g_variant_ref_sink (variant); + ret = mcd_keyfile_escape_variant (variant); + g_variant_unref (variant); return ret; } @@ -1721,6 +1672,53 @@ mcd_keyfile_set_value (GKeyFile *keyfile, if (value == NULL) { + return mcd_keyfile_set_variant (keyfile, name, key, NULL); + } + else + { + GVariant *variant; + gboolean ret; + + variant = dbus_g_value_build_g_variant (value); + + if (variant == NULL) + { + g_warning ("Unable to convert %s to GVariant", + G_VALUE_TYPE_NAME (value)); + return FALSE; + } + + g_variant_ref_sink (variant); + ret = mcd_keyfile_set_variant (keyfile, name, key, variant); + g_variant_unref (variant); + return ret; + } +} + +/* + * mcd_keyfile_set_variant: + * @keyfile: a keyfile + * @name: the name of a group + * @key: the key in the group + * @value: the value to be stored (or %NULL to erase it) + * + * Escape @variant and store it in the keyfile. + * + * Returns: %TRUE if the keyfile actually changed, + * so that the caller can decide whether to request a commit to + * long term storage or not. + */ +gboolean +mcd_keyfile_set_variant (GKeyFile *keyfile, + const gchar *name, + const gchar *key, + GVariant *value) +{ + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (key != NULL, FALSE); + + if (value == NULL) + { gchar *old = g_key_file_get_value (keyfile, name, key, NULL); gboolean updated = (old != NULL); @@ -1735,75 +1733,88 @@ mcd_keyfile_set_value (GKeyFile *keyfile, gchar *new = NULL; gchar *buf = NULL; - switch (G_VALUE_TYPE (value)) + switch (g_variant_classify (value)) { - case G_TYPE_STRING: + case G_VARIANT_CLASS_STRING: + case G_VARIANT_CLASS_OBJECT_PATH: + case G_VARIANT_CLASS_SIGNATURE: g_key_file_set_string (keyfile, name, key, - g_value_get_string (value)); + g_variant_get_string (value, NULL)); + break; + + case G_VARIANT_CLASS_UINT16: + buf = g_strdup_printf ("%u", g_variant_get_uint16 (value)); break; - case G_TYPE_UINT: - buf = g_strdup_printf ("%u", g_value_get_uint (value)); + case G_VARIANT_CLASS_UINT32: + buf = g_strdup_printf ("%u", g_variant_get_uint32 (value)); break; - case G_TYPE_INT: - g_key_file_set_integer (keyfile, name, key, - g_value_get_int (value)); + case G_VARIANT_CLASS_INT16: + buf = g_strdup_printf ("%d", g_variant_get_int16 (value)); break; - case G_TYPE_BOOLEAN: + case G_VARIANT_CLASS_INT32: + buf = g_strdup_printf ("%d", g_variant_get_int32 (value)); + break; + + case G_VARIANT_CLASS_BOOLEAN: g_key_file_set_boolean (keyfile, name, key, - g_value_get_boolean (value)); + g_variant_get_boolean (value)); break; - case G_TYPE_UCHAR: - buf = g_strdup_printf ("%u", g_value_get_uchar (value)); + case G_VARIANT_CLASS_BYTE: + buf = g_strdup_printf ("%u", g_variant_get_byte (value)); break; - case G_TYPE_UINT64: + case G_VARIANT_CLASS_UINT64: buf = g_strdup_printf ("%" G_GUINT64_FORMAT, - g_value_get_uint64 (value)); + g_variant_get_uint64 (value)); break; - case G_TYPE_INT64: + case G_VARIANT_CLASS_INT64: buf = g_strdup_printf ("%" G_GINT64_FORMAT, - g_value_get_int64 (value)); + g_variant_get_int64 (value)); break; - case G_TYPE_DOUBLE: + case G_VARIANT_CLASS_DOUBLE: g_key_file_set_double (keyfile, name, key, - g_value_get_double (value)); + g_variant_get_double (value)); break; - default: - if (G_VALUE_HOLDS (value, G_TYPE_STRV)) + case G_VARIANT_CLASS_ARRAY: + if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) { - gchar **strings = g_value_get_boxed (value); + gsize len; + const gchar **strings = g_variant_get_strv (value, &len); - g_key_file_set_string_list (keyfile, name, key, - (const gchar **)strings, - g_strv_length (strings)); + g_key_file_set_string_list (keyfile, name, key, strings, len); } - else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH)) + else if (g_variant_is_of_type (value, + G_VARIANT_TYPE_OBJECT_PATH_ARRAY)) { - g_key_file_set_string (keyfile, name, key, - g_value_get_boxed (value)); + gsize len; + const gchar **strings = g_variant_get_objv (value, &len); + + g_key_file_set_string_list (keyfile, name, key, strings, len); } - else if (G_VALUE_HOLDS (value, TP_ARRAY_TYPE_OBJECT_PATH_LIST)) + else { - GPtrArray *arr = g_value_get_boxed (value); - - g_key_file_set_string_list (keyfile, name, key, - (const gchar * const *) arr->pdata, arr->len); + g_warning ("Unexpected array type %s", + g_variant_get_type_string (value)); + return FALSE; } - else if (G_VALUE_HOLDS (value, TP_STRUCT_TYPE_SIMPLE_PRESENCE)) + break; + + case G_VARIANT_CLASS_TUPLE: + if (g_variant_is_of_type (value, G_VARIANT_TYPE ("(uss)"))) { - guint type; + guint32 type; /* enough for "4294967296" + \0 */ gchar printf_buf[11]; const gchar * strv[4] = { NULL, NULL, NULL, NULL }; - tp_value_array_unpack (g_value_get_boxed (value), 3, + g_variant_get (value, "(u&s&s)", &type, &(strv[1]), &(strv[2])); @@ -1814,8 +1825,16 @@ mcd_keyfile_set_value (GKeyFile *keyfile, } else { - g_warning ("Unexpected param type %s", - G_VALUE_TYPE_NAME (value)); + g_warning ("Unexpected struct type %s", + g_variant_get_type_string (value)); + return FALSE; + } + break; + + default: + { + g_warning ("Unexpected variant type %s", + g_variant_get_type_string (value)); return FALSE; } } @@ -1842,7 +1861,8 @@ mcd_keyfile_set_value (GKeyFile *keyfile, * @provider: the desired storage provider, or %NULL * @manager: the name of the manager * @protocol: the name of the protocol - * @params: A gchar * / GValue * hash table of account parameters + * @identification: the result of IdentifyAccount + * @plugin_out: (out) (transfer full): the plugin we used * @error: a #GError to fill when returning %NULL * * Create a new account in storage. This should not store any @@ -1857,11 +1877,16 @@ mcd_storage_create_account (McdStorage *self, const gchar *provider, const gchar *manager, const gchar *protocol, - GHashTable *params, + const gchar *identification, + McpAccountStorage **plugin_out, GError **error) { GList *store; McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); + gchar *ret; + + if (plugin_out != NULL) + *plugin_out = NULL; g_return_val_if_fail (MCD_IS_STORAGE (self), NULL); g_return_val_if_fail (!tp_str_empty (manager), NULL); @@ -1876,8 +1901,21 @@ mcd_storage_create_account (McdStorage *self, if (!tp_strdiff (mcp_account_storage_provider (plugin), provider)) { - return mcp_account_storage_create (plugin, ma, manager, - protocol, params, error); + ret = mcp_account_storage_create (plugin, ma, manager, + protocol, identification, error); + if (mcd_storage_add_account_from_plugin (self, plugin, ret, + error)) + { + if (plugin_out != NULL) + *plugin_out = g_object_ref (plugin); + + return ret; + } + else + { + g_free (ret); + return NULL; + } } } @@ -1889,50 +1927,30 @@ mcd_storage_create_account (McdStorage *self, /* No provider specified, let's pick the first plugin able to create this * account in priority order. - * - * FIXME: This is rather subtle, and relies on the fact that accounts - * aren't always strongly tied to a single plugin. - * - * For plugins that only store their accounts set up specifically - * through them (like the libaccounts/SSO pseudo-plugin, - * McdAccountManagerSSO), create() will fail as unimplemented, - * and we'll fall through to the next plugin. Eventually we'll - * reach the default keyfile+gnome-keyring plugin, or another - * plugin that accepts arbitrary accounts. When set() is called, - * the libaccounts/SSO plugin will reject that too, and again, - * we'll fall through to a plugin that accepts arbitrary - * accounts. - * - * Plugins that will accept arbitrary accounts being created - * via D-Bus (like the default keyfile+gnome-keyring plugin, - * and the account-diversion plugin in tests/twisted) - * should, in principle, implement create() to be successful. - * If they do, their create() will succeed, and later, so will - * their set(). - * - * We can't necessarily rely on all such plugins implementing - * create(), because it isn't a mandatory part of the plugin - * API (it was added later). However, as it happens, the - * default plugin returns successfully from create() without - * really doing anything. When we iterate through the accounts again - * to call set(), higher-priority plugins are given a second - * chance to intercept that; so we end up with create() in - * the default plugin being followed by set() from the - * higher-priority plugin. In theory that's bad because it - * splits the account across two plugins, but in practice - * it isn't a problem because the default plugin's create() - * doesn't really do anything anyway. */ for (store = stores; store != NULL; store = g_list_next (store)) { McpAccountStorage *plugin = store->data; - gchar *ret; - ret = mcp_account_storage_create (plugin, ma, manager, protocol, params, - error); + ret = mcp_account_storage_create (plugin, ma, manager, protocol, + identification, error); if (ret != NULL) - return ret; + { + if (mcd_storage_add_account_from_plugin (self, plugin, ret, + error)) + { + if (plugin_out != NULL) + *plugin_out = g_object_ref (plugin); + + return ret; + } + else + { + g_free (ret); + return NULL; + } + } g_clear_error (error); } @@ -1946,6 +1964,29 @@ mcd_storage_create_account (McdStorage *self, return NULL; } +static void +delete_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GError *error = NULL; + const gchar *account_name = user_data; + + if (mcp_account_storage_delete_finish (MCP_ACCOUNT_STORAGE (source), + res, &error)) + { + DEBUG ("deleted account %s", account_name); + } + else + { + DEBUG ("could not delete account %s (but no way to signal that): " + "%s #%d: %s", account_name, + g_quark_to_string (error->domain), error->code, error->message); + g_error_free (error); + } + + g_free (user_data); +} /* * mcd_storage_delete_account: @@ -1961,20 +2002,19 @@ void mcd_storage_delete_account (McdStorage *self, const gchar *account) { - GList *store; McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); + McpAccountStorage *plugin; g_return_if_fail (MCD_IS_STORAGE (self)); g_return_if_fail (account != NULL); - g_hash_table_remove (self->accounts, account); + plugin = g_hash_table_lookup (self->accounts, account); + g_return_if_fail (plugin != NULL); - for (store = stores; store != NULL; store = g_list_next (store)) - { - McpAccountStorage *plugin = store->data; - - mcp_account_storage_delete (plugin, ma, account, NULL); - } + /* FIXME: stop ignoring the error (if any), and make this method async + * in order to pass the error up to McdAccount */ + mcp_account_storage_delete_async (plugin, ma, account, NULL, + delete_cb, g_strdup (account)); } /* @@ -1988,27 +2028,21 @@ mcd_storage_delete_account (McdStorage *self, void mcd_storage_commit (McdStorage *self, const gchar *account) { - GList *store; McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); + McpAccountStorage *plugin; + const gchar *pname; g_return_if_fail (MCD_IS_STORAGE (self)); + g_return_if_fail (account != NULL); - for (store = stores; store != NULL; store = g_list_next (store)) - { - McpAccountStorage *plugin = store->data; - const gchar *pname = mcp_account_storage_name (plugin); + plugin = g_hash_table_lookup (self->accounts, account); + g_return_if_fail (plugin != NULL); - if (account != NULL) - { - DEBUG ("flushing plugin %s %s to long term storage", pname, account); - mcp_account_storage_commit_one (plugin, ma, account); - } - else - { - DEBUG ("flushing plugin %s to long term storage", pname); - mcp_account_storage_commit (plugin, ma); - } - } + pname = mcp_account_storage_name (plugin); + + /* FIXME: fd.o #29563: this should be async, really */ + DEBUG ("flushing plugin %s %s to long term storage", pname, account); + mcp_account_storage_commit (plugin, ma, account); } /* @@ -2049,20 +2083,27 @@ mcd_storage_set_strv (McdStorage *storage, return ret; } -void -mcd_storage_ready (McdStorage *self) +static GVariant * +mcpa_unescape_variant_from_keyfile (const McpAccountManager *mcpa, + const gchar *escaped, + const GVariantType *type, + GError **error) { - GList *store; - McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); + GKeyFile *keyfile; + GVariant *ret; - for (store = stores; store != NULL; store = g_list_next (store)) - { - McpAccountStorage *plugin = store->data; - const gchar *plugin_name = mcp_account_storage_name (plugin); + g_return_val_if_fail (escaped != NULL, NULL); + g_return_val_if_fail (type != NULL, NULL); - DEBUG ("Unblocking async account ops by %s", plugin_name); - mcp_account_storage_ready (plugin, ma); - } + keyfile = g_key_file_new (); + g_key_file_set_value (keyfile, "g", "k", escaped); + ret = mcd_keyfile_get_variant (keyfile, "g", "k", type, error); + g_key_file_free (keyfile); + + if (ret != NULL) + g_variant_ref_sink (ret); + + return ret; } static void @@ -2071,32 +2112,252 @@ plugin_iface_init (McpAccountManagerIface *iface, { DEBUG (); - iface->get_value = get_value; - iface->set_value = set_value; - iface->set_attribute = mcpa_set_attribute; - iface->set_parameter = mcpa_set_parameter; - iface->is_secret = is_secret; - iface->make_secret = make_secret; iface->unique_name = unique_name; - iface->list_keys = list_keys; - iface->escape_value_for_keyfile = mcpa_escape_value_for_keyfile; + iface->identify_account_async = identify_account_async; + iface->identify_account_finish = identify_account_finish; iface->escape_variant_for_keyfile = mcpa_escape_variant_for_keyfile; - iface->unescape_value_from_keyfile = mcpa_unescape_value_from_keyfile; - iface->init_value_for_attribute = mcpa_init_value_for_attribute; + iface->unescape_variant_from_keyfile = mcpa_unescape_variant_from_keyfile; } gboolean mcd_storage_add_account_from_plugin (McdStorage *self, McpAccountStorage *plugin, - const gchar *account) + const gchar *account, + GError **error) { - if (!mcp_account_storage_get (plugin, MCP_ACCOUNT_MANAGER (self), - account, NULL)) + McpAccountStorage *other = g_hash_table_lookup (self->accounts, account); + McpAccountManager *api = (McpAccountManager *) self; + gchar **typed_parameters; + gchar **untyped_parameters; + + if (other != NULL) { - g_warning ("plugin %s disowned account %s", - mcp_account_storage_name (plugin), account); + g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, + "account %s already exists in plugin '%s', cannot create " + "for plugin '%s'", + account, + mcp_account_storage_name (other), + mcp_account_storage_name (plugin)); return FALSE; } + g_hash_table_insert (self->accounts, g_strdup (account), + g_object_ref (plugin)); + + typed_parameters = mcp_account_storage_list_typed_parameters (plugin, api, + account); + untyped_parameters = mcp_account_storage_list_untyped_parameters (plugin, + api, account); + + DEBUG ("Account parameters for %s", account); + + if (typed_parameters != NULL) + { + gsize i; + + for (i = 0; typed_parameters[i] != NULL; i++) + { + GVariant *v = mcp_account_storage_get_parameter (plugin, api, account, + typed_parameters[i], NULL, NULL); + + if (v == NULL) + { + CRITICAL ("%s: could not be retrieved", typed_parameters[i]); + } + else + { + DEBUG ("%s: type '%s'", typed_parameters[i], + g_variant_get_type_string (v)); + g_variant_unref (v); + } + } + } + + if (untyped_parameters != NULL) + { + gsize i; + + for (i = 0; untyped_parameters[i] != NULL; i++) + { + DEBUG ("%s: type not stored", untyped_parameters[i]); + } + } + + DEBUG ("End of parameters"); + + g_strfreev (typed_parameters); + g_strfreev (untyped_parameters); + return TRUE; } + +GHashTable * +mcd_storage_dup_typed_parameters (McdStorage *self, + const gchar *account_name) +{ + McpAccountStorage *plugin; + McpAccountManager *api = (McpAccountManager *) self; + gsize i; + gchar **typed_parameters; + GHashTable *params; + + g_return_val_if_fail (MCD_IS_STORAGE (self), NULL); + + plugin = g_hash_table_lookup (self->accounts, account_name); + g_return_val_if_fail (plugin != NULL, NULL); + + typed_parameters = mcp_account_storage_list_typed_parameters (plugin, api, + account_name); + + params = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) tp_g_value_slice_free); + + for (i = 0; + typed_parameters != NULL && typed_parameters[i] != NULL; + i++) + { + GVariant *v = mcp_account_storage_get_parameter (plugin, api, + account_name, typed_parameters[i], NULL, NULL); + GValue *value; + + if (v == NULL) + { + CRITICAL ("%s was in list_typed_parameters() but could not be " + "retrieved", typed_parameters[i]); + continue; + } + + value = g_slice_new0 (GValue); + dbus_g_value_parse_g_variant (v, value); + + if (!G_IS_VALUE (value)) + { + CRITICAL ("could not turn %s into a GValue", typed_parameters[i]); + g_slice_free (GValue, value); + continue; + } + + g_hash_table_insert (params, g_strdup (typed_parameters[i]), + value); + g_variant_unref (v); + } + + return params; +} + +/* See whether we can migrate the parameters from being stored without + * their types, to being stored with their types. + * Commit changes and return TRUE if anything happened. */ +gboolean +mcd_storage_maybe_migrate_parameters (McdStorage *self, + const gchar *account_name, + TpProtocol *protocol) +{ + McpAccountManager *api = MCP_ACCOUNT_MANAGER (self); + McpAccountStorage *plugin; + gchar **untyped_parameters = NULL; + gsize i; + gboolean ret = FALSE; + + plugin = g_hash_table_lookup (self->accounts, account_name); + g_return_val_if_fail (plugin != NULL, FALSE); + + /* If the storage backend can't store typed parameters, there's no point. */ + if (!mcp_account_storage_has_any_flag (plugin, account_name, + MCP_ACCOUNT_STORAGE_FLAG_STORES_TYPES)) + goto finally; + + untyped_parameters = mcp_account_storage_list_untyped_parameters ( + plugin, api, account_name); + + /* If there's nothing to migrate, there's also no point. */ + if (untyped_parameters == NULL || untyped_parameters[0] == NULL) + goto finally; + + DEBUG ("trying to migrate %s", account_name); + + for (i = 0; untyped_parameters[i] != NULL; i++) + { + const gchar *param_name = untyped_parameters[i]; + const TpConnectionManagerParam *param = tp_protocol_get_param (protocol, + param_name); + GVariantType *type = NULL; + GVariant *value; + McpAccountStorageSetResult res; + + if (param == NULL) + { + DEBUG ("cannot migrate parameter '%s': not supported by %s/%s", + param_name, tp_protocol_get_cm_name (protocol), + tp_protocol_get_name (protocol)); + goto next_param; + } + + type = tp_connection_manager_param_dup_variant_type (param); + + DEBUG ("Migrating parameter '%s' of type '%.*s'", + param_name, + (gint) g_variant_type_get_string_length (type), + g_variant_type_peek_string (type)); + + value = mcp_account_storage_get_parameter (plugin, api, + account_name, param_name, type, NULL); + + if (value == NULL) + { + DEBUG ("cannot migrate parameter '%s'", param_name); + goto next_param; + } + + if (!g_variant_is_of_type (value, type)) + { + DEBUG ("trying to convert parameter from type '%s'", + g_variant_get_type_string (value)); + + /* consumes parameter */ + value = tp_variant_convert (value, type); + + if (value == NULL) + { + DEBUG ("could not convert parameter to desired type"); + goto next_param; + } + } + + res = mcp_account_storage_set_parameter (plugin, api, + account_name, param_name, value, MCP_PARAMETER_FLAG_NONE); + + switch (res) + { + case MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED: + /* it really ought to be CHANGED, surely? */ + DEBUG ("Tried to upgrade parameter %s but the " + "storage backend claims not to have changed it? " + "Not sure I really believe that", param_name); + /* fall through to the CHANGED case */ + + case MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED: + ret = TRUE; + break; + + case MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED: + WARNING ("Failed to set parameter %s", param_name); + break; + + default: + WARNING ("set_parameter returned invalid result code %d " + "for parameter %s", res, param_name); + } + +next_param: + if (type != NULL) + g_variant_type_free (type); + } + + if (ret) + mcp_account_storage_commit (plugin, api, account_name); + +finally: + g_strfreev (untyped_parameters); + return ret; +} diff --git a/src/mcd-storage.h b/src/mcd-storage.h index 893a0af3..56732f11 100644 --- a/src/mcd-storage.h +++ b/src/mcd-storage.h @@ -30,7 +30,7 @@ G_BEGIN_DECLS typedef struct { GObject parent; TpDBusDaemon *dbusd; - /* owned string => owned McdStorageAccount */ + /* owned string => owned McpAccountStorage */ GHashTable *accounts; } McdStorage; @@ -64,11 +64,7 @@ void mcd_storage_connect_signal (const gchar *signal, void mcd_storage_load (McdStorage *storage); -GStrv mcd_storage_dup_accounts (McdStorage *storage, gsize *n); - -GStrv mcd_storage_dup_attributes (McdStorage *storage, - const gchar *account, - gsize *n); +GHashTable *mcd_storage_get_accounts (McdStorage *storage); gboolean mcd_storage_set_string (McdStorage *storage, const gchar *account, @@ -88,14 +84,14 @@ gboolean mcd_storage_set_attribute (McdStorage *storage, gboolean mcd_storage_set_parameter (McdStorage *storage, const gchar *account, const gchar *parameter, - const GValue *value, - gboolean secret); + const GValue *value); gchar *mcd_storage_create_account (McdStorage *storage, const gchar *provider, const gchar *manager, const gchar *protocol, - GHashTable *params, + const gchar *identification, + McpAccountStorage **plugin_out, GError **error); void mcd_storage_delete_account (McdStorage *storage, const gchar *account); @@ -109,12 +105,14 @@ gchar *mcd_storage_dup_string (McdStorage *storage, gboolean mcd_storage_get_attribute (McdStorage *storage, const gchar *account, const gchar *attribute, + const GVariantType *type, GValue *value, GError **error); gboolean mcd_storage_get_parameter (McdStorage *storage, const gchar *account, const gchar *parameter, + const GVariantType *type, GValue *value, GError **error); @@ -133,13 +131,23 @@ G_GNUC_INTERNAL void _mcd_storage_store_connections (McdStorage *storage); gboolean mcd_storage_add_account_from_plugin (McdStorage *storage, McpAccountStorage *plugin, - const gchar *account); + const gchar *account, + GError **error); +GVariant *mcd_keyfile_get_variant (GKeyFile *keyfile, + const gchar *group, + const gchar *key, + const GVariantType *type, + GError **error); gboolean mcd_keyfile_get_value (GKeyFile *keyfile, const gchar *group, const gchar *key, GValue *value, GError **error); +gboolean mcd_keyfile_set_variant (GKeyFile *keyfile, + const gchar *name, + const gchar *key, + GVariant *value); gboolean mcd_keyfile_set_value (GKeyFile *keyfile, const gchar *name, const gchar *key, @@ -150,9 +158,17 @@ gboolean mcd_keyfile_unescape_value (const gchar *escaped, GValue *value, GError **error); -const gchar *mcd_storage_get_attribute_type (const gchar *attribute); +const GVariantType *mcd_storage_get_attribute_type (const gchar *attribute); gboolean mcd_storage_init_value_for_attribute (GValue *value, - const gchar *attribute); + const gchar *attribute, + const GVariantType **variant_type); + +GHashTable *mcd_storage_dup_typed_parameters (McdStorage *self, + const gchar *account); + +gboolean mcd_storage_maybe_migrate_parameters (McdStorage *self, + const gchar *account_name, + TpProtocol *protocol); G_END_DECLS diff --git a/src/mcd.xml b/src/mcd.xml deleted file mode 100644 index 27ae8d62..00000000 --- a/src/mcd.xml +++ /dev/null @@ -1,17 +0,0 @@ -<tp:spec - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - xmlns:xi="http://www.w3.org/2001/XInclude"> - -<tp:copyright>Copyright (C) 2008 Nokia Corporation</tp:copyright> - -<xi:include href="../xml/Connection_Manager_Interface_Account_Storage.xml"/> - -<xi:include href="../xml/Account_Interface_Conditions.xml"/> -<xi:include href="../xml/Account_Interface_External_Password_Storage.xml"/> -<xi:include href="../xml/Account_Interface_Hidden.xml"/> - -<xi:include href="../xml/Account_Manager_Interface_Hidden.xml"/> - -<xi:include href="dispatcher.xml"/> - -</tp:spec> diff --git a/src/plugin-dispatch-operation.c b/src/plugin-dispatch-operation.c index cd9c279c..22d777cd 100644 --- a/src/plugin-dispatch-operation.c +++ b/src/plugin-dispatch-operation.c @@ -34,7 +34,6 @@ typedef enum { PLUGIN_ACTION_NONE, PLUGIN_ACTION_CLOSE, - PLUGIN_ACTION_LEAVE, PLUGIN_ACTION_DESTROY } PluginAction; @@ -279,38 +278,6 @@ plugin_do_end_delay (McpDispatchOperation *obj, g_object_unref (self); } - -/* Close */ -static void -plugin_do_leave_channels (McpDispatchOperation *obj, - gboolean wait_for_observers, TpChannelGroupChangeReason reason, - const gchar *message) -{ - McdPluginDispatchOperation *self = MCD_PLUGIN_DISPATCH_OPERATION (obj); - - DEBUG ("%p (wait=%c reason=%d message=%s)", self, - wait_for_observers ? 'T' : 'F', reason, message); - - g_return_if_fail (self != NULL); - - if (wait_for_observers) - { - if (self->after_observers < PLUGIN_ACTION_LEAVE) - { - DEBUG ("Remembering for later"); - self->after_observers = PLUGIN_ACTION_LEAVE; - self->reason = reason; - g_free (self->message); - self->message = g_strdup (message); - } - } - else - { - DEBUG ("Leaving now"); - _mcd_dispatch_operation_leave_channels (self->real_cdo, reason, message); - } -} - static void plugin_do_close_channels (McpDispatchOperation *obj, gboolean wait_for_observers) @@ -370,12 +337,6 @@ _mcd_plugin_dispatch_operation_observers_finished ( _mcd_dispatch_operation_destroy_channels (self->real_cdo); break; - case PLUGIN_ACTION_LEAVE: - DEBUG ("leaving now: %d %s", self->reason, self->message); - _mcd_dispatch_operation_leave_channels (self->real_cdo, - self->reason, self->message); - break; - case PLUGIN_ACTION_CLOSE: DEBUG ("closing now"); _mcd_dispatch_operation_close_channels (self->real_cdo); @@ -412,7 +373,6 @@ plugin_iface_init (McpDispatchOperationIface *iface, iface->start_delay = plugin_do_start_delay; iface->end_delay = plugin_do_end_delay; - iface->leave_channels = plugin_do_leave_channels; iface->close_channels = plugin_do_close_channels; iface->destroy_channels = plugin_do_destroy_channels; } diff --git a/src/plugin-loader.c b/src/plugin-loader.c index 7062c3a2..d6beefdc 100644 --- a/src/plugin-loader.c +++ b/src/plugin-loader.c @@ -27,10 +27,6 @@ #include "mcd-debug.h" -#if ENABLE_AEGIS -#include "plugins/mcp-dbus-aegis-acl.h" -#endif - static gsize ready = 0; void @@ -38,9 +34,6 @@ _mcd_plugin_loader_init (void) { if (g_once_init_enter (&ready)) { -#if ENABLE_AEGIS - GObject *pseudo_plugin; -#endif const gchar *dir = g_getenv ("MC_FILTER_PLUGIN_DIR"); if (dir == NULL) @@ -48,15 +41,6 @@ _mcd_plugin_loader_init (void) mcp_read_dir (dir); -#if ENABLE_AEGIS - /* The last object added by mcp_add_object() will be treated as highest - * priority, at least for the interfaces used here */ - DEBUG ("Initialising built-in Aegis ACL plugin"); - pseudo_plugin = G_OBJECT (aegis_acl_new ()); - mcp_add_object (pseudo_plugin); - g_object_unref (pseudo_plugin); -#endif - g_once_init_leave (&ready, 1); } } diff --git a/src/request.c b/src/request.c index ce91440f..e59ec4c1 100644 --- a/src/request.c +++ b/src/request.c @@ -34,7 +34,6 @@ #include "mcd-misc.h" #include "plugin-loader.h" #include "plugin-request.h" -#include "_gen/interfaces.h" enum { PROP_0, diff --git a/tests/Makefile.am b/tests/Makefile.am index cf7edc9c..a808ac7a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = \ +AM_CPPFLAGS = \ $(DBUS_CFLAGS) \ $(TELEPATHY_CFLAGS) \ -I$(top_srcdir) \ @@ -37,11 +37,8 @@ tease_the_minotaur_LDADD = $(top_builddir)/src/libmcd-convenience.la account_store_LDADD = $(GLIB_LIBS) account_store_SOURCES = \ account-store.c \ - account-store-default.c \ - account-store-default.h - -if ENABLE_LIBACCOUNTS_SSO -account_store_SOURCES += account-store-libaccounts.c account-store-libaccounts.h -account_store_LDADD += $(LIBACCOUNTS_SSO_LIBS) -INCLUDES += $(LIBACCOUNTS_SSO_CFLAGS) -endif + account-store-keyfile.c \ + account-store-keyfile.h \ + account-store-variant-file.c \ + account-store-variant-file.h \ + $(NULL) diff --git a/tests/account-store-default.c b/tests/account-store-keyfile.c index 7f4c8c3b..efa96b0a 100644 --- a/tests/account-store-default.c +++ b/tests/account-store-keyfile.c @@ -1,5 +1,5 @@ /* - * MC account storage backend inspector, default backend + * MC account storage inspector: MC 5.0-compatible single keyfile backend * * Copyright © 2010 Nokia Corporation * Copyright © 2010 Collabora Ltd. @@ -23,7 +23,7 @@ #include <glib.h> #include <string.h> -#include "account-store-default.h" +#include "account-store-keyfile.h" static const gchar *default_config (void) { @@ -76,14 +76,14 @@ static gboolean commit_changes (void) } gchar * -default_get (const gchar *account, +keyfile_get (const gchar *account, const gchar *key) { return g_key_file_get_string (default_keyfile (), account, key, NULL); } gboolean -default_set (const gchar *account, +keyfile_set (const gchar *account, const gchar *key, const gchar *value) { @@ -100,7 +100,7 @@ default_set (const gchar *account, } gboolean -default_delete (const gchar *account) +keyfile_delete (const gchar *account) { GKeyFile *keyfile = default_keyfile (); @@ -110,13 +110,13 @@ default_delete (const gchar *account) } gboolean -default_exists (const gchar *account) +keyfile_exists (const gchar *account) { return g_key_file_has_group (default_keyfile (), account); } GStrv -default_list (void) +keyfile_list (void) { return g_key_file_get_groups (default_keyfile (), NULL); } diff --git a/tests/account-store-default.h b/tests/account-store-keyfile.h index 739f95cc..a6ce2179 100644 --- a/tests/account-store-default.h +++ b/tests/account-store-keyfile.h @@ -1,5 +1,5 @@ /* - * MC account storage backend inspector, default backend + * MC account storage inspector: MC 5.0-compatible single keyfile backend * * Copyright © 2010 Nokia Corporation * Copyright © 2010 Collabora Ltd. @@ -19,23 +19,23 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _ACCOUNT_STORE_DEFAULT_H_ -#define _ACCOUNT_STORE_DEFAULT_H_ +#ifndef _ACCOUNT_STORE_KEYFILE_H_ +#define _ACCOUNT_STORE_KEYFILE_H_ #include <glib.h> #include <glib-object.h> -gchar * default_get (const gchar *account, +gchar * keyfile_get (const gchar *account, const gchar *key); -gboolean default_set (const gchar *account, +gboolean keyfile_set (const gchar *account, const gchar *key, const gchar *value); -gboolean default_delete (const gchar *account); +gboolean keyfile_delete (const gchar *account); -gboolean default_exists (const gchar *account); +gboolean keyfile_exists (const gchar *account); -GStrv default_list (void); +GStrv keyfile_list (void); #endif diff --git a/tests/account-store-libaccounts.c b/tests/account-store-libaccounts.c deleted file mode 100644 index e2756969..00000000 --- a/tests/account-store-libaccounts.c +++ /dev/null @@ -1,629 +0,0 @@ -/* - * MC account storage backend inspector, libaccounts backend - * - * Copyright © 2010 Nokia Corporation - * Copyright © 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 "account-store-libaccounts.h" - -#include <libaccounts-glib/ag-manager.h> -#include <libaccounts-glib/ag-account.h> -#include <libaccounts-glib/ag-service.h> - -#include <string.h> - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "account-store-libaccounts" - -/* MC <-> AG global/local setting meta data */ -#define MCPP "param-" -#define AGPP "parameters/" -#define LIBACCT_ID_KEY "libacct-uid" - -#define MC_ENABLED_KEY "Enabled" -#define AG_ENABLED_KEY "enabled" - -#define AG_LABEL_KEY "name" -#define MC_LABEL_KEY "DisplayName" - -#define AG_ACCOUNT_KEY "username" -#define MC_ACCOUNT_KEY "account" -#define PASSWORD_KEY "password" -#define AG_ACCOUNT_ALT_KEY AGPP "account" - -#define MC_CMANAGER_KEY "manager" -#define MC_PROTOCOL_KEY "protocol" -#define MC_IDENTITY_KEY "tmc-uid" - -#define SERVICES_KEY "sso-services" - -#define MC_SERVICE_KEY "Service" - -typedef struct { - gchar *mc_name; - gchar *ag_name; - gboolean global; /* global ag setting or service specific? */ - gboolean readable; /* does the _standard_ read method copy this into MC? */ - gboolean writable; /* does the _standard_ write method copy this into AG? */ - gboolean freeable; /* should clear_setting_data deallocate the names? */ -} Setting; - -#define GLOBAL TRUE -#define SERVICE FALSE -#define READABLE TRUE -#define UNREADABLE FALSE -#define WRITABLE TRUE -#define UNWRITABLE FALSE - -typedef enum { - SETTING_MC, - SETTING_AG, -} SettingType; - -Setting setting_map[] = { - { MC_ENABLED_KEY , AG_ENABLED_KEY , GLOBAL , UNREADABLE, UNWRITABLE }, - { MCPP MC_ACCOUNT_KEY, AG_ACCOUNT_KEY , GLOBAL , READABLE , UNWRITABLE }, - { MCPP PASSWORD_KEY , PASSWORD_KEY , GLOBAL , READABLE , WRITABLE }, - { MC_LABEL_KEY , AG_LABEL_KEY , GLOBAL , READABLE , WRITABLE }, - { LIBACCT_ID_KEY , LIBACCT_ID_KEY , GLOBAL , UNREADABLE, UNWRITABLE }, - { MC_IDENTITY_KEY , MC_IDENTITY_KEY, SERVICE, READABLE , WRITABLE }, - { MC_CMANAGER_KEY , MC_CMANAGER_KEY, SERVICE, READABLE , UNWRITABLE }, - { MC_PROTOCOL_KEY , MC_PROTOCOL_KEY, SERVICE, READABLE , UNWRITABLE }, - { MC_SERVICE_KEY , MC_SERVICE_KEY , SERVICE, UNREADABLE, UNWRITABLE }, - { SERVICES_KEY , SERVICES_KEY , GLOBAL , UNREADABLE, UNWRITABLE }, - { NULL , NULL , SERVICE, UNREADABLE, UNWRITABLE } -}; - -static void -clear_setting_data (Setting *setting) -{ - if (setting == NULL) - return; - - if (!setting->freeable) - return; - - g_free (setting->mc_name); - g_free (setting->ag_name); - setting->mc_name = NULL; - setting->ag_name = NULL; -} - -static Setting * -setting_data (const gchar *name, SettingType type) -{ - guint i = 0; - static Setting parameter = { NULL, NULL, SERVICE, READABLE, WRITABLE, TRUE }; - const gchar *prefix; - - for (; setting_map[i].mc_name != NULL; i++) - { - const gchar *setting_name = NULL; - - if (type == SETTING_MC) - setting_name = setting_map[i].mc_name; - else - setting_name = setting_map[i].ag_name; - - if (g_strcmp0 (name, setting_name) == 0) - return &setting_map[i]; - } - - prefix = (type == SETTING_MC) ? MCPP : AGPP; - - if (!g_str_has_prefix (name, prefix)) - { /* a non-parameter setting */ - parameter.mc_name = g_strdup (name); - parameter.ag_name = g_strdup (name); - } - else - { /* a setting that is a parameter on both sides (AG & MC) */ - const guint plength = strlen (prefix); - - parameter.mc_name = g_strdup_printf ("%s%s", MCPP, name + plength); - parameter.ag_name = g_strdup_printf ("%s%s", AGPP, name + plength); - } - - return ¶meter; -} - - -/* logging helpers: */ -static void -_g_log_handler (const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer unused_data) -{ - /* the libaccounts code is currently very chatty when debugging: * - * we are only interested in or own debugging output for now. */ - if ((gchar *)log_domain != (gchar *)G_LOG_DOMAIN) - return; - - g_log_default_handler (log_domain, log_level, message, unused_data); -} - -static void -toggle_mute (void) -{ - static GLogFunc old = NULL; - - if (old == NULL) - { - old = g_log_set_default_handler (_g_log_handler, NULL); - } - else - { - g_log_set_default_handler (old, NULL); - old = NULL; - } -} - -static gchar * -_gvalue_to_string (const GValue *val) -{ - switch (G_VALUE_TYPE (val)) - { - case G_TYPE_STRING: - return g_value_dup_string (val); - case G_TYPE_BOOLEAN: - return g_strdup (g_value_get_boolean (val) ? "true" : "false"); - case G_TYPE_CHAR: - return g_strdup_printf ("%c", g_value_get_uchar (val)); - case G_TYPE_UCHAR: - return g_strdup_printf ("%c", g_value_get_char (val)); - case G_TYPE_INT: - return g_strdup_printf ("%i", g_value_get_int (val)); - case G_TYPE_UINT: - return g_strdup_printf ("%u", g_value_get_uint (val)); - case G_TYPE_LONG: - return g_strdup_printf ("%ld", g_value_get_long (val)); - case G_TYPE_ULONG: - return g_strdup_printf ("%lu", g_value_get_ulong (val)); - case G_TYPE_INT64: - return g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (val)); - case G_TYPE_UINT64: - return g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (val)); - case G_TYPE_ENUM: - return g_strdup_printf ("%d" , g_value_get_enum (val)); - case G_TYPE_FLAGS: - return g_strdup_printf ("%u", g_value_get_flags (val)); - case G_TYPE_FLOAT: - return g_strdup_printf ("%f", g_value_get_float (val)); - case G_TYPE_DOUBLE: - return g_strdup_printf ("%g", g_value_get_double (val)); - default: - g_warning ("Unsupported type %s", G_VALUE_TYPE_NAME (val)); - return NULL; - } -} - -static AgManager * -get_ag_manager (void) -{ - static AgManager *agm = NULL; - - toggle_mute (); - - if (agm != NULL) - return agm; - - agm = ag_manager_new (); - - toggle_mute (); - - return agm; -} - -static AgAccount * -get_ag_account (const gchar *mc_account) -{ - AgAccount *ag_account = NULL; - AgManager *ag_manager = get_ag_manager (); - GList *ag_ids = NULL; - GList *ag_id; - - toggle_mute (); - - ag_ids = ag_manager_list_by_service_type (ag_manager, "IM"); - g_debug ("%d accounts in SSO", g_list_length (ag_ids)); - - for (ag_id = ag_ids; ag_id != NULL; ag_id = g_list_next (ag_id)) - { - AgAccountId id = GPOINTER_TO_UINT (ag_id->data); - AgAccount *account = ag_manager_get_account (ag_manager, id); - - if (account != NULL) - { - GValue value = G_VALUE_INIT; - AgSettingSource source = AG_SETTING_SOURCE_NONE; - - g_value_init (&value, G_TYPE_STRING); - ag_account_select_service (account, NULL); - - source = ag_account_get_value (account, MC_IDENTITY_KEY, &value); - - if (source != AG_SETTING_SOURCE_NONE) - { - if (g_str_equal (g_value_get_string (&value), mc_account)) - { - ag_account = g_object_ref (account); - ag_id = NULL; - } - - g_value_unset (&value); - } - - g_object_unref (account); - } - } - - ag_manager_list_free (ag_ids); - - toggle_mute (); - - return ag_account; -} - -static gboolean -_ag_account_select_default_im_service (AgAccount *account) -{ - gboolean have_im_service = FALSE; - GList *first = ag_account_list_services_by_type (account, "IM"); - - if (first != NULL && first->data != NULL) - { - have_im_service = TRUE; - ag_account_select_service (account, first->data); - } - - ag_service_list_free (first); - - return have_im_service; -} - -/* enabled is actually a tri-state<->boolean mapping */ -static gboolean _sso_account_enabled (AgAccount *account, AgService *service) -{ - gboolean local = FALSE; - gboolean global = FALSE; - AgService *original = ag_account_get_selected_service (account); - - if (service == NULL) - { - _ag_account_select_default_im_service (account); - local = ag_account_get_enabled (account); - } - else - { - if (original != service) - ag_account_select_service (account, service); - - local = ag_account_get_enabled (account); - } - - ag_account_select_service (account, NULL); - global = ag_account_get_enabled (account); - - ag_account_select_service (account, original); - - g_debug ("_sso_account_enabled: global:%d && local:%d", global, local); - - return local && global; -} - -static void _sso_account_enable (AgAccount *account, - AgService *service, - gboolean on) -{ - AgService *original = ag_account_get_selected_service (account); - - /* turn the local enabled flag on/off as required */ - if (service != NULL) - ag_account_select_service (account, service); - else - _ag_account_select_default_im_service (account); - - ag_account_set_enabled (account, on); - - /* if we are turning the account on, the global flag must also be set * - * NOTE: this isn't needed when turning the account off */ - if (on) - { - ag_account_select_service (account, NULL); - ag_account_set_enabled (account, on); - } - - ag_account_select_service (account, original); -} - -/* saving settings other than the enabled tri-state */ -static void -save_setting (AgAccount *account, - const Setting *setting, - const gchar *val) -{ - AgService *service = ag_account_get_selected_service (account); - - if (!setting->writable) - return; - - if (setting->global) - ag_account_select_service (account, NULL); - else if (service == NULL) - _ag_account_select_default_im_service (account); - - if (val != NULL) - { - GValue value = G_VALUE_INIT; - - g_value_init (&value, G_TYPE_STRING); - g_value_set_string (&value, val); - ag_account_set_value (account, setting->ag_name, &value); - g_value_unset (&value); - } - else - { - ag_account_set_value (account, setting->ag_name, NULL); - } - - /* leave the selected service as we found it: */ - ag_account_select_service (account, service); -} - -gchar * -libaccounts_get (const gchar *mc_account, const gchar *key) -{ - gchar *rval = NULL; - AgAccount *ag_account = get_ag_account (mc_account); - Setting *setting = setting_data (key, SETTING_MC); - - toggle_mute (); - - if (ag_account != NULL) - { - - if (setting == NULL) - { - g_debug ("setting %s is unknown/unmapped, aborting update", key); - rval = g_strdup (""); - goto done; - } - - g_debug ("MC key %s -> AG key %s", key, setting->ag_name); - - if (g_str_equal (setting->ag_name, AG_ENABLED_KEY)) - { - gboolean on = _sso_account_enabled (ag_account, NULL); - - rval = g_strdup (on ? "true" : "false"); - goto done; - } - else - { - GValue value = G_VALUE_INIT; - AgSettingSource source = AG_SETTING_SOURCE_NONE; - - g_value_init (&value, G_TYPE_STRING); - - /* the 'account' parameter is a special case for historical reasons */ - if (g_str_equal (key, MCPP MC_ACCOUNT_KEY)) - { - _ag_account_select_default_im_service (ag_account); - source = - ag_account_get_value (ag_account, AG_ACCOUNT_ALT_KEY, &value); - - if (source != AG_SETTING_SOURCE_NONE) - goto found; - } - - if (setting->global) - ag_account_select_service (ag_account, NULL); - else - _ag_account_select_default_im_service (ag_account); - - source = ag_account_get_value (ag_account, setting->ag_name, &value); - - found: - if (source != AG_SETTING_SOURCE_NONE) - { - rval = _gvalue_to_string (&value); - g_value_unset (&value); - } - } - } - - done: - toggle_mute (); - - if (ag_account) - g_object_unref (ag_account); - - clear_setting_data (setting); - - return rval; -} - -gboolean -libaccounts_set (const gchar *mc_account, - const gchar *key, - const gchar *value) -{ - gboolean done = FALSE; - AgAccount *ag_account = get_ag_account (mc_account); - Setting *setting = setting_data (key, SETTING_MC); - - toggle_mute (); - - if (ag_account != NULL) - { - if (setting == NULL) - { - g_debug ("setting %s is unknown/unmapped, aborting update", key); - goto done; - } - - if (g_str_equal (setting->ag_name, MC_ENABLED_KEY)) - { - gboolean on = g_str_equal (value, "true"); - - _sso_account_enable (ag_account, NULL, on); - done = TRUE; - goto done; - } - else - { - save_setting (ag_account, setting, value); - done = TRUE; - } - - if (done) - ag_account_store (ag_account, NULL, NULL); - - } - - done: - toggle_mute (); - - if (ag_account) - g_object_unref (ag_account); - - clear_setting_data (setting); - - return done; -} - -gboolean -libaccounts_delete (const gchar *mc_account) -{ - gboolean done = FALSE; - AgAccount *ag_account = get_ag_account (mc_account); - - toggle_mute (); - - if(ag_account != NULL) - { - ag_account_delete (ag_account); - ag_account_store (ag_account, NULL, NULL); - g_object_unref (ag_account); - done = TRUE; - } - - toggle_mute (); - - return done; -} - -gboolean -libaccounts_exists (const gchar *mc_account) -{ - gboolean exists = FALSE; - AgAccount *ag_account = get_ag_account (mc_account); - - toggle_mute (); - - if (ag_account != NULL) - { - exists = TRUE; - g_object_unref (ag_account); - } - - toggle_mute (); - - return exists; -} - -GStrv -libaccounts_list (void) -{ - AgManager *ag_manager = get_ag_manager (); - GList *ag_ids = ag_manager_list_by_service_type (ag_manager, "IM"); - guint len = g_list_length (ag_ids); - GStrv rval = NULL; - GList *id; - guint i = 0; - Setting *setting = setting_data (MC_IDENTITY_KEY, SETTING_AG); - - if (len == 0) - goto done; - - rval = g_new (gchar *, len + 1); - rval[len] = NULL; - - for (id = ag_ids; id && i < len; id = g_list_next (id)) - { - GValue value = G_VALUE_INIT; - AgAccountId uid = GPOINTER_TO_UINT (id->data); - AgAccount *ag_account = ag_manager_get_account (ag_manager, uid); - AgSettingSource source = AG_SETTING_SOURCE_NONE; - - if (ag_account) - { - if (setting->global) - ag_account_select_service (ag_account, NULL); - else - _ag_account_select_default_im_service (ag_account); - - source = ag_account_get_value (ag_account, setting->ag_name, &value); - } - - if (source != AG_SETTING_SOURCE_NONE) - { - rval[i++] = _gvalue_to_string (&value); - g_value_unset (&value); - } - else - { - GValue cmanager = G_VALUE_INIT; - GValue protocol = G_VALUE_INIT; - GValue account = G_VALUE_INIT; - const gchar *acct = NULL; - const gchar *cman = NULL; - const gchar *proto = NULL; - - g_value_init (&cmanager, G_TYPE_STRING); - g_value_init (&protocol, G_TYPE_STRING); - g_value_init (&account, G_TYPE_STRING); - - _ag_account_select_default_im_service (ag_account); - - ag_account_get_value (ag_account, MC_CMANAGER_KEY, &cmanager); - cman = g_value_get_string (&cmanager); - - ag_account_get_value (ag_account, MC_PROTOCOL_KEY, &protocol); - proto = g_value_get_string (&protocol); - - ag_account_select_service (ag_account, NULL); - ag_account_get_value (ag_account, AG_ACCOUNT_KEY, &account); - acct = g_value_get_string (&account); - - rval[i++] = g_strdup_printf ("unnamed account #%u (%s/%s/%s)", - uid, cman, proto, acct); - - g_value_unset (&cmanager); - g_value_unset (&protocol); - g_value_unset (&account); - } - } - - done: - g_list_free (ag_ids); - clear_setting_data (setting); - - return rval; -} diff --git a/tests/account-store-libaccounts.h b/tests/account-store-libaccounts.h deleted file mode 100644 index 8e88f696..00000000 --- a/tests/account-store-libaccounts.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * MC account storage backend inspector, libaccounts backend - * - * Copyright © 2010 Nokia Corporation - * Copyright © 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 <glib-object.h> - -#ifndef _ACCOUNT_STORE_LIBACCOUNTS_H_ -#define _ACCOUNT_STORE_LIBACCOUNTS_H_ - -G_BEGIN_DECLS - -gchar * libaccounts_get (const gchar *mc_account, - const gchar *key); - -gboolean libaccounts_set (const gchar *mc_account, - const gchar *key, - const gchar *value); - -gboolean libaccounts_delete (const gchar *mc_account); - -gboolean libaccounts_exists (const gchar *mc_account); - -GStrv libaccounts_list (void); - -G_END_DECLS - -#endif diff --git a/tests/account-store-variant-file.c b/tests/account-store-variant-file.c new file mode 100644 index 00000000..b3fc7565 --- /dev/null +++ b/tests/account-store-variant-file.c @@ -0,0 +1,189 @@ +/* + * MC account storage inspector: MC 5.14 GVariant-file backend + * + * Copyright © 2010 Nokia Corporation + * Copyright © 2010-2013 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 "config.h" +#include "account-store-variant-file.h" + +#include <errno.h> + +#include <glib.h> +#include <glib/gstdio.h> + +static gchar * +get_path (const gchar *account) +{ + gchar *ret; + gchar *basename; + + basename = g_strdup_printf ("%s.account", account); + g_strdelimit (basename, "/", '-'); + + ret = g_build_filename (g_get_user_data_dir (), "telepathy", + "mission-control", basename, NULL); + g_free (basename); + return ret; +} + +static GVariant * +load (const gchar *account) +{ + GError *error = NULL; + gchar *contents = NULL; + gsize len; + GVariant *ret = NULL; + gchar *path = NULL; + + path = get_path (account); + + if (!g_file_get_contents (path, &contents, &len, &error)) + goto finally; + + ret = g_variant_parse (G_VARIANT_TYPE_VARDICT, contents, contents + len, + NULL, &error); + +finally: + if (error != NULL) + g_warning ("variant file '%s' error: %s", path, error->message); + + g_free (path); + g_clear_error (&error); + g_free (contents); + return ret; +} + +gchar * +variant_get (const gchar *account, + const gchar *key) +{ + GVariant *asv = load (account); + GVariant *v = NULL; + GString *ret = NULL; + + if (asv == NULL) + return NULL; + + if (g_str_has_prefix (key, "param-")) + { + GVariant *intermediate = g_variant_lookup_value (asv, + "Parameters", NULL); + + if (intermediate != NULL) + { + g_assert (g_variant_is_of_type (intermediate, + G_VARIANT_TYPE ("a{sv}"))); + v = g_variant_lookup_value (intermediate, key + 6, NULL); + } + + intermediate = g_variant_lookup_value (asv, + "KeyFileParameters", NULL); + + if (v == NULL && intermediate != NULL) + { + g_assert (g_variant_is_of_type (intermediate, + G_VARIANT_TYPE ("a{ss}"))); + v = g_variant_lookup_value (intermediate, key + 6, + G_VARIANT_TYPE_STRING); + + if (v != NULL) + ret = g_string_new ("keyfile-escaped "); + } + } + else + { + v = g_variant_lookup_value (asv, key, NULL); + } + + if (v != NULL) + { + ret = g_variant_print_string (v, ret, TRUE); + g_variant_unref (v); + } + + g_variant_unref (asv); + + if (ret == NULL) + return NULL; + + return g_string_free (ret, FALSE); +} + +gboolean +variant_delete (const gchar *account) +{ + gchar *path = get_path (account); + + if (g_unlink (path) != 0) + { + g_warning ("%s", g_strerror (errno)); + g_free (path); + return FALSE; + } + + g_free (path); + return TRUE; +} + +gboolean +variant_exists (const gchar *account) +{ + gchar *path = get_path (account); + gboolean ret = g_file_test (path, G_FILE_TEST_EXISTS); + + g_free (path); + return ret; +} + +GStrv +variant_list (void) +{ + GPtrArray *ret = g_ptr_array_new (); + gchar *dir_path = g_build_filename (g_get_user_data_dir (), "telepathy", + "mission-control", NULL); + GDir *dir = g_dir_open (dir_path, 0, NULL); + + if (dir != NULL) + { + const gchar *name; + + for (name = g_dir_read_name (dir); + name != NULL; + name = g_dir_read_name (dir)) + { + gchar *dup; + + if (!g_str_has_suffix (name, ".account")) + continue; + + /* this is not production code so we're ignoring the possibility + * of invalid account names here */ + dup = g_strdup (name); + g_strdelimit (dup, "-", '/'); + g_strdelimit (dup, ".", '\0'); + g_ptr_array_add (ret, dup); + } + + g_dir_close (dir); + } + + g_free (dir_path); + g_ptr_array_add (ret, NULL); + return (GStrv) g_ptr_array_free (ret, FALSE); +} diff --git a/plugins/mcp-dbus-aegis-acl.h b/tests/account-store-variant-file.h index 96baaf46..dad07bf2 100644 --- a/plugins/mcp-dbus-aegis-acl.h +++ b/tests/account-store-variant-file.h @@ -1,8 +1,8 @@ /* - * A pseudo-plugin that checks the caller's Aegis permission tokens + * MC account storage inspector: MC 5.14 GVariant-file backend * - * Copyright © 2010-2011 Nokia Corporation - * Copyright © 2010-2011 Collabora Ltd. + * Copyright © 2010 Nokia Corporation + * Copyright © 2010-2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,16 +19,19 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AEGIS_ACL_H -#define AEGIS_ACL_H +#ifndef _ACCOUNT_STORE_VARIANT_FILE_H_ +#define _ACCOUNT_STORE_VARIANT_FILE_H_ -#include <mission-control-plugins/mission-control-plugins.h> +#include <glib.h> #include <glib-object.h> -G_BEGIN_DECLS +gchar *variant_get (const gchar *account, + const gchar *key); -GObject *aegis_acl_new (void); +gboolean variant_delete (const gchar *account); -G_END_DECLS +gboolean variant_exists (const gchar *account); + +GStrv variant_list (void); #endif diff --git a/tests/account-store.c b/tests/account-store.c index 4482ea5b..419d178a 100644 --- a/tests/account-store.c +++ b/tests/account-store.c @@ -27,7 +27,8 @@ #include <glib.h> #include <glib-object.h> -#include "account-store-default.h" +#include "account-store-keyfile.h" +#include "account-store-variant-file.h" #define DOCSTRING_A \ "%s OP BACKEND ACCOUNT [KEY [VALUE]]\n\n" \ @@ -40,10 +41,6 @@ " KEY := <manager | protocol | DisplayName | param-<PARAMETER>>\n" \ " VALUE := <STRING>\n\n" -#if ENABLE_LIBACCOUNTS_SSO -#include "account-store-libaccounts.h" -#endif - typedef struct { const gchar *name; gchar * (*get) (const gchar *account, const gchar *key); @@ -63,21 +60,19 @@ typedef enum { } Operation; const Backend backends[] = { - { "default", - default_get, - default_set, - default_delete, - default_exists, - default_list }, - -#if ENABLE_LIBACCOUNTS_SSO - { "libaccounts", - libaccounts_get, - libaccounts_set, - libaccounts_delete, - libaccounts_exists, - libaccounts_list }, -#endif + { "keyfile", + keyfile_get, + keyfile_set, + keyfile_delete, + keyfile_exists, + keyfile_list }, + + { "variant-file", + variant_get, + NULL, + variant_delete, + variant_exists, + variant_list }, { NULL } }; @@ -182,6 +177,12 @@ int main (int argc, char **argv) break; case OP_SET: + if (store->set == NULL) + { + g_printerr ("Listing is unimplemented for this backend"); + break; + } + success = store->set (account, setting, value); output = g_strdup_printf ("%s.%s set to '%s' in %s", account, setting, value, store->name); diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index 26ac627b..7091bfa8 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -19,6 +19,7 @@ TWISTED_BASIC_TESTS = \ account-manager/recover-from-disconnect.py \ account-manager/req-conn-fails.py \ account-manager/request-online.py \ + account-manager/restricted-storage.py \ account-manager/service.py \ account-manager/update-parameters.py \ account-requests/cancel.py \ @@ -27,10 +28,8 @@ TWISTED_BASIC_TESTS = \ account/addressing.py \ capabilities/contact-caps.py \ dispatcher/already-has-channel.py \ - dispatcher/already-has-obsolete.py \ dispatcher/approver-fails.py \ dispatcher/bypass-approval.py \ - dispatcher/bypass-observers.py \ dispatcher/cancel.py \ dispatcher/capture-bundle.py \ dispatcher/cdo-claim.py \ @@ -75,9 +74,12 @@ TWISTED_BASIC_TESTS = \ # account-storage/*.py need their own instances. TWISTED_SPECIAL_BUILD_TESTS = \ account-manager/connectivity.py \ - account-manager/hidden.py \ - account-storage/default-keyring-storage.py \ - account-storage/diverted-storage.py + account-storage/diverted-storage.py \ + account-storage/5-12.py \ + account-storage/5-14.py \ + account-storage/create-new.py \ + account-storage/load-keyfiles.py \ + $(NULL) # Tests that are usually too slow to run. TWISTED_SLOW_TESTS = \ @@ -104,11 +106,10 @@ TWISTED_TESTS = \ TWISTED_OTHER_FILES = \ constants.py \ fakeaccountsservice.py \ - fakeclient.py \ - fakecm.py \ fakeconnectivity.py \ mctest.py \ servicetest.py \ + account-storage/storage_helper.py \ telepathy/clients/README \ telepathy/clients/AbiWord.client \ telepathy/clients/Logger.client \ @@ -122,6 +123,14 @@ TWISTED_OTHER_FILES = \ mc-twisted-tests.list: $(AM_V_GEN)echo $(TWISTED_BASIC_TESTS) $(TWISTED_SEPARATE_TESTS) > $@ +twisted-tests.test: Makefile + $(AM_V_GEN)( echo '[Test]'; \ + echo 'Exec=${twistedtestsdir}/run-test.sh'; \ + echo 'Type=session'; \ + echo 'Output=TAP' ) > $@.tmp + @chmod +x $@.tmp + @mv $@.tmp $@ + run-test.sh: run-test.sh.in Makefile $(AM_V_GEN)sed \ -e "s|[@]mctestsdir[@]|@mctestsdir@|g" \ @@ -130,6 +139,9 @@ run-test.sh: run-test.sh.in Makefile @chmod +x $@ if ENABLE_INSTALLED_TESTS +insttestdir = ${datadir}/installed-tests/telepathy-mission-control +nodist_insttest_DATA = twisted-tests.test + # Install files in each directory. They could be tests, pristine data files, # scripts or built source twistedtestsdir = @mctestsdir@/twisted @@ -147,13 +159,6 @@ nobase_nodist_twistedtests_DATA = \ $(NULL) endif - -if HAVE_MCE -HAVE_MCE_PYBOOL = True -else -HAVE_MCE_PYBOOL = False -endif - if HAVE_NM HAVE_NM_PYBOOL = True else @@ -162,13 +167,13 @@ endif config.py: Makefile $(AM_V_GEN) { \ - echo "HAVE_MCE = $(HAVE_MCE_PYBOOL)"; \ echo "HAVE_NM = $(HAVE_NM_PYBOOL)"; \ } > $@ BUILT_SOURCES = \ config.py \ mc-twisted-tests.list \ + twisted-tests.test \ run-test.sh \ $(NULL) @@ -177,7 +182,6 @@ AM_CFLAGS = $(ERROR_CFLAGS) plugins_list = \ mcp-plugin.la \ mcp-account-diversion.la \ - mcp-dbus-caller-permission.la \ libgiofakenetworkmonitor.la \ $(NULL) @@ -215,9 +219,6 @@ libgiofakenetworkmonitor_la_LDFLAGS = $(mcp_plugin_la_LDFLAGS) mcp_account_diversion_la_SOURCES = mcp-account-diversion.c mcp_account_diversion_la_LDFLAGS = $(mcp_plugin_la_LDFLAGS) -mcp_dbus_caller_permission_la_SOURCES = mcp-dbus-caller-permission.c -mcp_dbus_caller_permission_la_LDFLAGS = $(mcp_plugin_la_LDFLAGS) - # A debug version of the normal MC executable, which exits cleanly on # disconnection from D-Bus (so gcov info gets written out) noinst_PROGRAMS = mc-debug-server @@ -225,7 +226,7 @@ mc_debug_server_SOURCES = mc-debug-server.c mc_debug_server_LDADD = \ $(top_builddir)/src/libmcd-convenience.la -INCLUDES = \ +AM_CPPFLAGS = \ -I$(top_srcdir) -I$(top_builddir) \ -I$(top_srcdir)/src -I$(top_builddir)/src \ $(DBUS_CFLAGS) \ diff --git a/tests/twisted/account-manager/account-basics.py b/tests/twisted/account-manager/account-basics.py index 18b35cb2..4e643cc9 100644 --- a/tests/twisted/account-manager/account-basics.py +++ b/tests/twisted/account-manager/account-basics.py @@ -46,7 +46,7 @@ def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_path = account.__dbus_object_path__ @@ -83,7 +83,6 @@ def test(q, bus, mc): interfaces = properties.get('Interfaces') assert cs.ACCOUNT_IFACE_AVATAR in interfaces, interfaces - assert cs.ACCOUNT_IFACE_NOKIA_CONDITIONS in interfaces, interfaces # sanity check for k in properties: @@ -174,32 +173,6 @@ def test(q, bus, mc): ) assert account_props.Get(cs.ACCOUNT, 'Nickname') == 'Joe Bloggs' - call_async(q, account_props, 'Set', cs.ACCOUNT_IFACE_NOKIA_CONDITIONS, - 'Condition', - dbus.Dictionary({':foo': 'bar'}, signature='ss')) - # there's no change notification for the Condition - q.expect_many( - EventPattern('dbus-return', method='Set'), - EventPattern('dbus-signal', - interface=cs.TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - signal='DeferringSetAttribute', - args=[account_path, 'condition-:foo', 'bar']), - EventPattern('dbus-signal', - interface=cs.TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - signal='CommittingOne', - args=[account_path]), - EventPattern('dbus-method-call', - interface=cs.TEST_DBUS_ACCOUNT_SERVICE_IFACE, - method='UpdateAttributes', - args=[account_path[len(cs.ACCOUNT_PATH_PREFIX):], - {'condition-:foo': 'bar'}, - {'condition-:foo': 0}, # flags - []], - ), - ) - assert account_props.Get(cs.ACCOUNT_IFACE_NOKIA_CONDITIONS, - 'Condition') == {':foo': 'bar'} - assertEquals(dbus.Array(signature='o'), account_props.Get(cs.ACCOUNT, 'Supersedes')) call_async(q, account_props, 'Set', cs.ACCOUNT, 'Supersedes', @@ -271,16 +244,6 @@ def test(q, bus, mc): else: raise AssertionError('Setting %s with wrong type should fail' % p) - for p in ('Condition',): - try: - account_props.Set(cs.ACCOUNT_IFACE_NOKIA_CONDITIONS, p, - badly_typed) - except dbus.DBusException, e: - assert e.get_dbus_name() == cs.INVALID_ARGUMENT, \ - (p, e.get_dbus_name()) - else: - raise AssertionError('Setting %s with wrong type should fail' % p) - # Make sure MC hasn't crashed yet, and make sure some properties are what # we expect them to be diff --git a/tests/twisted/account-manager/auto-connect.py b/tests/twisted/account-manager/auto-connect.py index a623902c..28cfb2ab 100644 --- a/tests/twisted/account-manager/auto-connect.py +++ b/tests/twisted/account-manager/auto-connect.py @@ -21,22 +21,20 @@ import dbus """ import os +import os.path import dbus import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async, assertEquals -from mctest import exec_test, SimulatedConnection, create_fakecm_account, MC +from mctest import (exec_test, SimulatedConnection, + SimulatedConnectionManager, MC) import constants as cs -cm_name_ref = dbus.service.BusName( - cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=dbus.SessionBus()) - account_id = 'fakecm/fakeprotocol/jc_2edenton_40unatco_2eint' def preseed(q, bus, fake_accounts_service): - accounts_dir = os.environ['MC_ACCOUNT_DIR'] try: @@ -68,8 +66,12 @@ def preseed(q, bus, fake_accounts_service): 'password': r'\\\\ionstorm\\\\', }) - os.makedirs(accounts_dir + '/' + account_id) - avatar_bin = open(accounts_dir + '/' + account_id + '/avatar.bin', 'w') + avatar_filename = account_id + avatar_filename = avatar_filename.replace('/', '-') + '.avatar' + avatar_filename = (os.environ['XDG_DATA_HOME'] + + '/telepathy/mission-control/' + avatar_filename) + os.makedirs(os.path.dirname(avatar_filename)) + avatar_bin = open(avatar_filename, 'w') avatar_bin.write('Deus Ex') avatar_bin.close() @@ -78,6 +80,8 @@ def preseed(q, bus, fake_accounts_service): account_connections_file.close() def test(q, bus, unused, **kwargs): + simulated_cm = SimulatedConnectionManager(q, bus) + fake_accounts_service = kwargs['fake_accounts_service'] preseed(q, bus, fake_accounts_service) @@ -124,7 +128,7 @@ def test(q, bus, unused, **kwargs): assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails')) assertEquals(cs.CONN_STATUS_CONNECTING, prop_changed.args[0].get('ConnectionStatus')) - assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + assertEquals(cs.CSR_REQUESTED, prop_changed.args[0].get('ConnectionStatusReason')) q.dbus_return(request_conn.message, conn.bus_name, conn.object_path, @@ -147,16 +151,16 @@ def test(q, bus, unused, **kwargs): assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails')) assertEquals(cs.CONN_STATUS_CONNECTING, prop_changed.args[0].get('ConnectionStatus')) - assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + assertEquals(cs.CSR_REQUESTED, prop_changed.args[0].get('ConnectionStatusReason')) props = account.GetAll(cs.ACCOUNT, dbus_interface=cs.PROPERTIES_IFACE) assert props['Connection'] == conn.object_path assert props['ConnectionStatus'] == cs.CONN_STATUS_CONNECTING - assert props['ConnectionStatusReason'] == cs.CONN_STATUS_REASON_REQUESTED + assert props['ConnectionStatusReason'] == cs.CSR_REQUESTED print "becoming connected" - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) set_aliases, set_presence, set_avatar, prop_changed = q.expect_many( EventPattern('dbus-method-call', @@ -183,11 +187,11 @@ def test(q, bus, unused, **kwargs): assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails')) assertEquals(cs.CONN_STATUS_CONNECTED, prop_changed.args[0].get('ConnectionStatus')) - assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + assertEquals(cs.CSR_REQUESTED, prop_changed.args[0].get('ConnectionStatusReason')) assert account.Get(cs.ACCOUNT, 'CurrentPresence', - dbus_interface=cs.PROPERTIES_IFACE) == (cs.PRESENCE_TYPE_AVAILABLE, + dbus_interface=cs.PROPERTIES_IFACE) == (cs.PRESENCE_AVAILABLE, 'available', 'My vision is augmented') q.dbus_return(set_aliases.message, signature='') diff --git a/tests/twisted/account-manager/avatar-refresh.py b/tests/twisted/account-manager/avatar-refresh.py index fe75d3a3..d914c439 100644 --- a/tests/twisted/account-manager/avatar-refresh.py +++ b/tests/twisted/account-manager/avatar-refresh.py @@ -28,12 +28,10 @@ import dbus.service from servicetest import (EventPattern, tp_name_prefix, tp_path_prefix, call_async, assertEquals, sync_dbus) -from mctest import exec_test, SimulatedConnection, create_fakecm_account, MC +from mctest import (exec_test, SimulatedConnection, + SimulatedConnectionManager, MC) import constants as cs -cm_name_ref = dbus.service.BusName( - cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=dbus.SessionBus()) - class Account(object): def __init__(self, fake_accounts_service, accounts_dir, avatars_persist, server_delays, local_avatar, remote_avatar): @@ -92,15 +90,6 @@ class Account(object): avatar_bin = open(avatar_filename, 'w') avatar_bin.write(local_avatar) avatar_bin.close() - elif not avatars_persist: - self.avatar_location = 'old' - # exercise migration from ~/.mission-control in a - # situation where MC should "win" - os.makedirs(accounts_dir + '/' + self.id) - avatar_bin = open( - accounts_dir + '/' + self.id + '/avatar.bin', 'w') - avatar_bin.write(local_avatar) - avatar_bin.close() else: # store it in the normal location self.avatar_location = 'home' @@ -187,23 +176,21 @@ class Account(object): q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') - forbidden = [] - if self.winner != 'MC': - forbidden.append( - EventPattern('dbus-method-call', method='SetAvatar')) + q.forbid_events([ + EventPattern('dbus-method-call', method='SetAvatar'), + ]) if self.winner != 'service': - forbidden.append( + q.forbid_events([ EventPattern('dbus-signal', signal='AvatarChanged', - path=account_path)) - - q.forbid_events(forbidden) + path=account_path), + ]) q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True, interface=cs.CONN) - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) if self.winner == 'MC': # MC should upload the avatar. @@ -256,24 +243,13 @@ class Account(object): byte_arrays=True) == conn.avatar sync_dbus(bus, q, mc) - q.unforbid_events(forbidden) + q.unforbid_all() if self.local_avatar: self.test_migration(bus, q, conn, account_proxy) def test_migration(self, bus, q, conn, account_proxy): - if self.avatar_location == 'old': - # The avatar got migrated to the new location. - assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/' + - self.id + '/avatar.bin') - assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/fakecm') - avatar_filename = self.id - avatar_filename = avatar_filename.replace('/', '-') + '.avatar' - avatar_filename = (os.environ['XDG_DATA_HOME'] + - '/telepathy/mission-control/' + avatar_filename) - assertEquals(conn.avatar[0], ''.join(open(avatar_filename, - 'r').readlines())) - elif self.avatar_location == 'datadir' and self.winner == 'service': + if self.avatar_location == 'datadir' and self.winner == 'service': # The avatar wasn't deleted from $XDG_DATA_DIRS, but it was # overridden. assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/' + @@ -359,6 +335,8 @@ def preseed(q, bus, fake_accounts_service): return accounts def test(q, bus, unused, **kwargs): + simulated_cm = SimulatedConnectionManager(q, bus) + fake_accounts_service = kwargs['fake_accounts_service'] accounts = preseed(q, bus, fake_accounts_service) diff --git a/tests/twisted/account-manager/avatar.py b/tests/twisted/account-manager/avatar.py index 353c8992..b31ab975 100644 --- a/tests/twisted/account-manager/avatar.py +++ b/tests/twisted/account-manager/avatar.py @@ -29,7 +29,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "me@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) diff --git a/tests/twisted/account-manager/backend-makes-changes.py b/tests/twisted/account-manager/backend-makes-changes.py index 89eb4c1d..a4caeac0 100644 --- a/tests/twisted/account-manager/backend-makes-changes.py +++ b/tests/twisted/account-manager/backend-makes-changes.py @@ -34,7 +34,7 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): {}, # attr flags {'account': 'ezio@firenze.fic', 'password': 'nothing is true'}, {}, # untyped parameters - {'password': cs.PARAM_FLAG_SECRET}) # param flags + {'password': cs.PARAM_SECRET}) # param flags q.expect_many( EventPattern('dbus-signal', path=cs.TEST_DBUS_ACCOUNT_SERVICE_PATH, @@ -47,7 +47,8 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): {'account': 'ezio@firenze.fic', 'password': 'nothing is true'}, {}, - {'account': 0, 'password': cs.PARAM_FLAG_SECRET}]), + {'account': 0, 'password': cs.PARAM_SECRET}, + 0]), EventPattern('dbus-signal', path=cs.AM_PATH, signal='AccountValidityChanged', @@ -184,14 +185,14 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): account.Properties.Get(cs.ACCOUNT, 'Supersedes')) fake_accounts_service.update_attributes(account_tail, - {'AutomaticPresence': (dbus.UInt32(cs.PRESENCE_TYPE_HIDDEN), 'hidden', + {'AutomaticPresence': (dbus.UInt32(cs.PRESENCE_HIDDEN), 'hidden', 'in a haystack or something')}) q.expect_many( EventPattern('dbus-signal', path=cs.TEST_DBUS_ACCOUNT_SERVICE_PATH, signal='AttributesChanged', args=[account_tail, - {'AutomaticPresence': (cs.PRESENCE_TYPE_HIDDEN, + {'AutomaticPresence': (cs.PRESENCE_HIDDEN, 'hidden', 'in a haystack or something')}, {'AutomaticPresence': 0}, @@ -201,14 +202,14 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): signal='AccountPropertyChanged', interface=cs.ACCOUNT, args=[{'AutomaticPresence': - (cs.PRESENCE_TYPE_HIDDEN, 'hidden', + (cs.PRESENCE_HIDDEN, 'hidden', 'in a haystack or something')}]), EventPattern('dbus-signal', path=cs.TEST_DBUS_ACCOUNT_PLUGIN_PATH, signal='AttributeChanged', args=[account_path, 'AutomaticPresence']), ) - assertEquals((cs.PRESENCE_TYPE_HIDDEN, 'hidden', + assertEquals((cs.PRESENCE_HIDDEN, 'hidden', 'in a haystack or something'), account.Properties.Get(cs.ACCOUNT, 'AutomaticPresence')) @@ -276,13 +277,13 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): account.Properties.Get(cs.ACCOUNT, 'Service')) fake_accounts_service.update_parameters(account_tail, { - 'password': 'high profile'}, flags={'password': cs.PARAM_FLAG_SECRET}) + 'password': 'high profile'}, flags={'password': cs.PARAM_SECRET}) q.expect_many( EventPattern('dbus-signal', path=cs.TEST_DBUS_ACCOUNT_SERVICE_PATH, signal='ParametersChanged', args=[account_tail, {'password': 'high profile'}, - {}, {'password': cs.PARAM_FLAG_SECRET}, []]), + {}, {'password': cs.PARAM_SECRET}, []]), EventPattern('dbus-signal', path=account_path, signal='AccountPropertyChanged', @@ -296,13 +297,13 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): ) fake_accounts_service.update_parameters(account_tail, untyped={ - 'password': r'\\\n'}, flags={'password': cs.PARAM_FLAG_SECRET}) + 'password': r'\\\n'}, flags={'password': cs.PARAM_SECRET}) q.expect_many( EventPattern('dbus-signal', path=cs.TEST_DBUS_ACCOUNT_SERVICE_PATH, signal='ParametersChanged', args=[account_tail, {}, {'password': r'\\\n'}, - {'password': cs.PARAM_FLAG_SECRET}, []]), + {'password': cs.PARAM_SECRET}, []]), EventPattern('dbus-signal', path=account_path, signal='AccountPropertyChanged', diff --git a/tests/twisted/account-manager/bad-cm.py b/tests/twisted/account-manager/bad-cm.py index 74277b46..008ac605 100644 --- a/tests/twisted/account-manager/bad-cm.py +++ b/tests/twisted/account-manager/bad-cm.py @@ -22,11 +22,12 @@ import dbus from servicetest import call_async, assertEquals, assertContains -from mctest import exec_test, AccountManager +from mctest import (exec_test, SimulatedConnectionManager, AccountManager) import constants as cs def test(q, bus, mc): am = AccountManager(bus) + simulated_cm = SimulatedConnectionManager(q, bus) def call_create(cm='fakecm', protocol='fakeprotocol', parameters=None): if parameters is None: @@ -74,5 +75,13 @@ def test(q, bus, mc): assertEquals(cs.INVALID_ARGUMENT, e.name) assertContains("password", e.message) + # Create an account that will fail IdentifyAccount + call_create(parameters={ "account": "", + "password": "ohai", + }) + e = q.expect('dbus-error', method='CreateAccount') + assertEquals(cs.INVALID_HANDLE, e.name) + assertContains("Invalid account name", e.message) + if __name__ == '__main__': exec_test(test, {}) diff --git a/tests/twisted/account-manager/connectivity.py b/tests/twisted/account-manager/connectivity.py index 8d80c1d4..1e378dc4 100644 --- a/tests/twisted/account-manager/connectivity.py +++ b/tests/twisted/account-manager/connectivity.py @@ -46,7 +46,7 @@ def test(q, bus, mc): {"account": "yum yum network manager", "password": "boo boo connman (although your API *is* simpler)", }, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) # While we're not connected to the internet, RequestConnection should not # be called. @@ -56,13 +56,13 @@ def test(q, bus, mc): q.forbid_events(request_connection_event) account.Properties.Set(cs.ACCOUNT, 'RequestedPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', 'hlaghalgh')) + (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'hlaghalgh')) # Turn the account on, re-request an online presence, and even tell it to # connect automatically, to check that none of these make it sign in. call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'Enabled', True) q.expect('dbus-return', method='Set') - requested_presence = (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', 'gtfo') + requested_presence = (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'gtfo') call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'RequestedPresence', requested_presence) q.expect('dbus-return', method='Set') @@ -149,7 +149,7 @@ def test(q, bus, mc): # connection comes back up the account should not be brought back online. q.forbid_events(request_connection_event) account.Properties.Set(cs.ACCOUNT, 'RequestedPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '')) + (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '')) mc.connectivity.go_online() # Make sure MC has noticed that the network connection has come back. sync_connectivity_state(mc) diff --git a/tests/twisted/account-manager/crashy-cm.py b/tests/twisted/account-manager/crashy-cm.py index 2eebeb8b..ca8f0ae5 100644 --- a/tests/twisted/account-manager/crashy-cm.py +++ b/tests/twisted/account-manager/crashy-cm.py @@ -38,14 +38,14 @@ def test(q, bus, mc): {"account": "someguy@example.com", "password": "secrecy", }, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params, + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params, cm_bus=cm_bus) account.Properties.Set(cs.ACCOUNT, 'Enabled', True) # Set online presence presence = dbus.Struct( - (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', 'Fixing MC bugs'), + (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Fixing MC bugs'), signature='uss') account.Properties.Set(cs.ACCOUNT, 'RequestedPresence', presence) @@ -65,11 +65,11 @@ def test(q, bus, mc): path=conn.object_path, handled=True) # Connect succeeds - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) # CM crashes conn.release_name() - del cm_name_ref + simulated_cm.release_name() cm_bus.flush() cm_bus.close() @@ -80,7 +80,7 @@ def test(q, bus, mc): assertEquals('/', changed['Connection']) assertEquals(cs.CONN_STATUS_DISCONNECTED, changed['ConnectionStatus']) # In the absence of a better code, None will have to do. - assertEquals(cs.CONN_STATUS_REASON_NONE, changed['ConnectionStatusReason']) + assertEquals(cs.CSR_NONE_SPECIFIED, changed['ConnectionStatusReason']) # And NoReply will do as “it crashed”. assertEquals(cs.DBUS_ERROR_NO_REPLY, changed['ConnectionError']) diff --git a/tests/twisted/account-manager/create-auto-connect.py b/tests/twisted/account-manager/create-auto-connect.py index fa2cb470..45acda05 100644 --- a/tests/twisted/account-manager/create-auto-connect.py +++ b/tests/twisted/account-manager/create-auto-connect.py @@ -28,7 +28,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "smcv@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) @@ -36,11 +36,11 @@ def test(q, bus, mc): # Ensure that it's enabled but has offline RP call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '')) + (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '')) q.expect('dbus-return', method='Set') call_async(q, account_props, 'Set', cs.ACCOUNT, 'AutomaticPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Testing automatic presence')) q.expect('dbus-return', method='Set') q.expect('dbus-signal', signal='AccountPropertyChanged', diff --git a/tests/twisted/account-manager/create-twice.py b/tests/twisted/account-manager/create-twice.py index 2f0c0e0c..3f1b8e20 100644 --- a/tests/twisted/account-manager/create-twice.py +++ b/tests/twisted/account-manager/create-twice.py @@ -22,7 +22,7 @@ import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async -from mctest import exec_test, create_fakecm_account, get_account_manager +from mctest import exec_test, SimulatedConnectionManager, get_account_manager import constants as cs def test(q, bus, mc): @@ -35,8 +35,7 @@ def test(q, bus, mc): params = dbus.Dictionary({"account": "create-twice", "password": "secrecy"}, signature='sv') - cm_name_ref = dbus.service.BusName(cs.tp_name_prefix + - '.ConnectionManager.fakecm', bus=bus) + simulated_cm = SimulatedConnectionManager(q, bus) account_manager = bus.get_object(cs.AM, cs.AM_PATH) am_iface = dbus.Interface(account_manager, cs.AM) diff --git a/tests/twisted/account-manager/create-with-properties.py b/tests/twisted/account-manager/create-with-properties.py index 6e208806..fb276c48 100644 --- a/tests/twisted/account-manager/create-with-properties.py +++ b/tests/twisted/account-manager/create-with-properties.py @@ -22,7 +22,7 @@ import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async, assertEquals, assertContains -from mctest import exec_test, create_fakecm_account, AccountManager +from mctest import (exec_test, SimulatedConnectionManager, AccountManager) import constants as cs def test(q, bus, mc): @@ -45,33 +45,29 @@ def test(q, bus, mc): assert (cs.ACCOUNT + '.Nickname') in supported assert (cs.ACCOUNT + '.ConnectAutomatically') in supported assert (cs.ACCOUNT_IFACE_AVATAR + '.Avatar') in supported - assert (cs.ACCOUNT_IFACE_NOKIA_CONDITIONS + '.Condition') in supported assert (cs.ACCOUNT + '.RequestedPresence') in supported assert (cs.ACCOUNT + '.Supersedes') in supported assertContains(cs.ACCOUNT + '.Service', supported) - params = dbus.Dictionary({"account": "anarki@example.com", + params = dbus.Dictionary({"account": "aNaRkI@eXaMpLe.CoM", "password": "secrecy"}, signature='sv') - cm_name_ref = dbus.service.BusName(cs.tp_name_prefix + - '.ConnectionManager.fakecm', bus=bus) + simulated_cm = SimulatedConnectionManager(q, bus) creation_properties = dbus.Dictionary({ cs.ACCOUNT + '.Enabled': True, cs.ACCOUNT + '.AutomaticPresence': dbus.Struct(( - dbus.UInt32(cs.PRESENCE_TYPE_BUSY), + dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Exploding'), signature='uss'), cs.ACCOUNT + '.RequestedPresence': dbus.Struct(( - dbus.UInt32(cs.PRESENCE_TYPE_AWAY), + dbus.UInt32(cs.PRESENCE_AWAY), 'away', 'Respawning'), signature='uss'), cs.ACCOUNT + '.Icon': 'quake3arena', cs.ACCOUNT + '.Nickname': 'AnArKi', cs.ACCOUNT + '.ConnectAutomatically': True, cs.ACCOUNT_IFACE_AVATAR + '.Avatar': (dbus.ByteArray('foo'), 'image/jpeg'), - cs.ACCOUNT_IFACE_NOKIA_CONDITIONS + '.Condition': - dbus.Dictionary({ 'has-quad-damage': ':y' }, signature='ss'), cs.ACCOUNT + '.Supersedes': dbus.Array([ cs.ACCOUNT_PATH_PREFIX + 'q1/q1/Ranger', cs.ACCOUNT_PATH_PREFIX + 'q2/q2/Grunt', @@ -98,6 +94,9 @@ def test(q, bus, mc): ) account_path = ret.value[0] assert am_signal.args == [account_path, True], am_signal.args + # We called IdentifyAccount, which normalized the silly account name. + # The _xx hex-escaping and the trailing digit are implementation details. + assert account_path.endswith('/anarki_40example_2ecom0'), account_path assert account_path is not None @@ -107,10 +106,10 @@ def test(q, bus, mc): account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) properties = account_props.GetAll(cs.ACCOUNT) - assert properties.get('AutomaticPresence') == (cs.PRESENCE_TYPE_BUSY, + assert properties.get('AutomaticPresence') == (cs.PRESENCE_BUSY, 'busy', 'Exploding'), \ properties.get('AutomaticPresence') - assert properties.get('RequestedPresence') == (cs.PRESENCE_TYPE_AWAY, + assert properties.get('RequestedPresence') == (cs.PRESENCE_AWAY, 'away', 'Respawning'), \ properties.get('RequestedPresence') assert properties.get('ConnectAutomatically') == True, \ @@ -135,11 +134,6 @@ def test(q, bus, mc): assert properties.get('Avatar') == ([ord('f'), ord('o'), ord('o')], 'image/jpeg') - properties = account_props.GetAll(cs.ACCOUNT_IFACE_NOKIA_CONDITIONS) - assert properties.get('Condition') == { - 'has-quad-damage': ':y', - } - # tests for errors when creating an account creation_properties2 = creation_properties.copy() diff --git a/tests/twisted/account-manager/device-idle.py b/tests/twisted/account-manager/device-idle.py index 196b6a04..dcc76ec9 100644 --- a/tests/twisted/account-manager/device-idle.py +++ b/tests/twisted/account-manager/device-idle.py @@ -82,8 +82,8 @@ def _create_and_enable(q, bus, mc, account_name, power_saving_supported, extra_interfaces = [cs.CONN_IFACE_POWER_SAVING] params = dbus.Dictionary({"account": account_name, "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) - conn = enable_fakecm_account(q, bus, mc, account, params, has_requests=False, + simulated_cm, account = create_fakecm_account(q, bus, mc, params) + conn = enable_fakecm_account(q, bus, mc, account, params, extra_interfaces=extra_interfaces, expect_after_connect=expect_after_connect) diff --git a/tests/twisted/account-manager/enable-auto-connect.py b/tests/twisted/account-manager/enable-auto-connect.py index 2cb91c31..d8ff3aee 100644 --- a/tests/twisted/account-manager/enable-auto-connect.py +++ b/tests/twisted/account-manager/enable-auto-connect.py @@ -28,7 +28,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "smcv@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) @@ -36,11 +36,11 @@ def test(q, bus, mc): # Ensure that it's enabled but has offline RP call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '')) + (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '')) q.expect('dbus-return', method='Set') call_async(q, account_props, 'Set', cs.ACCOUNT, 'AutomaticPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Testing automatic presence')) q.expect('dbus-return', method='Set') q.expect('dbus-signal', signal='AccountPropertyChanged', diff --git a/tests/twisted/account-manager/enable.py b/tests/twisted/account-manager/enable.py index 50e489d1..db659f3b 100644 --- a/tests/twisted/account-manager/enable.py +++ b/tests/twisted/account-manager/enable.py @@ -28,13 +28,13 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "smcv@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '')) + (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '')) q.expect('dbus-return', method='Set') call_async(q, account_props, 'Set', cs.ACCOUNT, 'Enabled', False) @@ -45,7 +45,7 @@ def test(q, bus, mc): q.expect('dbus-return', method='Set') call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', 'Testing Enabled')) + (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Testing Enabled')) q.expect('dbus-return', method='Set') # Go online by setting Enabled diff --git a/tests/twisted/account-manager/hidden.py b/tests/twisted/account-manager/hidden.py deleted file mode 100644 index 9e9351b0..00000000 --- a/tests/twisted/account-manager/hidden.py +++ /dev/null @@ -1,110 +0,0 @@ -# vim: set fileencoding=utf-8 : thanks python! you've been great -# Copyright © 2010 Nokia Corporation -# Copyright © 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 - -from mctest import ( - exec_test, create_fakecm_account, Account, AccountManager, - tell_mc_to_die, resuscitate_mc, - ) -from servicetest import ( - EventPattern, assertEquals, assertContains, assertDoesNotContain, - call_async, - ) -import constants as cs - -def test_unhidden_account(q, bus, mc): - """ - Check that accounts don't default to being hidden, and don't show up in the - lists of hidden accounts. - """ - am = AccountManager(bus) - - params = { "account": "oh", "password": "hai" } - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) - - assert not account.Properties.Get(cs.ACCOUNT_IFACE_HIDDEN, 'Hidden') - - am_hidden_props = am.Properties.GetAll(cs.AM_IFACE_HIDDEN) - assertEquals([], am_hidden_props['ValidHiddenAccounts']) - assertEquals([], am_hidden_props['InvalidHiddenAccounts']) - -def test_create_hidden_account(q, bus, mc): - """ - Check that a newly-created hidden account does not show up on the main - AccountManager interface, but does show up on AM.I.Hidden, has its - Hidden property set to True, and can be removed. - """ - am = AccountManager(bus) - - call_async(q, am.Properties, 'Get', cs.AM, - 'SupportedAccountProperties') - supported = q.expect('dbus-return', method='Get').value[0] - assertContains(cs.ACCOUNT_IFACE_HIDDEN + '.Hidden', supported) - - # Make a new hidden account, and check that it really is hidden. - params = { "account": "aperture@porti.co", "password": "tollgate" } - properties = { cs.ACCOUNT_IFACE_HIDDEN + '.Hidden': True } - - q.forbid_events([ - EventPattern('dbus-signal', path=cs.AM_PATH, - signal='AccountValidityChanged', interface=cs.AM), - ]) - cm_name_ref, account = create_fakecm_account(q, bus, mc, params, properties) - - valid_accounts = am.Properties.Get(cs.AM, 'ValidAccounts') - assertDoesNotContain(account.object_path, valid_accounts) - - valid_hidden_accounts = am.Properties.Get(cs.AM_IFACE_HIDDEN, - 'ValidHiddenAccounts') - assertContains(account.object_path, valid_hidden_accounts) - - # Blow MC away, revive it, and check that the account is still hidden. - tell_mc_to_die(q, bus) - am, properties, interfaces = resuscitate_mc(q, bus, mc) - account = Account(bus, account.object_path) - - assert account.Properties.Get(cs.ACCOUNT_IFACE_HIDDEN, 'Hidden') - - assertDoesNotContain(account.object_path, properties['ValidAccounts']) - - valid_hidden_accounts = am.Properties.Get(cs.AM_IFACE_HIDDEN, - 'ValidHiddenAccounts') - assertContains(account.object_path, valid_hidden_accounts) - - # Delete the account, and check that its removal is signalled only on - # AM.I.Hidden, not on the main AM interface. - q.forbid_events([ - EventPattern('dbus-signal', path=cs.AM_PATH, - signal='AccountRemoved', interface=cs.AM, - args=[account.object_path]), - ]) - - account.Remove() - q.expect_many( - EventPattern('dbus-signal', path=cs.AM_PATH, - signal='HiddenAccountRemoved', interface=cs.AM_IFACE_HIDDEN), - EventPattern('dbus-signal', path=account.object_path, - signal='Removed', interface=cs.ACCOUNT), - ) - -def test(q, bus, mc): - test_unhidden_account(q, bus, mc) - test_create_hidden_account(q, bus, mc) - -if __name__ == '__main__': - exec_test(test, {}, timeout=10) diff --git a/tests/twisted/account-manager/irc.py b/tests/twisted/account-manager/irc.py index 2b249f19..0d0de71f 100644 --- a/tests/twisted/account-manager/irc.py +++ b/tests/twisted/account-manager/irc.py @@ -32,7 +32,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "brucewayne", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) @@ -64,7 +64,7 @@ def test(q, bus, mc): interface=cs.ACCOUNT, predicate=lambda e: e.args[0].get('CurrentPresence') == - (cs.PRESENCE_TYPE_UNSET, '', '')), + (cs.PRESENCE_UNSET, '', '')), ] conn, get_aliases, set_aliases, _ = enable_fakecm_account(q, bus, mc, diff --git a/tests/twisted/account-manager/make-valid.py b/tests/twisted/account-manager/make-valid.py index 7c1df852..6e7ea34e 100644 --- a/tests/twisted/account-manager/make-valid.py +++ b/tests/twisted/account-manager/make-valid.py @@ -27,12 +27,10 @@ import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async, sync_dbus -from mctest import exec_test, SimulatedConnection, create_fakecm_account, MC +from mctest import (exec_test, SimulatedConnection, create_fakecm_account, MC, + SimulatedConnectionManager) import constants as cs -cm_name_ref = dbus.service.BusName( - cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=dbus.SessionBus()) - account1_id = 'fakecm/fakeprotocol/jc_2edenton_40unatco_2eint' account2_id = 'fakecm/fakeprotocol/jc_2edenton_40example_2ecom' @@ -95,6 +93,7 @@ def preseed(q, bus, fake_accounts_service): account_connections_file.close() def test(q, bus, unused, **kwargs): + simulated_cm = SimulatedConnectionManager(q, bus) # make sure RequestConnection doesn't get called yet events = [EventPattern('dbus-method-call', method='RequestConnection')] @@ -169,7 +168,7 @@ def test(q, bus, unused, **kwargs): q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True, interface=cs.CONN) - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) set_presence, e = q.expect_many( EventPattern('dbus-method-call', path=conn.object_path, @@ -181,7 +180,7 @@ def test(q, bus, unused, **kwargs): and e.args[0]['CurrentPresence'][2] != ''), ) - assert e.args[0]['CurrentPresence'] == (cs.PRESENCE_TYPE_AVAILABLE, + assert e.args[0]['CurrentPresence'] == (cs.PRESENCE_AVAILABLE, 'available', 'My vision is augmented') # Request an online presence on account 2, then make it valid @@ -191,7 +190,7 @@ def test(q, bus, unused, **kwargs): account_path = (cs.tp_path_prefix + '/Account/' + account2_id) account = bus.get_object(cs.MC, account_path) - requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_BUSY), + requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Talking to Illuminati')) account.Set(cs.ACCOUNT, 'RequestedPresence', dbus.Struct(requested_presence, variant_level=1), @@ -221,7 +220,7 @@ def test(q, bus, unused, **kwargs): q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True, interface=cs.CONN) - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) set_presence = q.expect('dbus-method-call', path=conn.object_path, interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', @@ -232,7 +231,7 @@ def test(q, bus, unused, **kwargs): predicate=lambda e: 'CurrentPresence' in e.args[0] and e.args[0]['CurrentPresence'][1] == 'busy') - assert e.args[0]['CurrentPresence'] == (cs.PRESENCE_TYPE_BUSY, + assert e.args[0]['CurrentPresence'] == (cs.PRESENCE_BUSY, 'busy', 'Talking to Illuminati') if __name__ == '__main__': diff --git a/tests/twisted/account-manager/nickname.py b/tests/twisted/account-manager/nickname.py index b128942c..e10342d3 100644 --- a/tests/twisted/account-manager/nickname.py +++ b/tests/twisted/account-manager/nickname.py @@ -28,7 +28,7 @@ import constants as cs def test(q, bus, mc, nickname): params = dbus.Dictionary({"account": "wjt@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) diff --git a/tests/twisted/account-manager/param-types.py b/tests/twisted/account-manager/param-types.py index 8dafd7f1..68eff808 100644 --- a/tests/twisted/account-manager/param-types.py +++ b/tests/twisted/account-manager/param-types.py @@ -22,13 +22,13 @@ import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async -from mctest import exec_test, create_fakecm_account, get_account_manager +from mctest import (exec_test, create_fakecm_account, get_account_manager, + SimulatedConnectionManager) import constants as cs def test(q, bus, mc): - cm_name_ref = dbus.service.BusName( - cs.tp_name_prefix + '.ConnectionManager.onewitheverything', - bus=bus) + simulated_cm = SimulatedConnectionManager(q, bus, + cm_name='onewitheverything', protocol_names=['serializable']) # Get the AccountManager interface account_manager = get_account_manager(bus) diff --git a/tests/twisted/account-manager/presence.py b/tests/twisted/account-manager/presence.py index 5577f5a6..9f04f675 100755 --- a/tests/twisted/account-manager/presence.py +++ b/tests/twisted/account-manager/presence.py @@ -28,13 +28,13 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "jc.denton@example.com", "password": "ionstorm"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) - presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Fighting conspiracies'), signature='uss') def mk_offline(message=''): - return dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', + return dbus.Struct((dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', message), signature='uss') offline = mk_offline() @@ -78,10 +78,10 @@ def test(q, bus, mc): requested_presence=presence, expect_before_connect=[ EventPattern('dbus-method-call', - interface=cs.CONN, method='GetInterfaces', - args=[], + interface=cs.PROPERTIES_IFACE, method='Get', + args=[cs.CONN, 'Interfaces'], handled=True, - predicate=(lambda e: log.append('GetInterfaces') or True)), + predicate=(lambda e: log.append('Get(Interfaces)') or True)), EventPattern('dbus-method-call', interface=cs.PROPERTIES_IFACE, method='Get', args=[cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses'], @@ -112,17 +112,17 @@ def test(q, bus, mc): e.args[0].get('CurrentPresence') == presence), ]) - # The events before Connect must happen in this order. GetInterfaces() may + # The events before Connect must happen in this order. Get(Interfaces) may # be called once or 2 times if len(log) == 5: - assert log == ['GetInterfaces', 'Get(Statuses)[1]', 'SetPresence[1]', + assert log == ['Get(Interfaces)', 'Get(Statuses)[1]', 'SetPresence[1]', 'Get(Statuses)[2]', 'SetPresence[2]'], log else: - assert log == ['GetInterfaces', 'GetInterfaces', 'Get(Statuses)[1]', 'SetPresence[1]', + assert log == ['Get(Interfaces)', 'Get(Interfaces)', 'Get(Statuses)[1]', 'SetPresence[1]', 'Get(Statuses)[2]', 'SetPresence[2]'], log # Change requested presence after going online - presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_AWAY), 'away', + presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_AWAY), 'away', 'In Hong Kong'), signature='uss') call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'RequestedPresence', presence) @@ -151,7 +151,7 @@ def test(q, bus, mc): predicate=lambda e: e.args[0].get('RequestedPresence') is not None)] q.forbid_events(events) - presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_AWAY), 'away', + presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_AWAY), 'away', 'In Hong Kong'), signature='uss') call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'RequestedPresence', presence) diff --git a/tests/twisted/account-manager/reconnect.py b/tests/twisted/account-manager/reconnect.py index 44af4100..39624a9c 100644 --- a/tests/twisted/account-manager/reconnect.py +++ b/tests/twisted/account-manager/reconnect.py @@ -27,13 +27,10 @@ from mctest import exec_test, SimulatedConnection, create_fakecm_account,\ import constants as cs def test(q, bus, mc): - cm_name_ref = dbus.service.BusName( - tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) - # Create an account params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) # Events that indicate that Reconnect might have done something looks_like_reconnection = [ @@ -51,7 +48,7 @@ def test(q, bus, mc): dbus_interface=cs.PROPERTIES_IFACE) q.expect('dbus-return', method='Set') - requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_AVAILABLE), + requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_AVAILABLE), dbus.String(u'available'), dbus.String(u''))) call_async(q, account, 'Set', cs.ACCOUNT, 'RequestedPresence', requested_presence, @@ -65,7 +62,7 @@ def test(q, bus, mc): # While we want to be offline but the account is enabled, Reconnect is # still a no-op. - requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), + requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_OFFLINE), dbus.String(u'offline'), dbus.String(u''))) call_async(q, account, 'Set', cs.ACCOUNT, 'RequestedPresence', requested_presence, @@ -98,7 +95,7 @@ def test(q, bus, mc): q.unforbid_events(looks_like_reconnection) - requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_AVAILABLE), + requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_AVAILABLE), dbus.String(u'brb'), dbus.String(u'Be back soon!'))) account.Set(cs.ACCOUNT, 'RequestedPresence', requested_presence, @@ -125,13 +122,13 @@ def test(q, bus, mc): path=conn.object_path, handled=True), ) - # MC calls GetStatus (maybe) and then Connect - + # MC prepares the connection, does any pre-Connect setup, then + # calls Connect q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) # Connect succeeds - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) # Assert that the NormalizedName is harvested from the Connection at some # point @@ -180,10 +177,10 @@ def test(q, bus, mc): q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) # Put the account offline - requested_presence = (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '') + requested_presence = (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '') account.Set(cs.ACCOUNT, 'RequestedPresence', requested_presence, dbus_interface=cs.PROPERTIES_IFACE) diff --git a/tests/twisted/account-manager/recover-from-disconnect.py b/tests/twisted/account-manager/recover-from-disconnect.py index 5cfe8686..932b4700 100644 --- a/tests/twisted/account-manager/recover-from-disconnect.py +++ b/tests/twisted/account-manager/recover-from-disconnect.py @@ -29,7 +29,7 @@ def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy", "register": True}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'Enabled', False) q.expect('dbus-return', method='Set') @@ -38,7 +38,7 @@ def test(q, bus, mc): call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'Enabled', True) # Set online presence - presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Fixing MC bugs'), signature='uss') call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'RequestedPresence', presence) @@ -55,13 +55,13 @@ def test(q, bus, mc): q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') - # MC calls GetStatus (maybe) and then Connect - + # MC prepares the connection, does any pre-Connect setup, then + # calls Connect q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) # Connect succeeds - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) q.expect('dbus-method-call', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, @@ -73,7 +73,7 @@ def test(q, bus, mc): conn.ConnectionError('com.example.My.Network.Is.Full.Of.Eels', {'eels': 23, 'capacity': 23, 'debug-message': 'Too many eels'}) conn.StatusChanged(cs.CONN_STATUS_DISCONNECTED, - cs.CONN_STATUS_REASON_NETWORK_ERROR) + cs.CSR_NETWORK_ERROR) # MC reconnects. This time, we expect it to have deleted the 'register' # parameter. @@ -106,7 +106,7 @@ def test(q, bus, mc): disconnected.args[0].get('ConnectionErrorDetails')) assertEquals(cs.CONN_STATUS_DISCONNECTED, disconnected.args[0].get('ConnectionStatus')) - assertEquals(cs.CONN_STATUS_REASON_NETWORK_ERROR, + assertEquals(cs.CSR_NETWORK_ERROR, disconnected.args[0].get('ConnectionStatusReason')) assertEquals('/', connecting.args[0].get('Connection')) @@ -117,7 +117,7 @@ def test(q, bus, mc): connecting.args[0].get('ConnectionErrorDetails')) assertEquals(cs.CONN_STATUS_CONNECTING, connecting.args[0].get('ConnectionStatus')) - assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + assertEquals(cs.CSR_REQUESTED, connecting.args[0].get('ConnectionStatusReason')) # The object path needs to be different from the first simulated @@ -130,8 +130,8 @@ def test(q, bus, mc): q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') - # MC calls GetStatus (maybe) and then Connect - + # MC prepares the connection, does any pre-Connect setup, then + # calls Connect connecting, _ = q.expect_many( EventPattern('dbus-signal', signal='AccountPropertyChanged', predicate=(lambda e: @@ -150,7 +150,7 @@ def test(q, bus, mc): connecting.args[0].get('ConnectionErrorDetails')) assertEquals(cs.CONN_STATUS_CONNECTING, connecting.args[0].get('ConnectionStatus')) - assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + assertEquals(cs.CSR_REQUESTED, connecting.args[0].get('ConnectionStatusReason')) assertEquals('com.example.My.Network.Is.Full.Of.Eels', @@ -160,7 +160,7 @@ def test(q, bus, mc): account.Properties.Get(cs.ACCOUNT, 'ConnectionErrorDetails')) # Connect succeeds - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) connected, _ = q.expect_many( EventPattern('dbus-signal', signal='AccountPropertyChanged', @@ -180,7 +180,7 @@ def test(q, bus, mc): assertEquals({}, connected.args[0].get('ConnectionErrorDetails')) assertEquals(cs.CONN_STATUS_CONNECTED, connected.args[0].get('ConnectionStatus')) - assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + assertEquals(cs.CSR_REQUESTED, connected.args[0].get('ConnectionStatusReason')) assertEquals('', account.Properties.Get(cs.ACCOUNT, 'ConnectionError')) diff --git a/tests/twisted/account-manager/req-conn-fails.py b/tests/twisted/account-manager/req-conn-fails.py index bc3f01a2..b9f05cba 100644 --- a/tests/twisted/account-manager/req-conn-fails.py +++ b/tests/twisted/account-manager/req-conn-fails.py @@ -31,13 +31,13 @@ def test(q, bus, mc): {"account": "someguy@example.com", "password": "secrecy", }, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account.Properties.Set(cs.ACCOUNT, 'Enabled', True) # Set online presence presence = dbus.Struct( - (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', 'Fixing MC bugs'), + (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Fixing MC bugs'), signature='uss') account.Properties.Set(cs.ACCOUNT, 'RequestedPresence', presence) @@ -56,7 +56,7 @@ def test(q, bus, mc): changed, = e.args assertEquals('/', changed['Connection']) assertEquals(cs.CONN_STATUS_DISCONNECTED, changed['ConnectionStatus']) - assertEquals(cs.CONN_STATUS_REASON_NONE, changed['ConnectionStatusReason']) + assertEquals(cs.CSR_NONE_SPECIFIED, changed['ConnectionStatusReason']) assertEquals(cs.NOT_IMPLEMENTED, changed['ConnectionError']) if __name__ == '__main__': diff --git a/tests/twisted/account-manager/request-online.py b/tests/twisted/account-manager/request-online.py index 3d8a6016..18186843 100644 --- a/tests/twisted/account-manager/request-online.py +++ b/tests/twisted/account-manager/request-online.py @@ -30,9 +30,6 @@ from mctest import ( import constants as cs def test(q, bus, mc): - cm_name_ref = dbus.service.BusName( - tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) - http_fixed_properties = dbus.Dictionary({ cs.CHANNEL + '.TargetHandleType': 1L, cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_STREAM_TUBE, @@ -50,7 +47,7 @@ def test(q, bus, mc): # Create an account params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) # The account is initially valid but disabled, and hence offline props = account.GetAll(cs.ACCOUNT, dbus_interface=cs.PROPERTIES_IFACE) @@ -59,7 +56,7 @@ def test(q, bus, mc): # The spec says it should be (Offline, "", "") but I don't think the # strings really matter. If anything, the second one should start out at # "offline". - assertEquals(cs.PRESENCE_TYPE_OFFLINE, props['CurrentPresence'][0]) + assertEquals(cs.PRESENCE_OFFLINE, props['CurrentPresence'][0]) # Enable the account account.Set(cs.ACCOUNT, 'Enabled', True, @@ -73,7 +70,7 @@ def test(q, bus, mc): assert props['Enabled'] assert props['Valid'] # Ditto above re. string fields. - assertEquals(cs.PRESENCE_TYPE_OFFLINE, props['CurrentPresence'][0]) + assertEquals(cs.PRESENCE_OFFLINE, props['CurrentPresence'][0]) # Go online requested_presence = dbus.Struct((dbus.UInt32(2L), dbus.String(u'brb'), @@ -102,13 +99,14 @@ def test(q, bus, mc): args=[cs.CONN_IFACE_REQUESTS], path=conn.object_path, handled=True), ) - # MC calls GetStatus (maybe) and then Connect + # MC prepares the connection, does any pre-Connect setup, then + # calls Connect q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) # Connect succeeds - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) # Assert that the NormalizedName is harvested from the Connection at some # point @@ -129,7 +127,7 @@ def test(q, bus, mc): # Since this Connection doesn't support SimplePresence, but it's online, # the spec says that CurrentPresence should be Unset. - assertEquals((cs.PRESENCE_TYPE_UNSET, "", ""), + assertEquals((cs.PRESENCE_UNSET, "", ""), properties.get('CurrentPresence')) new_channel = http_fixed_properties @@ -146,7 +144,7 @@ def test(q, bus, mc): q.dbus_return(e.message, signature='') # Put the account offline - requested_presence = (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '') + requested_presence = (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '') account.Set(cs.ACCOUNT, 'RequestedPresence', requested_presence, dbus_interface=cs.PROPERTIES_IFACE) diff --git a/tests/twisted/account-manager/restricted-storage.py b/tests/twisted/account-manager/restricted-storage.py new file mode 100644 index 00000000..6724863b --- /dev/null +++ b/tests/twisted/account-manager/restricted-storage.py @@ -0,0 +1,185 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009-2012 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 + +import dbus +import dbus.service + +from servicetest import EventPattern, call_async, assertEquals +from mctest import (exec_test, SimulatedConnection, + SimulatedConnectionManager, Account) +import constants as cs + +def test(q, bus, mc, fake_accounts_service=None, **kwargs): + simulated_cm = SimulatedConnectionManager(q, bus) + + for enabled in (True, False): + for online in (True, False): + account_tail = ('fakecm/fakeprotocol/ezio_2efirenze_40fic' + + (enabled and '_enabled' or '_disabled') + + (online and '_online' or '_offline')) + account_path = cs.ACCOUNT_PATH_PREFIX + account_tail + + try_to_connect = [ + EventPattern('dbus-method-call', method='RequestConnection', + destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', + path=cs.tp_path_prefix + '/ConnectionManager/fakecm', + interface=cs.tp_name_prefix + '.ConnectionManager', + predicate=lambda e: + e.args[1]['account'] == account_tail, + handled=False) + ] + + if enabled and online: + also_expected = try_to_connect[:] + else: + also_expected = [] + q.forbid_events(try_to_connect) + + args = ( + { + 'Enabled': enabled, + 'ConnectAutomatically': online, + 'manager': 'fakecm', + 'protocol': 'fakeprotocol', + 'AutomaticPresence': + dbus.Struct((dbus.UInt32(cs.PRESENCE_HIDDEN), + 'hidden', 'press X to blend'), + signature='uss'), + }, + { + 'Enabled': 0, + 'ConnectAutomatically': 0, + 'manager': 0, + 'protocol': 0, + 'AutomaticPresence': 0, + }, + { + 'account': account_tail, + 'password': 'nothing is true' + }, + {}, # untyped parameters + { + 'account': 0, + 'password': cs.PARAM_SECRET + }, + cs.StorageRestrictionFlags.CANNOT_SET_PRESENCE | + cs.StorageRestrictionFlags.CANNOT_SET_ENABLED, + ) + + fake_accounts_service.create_account(account_tail, *args) + + events = q.expect_many( + EventPattern('dbus-signal', + path=cs.TEST_DBUS_ACCOUNT_SERVICE_PATH, + signal='AccountCreated', + args=[account_tail] + list(args)), + EventPattern('dbus-signal', + path=cs.AM_PATH, + signal='AccountValidityChanged', + args=[account_path, True]), + *also_expected) + account = Account(bus, account_path) + + if enabled and online: + conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', + account_tail.replace('/', '_'), 'ezio', + has_presence=True) + q.dbus_return(events[-1].message, conn.bus_name, + conn.object_path, signature='so') + q.expect('dbus-method-call', method='SetPresence', + # the fake CM doesn't support 'hidden' by default + args=['busy', 'press X to blend']) + + requested_presence = (dbus.UInt32(cs.PRESENCE_HIDDEN), 'hidden', + 'press X to blend') + else: + requested_presence = (dbus.UInt32(cs.PRESENCE_OFFLINE), + 'offline', '') + + call_async(q, account.Properties, 'Get', cs.ACCOUNT, + 'RequestedPresence') + q.expect('dbus-return', method='Get', value=(requested_presence,)) + + # changes that are not really changes are fine + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'Enabled', enabled) + q.expect('dbus-return', method='Set') + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'ConnectAutomatically', online) + q.expect('dbus-return', method='Set') + + # changes that actually change the restrictive properties + # are not allowed + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'RequestedPresence', + ((dbus.UInt32(cs.PRESENCE_AVAILABLE), 'available', + 'highly conspicuous'))) + q.expect('dbus-error', method='Set') + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'AutomaticPresence', + ((dbus.UInt32(cs.PRESENCE_AVAILABLE), 'available', + 'highly conspicuous'))) + q.expect('dbus-error', method='Set') + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'Enabled', not enabled) + q.expect('dbus-error', method='Set') + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'ConnectAutomatically', not online) + q.expect('dbus-error', method='Set') + + call_async(q, account, 'Remove') + q.expect('dbus-error', method='Remove') + + # ... but the backend can still change them + if enabled and online: + q.forbid_events(try_to_connect) + fake_accounts_service.update_attributes(account_tail, + { + 'Enabled': False, + 'ConnectAutomatically': False, + }) + q.expect('dbus-method-call', method='Disconnect') + q.unforbid_events(try_to_connect) + else: + q.unforbid_events(try_to_connect) + fake_accounts_service.update_attributes(account_tail, + { + 'Enabled': True, + 'ConnectAutomatically': True, + }) + + events = q.expect_many(*try_to_connect) + conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', + account_tail.replace('/', '_'), 'ezio', + has_presence=True) + q.dbus_return(events[-1].message, conn.bus_name, + conn.object_path, signature='so') + + # we are connected: deleting the account should + # disconnect us + fake_accounts_service.delete_account(account_tail) + q.expect_many( + EventPattern('dbus-signal', signal='AccountRemoved', + args=[account_path]), + EventPattern('dbus-signal', signal='Removed', + path=account_path), + EventPattern('dbus-method-call', method='Disconnect'), + ) + +if __name__ == '__main__': + exec_test(test, {}, pass_kwargs=True) diff --git a/tests/twisted/account-manager/server-drops-us.py b/tests/twisted/account-manager/server-drops-us.py index b4db423f..9a9d6ca7 100644 --- a/tests/twisted/account-manager/server-drops-us.py +++ b/tests/twisted/account-manager/server-drops-us.py @@ -30,11 +30,8 @@ params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') def test(q, bus, mc): - cm_name_ref = dbus.service.BusName( - tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) - # Create an account - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) @@ -48,7 +45,7 @@ def test(q, bus, mc): dbus_interface=cs.PROPERTIES_IFACE) # Set online presence - presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Fixing MC bugs'), signature='uss') call_async(q, account, 'Set', cs.ACCOUNT, 'RequestedPresence', presence, @@ -66,13 +63,13 @@ def test(q, bus, mc): q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') - # MC calls GetStatus (maybe) and then Connect - + # MC prepares the connection, does any pre-Connect setup, then + # calls Connect q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) # Connect succeeds - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) conn = drop_and_expect_reconnect(q, bus, conn) conn = drop_and_expect_reconnect(q, bus, conn) @@ -83,7 +80,7 @@ def test(q, bus, mc): # Connection falls over for a miscellaneous reason conn.StatusChanged(cs.CONN_STATUS_DISCONNECTED, - cs.CONN_STATUS_REASON_NETWORK_ERROR) + cs.CSR_NETWORK_ERROR) # Right, that's it, I'm giving up... # This test can be considered to have succeeded if we don't @@ -98,7 +95,7 @@ def test(q, bus, mc): def drop_and_expect_reconnect(q, bus, conn): # Connection falls over for a miscellaneous reason conn.StatusChanged(cs.CONN_STATUS_DISCONNECTED, - cs.CONN_STATUS_REASON_NETWORK_ERROR) + cs.CSR_NETWORK_ERROR) # MC reconnects @@ -114,13 +111,13 @@ def drop_and_expect_reconnect(q, bus, conn): q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') - # MC calls GetStatus (maybe) and then Connect - + # MC prepares the connection, does any pre-Connect setup, then + # calls Connect q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) # Connect succeeds - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) return conn diff --git a/tests/twisted/account-manager/service.py b/tests/twisted/account-manager/service.py index f7df9903..7bed951a 100644 --- a/tests/twisted/account-manager/service.py +++ b/tests/twisted/account-manager/service.py @@ -27,7 +27,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "wjt@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) srv_name = 'fu-bar-42' account_iface = dbus.Interface(account, cs.ACCOUNT) diff --git a/tests/twisted/account-manager/update-parameters.py b/tests/twisted/account-manager/update-parameters.py index a7ec6a0a..a030a01f 100644 --- a/tests/twisted/account-manager/update-parameters.py +++ b/tests/twisted/account-manager/update-parameters.py @@ -30,16 +30,13 @@ from mctest import exec_test, SimulatedConnection, create_fakecm_account,\ import constants as cs def test(q, bus, mc, **kwargs): - cm_name_ref = dbus.service.BusName( - tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) - # Create an account params = dbus.Dictionary( {"account": "someguy@example.com", "password": "secrecy", "nickname": "albinoblacksheep", }, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) # Enable the account account.Set(cs.ACCOUNT, 'Enabled', True, @@ -77,13 +74,13 @@ def test(q, bus, mc, **kwargs): path=conn.object_path, handled=True), ) - # MC calls GetStatus (maybe) and then Connect - + # MC prepares the connection, does any pre-Connect setup, then + # calls Connect q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) # Connect succeeds - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) # Assert that the NormalizedName is harvested from the Connection at some # point @@ -255,13 +252,11 @@ def test(q, bus, mc, **kwargs): assertEquals(r'\\', kwargs['fake_accounts_service'].accounts [account.object_path[len(cs.ACCOUNT_PATH_PREFIX):]] - [2] # parameters of known type - ['account']) + .params['account']) assertEquals(None, kwargs['fake_accounts_service'].accounts [account.object_path[len(cs.ACCOUNT_PATH_PREFIX):]] - [3] # parameters of unknown type - .get('account', None)) + .untyped_params.get('account', None)) if __name__ == '__main__': exec_test(test, {}, pass_kwargs=True) diff --git a/tests/twisted/account-requests/cancel.py b/tests/twisted/account-requests/cancel.py index f236e5b8..9effcbec 100644 --- a/tests/twisted/account-requests/cancel.py +++ b/tests/twisted/account-requests/cancel.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/account-requests/create-text.py b/tests/twisted/account-requests/create-text.py index e71aca9d..c6d5922e 100644 --- a/tests/twisted/account-requests/create-text.py +++ b/tests/twisted/account-requests/create-text.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/account-requests/delete-account-during-request.py b/tests/twisted/account-requests/delete-account-during-request.py index 348c45a7..0b448eb8 100644 --- a/tests/twisted/account-requests/delete-account-during-request.py +++ b/tests/twisted/account-requests/delete-account-during-request.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/account-storage/5-12.py b/tests/twisted/account-storage/5-12.py new file mode 100644 index 00000000..9075d767 --- /dev/null +++ b/tests/twisted/account-storage/5-12.py @@ -0,0 +1,29 @@ +# Test for a former default account storage backend: +# ~/.mission-control/accounts.cfg, as used in MC 5.0 to 5.13.1 +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 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 + +from storage_helper import test_keyfile +from mctest import exec_test + +def test_5_12(q, bus, mc): + test_keyfile(q, bus, mc, '5.12') + +if __name__ == '__main__': + exec_test(test_5_12, {}, preload_mc=False, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/5-14.py b/tests/twisted/account-storage/5-14.py new file mode 100644 index 00000000..42deb3ec --- /dev/null +++ b/tests/twisted/account-storage/5-14.py @@ -0,0 +1,29 @@ +# Test for a former default account storage backend: +# XDG_DATA_HOME/telepathy/mission-control/accounts.cfg, as used in MC 5.14 +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 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 + +from storage_helper import test_keyfile +from mctest import exec_test + +def test_5_14(q, bus, mc): + test_keyfile(q, bus, mc, '5.14') + +if __name__ == '__main__': + exec_test(test_5_14, {}, preload_mc=False, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/create-new.py b/tests/twisted/account-storage/create-new.py new file mode 100644 index 00000000..f77624a9 --- /dev/null +++ b/tests/twisted/account-storage/create-new.py @@ -0,0 +1,136 @@ +# Test for "stringified GVariant per account" storage backend introduced in +# Mission Control 5.16, when creating a new account stored in this default +# backend +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 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 + +import time +import os +import os.path +import signal + +import dbus +import dbus.service + +from servicetest import ( + EventPattern, assertEquals, + ) +from mctest import ( + exec_test, create_fakecm_account, connect_to_mc, + ) +from storage_helper import (account_store) +import constants as cs + +def test(q, bus, mc): + ctl_dir = os.environ['MC_ACCOUNT_DIR'] + old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') + newer_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') + new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert_40example_2ecom0.account') + + account_manager, properties, interfaces = connect_to_mc(q, bus, mc) + + assert properties.get('ValidAccounts') == [], \ + properties.get('ValidAccounts') + assert properties.get('InvalidAccounts') == [], \ + properties.get('InvalidAccounts') + + params = dbus.Dictionary({"account": "dontdivert@example.com", + "password": "secrecy", + "snakes": dbus.UInt32(23)}, signature='sv') + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) + + account_path = account.__dbus_object_path__ + + # Check the account is correctly created + properties = account_manager.GetAll(cs.AM, + dbus_interface=cs.PROPERTIES_IFACE) + assert properties is not None + assert properties.get('ValidAccounts') == [account_path], properties + account_path = properties['ValidAccounts'][0] + assert isinstance(account_path, dbus.ObjectPath), repr(account_path) + assert properties.get('InvalidAccounts') == [], properties + + account_iface = dbus.Interface(account, cs.ACCOUNT) + account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) + + # Alter some miscellaneous r/w properties + + account_props.Set(cs.ACCOUNT, 'DisplayName', 'Work account') + account_props.Set(cs.ACCOUNT, 'Icon', 'im-jabber') + account_props.Set(cs.ACCOUNT, 'Nickname', 'Joe Bloggs') + account_props.Set(cs.ACCOUNT, 'ConnectAutomatically', True) + account_props.Set(cs.ACCOUNT, 'AutomaticPresence', + (dbus.UInt32(cs.PRESENCE_EXTENDED_AWAY), 'xa', + 'never online')) + + # .. let's check the keyfile + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(new_variant_file_name) + assert 'Joe Bloggs' in open(new_variant_file_name).read() + assertEquals("'fakecm'", account_store('get', 'variant-file', 'manager')) + assertEquals("'fakeprotocol'", account_store('get', 'variant-file', + 'protocol')) + assertEquals("'Work account'", account_store('get', 'variant-file', + 'DisplayName')) + assertEquals("'im-jabber'", account_store('get', 'variant-file', + 'Icon')) + assertEquals("'Joe Bloggs'", account_store('get', 'variant-file', + 'Nickname')) + assertEquals('true', account_store('get', 'variant-file', + 'ConnectAutomatically')) + assertEquals("(uint32 4, 'xa', 'never online')", + account_store('get', 'variant-file', 'AutomaticPresence')) + assertEquals("'dontdivert@example.com'", + account_store('get', 'variant-file', 'param-account')) + assertEquals("uint32 23", + account_store('get', 'variant-file', 'param-snakes')) + assertEquals("'secrecy'", + account_store('get', 'variant-file', 'param-password')) + + assertEquals({'password': 'secrecy', 'account': 'dontdivert@example.com', + 'snakes': 23}, account.Properties.Get(cs.ACCOUNT, 'Parameters')) + + # Delete the account + assert account_iface.Remove() is None + account_event, account_manager_event = q.expect_many( + EventPattern('dbus-signal', + path=account_path, + signal='Removed', + interface=cs.ACCOUNT, + args=[] + ), + EventPattern('dbus-signal', + path=cs.AM_PATH, + signal='AccountRemoved', + interface=cs.AM, + args=[account_path] + ), + ) + + # Check the account is correctly deleted + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert not os.path.exists(new_variant_file_name) + +if __name__ == '__main__': + exec_test(test, {}, timeout=10, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/default-keyring-storage.py b/tests/twisted/account-storage/default-keyring-storage.py deleted file mode 100644 index 2d3b2fcf..00000000 --- a/tests/twisted/account-storage/default-keyring-storage.py +++ /dev/null @@ -1,230 +0,0 @@ -# Test for default account storage backend. -# -# Copyright (C) 2009-2010 Nokia Corporation -# Copyright (C) 2009-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 - -import time -import os -import os.path -import signal - -import dbus -import dbus.service - -from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ - call_async, assertEquals -from mctest import ( - exec_test, create_fakecm_account, get_fakecm_account, connect_to_mc, - keyfile_read, tell_mc_to_die, resuscitate_mc - ) -import constants as cs - -# This doesn't escape its parameters before passing them to the shell, -# so be careful. -def account_store(op, backend, key=None, value=None, - account='fakecm/fakeprotocol/dontdivert_40example_2ecom0'): - cmd = [ '../account-store', op, backend, account ] - if key: - cmd.append(key) - if value: - cmd.append(value) - - lines = os.popen(' '.join(cmd)).read() - ret = [] - for line in lines.split('\n'): - if line.startswith('** '): - continue - - if line: - ret.append(line) - - if len(ret) > 0: - return ret[0] - else: - return None - -def test(q, bus, mc): - ctl_dir = os.environ['MC_ACCOUNT_DIR'] - old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') - new_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], - 'telepathy', 'mission-control', 'accounts.cfg') - group = 'fakecm/fakeprotocol/dontdivert_40example_2ecom0' - - account_manager, properties, interfaces = connect_to_mc(q, bus, mc) - - assert properties.get('ValidAccounts') == [], \ - properties.get('ValidAccounts') - assert properties.get('InvalidAccounts') == [], \ - properties.get('InvalidAccounts') - - params = dbus.Dictionary({"account": "dontdivert@example.com", - "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) - - account_path = account.__dbus_object_path__ - - # Check the account is correctly created - properties = account_manager.GetAll(cs.AM, - dbus_interface=cs.PROPERTIES_IFACE) - assert properties is not None - assert properties.get('ValidAccounts') == [account_path], properties - account_path = properties['ValidAccounts'][0] - assert isinstance(account_path, dbus.ObjectPath), repr(account_path) - assert properties.get('InvalidAccounts') == [], properties - - account_iface = dbus.Interface(account, cs.ACCOUNT) - account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) - - # Alter some miscellaneous r/w properties - - account_props.Set(cs.ACCOUNT, 'DisplayName', 'Work account') - account_props.Set(cs.ACCOUNT, 'Icon', 'im-jabber') - account_props.Set(cs.ACCOUNT, 'Nickname', 'Joe Bloggs') - - tell_mc_to_die(q, bus) - - # .. let's check the keyfile - assert not os.path.exists(old_key_file_name) - kf = keyfile_read(new_key_file_name) - assert group in kf, kf - assert kf[group]['manager'] == 'fakecm' - assert kf[group]['protocol'] == 'fakeprotocol' - assert kf[group]['param-account'] == params['account'], kf - assert kf[group]['DisplayName'] == 'Work account', kf - assert kf[group]['Icon'] == 'im-jabber', kf - assert kf[group]['Nickname'] == 'Joe Bloggs', kf - - # This works wherever the password is stored - pwd = account_store('get', 'default', 'param-password') - assert pwd == params['password'], pwd - - # We no longer use gnome-keyring, so the password is stored as clear-text. - assert kf[group]['param-password'] == params['password'], kf - - # Reactivate MC - account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) - account = get_fakecm_account(bus, mc, account_path) - account_iface = dbus.Interface(account, cs.ACCOUNT) - - # Delete the account - assert account_iface.Remove() is None - account_event, account_manager_event = q.expect_many( - EventPattern('dbus-signal', - path=account_path, - signal='Removed', - interface=cs.ACCOUNT, - args=[] - ), - EventPattern('dbus-signal', - path=cs.AM_PATH, - signal='AccountRemoved', - interface=cs.AM, - args=[account_path] - ), - ) - - # Check the account is correctly deleted - assert not os.path.exists(old_key_file_name) - kf = keyfile_read(new_key_file_name) - assert group not in kf, kf - - # Tell MC to die, again - tell_mc_to_die(q, bus) - - low_prio_key_file_name = os.path.join( - os.environ['XDG_DATA_DIRS'].split(':')[0], - 'telepathy', 'mission-control', 'accounts.cfg') - os.makedirs(os.path.dirname(low_prio_key_file_name), 0700) - - # This is deliberately a lower-priority location - os.remove(new_key_file_name) - open(low_prio_key_file_name, 'w').write( -r"""# Telepathy accounts -[%s] -manager=fakecm -protocol=fakeprotocol -param-account=dontdivert@example.com -param-password=password_in_keyfile -DisplayName=New and improved account -AutomaticPresence=2;available;; -""" % group) - - account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) - account = get_fakecm_account(bus, mc, account_path) - account_iface = dbus.Interface(account, cs.ACCOUNT) - - # Files in lower-priority XDG locations aren't copied until something - # actually changes, and they aren't deleted. - assert not os.path.exists(new_key_file_name) - assert os.path.exists(low_prio_key_file_name) - - # Delete the password (only), like Empathy 3.0-3.4 do when migrating - account_iface.UpdateParameters({}, ['password']) - q.expect('dbus-signal', - path=account_path, - signal='AccountPropertyChanged', - interface=cs.ACCOUNT, - predicate=(lambda e: - 'Parameters' in e.args[0]), - ) - - # Tell MC to die yet again - tell_mc_to_die(q, bus) - - # Check the account has copied (not moved! XDG_DATA_DIRS are, - # conceptually, read-only) from the old to the new name - assert not os.path.exists(old_key_file_name) - assert os.path.exists(low_prio_key_file_name) - kf = keyfile_read(new_key_file_name) - assert 'param-password' not in kf[group] - pwd = account_store('get', 'default', 'param-password') - assertEquals(None, pwd) - - # Write out an account configuration in the old keyfile, to test - # migration - os.remove(new_key_file_name) - os.remove(low_prio_key_file_name) - open(old_key_file_name, 'w').write( -r"""# Telepathy accounts -[%s] -manager=fakecm -protocol=fakeprotocol -param-account=dontdivert@example.com -DisplayName=Ye olde account -AutomaticPresence=2;available;; -""" % group) - - account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) - account = get_fakecm_account(bus, mc, account_path) - account_iface = dbus.Interface(account, cs.ACCOUNT) - - # This time it *does* get moved (really copied+deleted) automatically - # during MC startup - assert not os.path.exists(old_key_file_name) - assert not os.path.exists(low_prio_key_file_name) - kf = keyfile_read(new_key_file_name) - assert 'param-password' not in kf[group] - assertEquals('Ye olde account', kf[group]['DisplayName']) - -if __name__ == '__main__': - ctl_dir = os.environ['MC_ACCOUNT_DIR'] - try: - os.mkdir(ctl_dir, 0700) - except OSError: - pass - exec_test(test, {}, timeout=10, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/diverted-storage.py b/tests/twisted/account-storage/diverted-storage.py index 8692b09b..7aa011e8 100644 --- a/tests/twisted/account-storage/diverted-storage.py +++ b/tests/twisted/account-storage/diverted-storage.py @@ -53,7 +53,7 @@ def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_path = account.__dbus_object_path__ @@ -88,10 +88,6 @@ def test(q, bus, mc): assert kf[group]['Icon'] == 'im-jabber', kf assert kf[group]['Nickname'] == 'Joe Bloggs', kf - # default keyfile should be empty - ekf = keyfile_read(empty_key_file_name) - assert ekf == { None: {} }, ekf - # Reactivate MC account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) account = get_fakecm_account(bus, mc, account_path) diff --git a/tests/twisted/account-storage/libaccounts-sso-storage.py b/tests/twisted/account-storage/libaccounts-sso-storage.py deleted file mode 100644 index a2e7b512..00000000 --- a/tests/twisted/account-storage/libaccounts-sso-storage.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) 2009-2010 Nokia Corporation -# Copyright (C) 2009-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 - -raise AssertionError('Disabled for 5.6 branch') - -import time -import os -import os.path -import signal -import sys - -import dbus -import dbus.service - -from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ - call_async -from mctest import exec_test, create_fakecm_account, get_account_manager, \ - get_fakecm_account -import constants as cs - -if ('ACCOUNTS' not in os.environ or not os.environ['ACCOUNTS']): - print "Not testing accounts-sso storage" - sys.exit(0) - -def account_store(op, backend, key=None, value=None): - cmd = [ '../account-store', op, backend, - 'colltest42@googlemail.com' ] - if key: - cmd.append(key) - if value: - cmd.append(value) - - lines = os.popen(' '.join(cmd)).read() - ret = [] - for line in lines.split('\n'): - if line.startswith('** '): - continue - - if line: - ret.append(line) - - if len(ret) > 0: - return ret[0] - else: - return None - -def prepare_accounts_db(ctl_dir): - os.system('cp %s/../tools/example-accounts.db %s/accounts.db' % (ctl_dir, ctl_dir)) - os.system('cp %s/../tools/accounts-sso-example.service %s/google-talk.service' % (ctl_dir, ctl_dir)) - -def test(q, bus, mc): - account_manager, properties, interfaces = connect_to_mc(q, bus, mc) - - va = properties.get('ValidAccounts') - assert va == [], va - - ia = properties.get('InvalidAccounts') - assert len(ia) == 1 - - account_path = ia[0] - print repr(account_path) - - account = get_fakecm_account(bus, mc, account_path) - account_iface = dbus.Interface(account, cs.ACCOUNT) - account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) - - # FIXME at this point MC crashes - properties = account_props.GetAll(cs.ACCOUNT) - - -if __name__ == '__main__': - ctl_dir = os.environ['ACCOUNTS'] - prepare_accounts_db(ctl_dir) - exec_test(test, {}, timeout=10, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/load-keyfiles.py b/tests/twisted/account-storage/load-keyfiles.py new file mode 100644 index 00000000..b6c9d2ef --- /dev/null +++ b/tests/twisted/account-storage/load-keyfiles.py @@ -0,0 +1,283 @@ +# Test for "stringified GVariant per account" storage backend introduced in +# Mission Control 5.16, when loading pre-prepared files +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 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 + +import errno +import os +import os.path + +import dbus + +from servicetest import ( + assertEquals, assertContains, assertDoesNotContain, + ) +from mctest import ( + MC, exec_test, get_fakecm_account, connect_to_mc, + SimulatedConnectionManager, + ) +from storage_helper import ( + account_store, + ) +import constants as cs + +def test(q, bus, mc): + simulated_cm = SimulatedConnectionManager(q, bus) + + ctl_dir = os.environ['MC_ACCOUNT_DIR'] + old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') + newer_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') + + # We do several scenarios in one MC run, to speed up testing a bit. + scenarios = ('low', 'priority', 'masked', 'migration', 'absentcm') + + variant_file_names = {} + low_prio_variant_file_names = {} + account_paths = {} + tails = {} + + for s in scenarios: + variant_file_names[s] = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert%s_40example_2ecom0.account' + % s) + tails[s] = ('fakecm/fakeprotocol/dontdivert%s_40example_2ecom0' % s) + account_paths[s] = cs.ACCOUNT_PATH_PREFIX + tails[s] + low_prio_variant_file_names[s] = os.path.join( + os.environ['XDG_DATA_DIRS'].split(':')[0], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert%s_40example_2ecom0.account' % + s) + + try: + os.makedirs(os.path.dirname(variant_file_names[s]), 0700) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + try: + os.makedirs(os.path.dirname(low_prio_variant_file_names[s]), 0700) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + # This is deliberately a lower-priority location + open(low_prio_variant_file_names['low'], 'w').write( +"""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Account in a low-priority location'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'Parameters': <{ + 'account': <'dontdivertlow@example.com'>, + 'password': <'password_in_variant_file'>, + 'snakes': <uint32 42> + }> +} +""") + + # This is in a lower-priority location and we don't know the + # parameters' types yet + open(low_prio_variant_file_names['migration'], 'w').write( +"""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Account in a low-priority location with KeyFileParameters'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{ + 'account': 'dontdivertmigration@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + + # This is in a lower-priority location, and we don't know the + # parameters' types, and we can't learn them by asking the CM + # because it isn't installed + open(low_prio_variant_file_names['absentcm'], 'w').write( +"""{ +'manager': <'absentcm'>, +'protocol': <'absentprotocol'>, +'DisplayName': <'Account in a low-priority location with absent CM'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{ + 'account': 'dontdivertabsentcm@example.com', + 'password': 'hello', + 'snakes': '42' + }> +} +""") + + # This version of this account will be used + open(variant_file_names['priority'], 'w').write("""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Visible'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{'account': 'dontdivertpriority@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + # This one won't, because it's "masked" by the higher-priority one + open(low_prio_variant_file_names['priority'], 'w').write("""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Hidden'>, +'Nickname': <'Hidden'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{'account': 'dontdivertpriority@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + + # This empty file is considered to "mask" the lower-priority one + open(variant_file_names['masked'], 'w').write('') + open(low_prio_variant_file_names['masked'], 'w').write("""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{'account': 'dontdivert@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + + mc = MC(q, bus) + account_manager, properties, interfaces = connect_to_mc(q, bus, mc) + + for s in scenarios: + if s == 'masked': + assertDoesNotContain(account_paths[s], properties['ValidAccounts']) + assertDoesNotContain(account_paths[s], properties['InvalidAccounts']) + elif s == 'absentcm': + assertContains(account_paths[s], properties['InvalidAccounts']) + assertDoesNotContain(account_paths[s], properties['ValidAccounts']) + else: + assertContains(account_paths[s], properties['ValidAccounts']) + assertDoesNotContain(account_paths[s], properties['InvalidAccounts']) + + accounts = {} + account_ifaces = {} + + for s in scenarios: + if s != 'masked': + accounts[s] = get_fakecm_account(bus, mc, account_paths[s]) + account_ifaces[s] = dbus.Interface(accounts[s], cs.ACCOUNT) + + if s not in ('masked', 'absentcm'): + # We can't get untyped parameters if we don't know what types + # the CM gives them. + assertEquals(42, accounts[s].Properties.Get(cs.ACCOUNT, + 'Parameters')['snakes']) + assertEquals(dbus.UInt32, + type(accounts[s].Properties.Get(cs.ACCOUNT, + 'Parameters')['snakes'])) + + # Files in lower-priority XDG locations aren't copied until something + # actually changes, and they aren't deleted. + + if s == 'low': + assert os.path.exists(low_prio_variant_file_names[s]) + + # Delete the password (only), like Empathy 3.0-3.4 do when migrating. + # This results in the higher-priority file being written out. + account_ifaces['low'].UpdateParameters({}, ['password']) + q.expect('dbus-signal', + path=account_paths['low'], + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + predicate=(lambda e: + 'Parameters' in e.args[0]), + ) + # Check the account has copied (not moved! XDG_DATA_DIRS are, + # conceptually, read-only) 'low' from the old to the new name + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['low']) + assert os.path.exists(variant_file_names['low']) + + # test that priority works + assertContains(account_paths["priority"], properties['ValidAccounts']) + assertEquals('', + accounts['priority'].Properties.Get(cs.ACCOUNT, 'Nickname')) + assertEquals('Visible', + accounts['priority'].Properties.Get(cs.ACCOUNT, 'DisplayName')) + + # test what happens when we delete an account that has a lower-priority + # "other self": it becomes masked + assert accounts['priority'].Remove() is None + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['priority']) + assert os.path.exists(variant_file_names['priority']) + assert open(variant_file_names['priority'], 'r').read() == '' + assertContains('password_in_variant_file', + open(low_prio_variant_file_names['priority'], 'r').read()) + + # The masked account is still masked + assert open(variant_file_names['masked'], 'r').read() == '' + + # Because the CM exists, we can work out the correct types + # for the 'migration' account's parameters. This triggers a commit + # even though nothing has conceptually changed, so we have the type + # for later. The file is copied, not moved, because XDG_DATA_DIRS are, + # conceptually, read-only. + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['migration']) + assert os.path.exists(variant_file_names['migration']) + assertEquals("'password_in_variant_file'", + account_store('get', 'variant-file', 'param-password', + account=tails['migration'])) + assertEquals("uint32 42", account_store('get', 'variant-file', + 'param-snakes', account=tails['migration'])) + + # Setting the password still does the right thing. + account_ifaces['migration'].UpdateParameters({'password': 'hello'}, []) + q.expect('dbus-signal', + path=account_paths['migration'], + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + predicate=(lambda e: + 'Parameters' in e.args[0]), + ) + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['migration']) + assert os.path.exists(variant_file_names['migration']) + assertEquals("'hello'", + account_store('get', 'variant-file', 'param-password', + account=tails['migration'])) + + # 'absentcm' is still only in the low-priority location: we can't + # known the types of its parameters, so it doesn't get migrated. + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['absentcm']) + assert not os.path.exists(variant_file_names['absentcm']) + +if __name__ == '__main__': + exec_test(test, {}, preload_mc=False, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/storage_helper.py b/tests/twisted/account-storage/storage_helper.py new file mode 100644 index 00000000..0f67df92 --- /dev/null +++ b/tests/twisted/account-storage/storage_helper.py @@ -0,0 +1,174 @@ +# Helper code for former default account storage backends +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 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 + +import errno +import os +import os.path + +import dbus + +from servicetest import ( + assertEquals, assertContains, assertLength, + ) +from mctest import ( + exec_test, get_fakecm_account, connect_to_mc, + MC, SimulatedConnectionManager, + ) +import constants as cs + +# This doesn't escape its parameters before passing them to the shell, +# so be careful. +def account_store(op, backend, key=None, value=None, + account='fakecm/fakeprotocol/dontdivert_40example_2ecom0'): + cmd = [ '../account-store', op, backend, account ] + if key: + cmd.append(key) + if value: + cmd.append(value) + + lines = os.popen(' '.join(cmd)).read() + ret = [] + for line in lines.split('\n'): + if line.startswith('** '): + continue + + if line: + ret.append(line) + + if len(ret) > 0: + return ret[0] + else: + return None + +def test_keyfile(q, bus, mc, how_old='5.12'): + simulated_cm = SimulatedConnectionManager(q, bus) + + if how_old == '5.12': + # This is not actually ~/.mission-control, but it uses the same + # code paths. + dot_mission_control = os.environ['MC_ACCOUNT_DIR'] + old_key_file_name = os.path.join(dot_mission_control, 'accounts.cfg') + + os.makedirs(dot_mission_control + + '/fakecm/fakeprotocol/dontdivert1_40example_2ecom0') + avatar_bin = open(dot_mission_control + + '/fakecm/fakeprotocol/dontdivert1_40example_2ecom0/avatar.bin', + 'w') + avatar_bin.write('hello, world') + avatar_bin.close() + elif how_old == '5.14': + # Same format, different location. + old_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') + + # exercise override of an avatar in XDG_DATA_DIRS + avatar_dir = (os.environ['XDG_DATA_DIRS'].split(':')[0] + + '/telepathy/mission-control') + os.makedirs(avatar_dir) + avatar_bin = open(avatar_dir + + '/fakecm-fakeprotocol-dontdivert1_40example_2ecom0.avatar', + 'w') + avatar_bin.write('hello, world') + avatar_bin.close() + else: + raise AssertionError('Unsupported value for how_old') + + a1_new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert1_40example_2ecom0.account') + a1_tail = 'fakecm/fakeprotocol/dontdivert1_40example_2ecom0' + + a2_new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert2_40example_2ecom0.account') + a2_tail = 'fakecm/fakeprotocol/dontdivert2_40example_2ecom0' + + try: + os.makedirs(os.path.dirname(old_key_file_name), 0700) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + open(old_key_file_name, 'w').write( +r"""# Telepathy accounts +[%s] +manager=fakecm +protocol=fakeprotocol +param-account=dontdivert1@example.com +param-password=1 +DisplayName=First among equals +AutomaticPresence=2;available;; +AvatarMime=text/plain +avatar_token=hello, world + +[%s] +manager=fakecm +protocol=fakeprotocol +param-account=dontdivert2@example.com +param-password=2 +DisplayName=Second to none +AutomaticPresence=2;available;; +""" % (a1_tail, a2_tail)) + + mc = MC(q, bus) + account_manager, properties, interfaces = connect_to_mc(q, bus, mc) + + # During MC startup, it moved the old keyfile's contents into + # variant-based files, and deleted the old keyfile. + assert not os.path.exists(old_key_file_name) + assert os.path.exists(a1_new_variant_file_name) + assert os.path.exists(a2_new_variant_file_name) + assertEquals("'First among equals'", + account_store('get', 'variant-file', 'DisplayName', + account=a1_tail)) + assertEquals("'Second to none'", + account_store('get', 'variant-file', 'DisplayName', + account=a2_tail)) + # Because the CM is installed, we can work out the right types + # for the parameters, too. + assertEquals("'dontdivert1@example.com'", + account_store('get', 'variant-file', 'param-account', + account=a1_tail)) + assertEquals("'dontdivert2@example.com'", + account_store('get', 'variant-file', 'param-account', + account=a2_tail)) + + # Also, MC has both accounts in memory... + assertContains(cs.ACCOUNT_PATH_PREFIX + a1_tail, + properties['ValidAccounts']) + account = get_fakecm_account(bus, mc, cs.ACCOUNT_PATH_PREFIX + a1_tail) + assertEquals('dontdivert1@example.com', + account.Properties.Get(cs.ACCOUNT, 'Parameters')['account']) + assertEquals('First among equals', + account.Properties.Get(cs.ACCOUNT, 'DisplayName')) + assertEquals((dbus.ByteArray('hello, world'), 'text/plain'), + account.Properties.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + byte_arrays=True)) + + assertContains(cs.ACCOUNT_PATH_PREFIX + a2_tail, + properties['ValidAccounts']) + account = get_fakecm_account(bus, mc, cs.ACCOUNT_PATH_PREFIX + a2_tail) + assertEquals('dontdivert2@example.com', + account.Properties.Get(cs.ACCOUNT, 'Parameters')['account']) + assertEquals('Second to none', + account.Properties.Get(cs.ACCOUNT, 'DisplayName')) + + # ... and no other accounts. + assertLength(2, properties['ValidAccounts']) diff --git a/tests/twisted/account/addressing.py b/tests/twisted/account/addressing.py index e303c935..222546b0 100755 --- a/tests/twisted/account/addressing.py +++ b/tests/twisted/account/addressing.py @@ -31,7 +31,7 @@ def test(q, bus, mc): params = dbus.Dictionary ({"account": "jc.denton@example.com", "password": "ionstorm"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account (q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account (q, bus, mc, params) account_iface = dbus.Interface (account, cs.ACCOUNT) account_props = dbus.Interface (account, cs.PROPERTIES_IFACE) diff --git a/tests/twisted/capabilities/contact-caps.py b/tests/twisted/capabilities/contact-caps.py index 7a348df0..320a8c5e 100644 --- a/tests/twisted/capabilities/contact-caps.py +++ b/tests/twisted/capabilities/contact-caps.py @@ -33,13 +33,6 @@ from mctest import exec_test, SimulatedConnection, SimulatedClient, \ import constants as cs def test(q, bus, mc): - forbidden = [ - EventPattern('dbus-method-call', handled=False, - interface=cs.CONN_IFACE_CAPS, - method='AdvertiseCapabilities'), - ] - q.forbid_events(forbidden) - # Two clients want to handle channels: MediaCall is running, and AbiWord # is activatable. @@ -56,13 +49,13 @@ def test(q, bus, mc): }, signature='sv') media_fixed_properties = dbus.Dictionary({ - cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_STREAMED_MEDIA, + cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_CALL, }, signature='sv') media_call = SimulatedClient(q, bus, 'MediaCall', observe=[], approve=[], handle=[media_fixed_properties], - cap_tokens=[cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/ice-udp', - cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/audio/speex', - cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/video/theora'], + cap_tokens=[cs.CHANNEL_TYPE_CALL + '/ice', + cs.CHANNEL_TYPE_CALL + '/audio/speex', + cs.CHANNEL_TYPE_CALL + '/video/theora'], bypass_approval=False) # wait for MC to download the properties @@ -89,11 +82,11 @@ def test(q, bus, mc): assert len(filters[cs.CLIENT + '.AbiWord']) == 2 assert len(tokens[cs.CLIENT + '.MediaCall']) == 3 - assert cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/ice-udp' in \ + assert cs.CHANNEL_TYPE_CALL + '/ice' in \ tokens[cs.CLIENT + '.MediaCall'] - assert cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/audio/speex' in \ + assert cs.CHANNEL_TYPE_CALL + '/audio/speex' in \ tokens[cs.CLIENT + '.MediaCall'] - assert cs.CHANNEL_IFACE_MEDIA_SIGNALLING + '/video/theora' in \ + assert cs.CHANNEL_TYPE_CALL + '/video/theora' in \ tokens[cs.CLIENT + '.MediaCall'] assert len(tokens[cs.CLIENT + '.AbiWord']) == 2 @@ -104,10 +97,9 @@ def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn, before = enable_fakecm_account(q, bus, mc, account, params, - extra_interfaces=[cs.CONN_IFACE_CONTACT_CAPS, - cs.CONN_IFACE_CAPS], + extra_interfaces=[cs.CONN_IFACE_CONTACT_CAPS], expect_before_connect=[ EventPattern('dbus-method-call', handled=False, interface=cs.CONN_IFACE_CONTACT_CAPS, diff --git a/tests/twisted/constants.py b/tests/twisted/constants.py index 3a844d54..2a4b69b8 100644 --- a/tests/twisted/constants.py +++ b/tests/twisted/constants.py @@ -1,5 +1,5 @@ # Copyright (C) 2009 Nokia Corporation -# Copyright (C) 2009-2012 Collabora Ltd. +# Copyright (C) 2009-2013 Collabora Ltd. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -21,31 +21,59 @@ Some handy constants for other tests to share and enjoy. """ from dbus import PROPERTIES_IFACE, INTROSPECTABLE_IFACE -from servicetest import tp_name_prefix, tp_path_prefix -CM = "org.freedesktop.Telepathy.ConnectionManager" +PREFIX = "org.freedesktop.Telepathy" +PATH_PREFIX = '/' + PREFIX.replace('.', '/') -DBUS_ERROR_NO_REPLY = 'org.freedesktop.DBus.Error.NoReply' +tp_name_prefix = PREFIX +tp_path_prefix = PATH_PREFIX + +CM = PREFIX + ".ConnectionManager" +HT_NONE = 0 HT_CONTACT = 1 HT_ROOM = 2 +HT_LIST = 3 +HT_GROUP = 4 + +CHANNEL = PREFIX + ".Channel" -CHANNEL = tp_name_prefix + ".Channel" +CHANNEL_IFACE_CALL_STATE = CHANNEL + ".Interface.CallState" +CHANNEL_IFACE_CHAT_STATE = CHANNEL + '.Interface.ChatState' CHANNEL_IFACE_DESTROYABLE = CHANNEL + ".Interface.Destroyable" +CHANNEL_IFACE_DTMF = CHANNEL + ".Interface.DTMF" CHANNEL_IFACE_GROUP = CHANNEL + ".Interface.Group" CHANNEL_IFACE_HOLD = CHANNEL + ".Interface.Hold" CHANNEL_IFACE_MEDIA_SIGNALLING = CHANNEL + ".Interface.MediaSignalling" +CHANNEL_IFACE_MESSAGES = CHANNEL + ".Interface.Messages" +CHANNEL_IFACE_PASSWORD = CHANNEL + ".Interface.Password" +CHANNEL_IFACE_TUBE = CHANNEL + ".Interface.Tube" +CHANNEL_IFACE_SASL_AUTH = CHANNEL + ".Interface.SASLAuthentication" +CHANNEL_IFACE_CONFERENCE = CHANNEL + '.Interface.Conference' +CHANNEL_IFACE_ROOM = CHANNEL + '.Interface.Room2' +CHANNEL_IFACE_ROOM_CONFIG = CHANNEL + '.Interface.RoomConfig1' +CHANNEL_IFACE_SUBJECT = CHANNEL + '.Interface.Subject2' +CHANNEL_IFACE_FILE_TRANSFER_METADATA = CHANNEL + '.Interface.FileTransfer.Metadata' + +CHANNEL_TYPE_CALL = CHANNEL + ".Type.Call1" +CHANNEL_TYPE_CONTACT_LIST = CHANNEL + ".Type.ContactList" +CHANNEL_TYPE_CONTACT_SEARCH = CHANNEL + ".Type.ContactSearch" CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text" CHANNEL_TYPE_TUBES = CHANNEL + ".Type.Tubes" -CHANNEL_IFACE_TUBE = CHANNEL + ".Interface.Tube" CHANNEL_TYPE_STREAM_TUBE = CHANNEL + ".Type.StreamTube" CHANNEL_TYPE_DBUS_TUBE = CHANNEL + ".Type.DBusTube" -CHANNEL_TYPE_STREAMED_MEDIA = CHANNEL + ".Type.StreamedMedia" CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text" - -TP_AWKWARD_PROPERTIES = tp_name_prefix + ".Properties" +CHANNEL_TYPE_FILE_TRANSFER = CHANNEL + ".Type.FileTransfer" +CHANNEL_TYPE_ROOM_LIST = CHANNEL + ".Type.RoomList" +CHANNEL_TYPE_SERVER_AUTHENTICATION = \ + CHANNEL + ".Type.ServerAuthentication" +CHANNEL_TYPE_SERVER_TLS_CONNECTION = \ + CHANNEL + ".Type.ServerTLSConnection" + +TP_AWKWARD_PROPERTIES = PREFIX + ".Properties" PROPERTY_FLAG_READ = 1 PROPERTY_FLAG_WRITE = 2 +PROPERTY_FLAGS_RW = PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE CHANNEL_TYPE = CHANNEL + '.ChannelType' TARGET_HANDLE_TYPE = CHANNEL + '.TargetHandleType' @@ -56,69 +84,200 @@ INITIATOR_HANDLE = CHANNEL + '.InitiatorHandle' INITIATOR_ID = CHANNEL + '.InitiatorID' INTERFACES = CHANNEL + '.Interfaces' -CONN = tp_name_prefix + ".Connection" -CONN_IFACE_ALIASING = CONN + '.Interface.Aliasing' +CALL_CONTENTS = CHANNEL_TYPE_CALL + '.Contents' +CALL_CALL_STATE_DETAILS = CHANNEL_TYPE_CALL + '.CallStateDetails' +CALL_CALL_STATE = CHANNEL_TYPE_CALL + '.CallState' +CALL_CALL_FLAGS = CHANNEL_TYPE_CALL + '.CallFlags' +CALL_CALL_STATE_REASON = CHANNEL_TYPE_CALL + '.CallStateReason' +CALL_HARDWARE_STREAMING = CHANNEL_TYPE_CALL + '.HardwareStreaming' +CALL_CALL_MEMBERS = CHANNEL_TYPE_CALL + '.CallMembers' +CALL_MEMBER_IDENTIFIERS = CHANNEL_TYPE_CALL + '.MemberIdentifiers' +CALL_INITIAL_TRANSPORT = CHANNEL_TYPE_CALL + '.InitialTransport' +CALL_INITIAL_AUDIO = CHANNEL_TYPE_CALL + '.InitialAudio' +CALL_INITIAL_AUDIO_NAME = CHANNEL_TYPE_CALL + '.InitialAudioName' +CALL_INITIAL_VIDEO = CHANNEL_TYPE_CALL + '.InitialVideo' +CALL_INITIAL_VIDEO_NAME = CHANNEL_TYPE_CALL + '.InitialVideoName' +CALL_MUTABLE_CONTENTS = CHANNEL_TYPE_CALL + '.MutableContents' + +CALL_CONTENT = PREFIX + '.Call1.Content' +CALL_CONTENT_IFACE_MEDIA = CALL_CONTENT + '.Interface.Media' +CALL_CONTENT_IFACE_DTMF = CALL_CONTENT + '.Interface.DTMF' + +CALL_CONTENT_MEDIA_DESCRIPTION = CALL_CONTENT + '.MediaDescription' + +CALL_STREAM = PREFIX + '.Call1.Stream' +CALL_STREAM_IFACE_MEDIA = CALL_STREAM + '.Interface.Media' + +CALL_STREAM_ENDPOINT = CALL_STREAM + '.Endpoint' + +CALL_MEDIA_TYPE_AUDIO = 0 +CALL_MEDIA_TYPE_VIDEO = 1 + +CALL_CONTENT_PACKETIZATION_RTP = 0 +CALL_CONTENT_PACKETIZATION_RAW = 1 +CALL_CONTENT_PACKETIZATION_MSN_WEBCAM = 2 + +CALL_STREAM_TRANSPORT_UNKNOWN = 0 +CALL_STREAM_TRANSPORT_RAW_UDP = 1 +CALL_STREAM_TRANSPORT_ICE = 2 +CALL_STREAM_TRANSPORT_GTALK_P2P = 3 +CALL_STREAM_TRANSPORT_WLM_2009 = 4 +CALL_STREAM_TRANSPORT_SHM = 5 +CALL_STREAM_TRANSPORT_MULTICAST = 6 + +CALL_STATE_UNKNOWN = 0 +CALL_STATE_PENDING_INITIATOR = 1 +CALL_STATE_INITIALISING = 2 +CALL_STATE_INITIALISED = 3 +CALL_STATE_ACCEPTED = 4 +CALL_STATE_ACTIVE = 5 +CALL_STATE_ENDED = 6 + +CALL_FLAG_LOCALLY_HELD = 1 +CALL_FLAG_LOCALLY_RINGING = 2 +CALL_FLAG_LOCALLY_QUEUED = 4 +CALL_FLAG_FORWARDED = 8 +CALL_FLAG_CLEARING = 16 + +CALL_MEMBER_FLAG_RINGING = 1 +CALL_MEMBER_FLAG_HELD = 2 + +CALL_DISPOSITION_NONE = 0 +CALL_DISPOSITION_INITIAL = 1 + +CALL_SENDING_STATE_NONE = 0 +CALL_SENDING_STATE_PENDING_SEND = 1 +CALL_SENDING_STATE_SENDING = 2 +CALL_SENDING_STATE_PENDING_STOP_SENDING = 3 + +CALL_STREAM_FLOW_STATE_STOPPED = 0 +CALL_STREAM_FLOW_STATE_PENDING_START = 1 +CALL_STREAM_FLOW_STATE_PENDING_STOP = 2 +CALL_STREAM_FLOW_STATE_STARTED = 3 + +CALL_STREAM_ENDPOINT_STATE_CONNECTING = 0 +CALL_STREAM_ENDPOINT_STATE_PROVISIONALLY_CONNECTED = 1 +CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED = 2 +CALL_STREAM_ENDPOINT_STATE_EXHAUSTED_CANDIDATES = 3 +CALL_STREAM_ENDPOINT_STATE_FAILED = 4 + +CALL_STREAM_CANDIDATE_TYPE_HOST = 1 +CALL_STREAM_CANDIDATE_TYPE_SERVER_REFLEXIVE = 2 +CALL_STREAM_CANDIDATE_TYPE_RELAY = 4 + +CALL_STATE_CHANGE_REASON_UNKNOWN = 0 +CALL_STATE_CHANGE_REASON_PROGRESS_MADE = 1 +CALL_STATE_CHANGE_REASON_USER_REQUESTED = 2 +CALL_STATE_CHANGE_REASON_FORWARDED = 3 +CALL_STATE_CHANGE_REASON_REJECTED = 4 +CALL_STATE_CHANGE_REASON_NO_ANSWER = 5 +CALL_STATE_CHANGE_REASON_INVALID_CONTACT = 6 +CALL_STATE_CHANGE_REASON_PERMISSION_DENIED = 7 +CALL_STATE_CHANGE_REASON_BUSY = 8 +CALL_STATE_CHANGE_REASON_INTERNAL_ERROR = 9 +CALL_STATE_CHANGE_REASON_SERVICE_ERROR = 10 +CALL_STATE_CHANGE_REASON_NETWORK_ERROR = 11 +CALL_STATE_CHANGE_REASON_MEDIA_ERROR = 12 +CALL_STATE_CHANGE_REASON_CONNECTIVITY_ERROR = 13 + +CALL_STREAM_COMPONENT_UNKNOWN = 0 +CALL_STREAM_COMPONENT_DATA = 1 +CALL_STREAM_COMPONENT_CONTROL = 2 + +SUBSCRIPTION_STATE_UNKNOWN = 0 +SUBSCRIPTION_STATE_NO = 1 +SUBSCRIPTION_STATE_REMOVED_REMOTELY = 2 +SUBSCRIPTION_STATE_ASK = 3 +SUBSCRIPTION_STATE_YES = 4 + +CONTACT_LIST_STATE_NONE = 0 +CONTACT_LIST_STATE_WAITING = 1 +CONTACT_LIST_STATE_FAILURE = 2 +CONTACT_LIST_STATE_SUCCESS = 3 + +CONN = PREFIX + ".Connection" CONN_IFACE_AVATARS = CONN + '.Interface.Avatars' +CONN_IFACE_ALIASING = CONN + '.Interface.Aliasing' CONN_IFACE_CAPS = CONN + '.Interface.Capabilities' CONN_IFACE_CONTACTS = CONN + '.Interface.Contacts' CONN_IFACE_CONTACT_CAPS = CONN + '.Interface.ContactCapabilities' -CONN_IFACE_REQUESTS = CONN + '.Interface.Requests' +CONN_IFACE_CONTACT_INFO = CONN + ".Interface.ContactInfo" +CONN_IFACE_PRESENCE = CONN + '.Interface.Presence' CONN_IFACE_SIMPLE_PRESENCE = CONN + '.Interface.SimplePresence' +CONN_IFACE_REQUESTS = CONN + '.Interface.Requests' +CONN_IFACE_LOCATION = CONN + '.Interface.Location' +CONN_IFACE_GABBLE_DECLOAK = CONN + '.Interface.Gabble.Decloak' +CONN_IFACE_MAIL_NOTIFICATION = CONN + '.Interface.MailNotification' +CONN_IFACE_CONTACT_LIST = CONN + '.Interface.ContactList' +CONN_IFACE_CONTACT_GROUPS = CONN + '.Interface.ContactGroups' +CONN_IFACE_CLIENT_TYPES = CONN + '.Interface.ClientTypes' CONN_IFACE_POWER_SAVING = CONN + '.Interface.PowerSaving' +CONN_IFACE_CONTACT_BLOCKING = CONN + '.Interface.ContactBlocking' +CONN_IFACE_ADDRESSING = CONN + '.Interface.Addressing1' CONN_IFACE_SERVICE_POINT = CONN + '.Interface.ServicePoint' -CONN_STATUS_CONNECTED = 0 -CONN_STATUS_CONNECTING = 1 -CONN_STATUS_DISCONNECTED = 2 - ATTR_CONTACT_ID = CONN + '/contact-id' ATTR_ALIAS = CONN_IFACE_ALIASING + '/alias' ATTR_AVATAR_TOKEN = CONN_IFACE_AVATARS + '/token' +ATTR_CONTACT_CAPABILITIES = CONN_IFACE_CONTACT_CAPS + '/capabilities' ATTR_PRESENCE = CONN_IFACE_SIMPLE_PRESENCE + '/presence' +ATTR_SUBSCRIBE = CONN_IFACE_CONTACT_LIST + '/subscribe' +ATTR_PUBLISH = CONN_IFACE_CONTACT_LIST + '/publish' +ATTR_GROUPS = CONN_IFACE_CONTACT_GROUPS + '/groups' + +STREAM_HANDLER = PREFIX + '.Media.StreamHandler' -CONN_STATUS_REASON_NONE = 0 -CONN_STATUS_REASON_REQUESTED = 1 -CONN_STATUS_REASON_NETWORK_ERROR = 2 - -GROUP_REASON_NONE = 0 -GROUP_REASON_OFFLINE = 1 -GROUP_REASON_KICKED = 2 -GROUP_REASON_BUSY = 3 -GROUP_REASON_INVITED = 4 -GROUP_REASON_BANNED = 5 -GROUP_REASON_ERROR = 6 -GROUP_REASON_INVALID_CONTACT = 7 -GROUP_REASON_NO_ANSWER = 8 -GROUP_REASON_RENAMED = 9 -GROUP_REASON_PERMISSION_DENIED = 10 -GROUP_REASON_SEPARATED = 11 - -PRESENCE_TYPE_UNSET = 0 -PRESENCE_TYPE_OFFLINE = 1 -PRESENCE_TYPE_AVAILABLE = 2 -PRESENCE_TYPE_AWAY = 3 -PRESENCE_TYPE_XA = 4 -PRESENCE_TYPE_HIDDEN = 5 -PRESENCE_TYPE_BUSY = 6 -PRESENCE_TYPE_UNKNOWN = 7 -PRESENCE_TYPE_ERROR = 8 - -ERROR = tp_name_prefix + '.Error' +ERROR = PREFIX + '.Error' INVALID_ARGUMENT = ERROR + '.InvalidArgument' -INVALID_HANDLE = ERROR + '.InvalidHandle' NOT_IMPLEMENTED = ERROR + '.NotImplemented' NOT_AVAILABLE = ERROR + '.NotAvailable' PERMISSION_DENIED = ERROR + '.PermissionDenied' +OFFLINE = ERROR + '.Offline' +NOT_CAPABLE = ERROR + '.NotCapable' +CONNECTION_REFUSED = ERROR + '.ConnectionRefused' +CONNECTION_FAILED = ERROR + '.ConnectionFailed' +CONNECTION_LOST = ERROR + '.ConnectionLost' CANCELLED = ERROR + '.Cancelled' NOT_YOURS = ERROR + '.NotYours' DISCONNECTED = ERROR + '.Disconnected' -NOT_CAPABLE = ERROR + '.NotCapable' +REGISTRATION_EXISTS = ERROR + '.RegistrationExists' +AUTHENTICATION_FAILED = ERROR + '.AuthenticationFailed' +CONNECTION_REPLACED = ERROR + '.ConnectionReplaced' +ALREADY_CONNECTED = ERROR + '.AlreadyConnected' +NETWORK_ERROR = ERROR + '.NetworkError' +NOT_YET = ERROR + '.NotYet' +INVALID_HANDLE = ERROR + '.InvalidHandle' +CERT_UNTRUSTED = ERROR + '.Cert.Untrusted' +SERVICE_BUSY = ERROR + '.ServiceBusy' +SERVICE_CONFUSED = ERROR + '.ServiceConfused' +SOFTWARE_UPGRADE_REQUIRED = ERROR + '.SoftwareUpgradeRequired' + +BANNED = ERROR + '.Channel.Banned' + +DBUS_ERROR_UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod' +DBUS_ERROR_NO_REPLY = 'org.freedesktop.DBus.Error.NoReply' TUBE_PARAMETERS = CHANNEL_IFACE_TUBE + '.Parameters' TUBE_STATE = CHANNEL_IFACE_TUBE + '.State' STREAM_TUBE_SERVICE = CHANNEL_TYPE_STREAM_TUBE + '.Service' DBUS_TUBE_SERVICE_NAME = CHANNEL_TYPE_DBUS_TUBE + '.ServiceName' DBUS_TUBE_DBUS_NAMES = CHANNEL_TYPE_DBUS_TUBE + '.DBusNames' +DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS = CHANNEL_TYPE_DBUS_TUBE + '.SupportedAccessControls' +STREAM_TUBE_SUPPORTED_SOCKET_TYPES = CHANNEL_TYPE_STREAM_TUBE + '.SupportedSocketTypes' + +CONFERENCE_INITIAL_CHANNELS = CHANNEL_IFACE_CONFERENCE + '.InitialChannels' +CONFERENCE_INITIAL_INVITEE_HANDLES = CHANNEL_IFACE_CONFERENCE + '.InitialInviteeHandles' +CONFERENCE_INITIAL_INVITEE_IDS = CHANNEL_IFACE_CONFERENCE + '.InitialInviteeIDs' + +CONTACT_SEARCH_ASK = CHANNEL_TYPE_CONTACT_SEARCH + '.AvailableSearchKeys' +CONTACT_SEARCH_SERVER = CHANNEL_TYPE_CONTACT_SEARCH + '.Server' +CONTACT_SEARCH_STATE = CHANNEL_TYPE_CONTACT_SEARCH + '.SearchState' + +SEARCH_NOT_STARTED = 0 +SEARCH_IN_PROGRESS = 1 +SEARCH_MORE_AVAILABLE = 2 +SEARCH_COMPLETED = 3 +SEARCH_FAILED = 4 TUBE_CHANNEL_STATE_LOCAL_PENDING = 0 TUBE_CHANNEL_STATE_REMOTE_PENDING = 1 @@ -128,6 +287,13 @@ TUBE_CHANNEL_STATE_NOT_OFFERED = 3 MEDIA_STREAM_TYPE_AUDIO = 0 MEDIA_STREAM_TYPE_VIDEO = 1 +MEDIA_STREAM_BASE_PROTO_UDP = 0 +MEDIA_STREAM_BASE_PROTO_TCP = 1 + +MEDIA_STREAM_TRANSPORT_TYPE_LOCAL = 0 +MEDIA_STREAM_TRANSPORT_TYPE_DERIVED = 1 +MEDIA_STREAM_TRANSPORT_TYPE_RELAY = 2 + SOCKET_ADDRESS_TYPE_UNIX = 0 SOCKET_ADDRESS_TYPE_ABSTRACT_UNIX = 1 SOCKET_ADDRESS_TYPE_IPV4 = 2 @@ -166,41 +332,287 @@ MEDIA_STREAM_DIRECTION_SEND = 1 MEDIA_STREAM_DIRECTION_RECEIVE = 2 MEDIA_STREAM_DIRECTION_BIDIRECTIONAL = 3 +FT_STATE_NONE = 0 +FT_STATE_PENDING = 1 +FT_STATE_ACCEPTED = 2 +FT_STATE_OPEN = 3 +FT_STATE_COMPLETED = 4 +FT_STATE_CANCELLED = 5 + +FT_STATE_CHANGE_REASON_NONE = 0 +FT_STATE_CHANGE_REASON_REQUESTED = 1 +FT_STATE_CHANGE_REASON_LOCAL_STOPPED = 2 +FT_STATE_CHANGE_REASON_REMOTE_STOPPED = 3 +FT_STATE_CHANGE_REASON_LOCAL_ERROR = 4 +FT_STATE_CHANGE_REASON_REMOTE_ERROR = 5 + +FILE_HASH_TYPE_NONE = 0 +FILE_HASH_TYPE_MD5 = 1 +FILE_HASH_TYPE_SHA1 = 2 +FILE_HASH_TYPE_SHA256 = 3 + +FT_STATE = CHANNEL_TYPE_FILE_TRANSFER + '.State' +FT_CONTENT_TYPE = CHANNEL_TYPE_FILE_TRANSFER + '.ContentType' +FT_FILENAME = CHANNEL_TYPE_FILE_TRANSFER + '.Filename' +FT_SIZE = CHANNEL_TYPE_FILE_TRANSFER + '.Size' +FT_CONTENT_HASH_TYPE = CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType' +FT_CONTENT_HASH = CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash' +FT_DESCRIPTION = CHANNEL_TYPE_FILE_TRANSFER + '.Description' +FT_DATE = CHANNEL_TYPE_FILE_TRANSFER + '.Date' +FT_AVAILABLE_SOCKET_TYPES = CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes' +FT_TRANSFERRED_BYTES = CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes' +FT_INITIAL_OFFSET = CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset' +FT_FILE_COLLECTION = CHANNEL_TYPE_FILE_TRANSFER + '.FUTURE.FileCollection' +FT_URI = CHANNEL_TYPE_FILE_TRANSFER + '.URI' +FT_SERVICE_NAME = CHANNEL_IFACE_FILE_TRANSFER_METADATA + '.ServiceName' +FT_METADATA = CHANNEL_IFACE_FILE_TRANSFER_METADATA + '.Metadata' + +GF_CAN_ADD = 1 +GF_CAN_REMOVE = 2 +GF_CAN_RESCIND = 4 +GF_MESSAGE_ADD = 8 +GF_MESSAGE_REMOVE = 16 +GF_MESSAGE_ACCEPT = 32 +GF_MESSAGE_REJECT = 64 +GF_MESSAGE_RESCIND = 128 +GF_CHANNEL_SPECIFIC_HANDLES = 256 +GF_ONLY_ONE_GROUP = 512 +GF_HANDLE_OWNERS_NOT_AVAILABLE = 1024 +GF_PROPERTIES = 2048 +GF_MEMBERS_CHANGED_DETAILED = 4096 + +GC_REASON_NONE = 0 +GC_REASON_OFFLINE = 1 +GC_REASON_KICKED = 2 +GC_REASON_BUSY = 3 +GC_REASON_INVITED = 4 +GC_REASON_BANNED = 5 +GC_REASON_ERROR = 6 +GC_REASON_INVALID_CONTACT = 7 +GC_REASON_NO_ANSWER = 8 +GC_REASON_RENAMED = 9 +GC_REASON_PERMISSION_DENIED = 10 +GC_REASON_SEPARATED = 11 + +HS_UNHELD = 0 +HS_HELD = 1 +HS_PENDING_HOLD = 2 +HS_PENDING_UNHOLD = 3 + +HSR_NONE = 0 +HSR_REQUESTED = 1 +HSR_RESOURCE_NOT_AVAILABLE = 2 + +CONN_STATUS_CONNECTED = 0 +CONN_STATUS_CONNECTING = 1 +CONN_STATUS_DISCONNECTED = 2 + +CSR_NONE_SPECIFIED = 0 +CSR_REQUESTED = 1 +CSR_NETWORK_ERROR = 2 +CSR_AUTHENTICATION_FAILED = 3 +CSR_ENCRYPTION_ERROR = 4 +CSR_NAME_IN_USE = 5 +CSR_CERT_NOT_PROVIDED = 6 +CSR_CERT_UNTRUSTED = 7 +CSR_CERT_EXPIRED = 8 +CSR_CERT_NOT_ACTIVATED = 9 +CSR_CERT_HOSTNAME_MISMATCH = 10 +CSR_CERT_FINGERPRINT_MISMATCH = 11 +CSR_CERT_SELF_SIGNED = 12 +CSR_CERT_OTHER_ERROR = 13 + +BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo' +ACTIVITY_PROPERTIES = 'org.laptop.Telepathy.ActivityProperties' + +CHAT_STATE_GONE = 0 +CHAT_STATE_INACTIVE = 1 +CHAT_STATE_ACTIVE = 2 +CHAT_STATE_PAUSED = 3 +CHAT_STATE_COMPOSING = 4 + +# Channel_Media_Capabilities +MEDIA_CAP_AUDIO = 1 +MEDIA_CAP_VIDEO = 2 +MEDIA_CAP_STUN = 4 +MEDIA_CAP_GTALKP2P = 8 +MEDIA_CAP_ICEUDP = 16 +MEDIA_CAP_IMMUTABLE_STREAMS = 32 + +CLIENT = PREFIX + '.Client' + +PRESENCE_UNSET = 0 +PRESENCE_OFFLINE = 1 +PRESENCE_AVAILABLE = 2 +PRESENCE_AWAY = 3 +PRESENCE_EXTENDED_AWAY = 4 +PRESENCE_HIDDEN = 5 +PRESENCE_BUSY = 6 +PRESENCE_UNKNOWN = 7 +PRESENCE_ERROR = 8 + +CONTACT_INFO_FLAG_CAN_SET = 1 +CONTACT_INFO_FLAG_PUSH = 2 +CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT = 1 +CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME = 2 + +# Channel_Interface_SaslAuthentication +SASL_STATUS_NOT_STARTED = 0 +SASL_STATUS_IN_PROGRESS = 1 +SASL_STATUS_SERVER_SUCCEEDED = 2 +SASL_STATUS_CLIENT_ACCEPTED = 3 +SASL_STATUS_SUCCEEDED = 4 +SASL_STATUS_SERVER_FAILED = 5 +SASL_STATUS_CLIENT_FAILED = 6 + +SASL_ABORT_REASON_INVALID_CHALLENGE = 0 +SASL_ABORT_REASON_USER_ABORT = 1 + +AUTH_METHOD = CHANNEL_TYPE_SERVER_AUTHENTICATION + ".AuthenticationMethod" +SASL_AVAILABLE_MECHANISMS = CHANNEL_IFACE_SASL_AUTH + ".AvailableMechanisms" +SASL_STATUS = CHANNEL_IFACE_SASL_AUTH + ".SASLStatus" +SASL_ERROR = CHANNEL_IFACE_SASL_AUTH + ".SASLError" +SASL_ERROR_DETAILS = CHANNEL_IFACE_SASL_AUTH + ".SASLErrorDetails" +SASL_CONTEXT = CHANNEL_IFACE_SASL_AUTH + ".SASLContext" +SASL_AUTHORIZATION_IDENTITY = CHANNEL_IFACE_SASL_AUTH + ".AuthorizationIdentity" +SASL_DEFAULT_REALM = CHANNEL_IFACE_SASL_AUTH + ".DefaultRealm" +SASL_DEFAULT_USERNAME = CHANNEL_IFACE_SASL_AUTH + ".DefaultUsername" + +# Channel_Type_ServerTLSConnection +TLS_CERT_PATH = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".ServerCertificate" +TLS_HOSTNAME = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".Hostname" +TLS_REFERENCE_IDENTITIES = \ + CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".ReferenceIdentities" + +# Connection.Interface.Location + +LOCATION_FEATURE_CAN_SET = 1 + +# Channel.Type.Text + +MT_NORMAL = 0 +MT_ACTION = 1 +MT_NOTICE = 2 +MT_AUTO_REPLY = 3 +MT_DELIVERY_REPORT = 4 + +class MessageFlag(object): + TRUNCATED = 1 + NON_TEXT_CONTENT = 2 + SCROLLBACK = 4 + RESCUED = 8 + +class SendError(object): + UNKNOWN = 0 + OFFLINE = 1 + INVALID_CONTACT = 2 + PERMISSION_DENIED = 3 + TOO_LONG = 4 + NOT_IMPLEMENTED = 5 + +PROTOCOL = PREFIX + '.Protocol' +PROTOCOL_IFACE_PRESENCES = PROTOCOL + '.Interface.Presence' +PROTOCOL_IFACE_ADDRESSING = PROTOCOL + '.Interface.Addressing' +PROTOCOL_IFACE_AVATARS = PROTOCOL + '.Interface.Avatars' + +PARAM_REQUIRED = 1 +PARAM_REGISTER = 2 +PARAM_HAS_DEFAULT = 4 +PARAM_SECRET = 8 +PARAM_DBUS_PROPERTY = 16 + +AUTHENTICATION = PREFIX + '.Authentication' +AUTH_TLS_CERT = AUTHENTICATION + ".TLSCertificate" + +TLS_CERT_STATE_PENDING = 0 +TLS_CERT_STATE_ACCEPTED = 1 +TLS_CERT_STATE_REJECTED = 2 + +TLS_REJECT_REASON_UNKNOWN = 0 +TLS_REJECT_REASON_UNTRUSTED = 1 + +# Channel.Interface.Messages + +MESSAGE_PART_SUPPORT_FLAGS = CHANNEL_IFACE_MESSAGES + '.MessagePartSupportFlags' +DELIVERY_REPORTING_SUPPORT = CHANNEL_IFACE_MESSAGES + '.DeliveryReportingSupport' +SUPPORTED_CONTENT_TYPES = CHANNEL_IFACE_MESSAGES + '.SupportedContentTypes' + +MSG_SENDING_FLAGS_REPORT_DELIVERY = 1 +MSG_SENDING_FLAGS_REPORT_READ = 2 +MSG_SENDING_FLAGS_REPORT_DELETED = 4 + +DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_FAILURES = 1 +DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_SUCCESSES = 2 +DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_READ = 4 +DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_DELETED = 8 + +DELIVERY_STATUS_UNKNOWN = 0 +DELIVERY_STATUS_DELIVERED = 1 +DELIVERY_STATUS_TEMPORARILY_FAILED = 2 +DELIVERY_STATUS_PERMANENTLY_FAILED = 3 +DELIVERY_STATUS_ACCEPTED = 4 +DELIVERY_STATUS_READ = 5 +DELIVERY_STATUS_DELETED = 6 + +MEDIA_STREAM_ERROR_UNKNOWN = 0 +MEDIA_STREAM_ERROR_EOS = 1 +MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED = 2 +MEDIA_STREAM_ERROR_CONNECTION_FAILED = 3 +MEDIA_STREAM_ERROR_NETWORK_ERROR = 4 +MEDIA_STREAM_ERROR_NO_CODECS = 5 +MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR = 6 +MEDIA_STREAM_ERROR_MEDIA_ERROR = 7 + +PASSWORD_FLAG_PROVIDE = 8 + +# Channel.Interface.Room +ROOM_NAME = CHANNEL_IFACE_ROOM + '.RoomName' +ROOM_SERVER = CHANNEL_IFACE_ROOM + '.Server' + +# Channel.Interface.Subject +SUBJECT = CHANNEL_IFACE_ROOM + '.Subject' +SUBJECT_PRESENT = 1 +SUBJECT_CAN_SET = 2 + +DEBUG_IFACE = PREFIX + '.Debug' +DEBUG_PATH = PATH_PREFIX + '/debug' + SERVICE_POINT_TYPE_NONE = 0 SERVICE_POINT_TYPE_EMERGENCY = 1 SERVICE_POINT_TYPE_COUNSELING = 2 -CLIENT = tp_name_prefix + '.Client' -CLIENT_PATH = tp_path_prefix + '/Client' -OBSERVER = tp_name_prefix + '.Client.Observer' -APPROVER = tp_name_prefix + '.Client.Approver' -HANDLER = tp_name_prefix + '.Client.Handler' +CLIENT = PREFIX + '.Client' +CLIENT_PATH = PATH_PREFIX + '/Client' +OBSERVER = PREFIX + '.Client.Observer' +APPROVER = PREFIX + '.Client.Approver' +HANDLER = PREFIX + '.Client.Handler' CLIENT_IFACE_REQUESTS = CLIENT + '.Interface.Requests' -ACCOUNT = tp_name_prefix + '.Account' +ACCOUNT = PREFIX + '.Account' ACCOUNT_IFACE_AVATAR = ACCOUNT + '.Interface.Avatar' ACCOUNT_IFACE_ADDRESSING = ACCOUNT + '.Interface.Addressing' -ACCOUNT_IFACE_HIDDEN = ACCOUNT + '.Interface.Hidden.DRAFT1' -ACCOUNT_IFACE_NOKIA_CONDITIONS = 'com.nokia.Account.Interface.Conditions' -ACCOUNT_PATH_PREFIX = tp_path_prefix + '/Account/' +ACCOUNT_PATH_PREFIX = PATH_PREFIX + '/Account/' -AM = tp_name_prefix + '.AccountManager' -AM_IFACE_HIDDEN = AM + '.Interface.Hidden.DRAFT1' -AM_PATH = tp_path_prefix + '/AccountManager' +AM = PREFIX + '.AccountManager' +AM_PATH = PATH_PREFIX + '/AccountManager' -CR = tp_name_prefix + '.ChannelRequest' -CDO = tp_name_prefix + '.ChannelDispatchOperation' +CR = PREFIX + '.ChannelRequest' +CDO = PREFIX + '.ChannelDispatchOperation' -CD = tp_name_prefix + '.ChannelDispatcher' -CD_IFACE_OP_LIST = tp_name_prefix + '.ChannelDispatcher.Interface.OperationList' -CD_PATH = tp_path_prefix + '/ChannelDispatcher' +CD = PREFIX + '.ChannelDispatcher' +CD_IFACE_OP_LIST = PREFIX + '.ChannelDispatcher.Interface.OperationList' +CD_PATH = PATH_PREFIX + '/ChannelDispatcher' CD_REDISPATCH = CD + '.Interface.Redispatch.DRAFT' -MC = tp_name_prefix + '.MissionControl5' -MC_PATH = tp_path_prefix + '/MissionControl5' +MC = PREFIX + '.MissionControl5' +MC_PATH = PATH_PREFIX + '/MissionControl5' + +DTMF_CURRENTLY_SENDING_TONES = CHANNEL_IFACE_DTMF + '.CurrentlySendingTones' +DTMF_INITIAL_TONES = CHANNEL_IFACE_DTMF + '.InitialTones' +DTMF_DEFERRED_TONES = CHANNEL_IFACE_DTMF + '.DeferredTones' -TESTDOT = "org.freedesktop.Telepathy.MC.Test." -TESTSLASH = "/org/freedesktop/Telepathy/MC/Test/" +TESTDOT = PREFIX + ".Test." +TESTSLASH = PATH_PREFIX + "/Test/" TEST_DBUS_ACCOUNT_SERVICE = TESTDOT + "DBusAccountService" TEST_DBUS_ACCOUNT_SERVICE_PATH = TESTSLASH + "DBusAccountService" @@ -209,8 +621,8 @@ TEST_DBUS_ACCOUNT_SERVICE_IFACE = TEST_DBUS_ACCOUNT_SERVICE TEST_DBUS_ACCOUNT_PLUGIN_PATH = TESTSLASH + "DBusAccountPlugin" TEST_DBUS_ACCOUNT_PLUGIN_IFACE = TESTDOT + "DBusAccountPlugin" -PARAM_FLAG_REQUIRED = 1 -PARAM_FLAG_REGISTER = 2 -PARAM_FLAG_HAS_DEFAULT = 4 -PARAM_FLAG_SECRET = 8 -PARAM_FLAG_DBUS_PROPERTY = 16 +class StorageRestrictionFlags(object): + CANNOT_SET_PARAMETERS = 1 + CANNOT_SET_ENABLED = 2 + CANNOT_SET_PRESENCE = 4 + CANNOT_SET_SERVICE = 8 diff --git a/tests/twisted/crash-recovery/crash-recovery.py b/tests/twisted/crash-recovery/crash-recovery.py index 2bc45349..1656ab12 100644 --- a/tests/twisted/crash-recovery/crash-recovery.py +++ b/tests/twisted/crash-recovery/crash-recovery.py @@ -27,7 +27,7 @@ import dbus.service from servicetest import EventPattern, call_async from mctest import exec_test, SimulatedConnection, SimulatedClient, \ - create_fakecm_account, enable_fakecm_account, SimulatedChannel, \ + SimulatedChannel, \ expect_client_setup, MC import constants as cs diff --git a/tests/twisted/dbus-account-plugin.c b/tests/twisted/dbus-account-plugin.c index 16b1c9c0..79464ed9 100644 --- a/tests/twisted/dbus-account-plugin.c +++ b/tests/twisted/dbus-account-plugin.c @@ -23,10 +23,15 @@ #include "config.h" #include "dbus-account-plugin.h" +#include <telepathy-glib/telepathy-glib.h> +#include <telepathy-glib/telepathy-glib-dbus.h> + #define DEBUG(format, ...) g_debug ("%s: " format, G_STRFUNC, ##__VA_ARGS__) +#define CRITICAL(format, ...) \ + g_critical ("%s: " format, G_STRFUNC, ##__VA_ARGS__) -#define TESTDOT "org.freedesktop.Telepathy.MC.Test." -#define TESTSLASH "/org/freedesktop/Telepathy/MC/Test/" +#define TESTDOT "org.freedesktop.Telepathy.Test." +#define TESTSLASH "/org/freedesktop/Telepathy/Test/" #define TEST_DBUS_ACCOUNT_SERVICE TESTDOT "DBusAccountService" #define TEST_DBUS_ACCOUNT_SERVICE_PATH TESTSLASH "DBusAccountService" @@ -51,7 +56,8 @@ typedef struct { GHashTable *parameter_flags; /* set of strings */ GHashTable *uncommitted_parameters; - enum { UNCOMMITTED_CREATION, UNCOMMITTED_DELETION } flags; + enum { UNCOMMITTED_CREATION = 1 } flags; + TpStorageRestrictionFlags restrictions; } Account; static void @@ -116,29 +122,6 @@ async_data_free (AsyncData *ad) g_slice_free (AsyncData, ad); } -typedef enum { - EVENT_PARAMS, - EVENT_ATTRS, - EVENT_CREATION, - EVENT_DELETION -} EventType; - -typedef struct { - EventType type; - GVariant *args; -} Event; - -static Event * -event_new (EventType type, - GVariant *args) -{ - Event *e = g_slice_new0 (Event); - - e->type = type; - e->args = g_variant_ref_sink (args); - return e; -} - static Account * lookup_account (TestDBusAccountPlugin *self, const gchar *account_name) @@ -154,7 +137,7 @@ ensure_account (TestDBusAccountPlugin *self, if (account == NULL) { - account = g_slice_new (Account); + account = g_slice_new0 (Account); account->path = g_strdup_printf ("%s%s", TP_ACCOUNT_OBJECT_PATH_BASE, account_name); @@ -179,7 +162,6 @@ ensure_account (TestDBusAccountPlugin *self, g_hash_table_insert (self->accounts, g_strdup (account_name), account); } - account->flags &= ~UNCOMMITTED_DELETION; return account; } @@ -207,18 +189,14 @@ service_vanished_cb (GDBusConnection *bus, { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); GHashTableIter iter; - gpointer k, v; + gpointer k; self->active = FALSE; g_hash_table_iter_init (&iter, self->accounts); - while (g_hash_table_iter_next (&iter, &k, &v)) + while (g_hash_table_iter_next (&iter, &k, NULL)) { - Account *account = v; - - if ((account->flags & UNCOMMITTED_DELETION) == 0) - mcp_account_storage_emit_deleted (MCP_ACCOUNT_STORAGE (self), k); - + mcp_account_storage_emit_deleted (MCP_ACCOUNT_STORAGE (self), k); g_hash_table_iter_remove (&iter); } @@ -264,7 +242,8 @@ test_dbus_account_plugin_add_account (TestDBusAccountPlugin *self, GVariant *attribute_flags, GVariant *parameters, GVariant *untyped_parameters, - GVariant *param_flags) + GVariant *param_flags, + TpStorageRestrictionFlags restrictions) { GVariantIter iter; const gchar *k; @@ -303,13 +282,21 @@ test_dbus_account_plugin_add_account (TestDBusAccountPlugin *self, g_hash_table_insert (account->parameter_flags, g_strdup (k), GUINT_TO_POINTER (u)); + account->restrictions = restrictions; + return account; } static void -test_dbus_account_plugin_process_account_creation (TestDBusAccountPlugin *self, - GVariant *args) +account_created_cb (GDBusConnection *bus, + const gchar *sender_name, + const gchar *object_path, + const gchar *iface_name, + const gchar *signal_name, + GVariant *tuple, + gpointer user_data) { + TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); const gchar *account_name; Account *account; GVariant *attrs; @@ -317,10 +304,12 @@ test_dbus_account_plugin_process_account_creation (TestDBusAccountPlugin *self, GVariant *untyped_params; GVariant *attr_flags; GVariant *param_flags; + guint32 restrictions; - g_variant_get (args, "(&s@a{sv}@a{su}@a{sv}@a{ss}@a{su})", + g_variant_get (tuple, "(&s@a{sv}@a{su}@a{sv}@a{ss}@a{su}u)", &account_name, &attrs, &attr_flags, - ¶ms, &untyped_params, ¶m_flags); + ¶ms, &untyped_params, ¶m_flags, + &restrictions); DEBUG ("%s", account_name); account = lookup_account (self, account_name); @@ -336,7 +325,7 @@ test_dbus_account_plugin_process_account_creation (TestDBusAccountPlugin *self, * a lot of rubbish */ account = test_dbus_account_plugin_add_account (self, account_name, attrs, attr_flags, - params, untyped_params, param_flags); + params, untyped_params, param_flags, restrictions); mcp_account_storage_emit_created ( MCP_ACCOUNT_STORAGE (self), account_name); @@ -355,19 +344,25 @@ test_dbus_account_plugin_process_account_creation (TestDBusAccountPlugin *self, } static void -test_dbus_account_plugin_process_account_deletion (TestDBusAccountPlugin *self, - GVariant *args) +account_deleted_cb (GDBusConnection *bus, + const gchar *sender_name, + const gchar *object_path, + const gchar *iface_name, + const gchar *signal_name, + GVariant *tuple, + gpointer user_data) { + TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); const gchar *account_name; Account *account; - g_variant_get (args, "(&s)", &account_name); + g_variant_get (tuple, "(&s)", &account_name); DEBUG ("%s", account_name); account = lookup_account (self, account_name); if (account == NULL) { - g_warning ("accounts service deleted %s but we don't " + CRITICAL ("accounts service deleted %s but we don't " "have any record of that account", account_name); } else @@ -389,23 +384,29 @@ test_dbus_account_plugin_process_account_deletion (TestDBusAccountPlugin *self, } static void -test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, - GVariant *args) +attributes_changed_cb (GDBusConnection *bus, + const gchar *sender_name, + const gchar *object_path, + const gchar *iface_name, + const gchar *signal_name, + GVariant *tuple, + gpointer user_data) { + TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); const gchar *account_name; Account *account; GVariant *attrs; GVariant *attr_flags; GVariant *deleted; - g_variant_get (args, "(&s@a{sv}@a{su}@as)", + g_variant_get (tuple, "(&s@a{sv}@a{su}@as)", &account_name, &attrs, &attr_flags, &deleted); DEBUG ("%s", account_name); account = lookup_account (self, account_name); if (account == NULL) { - g_warning ("accounts service altered %s but we don't " + CRITICAL ("accounts service altered %s but we don't " "have any record of that account", account_name); } else @@ -439,8 +440,6 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, g_hash_table_insert (account->attributes, g_strdup (attr), g_variant_ref (value)); - mcp_account_manager_set_attribute (self->feedback, - account_name, attr, value, flags); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, attr); @@ -464,8 +463,6 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, DEBUG ("%s deleted", attr); g_hash_table_remove (account->attributes, attr); - mcp_account_manager_set_attribute (self->feedback, - account_name, attr, NULL, 0); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, attr); @@ -499,9 +496,15 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, } static void -test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, - GVariant *args) +parameters_changed_cb (GDBusConnection *bus, + const gchar *sender_name, + const gchar *object_path, + const gchar *iface_name, + const gchar *signal_name, + GVariant *tuple, + gpointer user_data) { + TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); const gchar *account_name; Account *account; GVariant *params; @@ -509,14 +512,14 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, GVariant *param_flags; GVariant *deleted; - g_variant_get (args, "(&s@a{sv}@a{ss}@a{su}@as)", + g_variant_get (tuple, "(&s@a{sv}@a{ss}@a{su}@as)", &account_name, ¶ms, &untyped_params, ¶m_flags, &deleted); DEBUG ("%s", account_name); account = lookup_account (self, account_name); if (account == NULL) { - g_warning ("accounts service altered %s but we don't " + CRITICAL ("accounts service altered %s but we don't " "have any record of that account", account_name); } else @@ -548,8 +551,6 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, g_hash_table_insert (account->parameters, g_strdup (param), g_variant_ref (value)); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_parameter (self->feedback, - account_name, param, value, flags); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); @@ -577,11 +578,10 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, (stored == NULL || !g_variant_equal (value, stored))) { + g_hash_table_remove (account->parameters, param); g_hash_table_insert (account->untyped_parameters, g_strdup (param), g_strdup (escaped)); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_value (self->feedback, - account_name, key, escaped); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); @@ -611,8 +611,6 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, g_hash_table_remove (account->untyped_parameters, param); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_parameter (self->feedback, - account_name, param, NULL, 0); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); @@ -633,126 +631,9 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, g_variant_unref (deleted); } -static void -test_dbus_account_plugin_process_events (TestDBusAccountPlugin *self) -{ - Event *event; - - if (self->feedback == NULL) - return; - - while ((event = g_queue_pop_head (&self->events)) != NULL) - { - switch (event->type) - { - case EVENT_CREATION: - test_dbus_account_plugin_process_account_creation (self, - event->args); - break; - - case EVENT_DELETION: - test_dbus_account_plugin_process_account_deletion (self, - event->args); - break; - - case EVENT_ATTRS: - test_dbus_account_plugin_process_attributes (self, - event->args); - break; - - case EVENT_PARAMS: - test_dbus_account_plugin_process_parameters (self, - event->args); - break; - } - - g_variant_unref (event->args); - g_slice_free (Event, event); - } -} - -static void -account_created_cb (GDBusConnection *bus, - const gchar *sender_name, - const gchar *object_path, - const gchar *iface_name, - const gchar *signal_name, - GVariant *tuple, - gpointer user_data) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); - const gchar *account_name; - - g_variant_get (tuple, "(&s@a{sv}@a{su}@a{sv}@a{ss}@a{su})", - &account_name, NULL, NULL, NULL, NULL, NULL); - DEBUG ("%s", account_name); - - g_queue_push_tail (&self->events, event_new (EVENT_CREATION, tuple)); - test_dbus_account_plugin_process_events (self); -} - -static void -account_deleted_cb (GDBusConnection *bus, - const gchar *sender_name, - const gchar *object_path, - const gchar *iface_name, - const gchar *signal_name, - GVariant *tuple, - gpointer user_data) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); - const gchar *account_name; - - g_variant_get (tuple, "(&s)", &account_name); - DEBUG ("%s", account_name); - - g_queue_push_tail (&self->events, event_new (EVENT_DELETION, tuple)); - test_dbus_account_plugin_process_events (self); -} - -static void -attributes_changed_cb (GDBusConnection *bus, - const gchar *sender_name, - const gchar *object_path, - const gchar *iface_name, - const gchar *signal_name, - GVariant *tuple, - gpointer user_data) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); - const gchar *account_name; - - g_variant_get (tuple, "(&s@a{sv}@a{su}@as)", &account_name, - NULL, NULL, NULL); - DEBUG ("%s", account_name); - - g_queue_push_tail (&self->events, event_new (EVENT_ATTRS, tuple)); - test_dbus_account_plugin_process_events (self); -} - -static void -parameters_changed_cb (GDBusConnection *bus, - const gchar *sender_name, - const gchar *object_path, - const gchar *iface_name, - const gchar *signal_name, - GVariant *tuple, - gpointer user_data) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); - const gchar *account_name; - - g_variant_get (tuple, "(&s@a{sv}@a{ss}@a{su}@as)", &account_name, - NULL, NULL, NULL, NULL); - DEBUG ("%s", account_name); - - g_queue_push_tail (&self->events, event_new (EVENT_PARAMS, tuple)); - test_dbus_account_plugin_process_events (self); -} - static GList * -test_dbus_account_plugin_list (const McpAccountStorage *storage, - const McpAccountManager *am) +test_dbus_account_plugin_list (McpAccountStorage *storage, + McpAccountManager *am) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); GError *error = NULL; @@ -762,6 +643,7 @@ test_dbus_account_plugin_list (const McpAccountStorage *storage, GVariantIter account_iter; const gchar *account_name; GList *ret = NULL; + guint32 restrictions; DEBUG ("called"); @@ -820,7 +702,7 @@ test_dbus_account_plugin_list (const McpAccountStorage *storage, TEST_DBUS_ACCOUNT_SERVICE_IFACE, "GetAccounts", NULL, /* no parameters */ - G_VARIANT_TYPE ("(a{s(a{sv}a{su}a{sv}a{ss}a{su})})"), + G_VARIANT_TYPE ("(a{s(a{sv}a{su}a{sv}a{ss}a{su}u)})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, /* no cancellable */ @@ -843,13 +725,13 @@ test_dbus_account_plugin_list (const McpAccountStorage *storage, g_variant_iter_init (&account_iter, accounts); while (g_variant_iter_loop (&account_iter, - "{s(@a{sv}@a{su}@a{sv}@a{ss}@a{su})}", &account_name, + "{s(@a{sv}@a{su}@a{sv}@a{ss}@a{su}u)}", &account_name, &attributes, &attribute_flags, - ¶meters, &untyped_parameters, ¶m_flags)) + ¶meters, &untyped_parameters, ¶m_flags, &restrictions)) { test_dbus_account_plugin_add_account (self, account_name, attributes, attribute_flags, parameters, untyped_parameters, - param_flags); + param_flags, restrictions); ret = g_list_prepend (ret, g_strdup (account_name)); } @@ -859,27 +741,12 @@ test_dbus_account_plugin_list (const McpAccountStorage *storage, return ret; } -static void -test_dbus_account_plugin_ready (const McpAccountStorage *storage, - const McpAccountManager *am) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - - DEBUG ("called"); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "Ready", NULL, NULL); - self->feedback = MCP_ACCOUNT_MANAGER (am); - - test_dbus_account_plugin_process_events (self); -} - static gchar * -test_dbus_account_plugin_create (const McpAccountStorage *storage, - const McpAccountManager *am, +test_dbus_account_plugin_create (McpAccountStorage *storage, + McpAccountManager *am, const gchar *manager, const gchar *protocol, - GHashTable *params, + const gchar *identifier, GError **error) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); @@ -890,7 +757,7 @@ test_dbus_account_plugin_create (const McpAccountStorage *storage, return FALSE; name = mcp_account_manager_get_unique_name ((McpAccountManager *) am, - manager, protocol, params); + manager, protocol, identifier); account = ensure_account (self, name); g_dbus_connection_emit_signal (self->bus, NULL, TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, @@ -898,218 +765,192 @@ test_dbus_account_plugin_create (const McpAccountStorage *storage, return name; } -static gboolean -test_dbus_account_plugin_delete (const McpAccountStorage *storage, - const McpAccountManager *am, +static void delete_account_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data); + +static void +test_dbus_account_plugin_delete_async (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account_name, - const gchar *key) + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GTask *task = g_task_new (self, cancellable, callback, user_data); - DEBUG ("called"); - - if (account == NULL || !self->active) - return FALSE; - - if (key == NULL) - { - account->flags |= UNCOMMITTED_DELETION; - g_hash_table_remove_all (account->attributes); - g_hash_table_remove_all (account->parameters); - g_hash_table_remove_all (account->untyped_parameters); - g_hash_table_remove_all (account->attribute_flags); - g_hash_table_remove_all (account->parameter_flags); + g_task_set_task_data (task, g_strdup (user_data), g_free); - account->flags &= ~UNCOMMITTED_CREATION; - g_hash_table_remove_all (account->uncommitted_attributes); - g_hash_table_remove_all (account->uncommitted_parameters); - - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "DeferringDelete", g_variant_new_parsed ("(%o,)", account->path), - NULL); - } - else if (g_str_has_prefix (key, "param-")) - { - g_hash_table_remove (account->parameters, key + 6); - g_hash_table_remove (account->untyped_parameters, key + 6); - g_hash_table_remove (account->parameter_flags, key + 6); - g_hash_table_add (account->uncommitted_parameters, g_strdup (key + 6)); + DEBUG ("called"); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "DeferringDeleteParameter", - g_variant_new_parsed ("(%o, %s)", account->path, key + 6), NULL); - } - else - { - g_hash_table_remove (account->attributes, key); - g_hash_table_remove (account->attribute_flags, key); - g_hash_table_add (account->uncommitted_attributes, g_strdup (key)); + g_return_if_fail (self->active); + g_return_if_fail (account != NULL); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "DeferringDeleteAttribute", - g_variant_new_parsed ("(%o, %s)", account->path, key), NULL); - } + /* deletion used to be delayed, so the regression tests will expect this + * to happen - leave them unmodified for now */ + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "DeferringDelete", g_variant_new_parsed ("(%o,)", account->path), + NULL); + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "CommittingOne", g_variant_new_parsed ("(%o,)", account->path), NULL); - return TRUE; + g_dbus_connection_call (self->bus, + TEST_DBUS_ACCOUNT_SERVICE, + TEST_DBUS_ACCOUNT_SERVICE_PATH, + TEST_DBUS_ACCOUNT_SERVICE_IFACE, + "DeleteAccount", + g_variant_new_parsed ("(%s,)", account_name), + G_VARIANT_TYPE_UNIT, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* no cancellable */ + delete_account_cb, + task); } static gboolean -test_dbus_account_plugin_get (const McpAccountStorage *storage, - const McpAccountManager *am, +test_dbus_account_plugin_delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static GVariant * +test_dbus_account_plugin_get_attribute (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account_name, - const gchar *key) + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GVariant *v; - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return FALSE; - - if (key == NULL) - { - GHashTableIter iter; - gpointer k, v; - - /* get everything */ - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "GetAllKeys", - g_variant_new_parsed ("(%o,)", account->path), NULL); - - g_hash_table_iter_init (&iter, account->attributes); - - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); + if (flags != NULL) + *flags = 0; - mcp_account_manager_set_value (am, account_name, k, escaped); - g_free (escaped); - } + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); - g_hash_table_iter_init (&iter, account->untyped_parameters); + v = g_hash_table_lookup (account->attributes, attribute); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *param_foo; - McpParameterFlags flags; - - param_foo = g_strdup_printf ("param-%s", (const gchar *) k); - mcp_account_manager_set_value (am, account_name, param_foo, v); + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "GetAttribute", + g_variant_new_parsed ("(%o, %s)", account->path, attribute), NULL); - flags = GPOINTER_TO_UINT (g_hash_table_lookup ( - account->parameter_flags, k)); + if (v != NULL) + { + return g_variant_ref (v); + } + else + { + return NULL; + } +} - if (flags & MCP_PARAMETER_FLAG_SECRET) - mcp_account_manager_parameter_make_secret (am, account_name, - param_foo); +static GVariant * +test_dbus_account_plugin_get_parameter (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account_name, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) +{ + TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); + Account *account = lookup_account (self, account_name); + GVariant *v; + const gchar *s; - g_free (param_foo); - } + if (flags != NULL) + *flags = 0; - g_hash_table_iter_init (&iter, account->parameters); + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *param_foo; - guint32 flags; - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "GetParameter", + g_variant_new_parsed ("(%o, %s)", account->path, parameter), NULL); - param_foo = g_strdup_printf ("param-%s", (const gchar *) k); - mcp_account_manager_set_value (am, account_name, param_foo, escaped); - g_free (escaped); + v = g_hash_table_lookup (account->parameters, parameter); + s = g_hash_table_lookup (account->untyped_parameters, parameter); - flags = GPOINTER_TO_UINT (g_hash_table_lookup (account->parameter_flags, - k)); + if (v != NULL) + { + return g_variant_ref (v); + } + else if (s != NULL && type != NULL) + { + return mcp_account_manager_unescape_variant_from_keyfile (am, + s, type, NULL); + } + else + { + return NULL; + } +} - if (flags & MCP_PARAMETER_FLAG_SECRET) - mcp_account_manager_parameter_make_secret (am, account_name, - param_foo); +static gchar ** +test_dbus_account_plugin_list_typed_parameters (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account_name) +{ + TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); + Account *account = lookup_account (self, account_name); + GPtrArray *arr; + GHashTableIter iter; + gpointer k; - g_free (param_foo); - } + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); - return TRUE; - } + arr = g_ptr_array_sized_new (g_hash_table_size (account->parameters) + 1); - /* get one parameter */ + g_hash_table_iter_init (&iter, account->parameters); - if (g_str_has_prefix (key, "param-")) - { - GVariant *v = g_hash_table_lookup (account->parameters, key + 6); - const gchar *s = g_hash_table_lookup (account->untyped_parameters, key + 6); - guint32 flags = GPOINTER_TO_UINT ( - g_hash_table_lookup (account->parameter_flags, key + 6)); + while (g_hash_table_iter_next (&iter, &k, NULL)) + g_ptr_array_add (arr, g_strdup (k)); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "GetParameter", - g_variant_new_parsed ("(%o, %s)", account->path, key + 6), NULL); + g_ptr_array_add (arr, NULL); - if (flags & MCP_PARAMETER_FLAG_SECRET) - mcp_account_manager_parameter_make_secret (am, account_name, key); + return (gchar **) g_ptr_array_free (arr, FALSE); +} - if (v != NULL) - { - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); +static gchar ** +test_dbus_account_plugin_list_untyped_parameters (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account_name) +{ + TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); + Account *account = lookup_account (self, account_name); + GPtrArray *arr; + GHashTableIter iter; + gpointer k; - mcp_account_manager_set_value (am, account_name, key, escaped); - g_free (escaped); - } - else if (s != NULL) - { - mcp_account_manager_set_value (am, account_name, key, s); - } - else - { - return FALSE; - } - } - else - { - GVariant *v = g_hash_table_lookup (account->attributes, key); + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "GetAttribute", - g_variant_new_parsed ("(%o, %s)", account->path, key), NULL); + arr = g_ptr_array_sized_new ( + g_hash_table_size (account->untyped_parameters) + 1); - if (v != NULL) - { - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); + g_hash_table_iter_init (&iter, account->untyped_parameters); - mcp_account_manager_set_value (am, account_name, key, escaped); - g_free (escaped); - } - else - { - return FALSE; - } - } + while (g_hash_table_iter_next (&iter, &k, NULL)) + g_ptr_array_add (arr, g_strdup (k)); - return TRUE; -} + g_ptr_array_add (arr, NULL); -static gboolean -test_dbus_account_plugin_set (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account_name, - const gchar *key, - const gchar *value) -{ - /* Now that we implement set_attribute and set_parameter, this no longer - * needs a real implementation. */ - return FALSE; + return (gchar **) g_ptr_array_free (arr, FALSE); } -static gboolean +static McpAccountStorageSetResult test_dbus_account_plugin_set_attribute (McpAccountStorage *storage, McpAccountManager *am, const gchar *account_name, @@ -1119,17 +960,40 @@ test_dbus_account_plugin_set_attribute (McpAccountStorage *storage, { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GVariant *old; + guint old_flags; g_return_val_if_fail (account_name != NULL, FALSE); g_return_val_if_fail (attribute != NULL, FALSE); - /* for deletions, MC would call delete() instead */ - g_return_val_if_fail (value != NULL, FALSE); DEBUG ("%s of %s", attribute, account_name); - if (!self->active || account == NULL || - (account->flags & UNCOMMITTED_DELETION)) - return FALSE; + g_return_val_if_fail (self->active, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (account != NULL, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + + if (value == NULL) + { + if (!g_hash_table_contains (account->attributes, attribute)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + + g_hash_table_remove (account->attributes, attribute); + g_hash_table_remove (account->attribute_flags, attribute); + g_hash_table_add (account->uncommitted_attributes, g_strdup (attribute)); + + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "DeferringDeleteAttribute", + g_variant_new_parsed ("(%o, %s)", account->path, attribute), NULL); + + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; + } + + old = g_hash_table_lookup (account->attributes, attribute); + old_flags = GPOINTER_TO_UINT (g_hash_table_lookup ( + account->attribute_flags, attribute)); + + if (old != NULL && g_variant_equal (old, value) && old_flags == flags) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; g_hash_table_insert (account->attributes, g_strdup (attribute), g_variant_ref (value)); @@ -1143,10 +1007,10 @@ test_dbus_account_plugin_set_attribute (McpAccountStorage *storage, g_variant_new_parsed ("(%o, %s, %v)", account->path, attribute, value), NULL); - return TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } -static gboolean +static McpAccountStorageSetResult test_dbus_account_plugin_set_parameter (McpAccountStorage *storage, McpAccountManager *am, const gchar *account_name, @@ -1156,17 +1020,42 @@ test_dbus_account_plugin_set_parameter (McpAccountStorage *storage, { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GVariant *old; + guint old_flags; g_return_val_if_fail (account_name != NULL, FALSE); g_return_val_if_fail (parameter != NULL, FALSE); - /* for deletions, MC would call delete() instead */ - g_return_val_if_fail (value != NULL, FALSE); DEBUG ("%s of %s", parameter, account_name); - if (!self->active || account == NULL || - (account->flags & UNCOMMITTED_DELETION)) - return FALSE; + g_return_val_if_fail (self->active, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (account != NULL, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + + if (value == NULL) + { + if (!g_hash_table_contains (account->parameters, parameter) && + !g_hash_table_contains (account->untyped_parameters, parameter)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + + g_hash_table_remove (account->parameters, parameter); + g_hash_table_remove (account->untyped_parameters, parameter); + g_hash_table_remove (account->parameter_flags, parameter); + g_hash_table_add (account->uncommitted_parameters, g_strdup (parameter)); + + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "DeferringDeleteParameter", + g_variant_new_parsed ("(%o, %s)", account->path, parameter), NULL); + + return TRUE; + } + + old = g_hash_table_lookup (account->parameters, parameter); + old_flags = GPOINTER_TO_UINT (g_hash_table_lookup ( + account->parameter_flags, parameter)); + + if (old != NULL && g_variant_equal (old, value) && old_flags == flags) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; g_hash_table_remove (account->untyped_parameters, parameter); g_hash_table_insert (account->parameters, g_strdup (parameter), @@ -1181,37 +1070,7 @@ test_dbus_account_plugin_set_parameter (McpAccountStorage *storage, g_variant_new_parsed ("(%o, %s, %v)", account->path, parameter, value), NULL); - return TRUE; -} - -static gboolean -test_dbus_account_plugin_commit (const McpAccountStorage *storage, - const McpAccountManager *am) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - GHashTableIter iter; - gpointer k; - - DEBUG ("called"); - - if (!self->active) - return FALSE; - - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "CommittingAll", NULL, NULL); - - g_hash_table_iter_init (&iter, self->accounts); - - while (g_hash_table_iter_next (&iter, &k, NULL)) - { - if (!mcp_account_storage_commit_one (storage, am, k)) - { - g_warning ("declined to commit account %s", (const gchar *) k); - } - } - - return TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } static void @@ -1219,27 +1078,31 @@ delete_account_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { - AsyncData *ad = user_data; + GTask *task = user_data; GVariant *tuple; GError *error = NULL; + TestDBusAccountPlugin *self = g_task_get_source_object (task); + const gchar *account_name = g_task_get_task_data (task); - tuple = g_dbus_connection_call_finish (ad->self->bus, res, &error); + tuple = g_dbus_connection_call_finish (self->bus, res, &error); if (tuple != NULL) { - g_hash_table_remove (ad->self->accounts, ad->account_name); + /* we'll emit ::deleted when we see the signal, which probably + * already happened */ + g_hash_table_remove (self->accounts, account_name); g_variant_unref (tuple); + g_task_return_boolean (task, TRUE); } else { - g_warning ("Unable to delete account %s: %s", ad->account_name, - error->message); - g_clear_error (&error); - /* FIXME: we could roll back the deletion by claiming that - * the service re-created the account? */ + g_prefix_error (&error, "Unable to delete account %s: ", + account_name); + g_warning ("%s", error->message); + g_task_return_error (task, error); } - async_data_free (ad); + g_object_unref (task); } static void @@ -1345,12 +1208,12 @@ update_parameters_cb (GObject *source_object, } static gboolean -test_dbus_account_plugin_commit_one (const McpAccountStorage *storage, - const McpAccountManager *am, +test_dbus_account_plugin_commit (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account_name) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - Account *account = lookup_account (self, account_name); + Account *account; GHashTableIter iter; gpointer k; GVariantBuilder a_sv_builder; @@ -1360,36 +1223,15 @@ test_dbus_account_plugin_commit_one (const McpAccountStorage *storage, DEBUG ("%s", account_name); - /* MC does not call @commit_one with parameter %NULL (meaning "all accounts") - * if we also implement commit(), which, as it happens, we do */ - g_return_val_if_fail (account_name != NULL, FALSE); + account = lookup_account (self, account_name); - if (!self->active || account == NULL) - return FALSE; + g_return_val_if_fail (self->active, FALSE); + g_return_val_if_fail (account != NULL, FALSE); g_dbus_connection_emit_signal (self->bus, NULL, TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, "CommittingOne", g_variant_new_parsed ("(%o,)", account->path), NULL); - if (account->flags & UNCOMMITTED_DELETION) - { - g_dbus_connection_call (self->bus, - TEST_DBUS_ACCOUNT_SERVICE, - TEST_DBUS_ACCOUNT_SERVICE_PATH, - TEST_DBUS_ACCOUNT_SERVICE_IFACE, - "DeleteAccount", - g_variant_new_parsed ("(%s,)", account_name), - G_VARIANT_TYPE_UNIT, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, /* no cancellable */ - delete_account_cb, - async_data_new (self, account_name)); - - /* this doesn't mean we succeeded: it means we tried */ - return TRUE; - } - if (account->flags & UNCOMMITTED_CREATION) { g_dbus_connection_call (self->bus, @@ -1516,7 +1358,7 @@ test_dbus_account_plugin_commit_one (const McpAccountStorage *storage, } static void -test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, +test_dbus_account_plugin_get_identifier (McpAccountStorage *storage, const gchar *account_name, GValue *identifier) { @@ -1525,8 +1367,8 @@ test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return; + g_return_if_fail (self->active); + g_return_if_fail (account != NULL); /* Our "library-specific unique identifier" is just the object-path * as a string. */ @@ -1535,7 +1377,7 @@ test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, } static GHashTable * -test_dbus_account_plugin_get_additional_info (const McpAccountStorage *storage, +test_dbus_account_plugin_get_additional_info (McpAccountStorage *storage, const gchar *account_name) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); @@ -1544,8 +1386,8 @@ test_dbus_account_plugin_get_additional_info (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return NULL; + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); ret = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); @@ -1556,7 +1398,7 @@ test_dbus_account_plugin_get_additional_info (const McpAccountStorage *storage, } static guint -test_dbus_account_plugin_get_restrictions (const McpAccountStorage *storage, +test_dbus_account_plugin_get_restrictions (McpAccountStorage *storage, const gchar *account_name) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); @@ -1564,27 +1406,17 @@ test_dbus_account_plugin_get_restrictions (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return 0; + g_return_val_if_fail (self->active, 0); + g_return_val_if_fail (account != NULL, 0); - /* FIXME: actually enforce this restriction */ - return TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_SERVICE; + return account->restrictions; } -static gboolean -test_dbus_account_plugin_owns (McpAccountStorage *storage, - McpAccountManager *am, - const gchar *account_name) +static McpAccountStorageFlags +test_dbus_account_plugin_get_flags (McpAccountStorage *storage, + const gchar *account) { - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - Account *account = lookup_account (self, account_name); - - DEBUG ("%s", account_name); - - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return FALSE; - - return TRUE; + return MCP_ACCOUNT_STORAGE_FLAG_STORES_TYPES; } static void @@ -1595,18 +1427,21 @@ account_storage_iface_init (McpAccountStorageIface *iface) /* this should be higher priority than the diverted-keyfile one */ iface->priority = MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_NORMAL + 100; - iface->get = test_dbus_account_plugin_get; - iface->set = test_dbus_account_plugin_set; + iface->get_flags = test_dbus_account_plugin_get_flags; + iface->get_attribute = test_dbus_account_plugin_get_attribute; + iface->get_parameter = test_dbus_account_plugin_get_parameter; + iface->list_typed_parameters = + test_dbus_account_plugin_list_typed_parameters; + iface->list_untyped_parameters = + test_dbus_account_plugin_list_untyped_parameters; iface->set_attribute = test_dbus_account_plugin_set_attribute; iface->set_parameter = test_dbus_account_plugin_set_parameter; iface->list = test_dbus_account_plugin_list; - iface->ready = test_dbus_account_plugin_ready; - iface->delete = test_dbus_account_plugin_delete; + iface->delete_async = test_dbus_account_plugin_delete_async; + iface->delete_finish = test_dbus_account_plugin_delete_finish; iface->commit = test_dbus_account_plugin_commit; - iface->commit_one = test_dbus_account_plugin_commit_one; iface->get_identifier = test_dbus_account_plugin_get_identifier; iface->get_additional_info = test_dbus_account_plugin_get_additional_info; iface->get_restrictions = test_dbus_account_plugin_get_restrictions; iface->create = test_dbus_account_plugin_create; - iface->owns = test_dbus_account_plugin_owns; } diff --git a/tests/twisted/dispatcher/already-has-channel.py b/tests/twisted/dispatcher/already-has-channel.py index 547acdf2..6b666e12 100644 --- a/tests/twisted/dispatcher/already-has-channel.py +++ b/tests/twisted/dispatcher/already-has-channel.py @@ -33,7 +33,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) text_fixed_properties = dbus.Dictionary({ cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, @@ -68,25 +68,19 @@ def test(q, bus, mc): interface=cs.tp_name_prefix + '.ConnectionManager', handled=False) - # Don't allow the Connection to become ready until we want it to, by - # avoiding a return from GetInterfaces + # Don't allow the Connection to have its list of channels + # until we want it to, by avoiding a return from GetAll(Requests). conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', - 'myself', implement_get_interfaces=False) - + 'myself', implement_get_channels=False) q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') - # this is the pre-Connect one - e = q.expect('dbus-method-call', method='GetInterfaces', - path=conn.object_path, handled=False) - q.dbus_raise(e.message, cs.DISCONNECTED, 'Not connected yet') + get_all_requests_call = q.expect('dbus-method-call', + method='GetAll', args=[cs.CONN_IFACE_REQUESTS]) q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) - - get_interfaces_call = q.expect('dbus-method-call', method='GetInterfaces', - path=conn.object_path, handled=False) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) # subscribe to the OperationList interface (MC assumes that until this # property has been retrieved once, nobody cares) @@ -95,7 +89,7 @@ def test(q, bus, mc): cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE) assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == [] - # Before returning from GetInterfaces, make a Channel spring into + # Before returning from GetAll(Requests), make a Channel spring into # existence channel_properties = dbus.Dictionary(text_fixed_properties, @@ -112,8 +106,8 @@ def test(q, bus, mc): chan = SimulatedChannel(conn, channel_properties) chan.announce() - # Now reply to GetInterfaces and say we have Requests - conn.GetInterfaces(get_interfaces_call) + # Now reply to GetAll(Requests) + conn.GetAll_Requests(get_all_requests_call) # A channel dispatch operation is created for the channel we already had diff --git a/tests/twisted/dispatcher/already-has-obsolete.py b/tests/twisted/dispatcher/already-has-obsolete.py deleted file mode 100644 index 05dcb115..00000000 --- a/tests/twisted/dispatcher/already-has-obsolete.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright (C) 2009 Nokia Corporation -# Copyright (C) 2009 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 - -import dbus -"""Regression test for dispatching an incoming Text channel that was already -there before the Connection became ready. -""" - -import dbus -import dbus.service - -from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ - call_async -from mctest import exec_test, SimulatedConnection, SimulatedClient, \ - create_fakecm_account, SimulatedChannel, expect_client_setup -import constants as cs - -def test(q, bus, mc): - params = dbus.Dictionary({"account": "someguy@example.com", - "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) - - text_fixed_properties = dbus.Dictionary({ - cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, - cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT, - }, signature='sv') - - # Two clients want to observe, approve and handle channels - empathy = SimulatedClient(q, bus, 'Empathy', - observe=[text_fixed_properties], approve=[text_fixed_properties], - handle=[text_fixed_properties], bypass_approval=False) - kopete = SimulatedClient(q, bus, 'Kopete', - observe=[text_fixed_properties], approve=[text_fixed_properties], - handle=[text_fixed_properties], bypass_approval=False) - - # wait for MC to download the properties - expect_client_setup(q, [empathy, kopete]) - - # Enable the account - account.Set(cs.ACCOUNT, 'Enabled', True, - dbus_interface=cs.PROPERTIES_IFACE) - - requested_presence = dbus.Struct((dbus.UInt32(2L), - dbus.String(u'available'), dbus.String(u''))) - account.Set(cs.ACCOUNT, - 'RequestedPresence', requested_presence, - dbus_interface=cs.PROPERTIES_IFACE) - - e = q.expect('dbus-method-call', method='RequestConnection', - args=['fakeprotocol', params], - destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', - path=cs.tp_path_prefix + '/ConnectionManager/fakecm', - interface=cs.tp_name_prefix + '.ConnectionManager', - handled=False) - - # Don't allow the Connection to become ready until we want it to, by - # avoiding a return from GetInterfaces - conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', - 'myself', implement_get_interfaces=False, has_requests=False) - - q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') - - # this is the pre-Connect one - e = q.expect('dbus-method-call', method='GetInterfaces', - path=conn.object_path, handled=False) - q.dbus_raise(e.message, cs.DISCONNECTED, 'Not connected yet') - - q.expect('dbus-method-call', method='Connect', - path=conn.object_path, handled=True) - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) - - get_interfaces_call = q.expect('dbus-method-call', method='GetInterfaces', - path=conn.object_path, handled=False) - - # subscribe to the OperationList interface (MC assumes that until this - # property has been retrieved once, nobody cares) - - cd = bus.get_object(cs.CD, cs.CD_PATH) - cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE) - assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == [] - - # Before returning from GetInterfaces, make a Channel spring into - # existence - - channel_properties = dbus.Dictionary(text_fixed_properties, - signature='sv') - channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet' - channel_properties[cs.CHANNEL + '.TargetHandle'] = \ - conn.ensure_handle(cs.HT_CONTACT, 'juliet') - channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet' - channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \ - conn.ensure_handle(cs.HT_CONTACT, 'juliet') - channel_properties[cs.CHANNEL + '.Requested'] = False - channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s') - - chan = SimulatedChannel(conn, channel_properties) - chan.announce() - - # Now reply to GetInterfaces and say we don't have Requests - conn.GetInterfaces(get_interfaces_call) - q.expect('dbus-method-call', - interface=cs.CONN, method='ListChannels', args=[], - path=conn.object_path, handled=True) - - # A channel dispatch operation is created for the channel we already had - - e = q.expect('dbus-signal', - path=cs.CD_PATH, - interface=cs.CD_IFACE_OP_LIST, - signal='NewDispatchOperation') - - cdo_path = e.args[0] - cdo_properties = e.args[1] - - assert cdo_properties[cs.CDO + '.Account'] == account.object_path - assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path - - handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:] - handlers.sort() - assert handlers == [cs.tp_name_prefix + '.Client.Empathy', - cs.tp_name_prefix + '.Client.Kopete'], handlers - - assert cs.CD_IFACE_OP_LIST in cd_props.Get(cs.CD, 'Interfaces') - assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') ==\ - [(cdo_path, cdo_properties)] - - cdo = bus.get_object(cs.CD, cdo_path) - cdo_iface = dbus.Interface(cdo, cs.CDO) - - # Both Observers are told about the new channel - - e, k = q.expect_many( - EventPattern('dbus-method-call', - path=empathy.object_path, - interface=cs.OBSERVER, method='ObserveChannels', - handled=False), - EventPattern('dbus-method-call', - path=kopete.object_path, - interface=cs.OBSERVER, method='ObserveChannels', - handled=False), - ) - assert e.args[0] == account.object_path, e.args - assert e.args[1] == conn.object_path, e.args - assert e.args[3] == cdo_path, e.args - assert e.args[4] == [], e.args # no requests satisfied - channels = e.args[2] - assert len(channels) == 1, channels - assert channels[0][0] == chan.object_path, channels - - assert k.args == e.args - - # Both Observers indicate that they are ready to proceed - q.dbus_return(k.message, signature='') - q.dbus_return(e.message, signature='') - - # The Approvers are next - - e, k = q.expect_many( - EventPattern('dbus-method-call', - path=empathy.object_path, - interface=cs.APPROVER, method='AddDispatchOperation', - handled=False), - EventPattern('dbus-method-call', - path=kopete.object_path, - interface=cs.APPROVER, method='AddDispatchOperation', - handled=False), - ) - - assert len(e.args) == 3 - assert len(e.args[0]) == 1 - assert e.args[0][0][0] == chan.object_path - assert e.args[1] == cdo_path - assert e.args[2] == cdo_properties - assert k.args == e.args - - q.dbus_return(e.message, signature='') - q.dbus_return(k.message, signature='') - - # Both Approvers now have a flashing icon or something, trying to get the - # user's attention - - # The user responds to Empathy first - call_async(q, cdo_iface, 'HandleWith', - cs.tp_name_prefix + '.Client.Empathy') - - # Empathy is asked to handle the channels - e = q.expect('dbus-method-call', - path=empathy.object_path, - interface=cs.HANDLER, method='HandleChannels', - handled=False) - - # Empathy accepts the channels - q.dbus_return(e.message, signature='') - - q.expect_many( - EventPattern('dbus-return', method='HandleWith'), - EventPattern('dbus-signal', interface=cs.CDO, signal='Finished'), - EventPattern('dbus-signal', interface=cs.CD_IFACE_OP_LIST, - signal='DispatchOperationFinished'), - ) - - # Now there are no more active channel dispatch operations - assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == [] - -if __name__ == '__main__': - exec_test(test, {}) diff --git a/tests/twisted/dispatcher/approver-fails.py b/tests/twisted/dispatcher/approver-fails.py index 67ce36a3..b8705bb7 100644 --- a/tests/twisted/dispatcher/approver-fails.py +++ b/tests/twisted/dispatcher/approver-fails.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/bypass-approval.py b/tests/twisted/dispatcher/bypass-approval.py index 0655a9a9..eddb3155 100644 --- a/tests/twisted/dispatcher/bypass-approval.py +++ b/tests/twisted/dispatcher/bypass-approval.py @@ -181,7 +181,7 @@ def expect_and_exercise_approval(q, bus, chan, channel_properties, def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) # Two clients want to observe, approve and handle channels. Additionally, diff --git a/tests/twisted/dispatcher/bypass-observers.py b/tests/twisted/dispatcher/bypass-observers.py deleted file mode 100644 index d8b02660..00000000 --- a/tests/twisted/dispatcher/bypass-observers.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (C) 2009-2010 Nokia Corporation -# Copyright (C) 2009-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 - -import dbus -"""Regression test for dispatching an incoming Text channel with bypassed -observers. -""" - -import dbus -import dbus.service - -from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ - call_async, sync_dbus, assertEquals, assertLength, assertContains -from mctest import exec_test, SimulatedConnection, SimulatedClient, \ - create_fakecm_account, enable_fakecm_account, SimulatedChannel, \ - expect_client_setup -import constants as cs - -text_fixed_properties = dbus.Dictionary({ - cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT, - }, signature='sv') -contact_text_fixed_properties = dbus.Dictionary({ - cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, - cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT, - }, signature='sv') -secret_fixed_properties = dbus.Dictionary({ - cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, - cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT, - 'com.example.Secrecy.Secret': True, - }, signature='sv') - -def announce_common(q, bus, empathy, kopete, account, conn, cd_props, - secret=False): - if secret: - jid = 'friar.lawrence' - else: - jid = 'juliet' - - channel_properties = dbus.Dictionary(contact_text_fixed_properties, - signature='sv') - channel_properties[cs.CHANNEL + '.TargetID'] = jid - channel_properties[cs.CHANNEL + '.TargetHandle'] = \ - conn.ensure_handle(cs.HT_CONTACT, jid) - channel_properties[cs.CHANNEL + '.InitiatorID'] = jid - channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \ - conn.ensure_handle(cs.HT_CONTACT, jid) - channel_properties[cs.CHANNEL + '.Requested'] = False - channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s') - - if secret: - channel_properties['com.example.Secrecy.Secret'] = True - - chan = SimulatedChannel(conn, channel_properties) - chan.announce() - - # A channel dispatch operation is created - - e = q.expect('dbus-signal', - path=cs.CD_PATH, - interface=cs.CD_IFACE_OP_LIST, - signal='NewDispatchOperation') - - cdo_path = e.args[0] - cdo_properties = e.args[1] - - assertEquals(cdo_properties[cs.CDO + '.Account'], account.object_path) - assertEquals(cdo_properties[cs.CDO + '.Connection'], conn.object_path) - assertContains(cs.CDO + '.Interfaces', cdo_properties) - - handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:] - - if secret: - # The handler with BypassApproval is first - assertEquals(cs.tp_name_prefix + '.Client.Kopete.Bypasser', - handlers[0]) - else: - handlers.sort() - assertEquals([cs.tp_name_prefix + '.Client.Empathy', - cs.tp_name_prefix + '.Client.Kopete'], handlers) - - assertContains(cs.CD_IFACE_OP_LIST, cd_props.Get(cs.CD, 'Interfaces')) - - assertEquals([(cdo_path, cdo_properties)], - cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations')) - - cdo = bus.get_object(cs.CD, cdo_path) - cdo_iface = dbus.Interface(cdo, cs.CDO) - - # Both Observers are told about the new channel - - if secret: - observe_events = [] - else: - e, k = q.expect_many( - EventPattern('dbus-method-call', - path=empathy.object_path, - interface=cs.OBSERVER, method='ObserveChannels', - handled=False), - EventPattern('dbus-method-call', - path=kopete.object_path, - interface=cs.OBSERVER, method='ObserveChannels', - handled=False), - ) - assertEquals(account.object_path, e.args[0]) - assertEquals(conn.object_path, e.args[1]) - assertEquals(cdo_path, e.args[3]) - assertEquals([], e.args[4]) # no requests satisfied - channels = e.args[2] - assertLength(1, channels) - assertEquals(chan.object_path, channels[0][0]) - assertEquals(channel_properties, channels[0][1]) - - assertEquals(k.args, e.args) - observe_events = [e, k] - - return cdo_iface, chan, channel_properties, observe_events - -def expect_and_exercise_approval(q, bus, chan, channel_properties, - empathy, kopete, cdo_iface, cd_props): - # The Approvers are next - - e, k = q.expect_many( - EventPattern('dbus-method-call', - path=empathy.object_path, - interface=cs.APPROVER, method='AddDispatchOperation', - handled=False), - EventPattern('dbus-method-call', - path=kopete.object_path, - interface=cs.APPROVER, method='AddDispatchOperation', - handled=False), - ) - - assertEquals([(chan.object_path, channel_properties)], e.args[0]) - assertEquals(k.args, e.args) - - # Both Approvers indicate that they are ready to proceed - q.dbus_return(e.message, signature='') - q.dbus_return(k.message, signature='') - - # Both Approvers now have a flashing icon or something, trying to get the - # user's attention - - # The user responds to Kopete first - call_async(q, cdo_iface, 'HandleWith', - cs.tp_name_prefix + '.Client.Kopete') - - # Kopete is asked to handle the channels - e = q.expect('dbus-method-call', - path=kopete.object_path, - interface=cs.HANDLER, method='HandleChannels', - handled=False) - - # Kopete accepts the channels - q.dbus_return(e.message, signature='') - - q.expect_many( - EventPattern('dbus-return', method='HandleWith'), - EventPattern('dbus-signal', interface=cs.CDO, signal='Finished'), - EventPattern('dbus-signal', interface=cs.CD_IFACE_OP_LIST, - signal='DispatchOperationFinished'), - ) - - # Now there are no more active channel dispatch operations - assertEquals([], cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations')) - - -def test(q, bus, mc): - params = dbus.Dictionary({"account": "someguy@example.com", - "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) - conn = enable_fakecm_account(q, bus, mc, account, params) - - # Two clients want to observe, approve and handle channels. Additionally, - # Kopete recognises a "Secret" flag on certain incoming channels, and - # wants to bypass approval and observers for them. Also, Empathy is a - # respawnable observer, which wants to get notified of existing channels - # if it gets restarted. - empathy = SimulatedClient(q, bus, 'Empathy', - observe=[text_fixed_properties], approve=[text_fixed_properties], - handle=[text_fixed_properties], bypass_approval=False, - wants_recovery=True) - kopete = SimulatedClient(q, bus, 'Kopete', - observe=[contact_text_fixed_properties], - approve=[contact_text_fixed_properties], - handle=[contact_text_fixed_properties], bypass_approval=False) - bypass = SimulatedClient(q, bus, 'Kopete.Bypasser', - observe=[], approve=[], - handle=[secret_fixed_properties], - bypass_approval=True, bypass_observers=True) - - # wait for MC to download the properties - expect_client_setup(q, [empathy, kopete, bypass]) - - # subscribe to the OperationList interface (MC assumes that until this - # property has been retrieved once, nobody cares) - - cd = bus.get_object(cs.CD, cs.CD_PATH) - cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE) - assertEquals([], cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations')) - - # First, a non-secret channel is created - - cdo_iface, chan, channel_properties, observe_events = announce_common(q, - bus, empathy, kopete, account, conn, cd_props, False) - - # Both Observers indicate that they are ready to proceed - for e in observe_events: - q.dbus_return(e.message, signature='') - - expect_and_exercise_approval(q, bus, chan, channel_properties, - empathy, kopete, cdo_iface, cd_props) - - nonsecret_chan = chan - - # Now a channel that bypasses approval and observers comes in. - # During this process, we should never be asked to approve or - # observe anything. - - approval = [ - EventPattern('dbus-method-call', method='AddDispatchOperation'), - ] - - q.forbid_events(approval) - - cdo_iface, chan, channel_properties, observe_events = announce_common(q, - bus, empathy, kopete, account, conn, cd_props, True) - - # Both Observers indicate that they are ready to proceed - for e in observe_events: - q.dbus_return(e.message, signature='') - - # Kopete's BypassApproval part is asked to handle the channels - e = q.expect('dbus-method-call', - path=bypass.object_path, - interface=cs.HANDLER, method='HandleChannels', - handled=False) - # Kopete accepts the channels - q.dbus_return(e.message, signature='') - - q.unforbid_events(approval) - - # Empathy, the observer, crashes - empathy.release_name() - - e = q.expect('dbus-signal', - signal='NameOwnerChanged', - predicate=(lambda e: - e.args[0] == empathy.bus_name and e.args[2] == ''), - ) - empathy_unique_name = e.args[1] - - bus.flush() - - # Empathy gets restarted - empathy.reacquire_name() - - e = q.expect('dbus-signal', - signal='NameOwnerChanged', - predicate=(lambda e: - e.args[0] == empathy.bus_name and e.args[1] == ''), - ) - empathy_unique_name = e.args[2] - - # Empathy is told to observe only the non-secret channel - e = q.expect('dbus-method-call', - path=empathy.object_path, - interface=cs.OBSERVER, method='ObserveChannels', - handled=False) - - channels = e.args[2] - assertLength(1, channels) - assertEquals(nonsecret_chan.object_path, channels[0][0]) - -if __name__ == '__main__': - exec_test(test, {}) - diff --git a/tests/twisted/dispatcher/cancel.py b/tests/twisted/dispatcher/cancel.py index 5b8d90af..fe23f005 100644 --- a/tests/twisted/dispatcher/cancel.py +++ b/tests/twisted/dispatcher/cancel.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/capture-bundle.py b/tests/twisted/dispatcher/capture-bundle.py index de773844..44e18e87 100644 --- a/tests/twisted/dispatcher/capture-bundle.py +++ b/tests/twisted/dispatcher/capture-bundle.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ @@ -43,7 +43,7 @@ def test(q, bus, mc): }, signature='sv') voip_fixed_properties = dbus.Dictionary({ cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, - cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_STREAMED_MEDIA, + cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_CALL, }, signature='sv') # Two clients want to observe, approve and handle text and VoIP channels. diff --git a/tests/twisted/dispatcher/cdo-claim.py b/tests/twisted/dispatcher/cdo-claim.py index ec98b93f..89474f9b 100644 --- a/tests/twisted/dispatcher/cdo-claim.py +++ b/tests/twisted/dispatcher/cdo-claim.py @@ -30,7 +30,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/connect-for-request.py b/tests/twisted/dispatcher/connect-for-request.py index 1be141fe..034cd054 100644 --- a/tests/twisted/dispatcher/connect-for-request.py +++ b/tests/twisted/dispatcher/connect-for-request.py @@ -29,7 +29,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "smcv@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) @@ -38,11 +38,11 @@ def test(q, bus, mc): # automatically call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '')) + (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '')) q.expect('dbus-return', method='Set') call_async(q, account_props, 'Set', cs.ACCOUNT, 'AutomaticPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Testing automatic presence')) q.expect('dbus-return', method='Set') q.expect('dbus-signal', signal='AccountPropertyChanged', @@ -102,14 +102,14 @@ def test(q, bus, mc): handled=False) conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', - 'myself', has_requests=True, has_presence=True) + 'myself', has_presence=True) q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) - conn.presence = dbus.Struct((cs.PRESENCE_TYPE_AVAILABLE, 'available', ''), + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) + conn.presence = dbus.Struct((cs.PRESENCE_AVAILABLE, 'available', ''), signature='uss') _, cm_request_call = q.expect_many( @@ -123,7 +123,7 @@ def test(q, bus, mc): q.dbus_emit(conn.object_path, cs.CONN_IFACE_SIMPLE_PRESENCE, 'PresencesChanged', - {conn.self_handle: (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + {conn.self_handle: (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Testing automatic presence')}, signature='a{u(uss)}') diff --git a/tests/twisted/dispatcher/create-at-startup.py b/tests/twisted/dispatcher/create-at-startup.py index e0d73722..fb515723 100644 --- a/tests/twisted/dispatcher/create-at-startup.py +++ b/tests/twisted/dispatcher/create-at-startup.py @@ -28,7 +28,7 @@ import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ call_async from mctest import exec_test, SimulatedConnection, SimulatedClient, \ - create_fakecm_account, enable_fakecm_account, SimulatedChannel, \ + SimulatedConnectionManager, SimulatedChannel, \ expect_client_setup, MC import constants as cs @@ -76,8 +76,7 @@ def test(q, bus, unused, **kwargs): observe=[text_fixed_properties], approve=[text_fixed_properties], handle=[text_fixed_properties], bypass_approval=False, implement_get_interfaces=False) - cm_name_ref = dbus.service.BusName( - cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) + simulated_cm = SimulatedConnectionManager(q, bus) # service-activate MC; it will try to introspect the running client. mc = MC(q, bus, wait_for_names=False) @@ -133,7 +132,7 @@ def test(q, bus, unused, **kwargs): q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) # A channel appears spontaneously diff --git a/tests/twisted/dispatcher/create-delayed-by-mini-plugin.py b/tests/twisted/dispatcher/create-delayed-by-mini-plugin.py index 8c2925c1..fcf021b9 100644 --- a/tests/twisted/dispatcher/create-delayed-by-mini-plugin.py +++ b/tests/twisted/dispatcher/create-delayed-by-mini-plugin.py @@ -41,7 +41,7 @@ def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn, e = enable_fakecm_account(q, bus, mc, account, params, extra_interfaces=[cs.CONN_IFACE_SERVICE_POINT], expect_after_connect=[ @@ -55,13 +55,6 @@ def test(q, bus, mc): q.dbus_return(e.message, points, signature='v') - # MC looks up the handles for these numbers - patterns = [EventPattern('dbus-method-call', path=conn.object_path, - interface=cs.CONN, method='RequestHandles', - args=[cs.HT_CONTACT, [num]], - handled=True) for num in e_numbers] - q.expect_many(*patterns) - # the service points change e_numbers = ['911', '112', '999'] points = dbus.Array([((cs.SERVICE_POINT_TYPE_EMERGENCY, 'urn:service:sos'), @@ -69,13 +62,6 @@ def test(q, bus, mc): q.dbus_emit(conn.object_path, cs.CONN_IFACE_SERVICE_POINT, 'ServicePointsChanged', points, signature='a((us)as)') - # MC looks up the new handles - patterns = [EventPattern('dbus-method-call', path=conn.object_path, - interface=cs.CONN, method='RequestHandles', - args=[cs.HT_CONTACT, [num]], - handled=True) for num in e_numbers] - q.expect_many(*patterns) - # MC used to critical if more than one emergency service point was # given by the CM. That's silly, so let's test it. e_numbers1 = ['911'] @@ -89,12 +75,6 @@ def test(q, bus, mc): e_numbers = e_numbers1 + e_numbers2 - patterns = [EventPattern('dbus-method-call', path=conn.object_path, - interface=cs.CONN, method='RequestHandles', - args=[cs.HT_CONTACT, [num]], - handled=True) for num in e_numbers] - q.expect_many(*patterns) - fixed_properties = dbus.Dictionary({ cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, cs.CHANNEL + '.ChannelType': DELAYED_CTYPE, diff --git a/tests/twisted/dispatcher/create-handler-fails.py b/tests/twisted/dispatcher/create-handler-fails.py index ccdae2f9..13523636 100644 --- a/tests/twisted/dispatcher/create-handler-fails.py +++ b/tests/twisted/dispatcher/create-handler-fails.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/create-hints.py b/tests/twisted/dispatcher/create-hints.py index daf8cdcd..3b8cf94b 100644 --- a/tests/twisted/dispatcher/create-hints.py +++ b/tests/twisted/dispatcher/create-hints.py @@ -33,7 +33,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/create-no-preferred-handler.py b/tests/twisted/dispatcher/create-no-preferred-handler.py index 3e078415..11ac44b5 100644 --- a/tests/twisted/dispatcher/create-no-preferred-handler.py +++ b/tests/twisted/dispatcher/create-no-preferred-handler.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/create-rejected-by-mini-plugin.py b/tests/twisted/dispatcher/create-rejected-by-mini-plugin.py index 2f517589..ecd5f6e6 100644 --- a/tests/twisted/dispatcher/create-rejected-by-mini-plugin.py +++ b/tests/twisted/dispatcher/create-rejected-by-mini-plugin.py @@ -47,7 +47,7 @@ def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/create-text.py b/tests/twisted/dispatcher/create-text.py index 5a0efe1a..a0897ba4 100644 --- a/tests/twisted/dispatcher/create-text.py +++ b/tests/twisted/dispatcher/create-text.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ @@ -71,7 +71,7 @@ def test(q, bus, mc): test_channel_creation(q, bus, account, client, conn, True) test_channel_creation(q, bus, account, client, conn, False, unsuitable) test_channel_creation(q, bus, account, client, conn, False, unsuitable, - cs.CHANNEL_TYPE_STREAMED_MEDIA) + cs.CHANNEL_TYPE_CALL) def test_channel_creation(q, bus, account, client, conn, ensure=False, prefer=None, channel_type=cs.CHANNEL_TYPE_TEXT): diff --git a/tests/twisted/dispatcher/created-behind-our-back.py b/tests/twisted/dispatcher/created-behind-our-back.py index 55d1b4b0..d87e69ac 100644 --- a/tests/twisted/dispatcher/created-behind-our-back.py +++ b/tests/twisted/dispatcher/created-behind-our-back.py @@ -39,7 +39,7 @@ def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/delay-approvers.py b/tests/twisted/dispatcher/delay-approvers.py index a627159d..2a7295f8 100644 --- a/tests/twisted/dispatcher/delay-approvers.py +++ b/tests/twisted/dispatcher/delay-approvers.py @@ -29,7 +29,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/delay-then-call-handle-with.py b/tests/twisted/dispatcher/delay-then-call-handle-with.py index 2f79b10e..28fdfb3e 100644 --- a/tests/twisted/dispatcher/delay-then-call-handle-with.py +++ b/tests/twisted/dispatcher/delay-then-call-handle-with.py @@ -29,7 +29,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/delay-then-dont-call-approvers.py b/tests/twisted/dispatcher/delay-then-dont-call-approvers.py index 90040228..25ebd908 100644 --- a/tests/twisted/dispatcher/delay-then-dont-call-approvers.py +++ b/tests/twisted/dispatcher/delay-then-dont-call-approvers.py @@ -29,7 +29,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/dispatch-activatable.py b/tests/twisted/dispatcher/dispatch-activatable.py index cb7b9cb1..6da442b2 100644 --- a/tests/twisted/dispatcher/dispatch-activatable.py +++ b/tests/twisted/dispatcher/dispatch-activatable.py @@ -33,7 +33,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) abiword_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/dispatch-before-connected.py b/tests/twisted/dispatcher/dispatch-before-connected.py index 9925e0fe..1037ef4c 100644 --- a/tests/twisted/dispatcher/dispatch-before-connected.py +++ b/tests/twisted/dispatcher/dispatch-before-connected.py @@ -29,17 +29,10 @@ from mctest import exec_test, SimulatedConnection, SimulatedClient, \ expect_client_setup import constants as cs -CHANNEL_TYPE_SERVER_VERIFICATION = \ - 'org.freedesktop.Telepathy.Channel.Type.ServerVerification.DRAFT' -CHANNEL_IFACE_VERIFICATION = \ - 'org.freedesktop.Telepathy.Channel.Interface.Verification.DRAFT ' -CHANNEL_IFACE_IDENT_EXCHANGE = \ - 'org.freedesktop.Telepathy.Channel.Interface.IdentityExchange.DRAFT' - def test(q, bus, mc): params = dbus.Dictionary({"account": "someone@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) @@ -49,7 +42,7 @@ def test(q, bus, mc): verification_filter = dbus.Dictionary({ cs.CHANNEL + '.TargetHandleType': 0, - cs.CHANNEL + '.ChannelType': CHANNEL_TYPE_SERVER_VERIFICATION, + cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_SERVER_TLS_CONNECTION, }, signature='sv') verifier_bus = dbus.bus.BusConnection() @@ -61,7 +54,7 @@ def test(q, bus, mc): expect_client_setup(q, [verifier]) account_props.Set(cs.ACCOUNT, 'RequestedPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_AVAILABLE), 'available', '')) + (dbus.UInt32(cs.PRESENCE_AVAILABLE), 'available', '')) account_props.Set(cs.ACCOUNT, 'Enabled', True) @@ -73,7 +66,7 @@ def test(q, bus, mc): handled=False) conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', - 'myself', has_requests=True, has_presence=True) + 'myself', has_presence=True) q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') @@ -87,10 +80,7 @@ def test(q, bus, mc): channel_properties[cs.CHANNEL + '.InitiatorID'] = '' channel_properties[cs.CHANNEL + '.InitiatorHandle'] = 0 channel_properties[cs.CHANNEL + '.Requested'] = False - channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array([ - CHANNEL_IFACE_IDENT_EXCHANGE, - CHANNEL_IFACE_VERIFICATION, - cs.CHANNEL], signature='s') + channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array([], signature='s') chan = SimulatedChannel(conn, channel_properties) chan.announce() diff --git a/tests/twisted/dispatcher/dispatch-delayed-by-mini-plugin.py b/tests/twisted/dispatcher/dispatch-delayed-by-mini-plugin.py index eeab38cb..d2c0855d 100644 --- a/tests/twisted/dispatcher/dispatch-delayed-by-mini-plugin.py +++ b/tests/twisted/dispatcher/dispatch-delayed-by-mini-plugin.py @@ -99,7 +99,7 @@ def signal_channel_expect_query(q, bus, account, conn, empathy, kopete): def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) policy_bus_name_ref = dbus.service.BusName('com.example.Policy', bus) @@ -145,10 +145,8 @@ def test(q, bus, mc): args=[cdo_path]), EventPattern('dbus-method-call', path=chan.object_path, - interface=cs.CHANNEL_IFACE_GROUP, - # this error message is from the plugin - method='RemoveMembersWithReason', args=[[conn.self_handle], - "Computer says no", cs.GROUP_REASON_PERMISSION_DENIED], + interface=cs.CHANNEL_IFACE_DESTROYABLE, + method='Destroy', handled=False), ) q.dbus_return(e.message, signature='') diff --git a/tests/twisted/dispatcher/dispatch-obsolete.py b/tests/twisted/dispatcher/dispatch-obsolete.py index 4431631a..16d76517 100644 --- a/tests/twisted/dispatcher/dispatch-obsolete.py +++ b/tests/twisted/dispatcher/dispatch-obsolete.py @@ -34,158 +34,18 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params, has_requests=False) - text_fixed_properties = dbus.Dictionary({ - cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, - cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT, - }, signature='sv') - - # Two clients want to observe, approve and handle channels - empathy = SimulatedClient(q, bus, 'Empathy', - observe=[text_fixed_properties], approve=[text_fixed_properties], - handle=[text_fixed_properties], bypass_approval=False) - kopete = SimulatedClient(q, bus, 'Kopete', - observe=[text_fixed_properties], approve=[text_fixed_properties], - handle=[text_fixed_properties], bypass_approval=False) - - # wait for MC to download the properties - expect_client_setup(q, [empathy, kopete]) - - # subscribe to the OperationList interface (MC assumes that until this - # property has been retrieved once, nobody cares) - - cd = bus.get_object(cs.CD, cs.CD_PATH) - cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE) - assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == [] - - channel_properties = dbus.Dictionary(text_fixed_properties, - signature='sv') - channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet' - channel_properties[cs.CHANNEL + '.TargetHandle'] = \ - conn.ensure_handle(cs.HT_CONTACT, 'juliet') - channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet' - channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \ - conn.ensure_handle(cs.HT_CONTACT, 'juliet') - channel_properties[cs.CHANNEL + '.Requested'] = False - channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s') - - chan = SimulatedChannel(conn, channel_properties) - chan.announce() - - # A channel dispatch operation is created - - e = q.expect('dbus-signal', - path=cs.CD_PATH, - interface=cs.CD_IFACE_OP_LIST, - signal='NewDispatchOperation') - - cdo_path = e.args[0] - cdo_properties = e.args[1] - - assert cdo_properties[cs.CDO + '.Account'] == account.object_path - assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path - assert cs.CDO + '.Interfaces' in cdo_properties - - handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:] - handlers.sort() - assert handlers == [cs.tp_name_prefix + '.Client.Empathy', - cs.tp_name_prefix + '.Client.Kopete'], handlers - - assert cs.CD_IFACE_OP_LIST in cd_props.Get(cs.CD, 'Interfaces') - assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') ==\ - [(cdo_path, cdo_properties)] - - cdo = bus.get_object(cs.CD, cdo_path) - cdo_iface = dbus.Interface(cdo, cs.CDO) - - # Both Observers are told about the new channel - - e, k = q.expect_many( - EventPattern('dbus-method-call', - path=empathy.object_path, - interface=cs.OBSERVER, method='ObserveChannels', - handled=False), - EventPattern('dbus-method-call', - path=kopete.object_path, - interface=cs.OBSERVER, method='ObserveChannels', - handled=False), - ) - assert e.args[0] == account.object_path, e.args - assert e.args[1] == conn.object_path, e.args - assert e.args[3] == cdo_path, e.args - assert e.args[4] == [], e.args # no requests satisfied - channels = e.args[2] - assert len(channels) == 1, channels - assert channels[0][0] == chan.object_path, channels - # the announced channel properties are some subset of what it actually has - for key in channels[0][1]: - assert channel_properties[key] == channels[0][1][key], key - assert cs.CHANNEL + '.TargetHandleType' in channels[0][1] - assert cs.CHANNEL + '.ChannelType' in channels[0][1] - assert cs.CHANNEL + '.TargetHandle' in channels[0][1] - - assert k.args == e.args - - # Both Observers indicate that they are ready to proceed - q.dbus_return(k.message, signature='') - q.dbus_return(e.message, signature='') - - # The Approvers are next - - e, k = q.expect_many( - EventPattern('dbus-method-call', - path=empathy.object_path, - interface=cs.APPROVER, method='AddDispatchOperation', - handled=False), - EventPattern('dbus-method-call', - path=kopete.object_path, - interface=cs.APPROVER, method='AddDispatchOperation', - handled=False), - ) - - assert e.args[1:] == [cdo_path, cdo_properties] - channels = e.args[0] - assert len(channels) == 1, channels - assert channels[0][0] == chan.object_path, channels - # the announced channel properties are some subset of what it actually has - for key in channels[0][1]: - assert channel_properties[key] == channels[0][1][key], key - assert cs.CHANNEL + '.TargetHandleType' in channels[0][1] - assert cs.CHANNEL + '.ChannelType' in channels[0][1] - assert cs.CHANNEL + '.TargetHandle' in channels[0][1] - assert k.args == e.args - - q.dbus_return(e.message, signature='') - q.dbus_return(k.message, signature='') - - # Both Approvers now have a flashing icon or something, trying to get the - # user's attention - - # The user responds to Empathy first - call_async(q, cdo_iface, 'HandleWith', - cs.tp_name_prefix + '.Client.Empathy') - - # Empathy is asked to handle the channels - e = q.expect('dbus-method-call', - path=empathy.object_path, - interface=cs.HANDLER, method='HandleChannels', - handled=False) - - # Empathy accepts the channels - q.dbus_return(e.message, signature='') - q.expect_many( - EventPattern('dbus-return', method='HandleWith'), - EventPattern('dbus-signal', interface=cs.CDO, signal='Finished'), - EventPattern('dbus-signal', interface=cs.CD_IFACE_OP_LIST, - signal='DispatchOperationFinished'), + EventPattern('dbus-signal', signal='AccountPropertyChanged', + predicate=lambda e: + e.args[0].get('ConnectionError') == + cs.SOFTWARE_UPGRADE_REQUIRED), + EventPattern('dbus-method-call', method='Disconnect', + handled=True), ) - # Now there are no more active channel dispatch operations - assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == [] - if __name__ == '__main__': exec_test(test, {}) diff --git a/tests/twisted/dispatcher/dispatch-rejected-by-mini-plugin.py b/tests/twisted/dispatcher/dispatch-rejected-by-mini-plugin.py index eeb6e9ec..37a87105 100644 --- a/tests/twisted/dispatcher/dispatch-rejected-by-mini-plugin.py +++ b/tests/twisted/dispatcher/dispatch-rejected-by-mini-plugin.py @@ -33,7 +33,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ @@ -205,9 +205,8 @@ def test(q, bus, mc): args=[cdo_path]), EventPattern('dbus-method-call', path=chan.object_path, - interface=cs.CHANNEL_IFACE_GROUP, - method='RemoveMembersWithReason', args=[[conn.self_handle], - "Can't touch this", cs.GROUP_REASON_PERMISSION_DENIED], + interface=cs.CHANNEL_IFACE_DESTROYABLE, + method='Destroy', handled=False), ) q.dbus_return(e.message, signature='') diff --git a/tests/twisted/dispatcher/dispatch-text.py b/tests/twisted/dispatcher/dispatch-text.py index b1f6ce60..689639ca 100644 --- a/tests/twisted/dispatcher/dispatch-text.py +++ b/tests/twisted/dispatcher/dispatch-text.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/ensure-and-redispatch.py b/tests/twisted/dispatcher/ensure-and-redispatch.py index 4d526c2e..f5a77a56 100644 --- a/tests/twisted/dispatcher/ensure-and-redispatch.py +++ b/tests/twisted/dispatcher/ensure-and-redispatch.py @@ -33,7 +33,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/ensure-is-approval.py b/tests/twisted/dispatcher/ensure-is-approval.py index a2fb0850..63c6f8a7 100644 --- a/tests/twisted/dispatcher/ensure-is-approval.py +++ b/tests/twisted/dispatcher/ensure-is-approval.py @@ -35,7 +35,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) # One client (Kopete) has less specific filters than the other (Empathy), diff --git a/tests/twisted/dispatcher/ensure-rapidly.py b/tests/twisted/dispatcher/ensure-rapidly.py index a338799b..f90a1f68 100644 --- a/tests/twisted/dispatcher/ensure-rapidly.py +++ b/tests/twisted/dispatcher/ensure-rapidly.py @@ -33,7 +33,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/exploding-bundles.py b/tests/twisted/dispatcher/exploding-bundles.py index 27d3be3a..27d1727e 100644 --- a/tests/twisted/dispatcher/exploding-bundles.py +++ b/tests/twisted/dispatcher/exploding-bundles.py @@ -33,7 +33,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ @@ -43,7 +43,7 @@ def test(q, bus, mc): media_fixed_properties = dbus.Dictionary({ cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, - cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_STREAMED_MEDIA, + cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_CALL, }, signature='sv') misc_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/fdo-21034.py b/tests/twisted/dispatcher/fdo-21034.py index 5a1ca3ca..e0ed001a 100644 --- a/tests/twisted/dispatcher/fdo-21034.py +++ b/tests/twisted/dispatcher/fdo-21034.py @@ -33,7 +33,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/handle-channels-fails.py b/tests/twisted/dispatcher/handle-channels-fails.py index 04b51ef7..4b3ae22e 100644 --- a/tests/twisted/dispatcher/handle-channels-fails.py +++ b/tests/twisted/dispatcher/handle-channels-fails.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/lose-text.py b/tests/twisted/dispatcher/lose-text.py index d652658c..42083c2c 100644 --- a/tests/twisted/dispatcher/lose-text.py +++ b/tests/twisted/dispatcher/lose-text.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/recover-from-disconnect.py b/tests/twisted/dispatcher/recover-from-disconnect.py index 9ae81e51..9e0d3c42 100644 --- a/tests/twisted/dispatcher/recover-from-disconnect.py +++ b/tests/twisted/dispatcher/recover-from-disconnect.py @@ -48,13 +48,10 @@ def test(q, bus, mc): # wait for MC to download the properties expect_client_setup(q, [empathy, kopete]) - cm_name_ref = dbus.service.BusName( - tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) - # Create an account params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) @@ -68,7 +65,7 @@ def test(q, bus, mc): dbus_interface=cs.PROPERTIES_IFACE) # Set online presence - presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Fixing MC bugs'), signature='uss') call_async(q, account, 'Set', cs.ACCOUNT, 'RequestedPresence', presence, @@ -93,19 +90,19 @@ def test(q, bus, mc): path=conn.object_path, handled=True), ) - # MC calls GetStatus (maybe) and then Connect - + # MC prepares the connection, does any pre-Connect setup, then + # calls Connect q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) # Connect succeeds - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) test_dispatching(q, bus, conn, account, empathy, kopete) # Connection falls over for a miscellaneous reason conn.StatusChanged(cs.CONN_STATUS_DISCONNECTED, - cs.CONN_STATUS_REASON_NETWORK_ERROR) + cs.CSR_NETWORK_ERROR) # MC reconnects @@ -128,13 +125,13 @@ def test(q, bus, mc): path=conn.object_path, handled=True), ) - # MC calls GetStatus (maybe) and then Connect - + # MC prepares the connection, does any pre-Connect setup, then + # calls Connect q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) # Connect succeeds - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) test_dispatching(q, bus, conn, account, empathy, kopete) diff --git a/tests/twisted/dispatcher/redispatch-channels.py b/tests/twisted/dispatcher/redispatch-channels.py index 6178a379..41800b1c 100644 --- a/tests/twisted/dispatcher/redispatch-channels.py +++ b/tests/twisted/dispatcher/redispatch-channels.py @@ -185,7 +185,7 @@ def test_delegate_channel(q, bus, mc, account, conn, chan, empathy, empathy_bus, def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/request-disabled-account.py b/tests/twisted/dispatcher/request-disabled-account.py index 863f0540..c3f1640b 100644 --- a/tests/twisted/dispatcher/request-disabled-account.py +++ b/tests/twisted/dispatcher/request-disabled-account.py @@ -29,17 +29,17 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "smcv@example.com", "password": "secrecy"}, signature='sv') - (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '')) + (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '')) q.expect('dbus-return', method='Set') call_async(q, account_props, 'Set', cs.ACCOUNT, 'AutomaticPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Testing automatic presence')) q.expect('dbus-return', method='Set') q.expect('dbus-signal', signal='AccountPropertyChanged', diff --git a/tests/twisted/dispatcher/respawn-activatable-observers.py b/tests/twisted/dispatcher/respawn-activatable-observers.py index 8f83ffbb..14727b40 100644 --- a/tests/twisted/dispatcher/respawn-activatable-observers.py +++ b/tests/twisted/dispatcher/respawn-activatable-observers.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/respawn-observers.py b/tests/twisted/dispatcher/respawn-observers.py index 81061ae6..bf69a794 100644 --- a/tests/twisted/dispatcher/respawn-observers.py +++ b/tests/twisted/dispatcher/respawn-observers.py @@ -34,7 +34,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/some-delay-approvers.py b/tests/twisted/dispatcher/some-delay-approvers.py index e06b13a6..16e0b7b0 100644 --- a/tests/twisted/dispatcher/some-delay-approvers.py +++ b/tests/twisted/dispatcher/some-delay-approvers.py @@ -29,7 +29,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/undispatchable.py b/tests/twisted/dispatcher/undispatchable.py index de63fbca..8a18ec03 100644 --- a/tests/twisted/dispatcher/undispatchable.py +++ b/tests/twisted/dispatcher/undispatchable.py @@ -35,7 +35,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary({ diff --git a/tests/twisted/dispatcher/vanishing-client.py b/tests/twisted/dispatcher/vanishing-client.py index 92a4079b..143ea4cb 100644 --- a/tests/twisted/dispatcher/vanishing-client.py +++ b/tests/twisted/dispatcher/vanishing-client.py @@ -33,7 +33,7 @@ import constants as cs def test(q, bus, mc): params = dbus.Dictionary({"account": "someguy@example.com", "password": "secrecy"}, signature='sv') - cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) bus_name = '.'.join([tp_name_prefix, 'Client.CrashMe']) diff --git a/tests/twisted/fakeaccountsservice.py b/tests/twisted/fakeaccountsservice.py index 3c10a156..d8c87a0b 100644 --- a/tests/twisted/fakeaccountsservice.py +++ b/tests/twisted/fakeaccountsservice.py @@ -25,12 +25,27 @@ from servicetest import (Event, EventPattern) import constants as cs -# indices into the tuple of dicts representing an account -ATTRS = 0 -ATTR_FLAGS = 1 -PARAMS = 2 -UNTYPED_PARAMS = 3 -PARAM_FLAGS = 4 +class FakeAccount(object): + + def __init__(self): + self.attrs = dbus.Dictionary({}, signature='sv') + self.attr_flags = dbus.Dictionary({}, signature='su') + self.params = dbus.Dictionary({}, signature='sv') + self.untyped_params = dbus.Dictionary({}, signature='ss') + self.param_flags = dbus.Dictionary({}, signature='su') + self.restrictions = 0 + + SIGNATURE = 'a{sv}a{su}a{sv}a{ss}a{su}u' + + def to_dbus(self): + return ( + self.attrs, + self.attr_flags, + self.params, + self.untyped_params, + self.param_flags, + dbus.UInt32(self.restrictions), + ) class FakeAccountsService(object): def __init__(self, q, bus): @@ -68,25 +83,28 @@ class FakeAccountsService(object): method='UpdateParameters') def create_account(self, account, attrs={}, attr_flags={}, params={}, - untyped_params={}, param_flags={}): + untyped_params={}, param_flags={}, restrictions=0): + if account in self.accounts: raise KeyError('Account %s already exists' % account) - self.accounts[account] = ({}, {}, {}, {}, {}) - self.accounts[account][ATTRS].update(attrs) + + self.accounts[account] = FakeAccount() + self.accounts[account].restrictions = restrictions + self.accounts[account].attrs.update(attrs) for attr in attrs: - self.accounts[account][ATTR_FLAGS][attr] = dbus.UInt32(0) - self.accounts[account][ATTR_FLAGS].update(attr_flags) - self.accounts[account][PARAMS].update(params) + self.accounts[account].attr_flags[attr] = dbus.UInt32(0) + self.accounts[account].attr_flags.update(attr_flags) + self.accounts[account].params.update(params) for param in params: - self.accounts[account][PARAM_FLAGS][param] = dbus.UInt32(0) - self.accounts[account][UNTYPED_PARAMS].update(untyped_params) + self.accounts[account].param_flags[param] = dbus.UInt32(0) + self.accounts[account].untyped_params.update(untyped_params) for param in untyped_params: - self.accounts[account][PARAM_FLAGS][param] = dbus.UInt32(0) - self.accounts[account][PARAM_FLAGS].update(param_flags) + self.accounts[account].param_flags[param] = dbus.UInt32(0) + self.accounts[account].param_flags.update(param_flags) self.q.dbus_emit(self.object_path, cs.TEST_DBUS_ACCOUNT_SERVICE_IFACE, 'AccountCreated', - account, *self.accounts[account], - signature='sa{sv}a{su}a{sv}a{ss}a{su}') + account, *(self.accounts[account].to_dbus()), + signature='s' + FakeAccount.SIGNATURE) def CreateAccount(self, e): try: @@ -111,28 +129,31 @@ class FakeAccountsService(object): self.q.dbus_return(e.message, signature='') def GetAccounts(self, e): - self.q.dbus_return(e.message, self.accounts, - signature='a{s(a{sv}a{su}a{sv}a{ss}a{su})}') + accounts = {} + for a in self.accounts: + accounts[a] = self.accounts[a].to_dbus() + self.q.dbus_return(e.message, accounts, + signature='a{s(' + FakeAccount.SIGNATURE + ')}') def update_attributes(self, account, changed={}, flags={}, deleted=[]): if account not in self.accounts: self.create_account(account) for (attribute, value) in changed.items(): - self.accounts[account][ATTRS][attribute] = value - self.accounts[account][ATTR_FLAGS][attribute] = flags.get( + self.accounts[account].attrs[attribute] = value + self.accounts[account].attr_flags[attribute] = flags.get( attribute, dbus.UInt32(0)) for attribute in deleted: - if attribute in self.accounts[account][ATTRS]: - del self.accounts[account][ATTRS][attribute] - if attribute in self.accounts[account][ATTR_FLAGS]: - del self.accounts[account][ATTR_FLAGS][attribute] + if attribute in self.accounts[account].attrs: + del self.accounts[account].attrs[attribute] + if attribute in self.accounts[account].attr_flags: + del self.accounts[account].attr_flags[attribute] self.q.dbus_emit(self.object_path, cs.TEST_DBUS_ACCOUNT_SERVICE_IFACE, 'AttributesChanged', account, changed, - dict([(a, self.accounts[account][ATTR_FLAGS][a]) + dict([(a, self.accounts[account].attr_flags[a]) for a in changed]), deleted, signature='sa{sv}a{su}as') @@ -150,31 +171,31 @@ class FakeAccountsService(object): self.create_account(account) for (param, value) in changed.items(): - self.accounts[account][PARAMS][param] = value - if param in self.accounts[account][UNTYPED_PARAMS]: - del self.accounts[account][UNTYPED_PARAMS][param] - self.accounts[account][PARAM_FLAGS][param] = flags.get( + self.accounts[account].params[param] = value + if param in self.accounts[account].untyped_params: + del self.accounts[account].untyped_params[param] + self.accounts[account].param_flags[param] = flags.get( param, dbus.UInt32(0)) for (param, value) in untyped.items(): - self.accounts[account][UNTYPED_PARAMS][param] = value - if param in self.accounts[account][PARAMS]: - del self.accounts[account][PARAMS][param] - self.accounts[account][PARAM_FLAGS][param] = flags.get( + self.accounts[account].untyped_params[param] = value + if param in self.accounts[account].params: + del self.accounts[account].params[param] + self.accounts[account].param_flags[param] = flags.get( param, dbus.UInt32(0)) for param in deleted: - if param in self.accounts[account][PARAMS]: - del self.accounts[account][PARAMS][param] - if param in self.accounts[account][UNTYPED_PARAMS]: - del self.accounts[account][UNTYPED_PARAMS][param] - if param in self.accounts[account][PARAM_FLAGS]: - del self.accounts[account][PARAM_FLAGS][param] + if param in self.accounts[account].params: + del self.accounts[account].params[param] + if param in self.accounts[account].untyped_params: + del self.accounts[account].untyped_params[param] + if param in self.accounts[account].param_flags: + del self.accounts[account].param_flags[param] self.q.dbus_emit(self.object_path, cs.TEST_DBUS_ACCOUNT_SERVICE_IFACE, 'ParametersChanged', account, changed, untyped, - dict([(p, self.accounts[account][PARAM_FLAGS][p]) + dict([(p, self.accounts[account].param_flags[p]) for p in (set(changed.keys()) | set(untyped.keys()))]), deleted, signature='sa{sv}a{ss}a{su}as') diff --git a/tests/twisted/fakeclient.py b/tests/twisted/fakeclient.py deleted file mode 100644 index d3c43813..00000000 --- a/tests/twisted/fakeclient.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (C) 2009 Nokia Corporation -# Copyright (C) 2009 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 - -import dbus -import dbus.service -from servicetest import Event -from servicetest import EventPattern, tp_name_prefix, tp_path_prefix - -client_iface = "org.freedesktop.Telepathy.Client" -client_observer_iface = "org.freedesktop.Telepathy.Client.Observer" -client_approver_iface = "org.freedesktop.Telepathy.Client.Approver" -client_handler_iface = "org.freedesktop.Telepathy.Client.Handler" - -properties_iface = "org.freedesktop.DBus.Properties" - -empty_caps = dbus.Array([], signature='a{sv}') - -class FakeClient(dbus.service.Object): - def __init__(self, object_path, q, bus, bus_name, nameref, - caps = empty_caps): - self.object_path = object_path - self.q = q - self.bus = bus - self.bus_name = bus_name - # keep a reference on nameref, otherwise, the name will be lost! - self.nameref = nameref - self.caps = caps - dbus.service.Object.__init__(self, bus, object_path) - - @dbus.service.method(dbus_interface=properties_iface, - in_signature='ss', out_signature='v') - def Get(self, interface_name, property_name): - self.q.append(Event('dbus-method-call', name="Get", - obj=self, interface_name=interface_name, - property_name=property_name)) - if interface_name == client_iface and property_name == "Interfaces": - return dbus.Array([ - client_observer_iface, - client_approver_iface, - client_handler_iface - ], signature='s') - if interface_name == client_observer_iface and \ - property_name == "ObserverChannelFilter": - return empty_caps - if interface_name == client_approver_iface and \ - property_name == "ApproverChannelFilter": - return empty_caps - if interface_name == client_handler_iface and \ - property_name == "HandlerChannelFilter": - return self.caps - print "Error: interface_name=%s property_name=%s" % \ - (interface_name, property_name) - return None - - @dbus.service.method(dbus_interface=properties_iface, - in_signature='s', out_signature='a{sv}') - def GetAll(self, interface_name): - self.q.append(Event('dbus-method-call', name="GetAll", - obj=self, interface_name=interface_name)) - if interface_name == client_iface: - return dbus.Dictionary({ - 'Interfaces': dbus.Array([ - client_observer_iface, - client_approver_iface, - client_handler_iface - ]) - }, signature='sv') - return None - - @dbus.service.method(dbus_interface=client_handler_iface, - in_signature='ooa(oa{sv})aot', out_signature='') - def HandleChannels(self, account, connection, channels, - requests_satisfied, user_action_time): - self.q.append(Event('dbus-method-call', name="HandleChannels", - obj=self, account=account, connection=connection, - channels=channels, requests_satisfied=requests_satisfied, - user_action_time=user_action_time)) - -def start_fake_client(q, bus, bus_name, object_path, caps): - nameref = dbus.service.BusName(bus_name, bus=bus) - client = FakeClient(object_path, q, bus, bus_name, nameref, caps) - return client - - diff --git a/tests/twisted/fakecm.py b/tests/twisted/fakecm.py deleted file mode 100644 index fb534e7b..00000000 --- a/tests/twisted/fakecm.py +++ /dev/null @@ -1,292 +0,0 @@ -# Copyright (C) 2009 Nokia Corporation -# Copyright (C) 2009 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 - -import dbus -import dbus.service -from servicetest import Event -from servicetest import EventPattern, tp_name_prefix, tp_path_prefix -from twisted.internet import reactor - -properties_iface = "org.freedesktop.DBus.Properties" -cm_iface = "org.freedesktop.Telepathy.ConnectionManager" -conn_iface = "org.freedesktop.Telepathy.Connection" -caps_iface = \ - "org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities.DRAFT" -requests_iface = "org.freedesktop.Telepathy.Connection.Interface.Requests" -channel_iface = "org.freedesktop.Telepathy.Channel" - -class FakeChannel(dbus.service.Object): - def __init__(self, conn, object_path, q, bus, nameref, props): - self.conn = conn - self.object_path = object_path - self.q = q - self.bus = bus - # keep a reference on nameref, otherwise, the name will be lost! - self.nameref = nameref - self.props = props - - if channel_iface + '.TargetHandle' not in props: - self.props[channel_iface + '.TargetHandle'] = \ - self.conn.get_handle(props[channel_iface + '.TargetID']) - - dbus.service.Object.__init__(self, bus, object_path) - - def called(self, method): - self.q.append(Event('dbus-method-call', name=method, obj=self, - path=self.object_path)) - - @dbus.service.method(dbus_interface=channel_iface, - in_signature='', out_signature='as') - def GetInterfaces(self): - self.called('GetInterfaces') - return [self.props[channel_iface + '.ChannelType']] - - @dbus.service.method(dbus_interface=channel_iface, - in_signature='', out_signature='u') - def GetHandle(self): - return self.props[channel_iface + '.TargetHandle'] - - @dbus.service.method(dbus_interface=channel_iface, - in_signature='', out_signature='') - def Close(self): - self.Closed() - - @dbus.service.signal(dbus_interface=channel_iface, signature='') - def Closed(self): - pass - - -class FakeConn(dbus.service.Object): - def __init__(self, object_path, q, bus, nameref): - self.object_path = object_path - self.q = q - self.bus = bus - # keep a reference on nameref, otherwise, the name will be lost! - self.nameref = nameref - self.status = 2 # Connection_Status_Disconnected - self.next_channel_id = 1 - self.channels = [] - self.handles = {} - self.next_handle = 1337 # break people depending on SelfHandle == 1 - dbus.service.Object.__init__(self, bus, object_path) - - # interface Connection - - @dbus.service.method(dbus_interface=conn_iface, - in_signature='', out_signature='') - def Connect(self): - self.StatusChanged(1, 1) - self.StatusChanged(0, 1) - self.q.append(Event('dbus-method-call', name="Connect", obj=self, - path=self.object_path)) - return None - - @dbus.service.method(dbus_interface=conn_iface, - in_signature='', out_signature='as') - def GetInterfaces(self): - self.q.append(Event('dbus-method-call', name="GetInterfaces", - obj=self, path=self.object_path)) - return dbus.Array([conn_iface, caps_iface, requests_iface]) - - def get_handle(self, id): - for handle, id_ in self.handles.iteritems(): - if id_ == id: - return handle - handle = self.next_handle - self.next_handle += 1 - - self.handles[handle] = id - return handle - - @dbus.service.method(dbus_interface=conn_iface, - in_signature='', out_signature='u') - def GetSelfHandle(self): - self.q.append(Event('dbus-method-call', name="GetSelfHandle", - obj=self, path=self.object_path)) - return self.get_handle('fakeaccount') - - @dbus.service.method(dbus_interface=conn_iface, - in_signature='', out_signature='u') - def GetStatus(self): - self.q.append(Event('dbus-method-call', name="GetStatus", - obj=self, path=self.object_path)) - return self.status - - @dbus.service.method(dbus_interface=conn_iface, - in_signature='uau', out_signature='as') - def InspectHandles(self, handle_type, handles): - self.q.append(Event('dbus-method-call', name="InspectHandles", - obj=self, path=self.object_path, handle_type=handle_type, - handles=handles)) - if handle_type != 1: - raise "non-contact handles don't exist" - - ret = [] - for handle in handles: - if handle not in self.handles: - raise "%d is not a valid handle" % handle - ret.append(self.handles[handle]) - - return ret - - @dbus.service.method(dbus_interface=conn_iface, - in_signature='uas', out_signature='au') - def RequestHandles(self, type, ids): - if type != 1: - raise "non-contact handles don't exist" - - ret = [] - for id in ids: - ret.append(self.get_handle(id)) - - return ret - - @dbus.service.signal(dbus_interface=conn_iface, - signature='uu') - def StatusChanged(self, status, reason): - self.status = status - - # interface Connection.Interface.ContactCapabilities.DRAFT - - @dbus.service.method(dbus_interface=caps_iface, - in_signature='aa{sv}', out_signature='') - def SetSelfCapabilities(self, caps): - self.q.append(Event('dbus-method-call', name="SetSelfCapabilities", - obj=self, path=self.object_path, caps=caps)) - return None - - @dbus.service.signal(dbus_interface=requests_iface, - signature='a(oa{sv})') - def NewChannels(self, array): - self.channels = self.channels + array - - @dbus.service.signal(dbus_interface=conn_iface, - signature='osuub') - def NewChannel(self, object_path, channel_type, handle_type, handle, - suppress_handle): - pass - - @dbus.service.method(dbus_interface=properties_iface, - in_signature='ss', out_signature='v') - def Get(self, interface_name, property_name): - self.q.append(Event('dbus-method-call', name="Get", - obj=self, interface_name=interface_name, - property_name=property_name)) - if interface_name == requests_iface and \ - property_name == "Channels": - return dbus.Array(self.channels, signature='(oa{sv})') - print "Error: interface_name=%s property_name=%s" % \ - (interface_name, property_name) - return None - - @dbus.service.method(dbus_interface=properties_iface, - in_signature='s', out_signature='a{sv}') - def GetAll(self, interface_name): - self.q.append(Event('dbus-method-call', name="GetAll", - obj=self, interface_name=interface_name)) - if interface_name == conn_iface: - return dbus.Dictionary({ - 'SelfHandle': 0L - }, signature='sv') - if interface_name == requests_iface: - return dbus.Dictionary({ - 'Channels': dbus.Array(self.channels, - signature='(oa{sv})') - }, signature='sv') - return None - - def new_incoming_channel(self, object_path, asv): - self.NewChannels(dbus.Array([(object_path, asv)], - signature='(oa{sv})')) - self.NewChannel(object_path, - asv['org.freedesktop.Telepathy.Channel.ChannelType'], - asv['org.freedesktop.Telepathy.Channel.TargetHandleType'], - asv['org.freedesktop.Telepathy.Channel.TargetHandle'], - False) - - # interface Connection.Interface.Requests - def make_channel(self, props): - path = self.object_path + "/channel%d" % self.next_channel_id - self.next_channel_id += 1 - chan = FakeChannel(self, path, self.q, self.bus, self.nameref, props) - reactor.callLater(0, self.NewChannels, [(chan, props)]) - return chan - - @dbus.service.method(dbus_interface=requests_iface, - in_signature='a{sv}', out_signature='oa{sv}') - def CreateChannel(self, request): - self.q.append(Event('dbus-method-call', name="CreateChannel", - obj=self, interface_name=requests_iface)) - chan = self.make_channel(request) - return (chan, request) - - @dbus.service.method(dbus_interface=requests_iface, - in_signature='a{sv}', out_signature='boa{sv}') - def EnsureChannel(self, request): - self.q.append(Event('dbus-method-call', name="EnsureChannel", - obj=self, interface_name=requests_iface)) - chan = self.make_channel(request) - self.q - return (True, chan, request) - - @dbus.service.signal(dbus_interface=requests_iface, - signature="o") - def ChannelClosed(self, channel): - pass - - - -class FakeCM(dbus.service.Object): - def __init__(self, object_path, q, bus, bus_name, nameref): - self.object_path = object_path - self.q = q - self.bus = bus - self.bus_name = bus_name - # keep a reference on nameref, otherwise, the name will be lost! - self.nameref = nameref - dbus.service.Object.__init__(self, bus, object_path) - - @dbus.service.method(dbus_interface=cm_iface, - in_signature='s', out_signature='a(susv)') - def GetParameters(self, protocol): - self.q.append(Event('dbus-method-call', name="GetParameters", - protocol=protocol, obj=self)) - return [] - - @dbus.service.method(dbus_interface=cm_iface, - in_signature='', out_signature='as') - def ListProtocols(self, protocol): - self.q.append(Event('dbus-method-call', name="ListProtocols", obj=self)) - return ['fakeprotocol'] - - @dbus.service.method(dbus_interface=cm_iface, - in_signature='sa{sv}', out_signature='so') - def RequestConnection(self, protocol, parameters): - conn_path = tp_path_prefix + "/Connection/fakecm/fakeprotocol/conn1" - conn = FakeConn(conn_path, self.q, self.bus, self.nameref) - self.q.append(Event('dbus-method-call', name="RequestConnection", - protocol=protocol, parameters=parameters, - conn=conn, obj=self)) - return [self.bus_name, conn_path] - -def start_fake_connection_manager(q, bus, bus_name, object_path): - nameref = dbus.service.BusName(bus_name, bus=bus) - cm = FakeCM(object_path, q, bus, bus_name, nameref) - return cm - - diff --git a/tests/twisted/mc-debug-server.c b/tests/twisted/mc-debug-server.c index 48f02484..559f3f58 100644 --- a/tests/twisted/mc-debug-server.c +++ b/tests/twisted/mc-debug-server.c @@ -154,6 +154,7 @@ main (int argc, char **argv) { GError *error = NULL; GDBusConnection *gdbus = NULL; + GDBusConnection *gdbus_system = NULL; DBusConnection *connection = NULL; int ret = 1; GMainLoop *teardown_loop; @@ -174,26 +175,18 @@ main (int argc, char **argv) G_LOG_FATAL_MASK | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); gdbus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - - if (gdbus == NULL) - { - g_warning ("%s", error->message); - g_error_free (error); - error = NULL; - goto out; - } - + g_assert_no_error (error); + g_assert (gdbus != NULL); g_dbus_connection_set_exit_on_close (gdbus, FALSE); - bus_daemon = tp_dbus_daemon_dup (&error); + gdbus_system = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + g_assert_no_error (error); + g_assert (gdbus_system != NULL); + g_dbus_connection_set_exit_on_close (gdbus_system, FALSE); - if (bus_daemon == NULL) - { - g_warning ("%s", error->message); - g_error_free (error); - error = NULL; - goto out; - } + bus_daemon = tp_dbus_daemon_dup (&error); + g_assert_no_error (error); + g_assert (bus_daemon != NULL); /* It appears that dbus-glib registers a filter that wrongly returns * DBUS_HANDLER_RESULT_HANDLED for signals, so for *our* filter to have any @@ -230,14 +223,13 @@ main (int argc, char **argv) g_main_loop_run (teardown_loop); -out: - if (connection != NULL) { dbus_connection_flush (connection); } tp_clear_object (&gdbus); + tp_clear_object (&gdbus_system); tp_clear_object (&bus_daemon); dbus_shutdown (); diff --git a/tests/twisted/mcp-account-diversion.c b/tests/twisted/mcp-account-diversion.c index a9967648..0e8bfbc8 100644 --- a/tests/twisted/mcp-account-diversion.c +++ b/tests/twisted/mcp-account-diversion.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <config.h> #include <mission-control-plugins/mission-control-plugins.h> #define DONT_DIVERT "fakecm/fakeprotocol/dontdivert" @@ -110,109 +111,243 @@ _create_config (void) DEBUG ("created %s", file); } -static gboolean -_set (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *key, - const gchar *val) +static McpAccountStorageSetResult +_set (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *key, + GVariant *val, + McpParameterFlags flags) { AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + gchar *val_str; + gboolean changed; if (g_str_has_prefix (account, DONT_DIVERT)) - return FALSE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED; - adp->save = TRUE; - g_key_file_set_value (adp->keyfile, account, key, val); - - return TRUE; -} + if (val == NULL) + { + gsize n; + GStrv keys; -static gboolean -_get (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *key) -{ - AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + if (g_key_file_remove_key (adp->keyfile, account, key, NULL)) + { + adp->save = TRUE; + changed = TRUE; + } - if (key != NULL) - { - gchar *v = g_key_file_get_value (adp->keyfile, account, key, NULL); + keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); - if (v == NULL) - return FALSE; + if (keys == NULL || n == 0) + g_key_file_remove_group (adp->keyfile, account, NULL); - mcp_account_manager_set_value (am, account, key, v); - g_free (v); + g_strfreev (keys); } else { - gsize i; - gsize n; - GStrv keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); + gchar *old; - if (keys == NULL) - n = 0; + val_str = mcp_account_manager_escape_variant_for_keyfile (am, val); - for (i = 0; i < n; i++) - { - gchar *v = g_key_file_get_value (adp->keyfile, account, keys[i], NULL); - - if (v != NULL) - mcp_account_manager_set_value (am, account, keys[i], v); + old = g_key_file_get_value (adp->keyfile, account, key, NULL); - g_free (v); + if (tp_strdiff (old, val_str)) + { + g_key_file_set_value (adp->keyfile, account, key, val_str); + adp->save = TRUE; + changed = TRUE; } - g_strfreev (keys); + g_free (val_str); + g_free (old); } - return TRUE; + if (changed) + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; + else + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; } -static gboolean -_delete (const McpAccountStorage *self, - const McpAccountManager *am, +static McpAccountStorageSetResult +_set_attribute (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + GVariant *val, + McpAttributeFlags flags) +{ + return _set (self, am, account, attribute, val, flags); +} + +static McpAccountStorageSetResult +_set_parameter (McpAccountStorage *self, + McpAccountManager *am, const gchar *account, - const gchar *key) + const gchar *parameter, + GVariant *val, + McpParameterFlags flags) +{ + gchar *param = g_strdup_printf ("param-%s", parameter); + gboolean ret; + + ret = _set (self, am, account, param, val, flags); + g_free (param); + + return ret; +} + +static GVariant * +_get_attribute (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) +{ + AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + gchar *v; + GVariant *ret; + + if (flags != NULL) + *flags = 0; + + v = g_key_file_get_value (adp->keyfile, account, attribute, NULL); + + if (v == NULL) + return NULL; + + ret = mcp_account_manager_unescape_variant_from_keyfile (am, v, type, NULL); + g_free (v); + return ret; +} + +static GVariant * +_get_parameter (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) { AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + gchar *key; + gchar *v; + GVariant *ret; + + if (flags != NULL) + *flags = 0; + + /* this plugin does not store parameters' types, so we can't invent + * a sensible type from nowhere */ + if (type == NULL) + return NULL; - if (key == NULL) + key = g_strdup_printf ("param-%s", parameter); + v = g_key_file_get_value (adp->keyfile, account, key, NULL); + g_free (key); + + if (v == NULL) + return NULL; + + ret = mcp_account_manager_unescape_variant_from_keyfile (am, v, type, NULL); + g_free (v); + return ret; +} + +static gchar ** +list_typed_parameters (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account_name) +{ + /* this plugin can't store parameters' types */ + return NULL; +} + +static gchar ** +list_untyped_parameters (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account_name) +{ + AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (storage); + gchar **keys; + gsize i; + GPtrArray *arr; + + keys = g_key_file_get_keys (adp->keyfile, account_name, &i, NULL); + + if (keys == NULL) + return NULL; + + arr = g_ptr_array_sized_new (i); + + for (i = 0; keys[i] != NULL; i++) { - if (g_key_file_remove_group (adp->keyfile, account, NULL)) - adp->save = TRUE; + if (g_str_has_prefix (keys[i], "param-")) + g_ptr_array_add (arr, g_strdup (keys[i] + 6)); } - else - { - gsize n; - GStrv keys; - if (g_key_file_remove_key (adp->keyfile, account, key, NULL)) - adp->save = TRUE; + g_strfreev (keys); + g_ptr_array_add (arr, NULL); + return (gchar **) g_ptr_array_free (arr, FALSE); +} - keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); +static gboolean _commit (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account_name); - if (keys == NULL || n == 0) - g_key_file_remove_group (adp->keyfile, account, NULL); +static void +delete_async (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + GTask *task = g_task_new (adp, cancellable, callback, user_data); - g_strfreev (keys); + if (g_key_file_remove_group (adp->keyfile, account, NULL)) + adp->save = TRUE; + + if (_commit (self, am, account)) + { + mcp_account_storage_emit_deleted (self, account); + g_task_return_boolean (task, TRUE); + } + else + { + g_task_return_new_error (task, TP_ERROR, TP_ERROR_NOT_AVAILABLE, + "_commit()'s error handling is not good enough to know why " + "I couldn't commit the deletion of %s", account); } - return TRUE; + g_object_unref (task); } +static gboolean +delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} static gboolean -_commit (const McpAccountStorage *self, - const McpAccountManager *am) +_commit (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account_name G_GNUC_UNUSED) { gsize n; gchar *data; AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); gboolean rval = FALSE; + /* This simple implementation ignores account_name and commits everything: + * we're writing out the whole keyfile anyway. If MC is looping over + * accounts, the second and subsequent accounts will find that + * adp->save is false, so there's no write-amplification. */ + if (!adp->save) return TRUE; @@ -228,8 +363,8 @@ _commit (const McpAccountStorage *self, } static GList * -_list (const McpAccountStorage *self, - const McpAccountManager *am) +_list (McpAccountStorage *self, + McpAccountManager *am) { gsize i; gsize n; @@ -254,6 +389,35 @@ _list (const McpAccountStorage *self, return rval; } +static gchar * +create (McpAccountStorage *self, + McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + const gchar *identification, + GError **error) +{ + gchar *unique_name; + + unique_name = mcp_account_manager_get_unique_name (MCP_ACCOUNT_MANAGER (am), + manager, protocol, + identification); + + g_return_val_if_fail (unique_name != NULL, NULL); + + if (g_str_has_prefix (unique_name, DONT_DIVERT)) + { + g_free (unique_name); + return NULL; + } + + /* No need to actually create anything: we'll happily return values + * from get(., ., ., NULL) regardless of whether we have that account + * in our list */ + + return unique_name; +} + static void account_storage_iface_init (McpAccountStorageIface *iface, gpointer unused G_GNUC_UNUSED) @@ -262,11 +426,17 @@ account_storage_iface_init (McpAccountStorageIface *iface, iface->desc = PLUGIN_DESCRIPTION; iface->priority = PLUGIN_PRIORITY; - iface->get = _get; - iface->set = _set; - iface->delete = _delete; + iface->get_attribute = _get_attribute; + iface->get_parameter = _get_parameter; + iface->list_typed_parameters = list_typed_parameters; + iface->list_untyped_parameters = list_untyped_parameters; + iface->set_attribute = _set_attribute; + iface->set_parameter = _set_parameter; + iface->delete_async = delete_async; + iface->delete_finish = delete_finish; iface->commit = _commit; iface->list = _list; + iface->create = create; } diff --git a/tests/twisted/mcp-dbus-caller-permission.c b/tests/twisted/mcp-dbus-caller-permission.c deleted file mode 100644 index 96162baf..00000000 --- a/tests/twisted/mcp-dbus-caller-permission.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * A demonstration plugin that checks a DBus caller's md5sum - * - * Copyright © 2010 Nokia Corporation - * Copyright © 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 <mission-control-plugins/mission-control-plugins.h> -#include <sys/types.h> - -#define CONFFILE "mcp-dbus-caller-permissions.conf" - -#define DEBUG g_debug - -#define PLUGIN_NAME "dbus-caller-permission-checker" -#define PLUGIN_DESCRIPTION \ - "Test plugin that checks the md5 checksum of a DBus caller. " \ - "gkeyfile g_get_user_cache_dir()/" CONFFILE " holds the [paths " \ - "to] the binaries, and the permission tokens associated with each." - -/* Example conf file: -[/usr/local/bin/mc-tool] -org.freedesktop.Telepathy.AccountManager=1 -*=1 -*/ - -static void dbus_acl_iface_init (McpDBusAclIface *, - gpointer); - -typedef struct { - GObject parent; - GKeyFile *permits; - gboolean loaded; -} DBusCallerPermission; - -typedef struct { - GObjectClass parent_class; -} DBusCallerPermissionClass; - -GType dbus_caller_permission_get_type (void) G_GNUC_CONST; - -#define DBUS_CALLER_PERMISSION(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), dbus_caller_permission_get_type (), \ - DBusCallerPermission)) - -G_DEFINE_TYPE_WITH_CODE (DBusCallerPermission, dbus_caller_permission, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MCP_TYPE_DBUS_ACL, dbus_acl_iface_init)); - -static void -dbus_caller_permission_init (DBusCallerPermission *self) -{ - const gchar *dir; - gchar *file = NULL; - - self->permits = g_key_file_new (); - - dir = g_get_user_cache_dir (); - file = g_build_path (G_DIR_SEPARATOR_S, dir, CONFFILE, NULL); - - if (!g_file_test (file, G_FILE_TEST_EXISTS)) - { - g_mkdir_with_parents (dir, 0700); - g_file_set_contents (file, "# MC DBus permissions\n", -1, NULL); - } - - DEBUG ("conf file %s", file); - g_key_file_load_from_file (self->permits, file, G_KEY_FILE_NONE, NULL); - - g_free (file); -} - -static void -dbus_caller_permission_class_init (DBusCallerPermissionClass *cls) -{ -} - -static gboolean is_filtered (const McpDBusAcl *self, - DBusAclType type, - const gchar *name) -{ - DBusCallerPermission *plugin = DBUS_CALLER_PERMISSION (self); - GKeyFile *permits = plugin->permits; - - switch (type) - { - case DBUS_ACL_TYPE_METHOD: - return g_key_file_get_boolean (permits, "methods", name, NULL); - case DBUS_ACL_TYPE_GET_PROPERTY: - return g_key_file_get_boolean (permits, "get-property", name, NULL); - case DBUS_ACL_TYPE_SET_PROPERTY: - return g_key_file_get_boolean (permits, "set-property", name, NULL); - default: - return FALSE; - } -} - -static gboolean -pid_is_permitted (const McpDBusAcl *self, const gchar *name, pid_t pid) -{ - gboolean ok = FALSE; - - if (pid != 0) - { - gchar *path = g_strdup_printf ("/proc/%d/exe", pid); - gchar *executable = g_file_read_link (path, NULL); - - if (executable != NULL) - { - DBusCallerPermission *plugin = DBUS_CALLER_PERMISSION (self); - GKeyFile *permits = plugin->permits; - - DEBUG ("executable to check for permission is %s", executable); - ok = g_key_file_get_boolean (permits, executable, name, NULL); - DEBUG ("%s:%s = %s", executable, name, ok ? "TRUE" : "FALSE"); - - g_free (executable); - } - - g_free (path); - } - - return ok; -} - -static gboolean -caller_authorised (const McpDBusAcl *self, - const TpDBusDaemon *dbus, - const DBusGMethodInvocation *call, - DBusAclType type, - const gchar *name, - const GHashTable *params) -{ - DBusGConnection *dgc = tp_proxy_get_dbus_connection ((TpDBusDaemon *)dbus); - gboolean ok = TRUE; - - if (is_filtered (self, type, name)) - { - pid_t pid = 0; - GError *error = NULL; - gchar *caller = dbus_g_method_get_sender ((DBusGMethodInvocation *) call); - DBusGProxy *proxy = dbus_g_proxy_new_for_name (dgc, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS); - - dbus_g_proxy_call (proxy, "GetConnectionUnixProcessID", &error, - G_TYPE_STRING, caller, - G_TYPE_INVALID, - G_TYPE_UINT, &pid, - G_TYPE_INVALID); - - ok = pid_is_permitted (self, name, pid); - - g_free (caller); - g_object_unref (proxy); - } - - DEBUG ("sync caller-permission ACL check [%s]", ok ? "Allowed" : "Forbidden"); - - return ok; -} - -static void -async_authorised_cb (DBusGProxy *proxy, - DBusGProxyCall *call, - gpointer data) -{ - GError *error = NULL; - DBusAclAuthData *ad = data; - pid_t pid = 0; - const McpDBusAcl *self = ad->acl; - gboolean permitted = FALSE; - - /* if this returns FALSE, there's no PID, which means something bizarre * - * and untrustowrthy is going on, which in turn means we must deny: can't * - * authorise without first authenticating */ - permitted = dbus_g_proxy_end_call (proxy, call, &error, - G_TYPE_UINT, &pid, - G_TYPE_INVALID); - - if (permitted) - permitted = pid_is_permitted (self, ad->name, pid); - else - g_error_free (error); - - DEBUG ("finished async caller-permission ACL check [%u -> %s]", - pid, permitted ? "Allowed" : "Forbidden"); - - mcp_dbus_acl_authorised_async_step (ad, permitted); -} - -static void -caller_async_authorised (const McpDBusAcl *self, - DBusAclAuthData *data) -{ - DBusGConnection *dgc = tp_proxy_get_dbus_connection (data->dbus); - DBusGProxy *proxy = dbus_g_proxy_new_for_name (dgc, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS); - - DEBUG ("starting async caller-permission ACL check"); - - if (is_filtered (self, data->type, data->name)) - { - gchar *caller = dbus_g_method_get_sender (data->context); - - dbus_g_proxy_begin_call (proxy, "GetConnectionUnixProcessID", - async_authorised_cb, - data, - NULL, - G_TYPE_STRING, caller, - G_TYPE_INVALID); - - g_free (caller); - } - else /* not filtered, so the call is allowed: */ - { - mcp_dbus_acl_authorised_async_step (data, TRUE); - } -} - - -static void -dbus_acl_iface_init (McpDBusAclIface *iface, - gpointer unused G_GNUC_UNUSED) -{ - mcp_dbus_acl_iface_set_name (iface, PLUGIN_NAME); - mcp_dbus_acl_iface_set_desc (iface, PLUGIN_DESCRIPTION); - - mcp_dbus_acl_iface_implement_authorised (iface, caller_authorised); - mcp_dbus_acl_iface_implement_authorised_async (iface, caller_async_authorised); -} - -GObject * -mcp_plugin_ref_nth_object (guint n) -{ - DEBUG ("Initializing mcp-dbus-caller-id plugin (n=%u)", n); - - switch (n) - { - case 0: - return g_object_new (dbus_caller_permission_get_type (), NULL); - - default: - return NULL; - } -} - diff --git a/tests/twisted/mcp-plugin.c b/tests/twisted/mcp-plugin.c index 7d347b11..de74134f 100644 --- a/tests/twisted/mcp-plugin.c +++ b/tests/twisted/mcp-plugin.c @@ -49,7 +49,6 @@ GType test_no_op_plugin_get_type (void) G_GNUC_CONST; G_DEFINE_TYPE_WITH_CODE (TestNoOpPlugin, test_no_op_plugin, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MCP_TYPE_DBUS_ACL, NULL); G_IMPLEMENT_INTERFACE (MCP_TYPE_REQUEST_POLICY, NULL); G_IMPLEMENT_INTERFACE (MCP_TYPE_DISPATCH_OPERATION_POLICY, NULL)) @@ -137,11 +136,8 @@ permission_cb (DBusPendingCall *pc, } else { - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - mcp_dispatch_operation_leave_channels (ctx->dispatch_operation, - TRUE, TP_CHANNEL_GROUP_CHANGE_REASON_PERMISSION_DENIED, - "Computer says no"); - G_GNUC_END_IGNORE_DEPRECATIONS + mcp_dispatch_operation_destroy_channels (ctx->dispatch_operation, + TRUE); } } else @@ -513,12 +509,8 @@ test_rejection_plugin_check_cdo (McpDispatchOperationPolicy *policy, } else if (!tp_strdiff (target_id, "mc.hammer@example.net")) { - DEBUG ("MC Hammer detected, leaving channels when observers have run"); - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - mcp_dispatch_operation_leave_channels (dispatch_operation, TRUE, - TP_CHANNEL_GROUP_CHANGE_REASON_PERMISSION_DENIED, - "Can't touch this"); - G_GNUC_END_IGNORE_DEPRECATIONS + DEBUG ("MC Hammer detected, destroying channels when observers have run"); + mcp_dispatch_operation_destroy_channels (dispatch_operation, TRUE); } g_hash_table_unref (properties); diff --git a/tests/twisted/mctest.py b/tests/twisted/mctest.py index 2909acc9..549a742c 100644 --- a/tests/twisted/mctest.py +++ b/tests/twisted/mctest.py @@ -166,7 +166,7 @@ def exec_test_deferred (fun, params, protocol=None, timeout=None, try: account.Properties.Set(cs.ACCOUNT, 'RequestedPresence', - (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', + (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '')) except dbus.DBusException, e: print >> sys.stderr, "Can't set %s offline: %s" % (a, e) @@ -222,7 +222,7 @@ class SimulatedConnection(object): def __init__(self, q, bus, cmname, protocol, account_part, self_ident, self_alias=None, - implement_get_interfaces=True, has_requests=True, + implement_get_channels=True, has_requests=True, has_presence=False, has_aliasing=False, has_avatars=False, avatars_persist=True, extra_interfaces=[], has_hidden=False, implement_get_aliases=True, initial_avatar=None, @@ -292,10 +292,12 @@ class SimulatedConnection(object): interface=cs.PROPERTIES_IFACE, method='GetAll', args=[cs.CONN]) - if implement_get_interfaces: - q.add_dbus_method_impl(self.GetInterfaces, - path=self.object_path, interface=cs.CONN, - method='GetInterfaces') + q.add_dbus_method_impl(self.GetInterfaces, + path=self.object_path, interface=cs.CONN, + method='GetInterfaces') + q.add_dbus_method_impl(self.Get_Interfaces, + path=self.object_path, interface=cs.PROPERTIES_IFACE, + method='Get', args=[cs.CONN, 'Interfaces']) q.add_dbus_method_impl(self.RequestHandles, path=self.object_path, interface=cs.CONN, @@ -306,10 +308,12 @@ class SimulatedConnection(object): q.add_dbus_method_impl(self.HoldHandles, path=self.object_path, interface=cs.CONN, method='HoldHandles') - q.add_dbus_method_impl(self.GetAll_Requests, - path=self.object_path, - interface=cs.PROPERTIES_IFACE, method='GetAll', - args=[cs.CONN_IFACE_REQUESTS]) + + if implement_get_channels and has_requests: + q.add_dbus_method_impl(self.GetAll_Requests, + path=self.object_path, + interface=cs.PROPERTIES_IFACE, method='GetAll', + args=[cs.CONN_IFACE_REQUESTS]) q.add_dbus_method_impl(self.GetContactAttributes, path=self.object_path, @@ -373,22 +377,22 @@ class SimulatedConnection(object): method='SetAvatar') self.statuses = dbus.Dictionary({ - 'available': (cs.PRESENCE_TYPE_AVAILABLE, True, True), - 'away': (cs.PRESENCE_TYPE_AWAY, True, True), - 'lunch': (cs.PRESENCE_TYPE_XA, True, True), - 'busy': (cs.PRESENCE_TYPE_BUSY, True, True), - 'phone': (cs.PRESENCE_TYPE_BUSY, True, True), - 'offline': (cs.PRESENCE_TYPE_OFFLINE, False, False), - 'error': (cs.PRESENCE_TYPE_ERROR, False, False), - 'unknown': (cs.PRESENCE_TYPE_UNKNOWN, False, False), + 'available': (cs.PRESENCE_AVAILABLE, True, True), + 'away': (cs.PRESENCE_AWAY, True, True), + 'lunch': (cs.PRESENCE_EXTENDED_AWAY, True, True), + 'busy': (cs.PRESENCE_BUSY, True, True), + 'phone': (cs.PRESENCE_BUSY, True, True), + 'offline': (cs.PRESENCE_OFFLINE, False, False), + 'error': (cs.PRESENCE_ERROR, False, False), + 'unknown': (cs.PRESENCE_UNKNOWN, False, False), }, signature='s(ubb)') if has_hidden: - self.statuses['hidden'] = (cs.PRESENCE_TYPE_HIDDEN, True, True) + self.statuses['hidden'] = (cs.PRESENCE_HIDDEN, True, True) # "dbus.UInt32" to work around # https://bugs.freedesktop.org/show_bug.cgi?id=69967 - self.presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), + self.presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', ''), signature='uss') def change_self_ident(self, ident): @@ -495,7 +499,7 @@ class SimulatedConnection(object): else: # stub - MC doesn't care ret[contact] = dbus.Struct( - (cs.PRESENCE_TYPE_UNKNOWN, 'unknown', ''), + (cs.PRESENCE_UNKNOWN, 'unknown', ''), signature='uss') self.q.dbus_return(e.message, ret, signature='a{u(uss)}') @@ -530,14 +534,19 @@ class SimulatedConnection(object): def GetInterfaces(self, e): self.q.dbus_return(e.message, self.interfaces, signature='as') + def Get_Interfaces(self, e): + self.q.dbus_return(e.message, + dbus.Array(self.interfaces, signature='s'), + signature='v') + def Connect(self, e): self.StatusChanged(cs.CONN_STATUS_CONNECTING, - cs.CONN_STATUS_REASON_REQUESTED) + cs.CSR_REQUESTED) self.q.dbus_return(e.message, signature='') def Disconnect(self, e): self.StatusChanged(cs.CONN_STATUS_DISCONNECTED, - cs.CONN_STATUS_REASON_REQUESTED) + cs.CSR_REQUESTED) self.q.dbus_return(e.message, signature='') for c in self.channels: c.close() @@ -581,11 +590,10 @@ class SimulatedConnection(object): self.q.dbus_emit(self.object_path, cs.CONN, 'StatusChanged', status, reason, signature='uu') if self.status == cs.CONN_STATUS_CONNECTED and self.has_presence: - if self.presence[0] == cs.PRESENCE_TYPE_OFFLINE: + if self.presence[0] == cs.PRESENCE_OFFLINE: # "dbus.UInt32" to work around # https://bugs.freedesktop.org/show_bug.cgi?id=69967 - self.presence = dbus.Struct(( - dbus.UInt32(cs.PRESENCE_TYPE_AVAILABLE), + self.presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_AVAILABLE), 'available', ''), signature='uss') self.q.dbus_emit(self.object_path, @@ -837,7 +845,7 @@ class SimulatedClient(object): observe=[], approve=[], handle=[], cap_tokens=[], bypass_approval=False, wants_recovery=False, request_notification=True, implement_get_interfaces=True, - is_handler=None, bypass_observers=False, delay_approvers=False): + is_handler=None, delay_approvers=False): self.q = q self.bus = bus self.bus_name = '.'.join([cs.tp_name_prefix, 'Client', clientname]) @@ -847,7 +855,6 @@ class SimulatedClient(object): self.approve = aasv(approve) self.handle = aasv(handle) self.bypass_approval = bool(bypass_approval) - self.bypass_observers = bool(bypass_observers) self.delay_approvers = bool(delay_approvers) self.wants_recovery = bool(wants_recovery) self.request_notification = bool(request_notification) @@ -963,7 +970,6 @@ class SimulatedClient(object): self.q.dbus_return(e.message, { 'HandlerChannelFilter': self.handle, 'BypassApproval': self.bypass_approval, - 'BypassObservers': self.bypass_observers, 'HandledChannels': self.handled_channels, 'Capabilities': self.cap_tokens, }, @@ -987,21 +993,173 @@ class SimulatedClient(object): self.q.dbus_return(e.message, self.bypass_approval, signature='v', bus=self.bus) - def Get_BypassApproval(self, e): - assert self.handle - self.q.dbus_return(e.message, self.bypass_observers, signature='v', - bus=self.bus) - def Get_Recover(self, e): assert self.handle self.q.dbus_return(e.message, self.recover, signature='v', bus=self.bus) +class SimulatedConnectionManager(object): + def __init__(self, q, bus, cm_name='fakecm', + protocol_names=['fakeprotocol']): + self.q = q + self.bus = bus + self.cm_name = cm_name + self.bus_name = '.'.join([cs.CM, cm_name]) + self._bus_name_ref = dbus.service.BusName(self.bus_name, self.bus) + self.object_path = '/' + self.bus_name.replace('.', '/') + self.protocol_names = list(protocol_names) + + q.add_dbus_method_impl(self.GetAll_CM, + path=self.object_path, + interface=cs.PROPERTIES_IFACE, method='GetAll', + args=[cs.CM]) + + for protocol_name in protocol_names: + assert '-' not in protocol_name + + q.add_dbus_method_impl(self.IdentifyAccount, + interface=cs.PROTOCOL, method='IdentifyAccount') + q.add_dbus_method_impl(self.NormalizeContact, + interface=cs.PROTOCOL, method='NormalizeContact') + + def release_name(self): + del self._bus_name_ref + + def reacquire_name(self): + self._bus_name_ref = dbus.service.BusName(self.bus_name, self.bus) + + def get_protocols(self): + ret = dbus.Dictionary(signature='sa{sv}') + + for p in self.protocol_names: + # stub: assume all the protocols look "reasonably normal" + ret[p] = { + cs.PROTOCOL + '.Interfaces': dbus.Array(signature='s'), + cs.PROTOCOL + '.ConnectionInterfaces': + dbus.Array(signature='s'), + cs.PROTOCOL + '.RequestableChannelClasses': + dbus.Array(signature='(a{sv}as)'), + cs.PROTOCOL + '.VCardField': 'x-' + self.cm_name, + cs.PROTOCOL + '.EnglishName': self.cm_name, + cs.PROTOCOL + '.Icon': 'im-' + self.cm_name, + cs.PROTOCOL + '.AuthenticationTypes': + dbus.Array(signature='s'), + cs.PROTOCOL + '.Parameters': dbus.Array([ + ('account', cs.PARAM_REQUIRED, 's', ''), + ('password', cs.PARAM_SECRET, 's', ''), + ], signature='(susv)'), + } + + if self.cm_name == 'fakecm' and p == 'fakeprotocol': + ret[p][cs.PROTOCOL + '.Parameters'] = dbus.Array([ + ('account', cs.PARAM_REQUIRED|cs.PARAM_REGISTER, + 's', ''), + ('password', + cs.PARAM_SECRET|cs.PARAM_REQUIRED|cs.PARAM_REGISTER, + 's', ''), + ('nickname', cs.PARAM_REGISTER, 's', ''), + ('register', cs.PARAM_HAS_DEFAULT, 'b', False), + ('com.example.Badgerable.Badgered', + cs.PARAM_HAS_DEFAULT|cs.PARAM_DBUS_PROPERTY, + 'b', False), + ('secret-mushroom', cs.PARAM_SECRET, 's', ''), + ('snakes', 0, 'u', dbus.UInt32(0)), + ('contrived-example', cs.PARAM_HAS_DEFAULT, 'u', + dbus.UInt32(5)), + ], signature='(susv)') + + if self.cm_name == 'onewitheverything' and p == 'serializable': + ret[p][cs.PROTOCOL + '.Parameters'] = dbus.Array([ + ('s', cs.PARAM_REQUIRED, 's', ''), + ('o', 0, 'o', dbus.ObjectPath('/')), + ('b', 0, 'b', False), + ('q', 0, 'q', dbus.UInt16(0)), + ('u', 0, 'u', dbus.UInt32(0)), + ('t', 0, 't', dbus.UInt64(0)), + ('n', 0, 'n', dbus.Int16(0)), + ('i', 0, 'i', dbus.Int32(0)), + ('x', 0, 'x', dbus.Int64(0)), + ('d', 0, 'd', 0.0), + ('as', 0, 'as', dbus.Array(signature='s')), + ('y', 0, 'y', dbus.Byte(0)), + ], signature='(susv)') + + if self.cm_name == 'onewitheverything' and p == 'defaults': + ret[p][cs.PROTOCOL + '.Parameters'] = dbus.Array([ + ('s', cs.PARAM_HAS_DEFAULT, 's', 'foo'), + ('o', cs.PARAM_HAS_DEFAULT, 'o', dbus.ObjectPath('/foo')), + ('b', cs.PARAM_HAS_DEFAULT, 'b', True), + ('q', cs.PARAM_HAS_DEFAULT, 'q', dbus.UInt16(1)), + ('u', cs.PARAM_HAS_DEFAULT, 'u', dbus.UInt32(1)), + ('t', cs.PARAM_HAS_DEFAULT, 't', dbus.UInt64(1)), + ('n', cs.PARAM_HAS_DEFAULT, 'n', dbus.Int16(-1)), + ('i', cs.PARAM_HAS_DEFAULT, 'i', dbus.Int32(-1)), + ('x', cs.PARAM_HAS_DEFAULT, 'x', dbus.Int64(-1)), + ('d', cs.PARAM_HAS_DEFAULT, 'd', 1.5), + ('as', cs.PARAM_HAS_DEFAULT, 'as', + dbus.Array(['foo', 'bar', 'baz'], signature='s')), + ('y', cs.PARAM_HAS_DEFAULT, 'y', dbus.Byte(1)), + ], signature='(susv)') + + if self.cm_name == 'onewitheverything' and p == 'flags': + ret[p][cs.PROTOCOL + '.Parameters'] = dbus.Array([ + ('account', cs.PARAM_REQUIRED|cs.PARAM_REGISTER, 's', ''), + ('name', cs.PARAM_REGISTER, 's', ''), + ('key', + cs.PARAM_REGISTER|cs.PARAM_REQUIRED|cs.PARAM_SECRET, + 's', ''), + ('com.example.Badgerable.Badgers', cs.PARAM_DBUS_PROPERTY, + 's', ''), + ], signature='(susv)') + + return ret + + def get_interfaces(self): + return dbus.Array([], signature='s') + + def GetAll_CM(self, e): + self.q.dbus_return(e.message, { + 'Protocols': self.get_protocols(), + 'Interfaces': self.get_interfaces(), + }, signature='a{sv}', bus=self.bus) + + def IdentifyAccount(self, e): + if e.path.startswith(self.object_path + '/'): + protocol = e.path[len(self.object_path + '/'):] + + if protocol not in self.protocol_names: + self.q.dbus_raise(e.message, cs.DBUS_ERROR_UNKNOWN_METHOD, + 'Not my protocol') + return + else: + self.q.dbus_raise(e.message, cs.DBUS_ERROR_UNKNOWN_METHOD, + 'Not even my object path') + return + + if protocol in ('serializable', 'defaults') and 's' in e.args[0]: + ret = e.args[0]['s'].lower() + if ret: + self.q.dbus_return(e.message, ret, signature='s') + return + + if 'account' in e.args[0]: + ret = e.args[0]['account'].lower() + if ret: + self.q.dbus_return(e.message, ret, signature='s') + return + + self.q.dbus_raise(e.message, cs.INVALID_HANDLE, + 'Invalid account name %r' % e.args[0].get('account')) + + def NormalizeContact(self, e): + self.q.dbus_return(e.message, e.args[0].lower(), signature='s') + def take_fakecm_name(bus): return dbus.service.BusName(cs.CM + '.fakecm', bus=bus) def create_fakecm_account(q, bus, mc, params, properties={}, - cm_bus=None): + cm_bus=None, simulated_cm=None, + cm_name='fakecm', protocol_name='fakeprotocol'): """Create a fake connection manager and an account that uses it. Optional keyword arguments: @@ -1016,23 +1174,17 @@ def create_fakecm_account(q, bus, mc, params, properties={}, if cm_bus is None: cm_bus = bus - cm_name_ref = take_fakecm_name(cm_bus) + if simulated_cm is None: + simulated_cm = SimulatedConnectionManager(q, bus, + cm_name=cm_name, protocol_names=[protocol_name]) account_manager = AccountManager(bus) servicetest.call_async(q, account_manager, 'CreateAccount', - 'fakecm', 'fakeprotocol', 'fakeaccount', params, properties) - - # Check whether the account being created is to be hidden; if so, then - # expect a different signal. It annoys me that this has to be in here, but, - # eh. - if properties.get(cs.ACCOUNT_IFACE_HIDDEN + '.Hidden', False): - validity_changed_pattern = servicetest.EventPattern('dbus-signal', - path=cs.AM_PATH, signal='HiddenAccountValidityChanged', - interface=cs.AM_IFACE_HIDDEN) - else: - validity_changed_pattern = servicetest.EventPattern('dbus-signal', - path=cs.AM_PATH, signal='AccountValidityChanged', interface=cs.AM) + cm_name, protocol_name, 'fakeaccount', params, properties) + + validity_changed_pattern = servicetest.EventPattern('dbus-signal', + path=cs.AM_PATH, signal='AccountValidityChanged', interface=cs.AM) # The spec has no order guarantee here. # FIXME: MC ought to also introspect the CM and find out that the params @@ -1056,7 +1208,7 @@ def create_fakecm_account(q, bus, mc, params, properties={}, interface, prop = key.rsplit('.', 1) servicetest.assertEquals(value, account.Properties.Get(interface, prop)) - return (cm_name_ref, account) + return (simulated_cm, account) def get_fakecm_account(bus, mc, account_path): account = Account(bus, account_path) @@ -1096,19 +1248,25 @@ def expect_fakecm_connection(q, bus, mc, account, expected_params, extra_interfaces=[], expect_before_connect=(), expect_after_connect=(), has_hidden=False, - self_ident='myself'): + self_ident='myself', + cm_name='fakecm', + protocol_name='fakeprotocol'): # make (safely) mutable copies expect_before_connect = list(expect_before_connect) expect_after_connect = list(expect_after_connect) + # for simplicity we assume the sort of name that is invariant between + # Telepathy 0 and Telepathy 1 + assert '-' not in protocol_name + e = q.expect('dbus-method-call', method='RequestConnection', - args=['fakeprotocol', expected_params], - destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', - path=cs.tp_path_prefix + '/ConnectionManager/fakecm', - interface=cs.tp_name_prefix + '.ConnectionManager', + args=[protocol_name, expected_params], + destination=cs.CM + '.' + cm_name, + path='/' + cs.CM.replace('.', '/') + '/' + cm_name, + interface=cs.CM, handled=False) - conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', + conn = SimulatedConnection(q, bus, cm_name, protocol_name, account.object_path.split('/')[-1], self_ident, has_requests=has_requests, has_presence=has_presence, has_aliasing=has_aliasing, has_avatars=has_avatars, @@ -1133,21 +1291,12 @@ def expect_fakecm_connection(q, bus, mc, account, expected_params, q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) - conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) expect_after_connect = list(expect_after_connect) - if not has_requests: - expect_after_connect.append( - servicetest.EventPattern('dbus-method-call', - interface=cs.CONN, method='ListChannels', args=[], - path=conn.object_path, handled=True)) - events = events + list(q.expect_many(*expect_after_connect)) - if not has_requests: - del events[-1] - if events: return (conn,) + tuple(events) diff --git a/tests/twisted/run-test.sh.in b/tests/twisted/run-test.sh.in index 56ade7b3..5f29132d 100644 --- a/tests/twisted/run-test.sh.in +++ b/tests/twisted/run-test.sh.in @@ -19,7 +19,7 @@ export MC_TEST_CURDIR if test "x$MC_TEST_UNINSTALLED" = x; then script_fullname=`readlink -e "@mctestsdir@/twisted/run-test.sh"` if [ `readlink -e "$0"` != "$script_fullname" ] ; then - echo "This script is meant to be installed at $script_fullname" >&2 + echo "Bail out! This script is meant to be installed at $script_fullname" exit 1 fi @@ -35,11 +35,11 @@ if test "x$MC_TEST_UNINSTALLED" = x; then export MC_TWISTED_PATH else if test -z "$MC_ABS_TOP_SRCDIR"; then - echo "MC_ABS_TOP_SRCDIR must be set" >&2 + echo "Bail out! MC_ABS_TOP_SRCDIR must be set" exit 1 fi if test -z "$MC_ABS_TOP_BUILDDIR"; then - echo "MC_ABS_TOP_BUILDDIR must be set" >&2 + echo "Bail out! MC_ABS_TOP_BUILDDIR must be set" exit 1 fi @@ -59,9 +59,6 @@ MC_DEBUG=all export MC_DEBUG G_DEBUG=fatal-criticals export G_DEBUG -# for ENABLE_LIBACCOUNTS_SSO -AG_DEBUG=all -export AG_DEBUG GIO_EXTRA_MODULES="${plugins}" export GIO_EXTRA_MODULES @@ -82,11 +79,20 @@ else list=$(cat "${test_build}"/twisted/mc-twisted-tests.list) fi -any_failed=0 +n=0 for i in $list ; do - echo "Testing $i ..." + n=$(( $n + 1 )) +done + +echo "1..$n" + +i=0 +n_failed=0 +for t in $list ; do + i=$(( $i + 1 )) + echo "# Testing $i/$n: $t ..." - tmp="${MC_TEST_CURDIR}/tmp-`echo $i | tr ./ __`" + tmp="${MC_TEST_CURDIR}/tmp-`echo $t | tr ./ __`" rm -fr "$tmp" mkdir "$tmp" @@ -105,12 +111,6 @@ for i in $list ; do XDG_CACHE_DIR="${tmp}/cache" export XDG_CACHE_DIR - # for ENABLE_LIBACCOUNTS_SSO - ACCOUNTS="${tmp}/libaccounts-accounts" - export ACCOUNTS - AG_SERVICES="${tmp}/libaccounts-services" - export AG_SERVICES - CHECK_TWISTED_VERBOSE=1 export CHECK_TWISTED_VERBOSE @@ -120,32 +120,38 @@ for i in $list ; do --also-for-system \ --config-file="${config_file}" \ -- \ - @TEST_PYTHON@ -u "${test_src}/twisted/$i" \ + @TEST_PYTHON@ -u "${test_src}/twisted/$t" \ > "$tmp"/test.log 2>&1 || e=$? case "$e" in (0) - echo "PASS: $i" + echo "ok $i - $t" if test -z "$MC_TEST_KEEP_TEMP"; then rm -fr "$tmp" fi ;; (77) - echo "SKIP: $i" + echo "ok $i # SKIP $t" if test -z "$MC_TEST_KEEP_TEMP"; then rm -fr "$tmp" fi ;; (*) - any_failed=1 - echo "FAIL: $i ($e)" + n_failed=$(( $n_failed + 1 )) + echo "not ok $i - $t ($e)" ( cd $tmp && for x in *.log; do - echo "===== log file: $x =====" - cat "$x" + echo "# ===== log file: $x =====" + sed 's/^/# /' "$x" done + echo "# ===== end of log files for $t =====" ) ;; esac done -exit $any_failed +if test $n_failed != 0; then + echo "# Tests run: $n; tests failed: $n_failed" + exit 1 +else + exit 0 +fi diff --git a/tests/twisted/servicetest.py b/tests/twisted/servicetest.py index 4dc604f3..821240b0 100644 --- a/tests/twisted/servicetest.py +++ b/tests/twisted/servicetest.py @@ -1,5 +1,5 @@ # Copyright (C) 2009 Nokia Corporation -# Copyright (C) 2009 Collabora Ltd. +# Copyright (C) 2009-2013 Collabora Ltd. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,30 +17,55 @@ # 02110-1301 USA """ -Infrastructure code for testing Mission Control +Infrastructure code for testing Telepathy services. """ from twisted.internet import glib2reactor from twisted.internet.protocol import Protocol, Factory, ClientFactory glib2reactor.install() import sys +import time +import os import pprint import unittest import dbus import dbus.lowlevel -import dbus.glib +from dbus.mainloop.glib import DBusGMainLoop +DBusGMainLoop(set_as_default=True) from twisted.internet import reactor -tp_name_prefix = 'org.freedesktop.Telepathy' -tp_path_prefix = '/org/freedesktop/Telepathy' +import constants as cs -class Event: +tp_name_prefix = cs.PREFIX +tp_path_prefix = cs.PATH_PREFIX + +class DictionarySupersetOf (object): + """Utility class for expecting "a dictionary with at least these keys".""" + def __init__(self, dictionary): + self._dictionary = dictionary + def __repr__(self): + return "DictionarySupersetOf(%s)" % self._dictionary + def __eq__(self, other): + """would like to just do: + return set(other.items()).issuperset(self._dictionary.items()) + but it turns out that this doesn't work if you have another dict + nested in the values of your dicts""" + try: + for k,v in self._dictionary.items(): + if k not in other or other[k] != v: + return False + return True + except TypeError: # other is not iterable + return False + +class Event(object): def __init__(self, type, **kw): self.__dict__.update(kw) self.type = type + (self.subqueue, self.subtype) = type.split ("-", 1) def __str__(self): return '\n'.join([ str(type(self)) ] + format_event(self)) @@ -48,7 +73,7 @@ class Event: def format_event(event): ret = ['- type %s' % event.type] - for key in dir(event): + for key in sorted(dir(event)): if key != 'type' and not key.startswith('_'): ret.append('- %s: %s' % ( key, pprint.pformat(getattr(event, key)))) @@ -61,16 +86,17 @@ def format_event(event): class EventPattern: def __init__(self, type, **properties): self.type = type - self.predicate = lambda x: True + self.predicate = None if 'predicate' in properties: self.predicate = properties['predicate'] del properties['predicate'] self.properties = properties + (self.subqueue, self.subtype) = type.split ("-", 1) def __repr__(self): properties = dict(self.properties) - if self.predicate: + if self.predicate is not None: properties['predicate'] = self.predicate return '%s(%r, **%r)' % ( @@ -87,7 +113,7 @@ class EventPattern: except AttributeError: return False - if self.predicate(event): + if self.predicate is None or self.predicate(event): return True return False @@ -112,8 +138,8 @@ class BaseEventQueue: def __init__(self, timeout=None): self.verbose = False - self.past_events = [] self.forbidden_events = set() + self.event_queues = {} if timeout is None: self.timeout = 5 @@ -124,28 +150,14 @@ class BaseEventQueue: if self.verbose: print s - def log_event(self, event): - if self.verbose: - self.log('got event:') - - if self.verbose: - map(self.log, format_event(event)) - - def flush_past_events(self): - self.past_events = [] + def log_queues(self, queues): + self.log ("Waiting for event on: %s" % ", ".join(queues)) - def expect_racy(self, type, **kw): - pattern = EventPattern(type, **kw) - - for event in self.past_events: - if pattern.match(event): - self.log('past event handled') - map(self.log, format_event(event)) - self.log('') - self.past_events.remove(event) - return event + def log_event(self, event): + self.log('got event:') - return self.expect(type, **kw) + if self.verbose: + map(self.log, format_event(event)) def forbid_events(self, patterns): """ @@ -163,34 +175,86 @@ class BaseEventQueue: """ self.forbidden_events.difference_update(set(patterns)) + def unforbid_all(self): + """ + Remove all patterns from the set of forbidden events. + """ + self.forbidden_events.clear() + def _check_forbidden(self, event): for e in self.forbidden_events: if e.match(event): raise ForbiddenEventOccurred(event) def expect(self, type, **kw): + """ + Waits for an event matching the supplied pattern to occur, and returns + it. For example, to await a D-Bus signal with particular arguments: + + e = q.expect('dbus-signal', signal='Badgers', args=["foo", 42]) + """ pattern = EventPattern(type, **kw) + t = time.time() while True: - event = self.wait() - self.log_event(event) + try: + event = self.wait([pattern.subqueue]) + except TimeoutError: + self.log('timeout') + self.log('still expecting:') + self.log(' - %r' % pattern) + raise + self._check_forbidden(event) if pattern.match(event): - self.log('handled') + self.log('handled, took %0.3f ms' + % ((time.time() - t) * 1000.0) ) self.log('') return event - self.past_events.append(event) self.log('not handled') self.log('') def expect_many(self, *patterns): + """ + Waits for events matching all of the supplied EventPattern instances to + return, and returns a list of events in the same order as the patterns + they matched. After a pattern is successfully matched, it is not + considered for future events; if more than one unsatisfied pattern + matches an event, the first "wins". + + Note that the expected events may occur in any order. If you're + expecting a series of events in a particular order, use repeated calls + to expect() instead. + + This method is useful when you're awaiting a number of events which may + happen in any order. For instance, in telepathy-gabble, calling a D-Bus + method often causes a value to be returned immediately, as well as a + query to be sent to the server. Since these events may reach the test + in either order, the following is incorrect and will fail if the IQ + happens to reach the test first: + + ret = q.expect('dbus-return', method='Foo') + query = q.expect('stream-iq', query_ns=ns.FOO) + + The following would be correct: + + ret, query = q.expect_many( + EventPattern('dbus-return', method='Foo'), + EventPattern('stream-iq', query_ns=ns.FOO), + ) + """ ret = [None] * len(patterns) + t = time.time() while None in ret: try: - event = self.wait() + queues = set() + for i, pattern in enumerate(patterns): + if ret[i] is None: + queues.add(pattern.subqueue) + event = self.wait(queues) except TimeoutError: self.log('timeout') self.log('still expecting:') @@ -198,17 +262,16 @@ class BaseEventQueue: if ret[i] is None: self.log(' - %r' % pattern) raise - self.log_event(event) self._check_forbidden(event) for i, pattern in enumerate(patterns): if ret[i] is None and pattern.match(event): - self.log('handled') + self.log('handled, took %0.3f ms' + % ((time.time() - t) * 1000.0) ) self.log('') ret[i] = event break else: - self.past_events.append(event) self.log('not handled') self.log('') @@ -217,8 +280,7 @@ class BaseEventQueue: def demand(self, type, **kw): pattern = EventPattern(type, **kw) - event = self.wait() - self.log_event(event) + event = self.wait([pattern.subqueue]) if pattern.match(event): self.log('handled') @@ -228,19 +290,39 @@ class BaseEventQueue: self.log('not handled') raise RuntimeError('expected %r, got %r' % (pattern, event)) + def queues_available(self, queues): + if queues == None: + return self.event_queues.keys() + else: + available = self.event_queues.keys() + return filter(lambda x: x in available, queues) + + + def pop_next(self, queue): + events = self.event_queues[queue] + e = events.pop(0) + if not events: + self.event_queues.pop (queue) + return e + + def append(self, event): + self.log ("Adding to queue") + self.log_event (event) + self.event_queues[event.subqueue] = \ + self.event_queues.get(event.subqueue, []) + [event] + class IteratingEventQueue(BaseEventQueue): """Event queue that works by iterating the Twisted reactor.""" def __init__(self, timeout=None): BaseEventQueue.__init__(self, timeout) - self.events = [] self._dbus_method_impls = [] self._buses = [] # a message filter which will claim we handled everything self._dbus_dev_null = \ lambda bus, message: dbus.lowlevel.HANDLER_RESULT_HANDLED - def wait(self): + def wait(self, queues=None): stop = [False] def later(): @@ -248,21 +330,21 @@ class IteratingEventQueue(BaseEventQueue): delayed_call = reactor.callLater(self.timeout, later) - while (not self.events) and (not stop[0]): - reactor.iterate(0.1) + self.log_queues(queues) + + qa = self.queues_available(queues) + while not qa and (not stop[0]): + reactor.iterate(0.01) + qa = self.queues_available(queues) - if self.events: + if qa: delayed_call.cancel() - return self.events.pop(0) + e = self.pop_next (qa[0]) + self.log_event (e) + return e else: raise TimeoutError - def append(self, event): - self.events.append(event) - - # compatibility - handle_event = append - def add_dbus_method_impl(self, cb, bus=None, **kwargs): if bus is None: bus = self._buses[0] @@ -387,50 +469,74 @@ class IteratingEventQueue(BaseEventQueue): class TestEventQueue(BaseEventQueue): def __init__(self, events): BaseEventQueue.__init__(self) - self.events = events + for e in events: + self.append (e) - def wait(self): - if self.events: - return self.events.pop(0) + def wait(self, queues = None): + qa = self.queues_available(queues) + + if qa: + return self.pop_next (qa[0]) else: raise TimeoutError class EventQueueTest(unittest.TestCase): def test_expect(self): - queue = TestEventQueue([Event('foo'), Event('bar')]) - assert queue.expect('foo').type == 'foo' - assert queue.expect('bar').type == 'bar' + queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) + assert queue.expect('test-foo').type == 'test-foo' + assert queue.expect('test-bar').type == 'test-bar' def test_expect_many(self): - queue = TestEventQueue([Event('foo'), Event('bar')]) + queue = TestEventQueue([Event('test-foo'), + Event('test-bar')]) bar, foo = queue.expect_many( - EventPattern('bar'), - EventPattern('foo')) - assert bar.type == 'bar' - assert foo.type == 'foo' + EventPattern('test-bar'), + EventPattern('test-foo')) + assert bar.type == 'test-bar' + assert foo.type == 'test-foo' def test_expect_many2(self): # Test that events are only matched against patterns that haven't yet # been matched. This tests a regression. - queue = TestEventQueue([Event('foo', x=1), Event('foo', x=2)]) + queue = TestEventQueue([Event('test-foo', x=1), Event('test-foo', x=2)]) foo1, foo2 = queue.expect_many( - EventPattern('foo'), - EventPattern('foo')) - assert foo1.type == 'foo' and foo1.x == 1 - assert foo2.type == 'foo' and foo2.x == 2 + EventPattern('test-foo'), + EventPattern('test-foo')) + assert foo1.type == 'test-foo' and foo1.x == 1 + assert foo2.type == 'test-foo' and foo2.x == 2 + + def test_expect_queueing(self): + queue = TestEventQueue([Event('foo-test', x=1), + Event('foo-test', x=2)]) + + queue.append(Event('bar-test', x=1)) + queue.append(Event('bar-test', x=2)) + + queue.append(Event('baz-test', x=1)) + queue.append(Event('baz-test', x=2)) + + for x in xrange(1,2): + e = queue.expect ('baz-test') + assertEquals (x, e.x) + + e = queue.expect ('bar-test') + assertEquals (x, e.x) + + e = queue.expect ('foo-test') + assertEquals (x, e.x) def test_timeout(self): queue = TestEventQueue([]) - self.assertRaises(TimeoutError, queue.expect, 'foo') + self.assertRaises(TimeoutError, queue.expect, 'test-foo') def test_demand(self): - queue = TestEventQueue([Event('foo'), Event('bar')]) - foo = queue.demand('foo') - assert foo.type == 'foo' + queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) + foo = queue.demand('test-foo') + assert foo.type == 'test-foo' def test_demand_fail(self): - queue = TestEventQueue([Event('foo'), Event('bar')]) - self.assertRaises(RuntimeError, queue.demand, 'bar') + queue = TestEventQueue([Event('test-foo'), Event('test-bar')]) + self.assertRaises(RuntimeError, queue.demand, 'test-bar') def unwrap(x): """Hack to unwrap D-Bus values, so that they're easier to read when @@ -459,11 +565,11 @@ def call_async(test, proxy, method, *args, **kw): resulting method return/error.""" def reply_func(*ret): - test.handle_event(Event('dbus-return', method=method, + test.append(Event('dbus-return', method=method, value=unwrap(ret))) def error_func(err): - test.handle_event(Event('dbus-error', method=method, error=err, + test.append(Event('dbus-error', method=method, error=err, name=err.get_dbus_name(), message=str(err))) method_proxy = getattr(proxy, method) @@ -481,7 +587,7 @@ def sync_dbus(bus, q, proxy): q.expect('dbus-error', method='DummySyncDBus') class ProxyWrapper: - def __init__(self, object, default, others): + def __init__(self, object, default, others={}): self.object = object self.default_interface = dbus.Interface(object, default) self.Properties = dbus.Interface(object, dbus.PROPERTIES_IFACE) @@ -500,6 +606,41 @@ class ProxyWrapper: return getattr(self.default_interface, name) +class ConnWrapper(ProxyWrapper): + def inspect_contact_sync(self, handle): + return self.inspect_contacts_sync([handle])[0] + + def inspect_contacts_sync(self, handles): + h2asv = self.Contacts.GetContactAttributes(handles, [], True) + ret = [] + for h in handles: + ret.append(h2asv[h][cs.ATTR_CONTACT_ID]) + return ret + + def get_contact_handle_sync(self, identifier): + return self.Contacts.GetContactByID(identifier, [])[0] + + def get_contact_handles_sync(self, ids): + return [self.get_contact_handle_sync(i) for i in ids] + +def wrap_connection(conn): + return ConnWrapper(conn, tp_name_prefix + '.Connection', + dict([ + (name, tp_name_prefix + '.Connection.Interface.' + name) + for name in ['Aliasing', 'Avatars', 'Capabilities', 'Contacts', + 'SimplePresence', 'Requests']] + + [('Peer', 'org.freedesktop.DBus.Peer'), + ('ContactCapabilities', cs.CONN_IFACE_CONTACT_CAPS), + ('ContactInfo', cs.CONN_IFACE_CONTACT_INFO), + ('Location', cs.CONN_IFACE_LOCATION), + ('Future', tp_name_prefix + '.Connection.FUTURE'), + ('MailNotification', cs.CONN_IFACE_MAIL_NOTIFICATION), + ('ContactList', cs.CONN_IFACE_CONTACT_LIST), + ('ContactGroups', cs.CONN_IFACE_CONTACT_GROUPS), + ('PowerSaving', cs.CONN_IFACE_POWER_SAVING), + ('Addressing', cs.CONN_IFACE_ADDRESSING), + ])) + def wrap_channel(chan, type_, extra=None): interfaces = { type_: tp_name_prefix + '.Channel.Type.' + type_, @@ -513,14 +654,26 @@ def wrap_channel(chan, type_, extra=None): return ProxyWrapper(chan, tp_name_prefix + '.Channel', interfaces) + +def wrap_content(chan, extra=None): + interfaces = { } + + if extra: + interfaces.update(dict([ + (name, tp_name_prefix + '.Call1.Content.Interface.' + name) + for name in extra])) + + return ProxyWrapper(chan, tp_name_prefix + '.Call1.Content', interfaces) + def make_connection(bus, event_func, name, proto, params): cm = bus.get_object( tp_name_prefix + '.ConnectionManager.%s' % name, - tp_path_prefix + '/ConnectionManager/%s' % name) + tp_path_prefix + '/ConnectionManager/%s' % name, + introspect=False) cm_iface = dbus.Interface(cm, tp_name_prefix + '.ConnectionManager') connection_name, connection_path = cm_iface.RequestConnection( - proto, params) + proto, dbus.Dictionary(params, signature='sv')) conn = wrap_connection(bus.get_object(connection_name, connection_path)) return conn @@ -540,7 +693,7 @@ class EventProtocol(Protocol): def dataReceived(self, data): if self.queue is not None: - self.queue.handle_event(Event('socket-data', protocol=self, + self.queue.append(Event('socket-data', protocol=self, data=data)) def sendData(self, data): @@ -552,7 +705,7 @@ class EventProtocol(Protocol): def connectionLost(self, reason=None): if self.queue is not None: - self.queue.handle_event(Event('socket-disconnected', protocol=self)) + self.queue.append(Event('socket-disconnected', protocol=self)) class EventProtocolFactory(Factory): def __init__(self, queue, block_reading=False): @@ -564,7 +717,7 @@ class EventProtocolFactory(Factory): def buildProtocol(self, addr): proto = self._create_protocol() - self.queue.handle_event(Event('socket-connected', protocol=proto)) + self.queue.append(Event('socket-connected', protocol=proto)) return proto class EventProtocolClientFactory(EventProtocolFactory, ClientFactory): @@ -572,7 +725,7 @@ class EventProtocolClientFactory(EventProtocolFactory, ClientFactory): def watch_tube_signals(q, tube): def got_signal_cb(*args, **kwargs): - q.handle_event(Event('tube-signal', + q.append(Event('tube-signal', path=kwargs['path'], signal=kwargs['member'], args=map(unwrap, args), @@ -590,6 +743,15 @@ def assertEquals(expected, value): raise AssertionError( "expected:\n%s\ngot:\n%s" % (pretty(expected), pretty(value))) +def assertSameSets(expected, value): + exp_set = set(expected) + val_set = set(value) + + if exp_set != val_set: + raise AssertionError( + "expected contents:\n%s\ngot:\n%s" % ( + pretty(exp_set), pretty(val_set))) + def assertNotEquals(expected, value): if expected == value: raise AssertionError( @@ -624,15 +786,11 @@ def assertFlagsUnset(flags, value): "expected none of flags %u, but %u are set in %u" % ( flags, masked, value)) -def assertSameSets(expected, value): - exp_set = set(expected) - val_set = set(value) - - if exp_set != val_set: +def assertDBusError(name, error): + if error.get_dbus_name() != name: raise AssertionError( - "expected contents:\n%s\ngot:\n%s" % ( - pretty(exp_set), pretty(val_set))) - + "expected DBus error named:\n %s\ngot:\n %s\n(with message: %s)" + % (name, error.get_dbus_name(), error.message)) def install_colourer(): def red(s): @@ -652,14 +810,26 @@ def install_colourer(): self.patterns = patterns def write(self, s): - f = self.patterns.get(s, lambda x: x) - self.fh.write(f(s)) + for p, f in self.patterns.items(): + if s.startswith(p): + self.fh.write(f(p) + s[len(p):]) + return + + self.fh.write(s) sys.stdout = Colourer(sys.stdout, patterns) return sys.stdout +# this is just to shut up unittest. +class DummyStream(object): + def write(self, s): + if 'CHECK_TWISTED_VERBOSE' in os.environ: + print s, + def flush(self): + pass if __name__ == '__main__': - unittest.main() - + stream = DummyStream() + runner = unittest.TextTestRunner(stream=stream) + unittest.main(testRunner=runner) diff --git a/tests/twisted/tools/Makefile.am b/tests/twisted/tools/Makefile.am index ffe04eff..9eb17c79 100644 --- a/tests/twisted/tools/Makefile.am +++ b/tests/twisted/tools/Makefile.am @@ -27,25 +27,25 @@ run-mc.sh: run-mc.sh.in Makefile @chmod +x $@ servicedir-uninstalled/%.conf: servicedir-uninstalled/%.conf.in Makefile - $(AM_V_at)$(mkdir_p) servicedir-uninstalled + $(AM_V_at)$(MKDIR_P) servicedir-uninstalled $(AM_V_GEN)sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ servicedir-installed/%.conf: servicedir-installed/%.conf.in Makefile - $(AM_V_at)$(mkdir_p) servicedir-installed + $(AM_V_at)$(MKDIR_P) servicedir-installed $(AM_V_GEN)sed -e "s|[@]mctestsdir[@]|@mctestsdir@|g" $< > $@ # We don't use the full filename for the .in because > 99 character filenames # in tarballs are non-portable (and automake 1.8 doesn't let us build # non-archaic tarballs) servicedir-uninstalled/org.freedesktop.Telepathy.%.service: servicedir-uninstalled/%.service.in Makefile - $(AM_V_at)$(mkdir_p) servicedir-uninstalled + $(AM_V_at)$(MKDIR_P) servicedir-uninstalled $(AM_V_GEN)sed \ -e "s|[@]abs_top_srcdir[@]|@abs_top_srcdir@|g" \ -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" \ $< > $@ servicedir-installed/org.freedesktop.Telepathy.%.service: servicedir-installed/%.service.in Makefile - $(AM_V_at)$(mkdir_p) servicedir-installed + $(AM_V_at)$(MKDIR_P) servicedir-installed $(AM_V_GEN)sed \ -e "s|[@]mctestsdir[@]|@mctestsdir@|g" \ $< > $@ diff --git a/tests/value-is-same.c b/tests/value-is-same.c index 1f7854ba..74097908 100644 --- a/tests/value-is-same.c +++ b/tests/value-is-same.c @@ -23,6 +23,8 @@ * */ +#include <config.h> + /* Yes, this is a hack */ #include "mcd-account.c" diff --git a/tools/Makefile.am b/tools/Makefile.am index a6db81e3..563b9ff0 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,35 +1,8 @@ EXTRA_DIST = \ - c-constants-generator.xsl \ - c-interfaces-generator.xsl \ - doc-generator.xsl \ - glib-client-gen.py \ - glib-blocking-client-gen.py \ - glib-client-marshaller-gen.py \ - glib-interfaces-generator.xsl \ - glib-interfaces-body-generator.xsl \ - glib-ginterface-gen.py \ - glib-gtypes-generator.py \ - identity.xsl \ lcov.am \ - libtpcodegen.py \ - libglibcodegen.py \ run_and_bt.gdb \ - spec-to-introspect.xsl \ telepathy.am -CLEANFILES = libglibcodegen.pyc libglibcodegen.pyo - -all: $(EXTRA_DIST) - -libglibcodegen.py: libtpcodegen.py - $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ -glib-client-marshaller-gen.py: libglibcodegen.py - $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ -glib-ginterface-gen.py: libglibcodegen.py - $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ -glib-gtypes-generator.py: libglibcodegen.py - $(AM_V_GEN)test -e ${srcdir}/$@ && touch ${srcdir}/$@ - TELEPATHY_GLIB_SRCDIR = $(top_srcdir)/../telepathy-glib maintainer-update-from-telepathy-glib: set -e && cd $(srcdir) && \ diff --git a/tools/c-constants-generator.xsl b/tools/c-constants-generator.xsl deleted file mode 100644 index 18b2e495..00000000 --- a/tools/c-constants-generator.xsl +++ /dev/null @@ -1,299 +0,0 @@ -<!-- Stylesheet to extract C enumerations from the Telepathy spec. -The master copy of this stylesheet is in telepathy-glib - please make any -changes there. - -Copyright (C) 2006, 2007 Collabora Limited - -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 Street, Fifth Floor, Boston, MA 02110-1301, USA. ---> - -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - exclude-result-prefixes="tp"> - - <xsl:output method="text" indent="no" encoding="ascii"/> - - <xsl:param name="mixed-case-prefix" select="''"/> - - <xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/> - <xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/> - - <xsl:variable name="upper-case-prefix" select="concat(translate($mixed-case-prefix, $lower, $upper), '_')"/> - <xsl:variable name="lower-case-prefix" select="concat(translate($mixed-case-prefix, $upper, $lower), '_')"/> - - - <xsl:template match="tp:flags"> - <xsl:variable name="name"> - <xsl:choose> - <xsl:when test="@plural"> - <xsl:value-of select="@plural"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@name"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:variable name="value-prefix"> - <xsl:choose> - <xsl:when test="@singular"> - <xsl:value-of select="@singular"/> - </xsl:when> - <xsl:when test="@value-prefix"> - <xsl:value-of select="@value-prefix"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@name"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:text>/** </xsl:text> - <xsl:text> * </xsl:text> - <xsl:value-of select="translate(concat($mixed-case-prefix, $name), '_', '')"/> - <xsl:text>: </xsl:text> - <xsl:apply-templates mode="flag-or-enumvalue-gtkdoc"> - <xsl:with-param name="value-prefix" select="$value-prefix"/> - </xsl:apply-templates> - <xsl:text> * </xsl:text> - <xsl:if test="tp:docstring"> - <xsl:text> * <![CDATA[</xsl:text> - <xsl:value-of select="translate(string (tp:docstring), ' ', ' ')"/> - <xsl:text>]]> </xsl:text> - <xsl:text> * </xsl:text> - </xsl:if> - <xsl:text> * Bitfield/set of flags generated from the Telepathy specification. </xsl:text> - <xsl:text> */ </xsl:text> - <xsl:text>typedef enum { </xsl:text> - <xsl:apply-templates> - <xsl:with-param name="value-prefix" select="$value-prefix"/> - </xsl:apply-templates> - <xsl:text>} </xsl:text> - <xsl:value-of select="translate(concat($mixed-case-prefix, $name), '_', '')"/> - <xsl:text>; </xsl:text> - <xsl:text> </xsl:text> - </xsl:template> - - <xsl:template match="text()" mode="flag-or-enumvalue-gtkdoc"/> - - <xsl:template match="tp:enumvalue" mode="flag-or-enumvalue-gtkdoc"> - <xsl:param name="value-prefix"/> - <xsl:text> * @</xsl:text> - <xsl:value-of select="translate(concat($upper-case-prefix, $value-prefix, '_', @suffix), $lower, $upper)"/> - <xsl:text>: <![CDATA[</xsl:text> - <xsl:value-of select="translate(string(tp:docstring), ' ', ' ')"/> - <xsl:text>]]> </xsl:text> - </xsl:template> - - <xsl:template match="tp:flag" mode="flag-or-enumvalue-gtkdoc"> - <xsl:param name="value-prefix"/> - <xsl:text> * @</xsl:text> - <xsl:value-of select="translate(concat($upper-case-prefix, $value-prefix, '_', @suffix), $lower, $upper)"/> - <xsl:text>: <![CDATA[</xsl:text> - <xsl:value-of select="translate(string(tp:docstring), ' ', ' ')"/> - <xsl:text>]]> </xsl:text> - </xsl:template> - - <xsl:template match="tp:enum"> - <xsl:variable name="name"> - <xsl:choose> - <xsl:when test="@singular"> - <xsl:value-of select="@singular"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@name"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:variable name="value-prefix"> - <xsl:choose> - <xsl:when test="@singular"> - <xsl:value-of select="@singular"/> - </xsl:when> - <xsl:when test="@value-prefix"> - <xsl:value-of select="@value-prefix"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@name"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:variable name="name-plural"> - <xsl:choose> - <xsl:when test="@plural"> - <xsl:value-of select="@plural"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@name"/><xsl:text>s</xsl:text> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:text>/** </xsl:text> - <xsl:text> * </xsl:text> - <xsl:value-of select="translate(concat($mixed-case-prefix, $name), '_', '')"/> - <xsl:text>: </xsl:text> - <xsl:apply-templates mode="flag-or-enumvalue-gtkdoc"> - <xsl:with-param name="value-prefix" select="$value-prefix"/> - </xsl:apply-templates> - <xsl:text> * </xsl:text> - <xsl:if test="tp:docstring"> - <xsl:text> * <![CDATA[</xsl:text> - <xsl:value-of select="translate(string (tp:docstring), ' ', ' ')"/> - <xsl:text>]]> </xsl:text> - <xsl:text> * </xsl:text> - </xsl:if> - <xsl:text> * Bitfield/set of flags generated from the Telepathy specification. </xsl:text> - <xsl:text> */ </xsl:text> - <xsl:text>typedef enum { </xsl:text> - <xsl:apply-templates> - <xsl:with-param name="value-prefix" select="$value-prefix"/> - </xsl:apply-templates> - <xsl:text>} </xsl:text> - <xsl:value-of select="translate(concat($mixed-case-prefix, $name), '_', '')"/> - <xsl:text>; </xsl:text> - <xsl:text> </xsl:text> - <xsl:text>/** </xsl:text> - <xsl:text> * NUM_</xsl:text> - <xsl:value-of select="translate(concat($upper-case-prefix, $name-plural), $lower, $upper)"/> - <xsl:text>: </xsl:text> - <xsl:text> * </xsl:text> - <xsl:text> * 1 higher than the highest valid value of #</xsl:text> - <xsl:value-of select="translate(concat($mixed-case-prefix, $name), '_', '')"/> - <xsl:text>. </xsl:text> - <xsl:text> */ </xsl:text> - <xsl:text>#define NUM_</xsl:text> - <xsl:value-of select="translate(concat($upper-case-prefix, $name-plural), $lower, $upper)"/> - <xsl:text> (</xsl:text> - <xsl:value-of select="tp:enumvalue[position() = last()]/@value"/> - <xsl:text>+1) </xsl:text> - <xsl:text> </xsl:text> - </xsl:template> - - <xsl:template match="tp:flags/tp:flag"> - <xsl:param name="value-prefix"/> - <xsl:variable name="suffix"> - <xsl:choose> - <xsl:when test="@suffix"> - <xsl:value-of select="@suffix"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@name"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:variable name="name" select="translate(concat($upper-case-prefix, $value-prefix, '_', $suffix), $lower, $upper)"/> - - <xsl:if test="@name and @suffix and @name != @suffix"> - <xsl:message terminate="yes"> - <xsl:text>Flag name </xsl:text> - <xsl:value-of select="@name"/> - <xsl:text> != suffix </xsl:text> - <xsl:value-of select="@suffix"/> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:if> - <xsl:text> </xsl:text> - <xsl:value-of select="$name"/> - <xsl:text> = </xsl:text> - <xsl:value-of select="@value"/> - <xsl:text>, </xsl:text> - </xsl:template> - - <xsl:template match="tp:enum/tp:enumvalue"> - <xsl:param name="value-prefix"/> - <xsl:variable name="suffix"> - <xsl:choose> - <xsl:when test="@suffix"> - <xsl:value-of select="@suffix"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@name"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:variable name="name" select="translate(concat($upper-case-prefix, $value-prefix, '_', $suffix), $lower, $upper)"/> - - <xsl:if test="@name and @suffix and @name != @suffix"> - <xsl:message terminate="yes"> - <xsl:text>Enumvalue name </xsl:text> - <xsl:value-of select="@name"/> - <xsl:text> != suffix </xsl:text> - <xsl:value-of select="@suffix"/> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:if test="preceding-sibling::tp:enumvalue and number(preceding-sibling::tp:enumvalue[1]/@value) > number(@value)"> - <xsl:message terminate="yes"> - <xsl:text>Enum values must be in ascending numeric order, but </xsl:text> - <xsl:value-of select="$name"/> - <xsl:text> is less than the previous value</xsl:text> - </xsl:message> - </xsl:if> - - <xsl:text> </xsl:text> - <xsl:value-of select="$name"/> - <xsl:text> = </xsl:text> - <xsl:value-of select="@value"/> - <xsl:text>, </xsl:text> - </xsl:template> - - <xsl:template match="tp:flag"> - <xsl:message terminate="yes">tp:flag found outside tp:flags </xsl:message> - </xsl:template> - - <xsl:template match="tp:enumvalue"> - <xsl:message terminate="yes">tp:enumvalue found outside tp:enum </xsl:message> - </xsl:template> - - <xsl:template match="text()"/> - - <xsl:template match="/tp:spec"> - <xsl:if test="$mixed-case-prefix = ''"> - <xsl:message terminate="yes"> - <xsl:text>mixed-case-prefix param must be set </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:text>/* Generated from </xsl:text> - <xsl:value-of select="tp:title"/> - <xsl:if test="tp:version"> - <xsl:text>, version </xsl:text> - <xsl:value-of select="tp:version"/> - </xsl:if> - <xsl:text> </xsl:text> - <xsl:text> </xsl:text> - <xsl:for-each select="tp:copyright"> - <xsl:value-of select="."/> - <xsl:text> </xsl:text> - </xsl:for-each> - <xsl:value-of select="tp:license"/> - <xsl:text> </xsl:text> - <xsl:value-of select="tp:docstring"/> - <xsl:text> </xsl:text> - <xsl:text> */ </xsl:text> - <xsl:text> </xsl:text> - <xsl:text>#ifdef __cplusplus </xsl:text> - <xsl:text>extern "C" { </xsl:text> - <xsl:text>#endif </xsl:text> - <xsl:text> </xsl:text> - <xsl:apply-templates/> - <xsl:text> </xsl:text> - <xsl:text>#ifdef __cplusplus </xsl:text> - <xsl:text>} </xsl:text> - <xsl:text>#endif </xsl:text> - </xsl:template> - -</xsl:stylesheet> - -<!-- vim:set sw=2 sts=2 et noai noci: --> diff --git a/tools/c-interfaces-generator.xsl b/tools/c-interfaces-generator.xsl deleted file mode 100644 index f965a705..00000000 --- a/tools/c-interfaces-generator.xsl +++ /dev/null @@ -1,84 +0,0 @@ -<!-- Stylesheet to extract C enumerations from the Telepathy spec. -The master copy of this stylesheet is in telepathy-glib - please make any -changes there. - -Copyright (C) 2006, 2007 Collabora Limited - -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 Street, Fifth Floor, Boston, MA 02110-1301, USA. ---> - -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - exclude-result-prefixes="tp"> - - <xsl:param name="mixed-case-prefix" select="''"/> - - <xsl:variable name="PREFIX" - select="translate($mixed-case-prefix, $lower, $upper)"/> - <xsl:variable name="Prefix" select="$mixed-case-prefix"/> - <xsl:variable name="prefix" - select="translate($mixed-case-prefix, $upper, $lower)"/> - - <xsl:output method="text" indent="no" encoding="ascii"/> - - <xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/> - <xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/> - - <xsl:template match="interface"> - <xsl:text>/** * </xsl:text> - <xsl:value-of select="$PREFIX"/> - <xsl:text>_IFACE_</xsl:text> - <xsl:value-of select="translate(../@name, concat($lower, '/'), $upper)"/> - <xsl:text>: * * The interface name "</xsl:text> - <xsl:value-of select="@name"/> - <xsl:text>" */ #define </xsl:text> - <xsl:value-of select="$PREFIX"/> - <xsl:text>_IFACE_</xsl:text> - <xsl:value-of select="translate(../@name, concat($lower, '/'), $upper)"/> - <xsl:text> \ "</xsl:text> - <xsl:value-of select="@name"/> - <xsl:text>" </xsl:text> - </xsl:template> - - <xsl:template match="text()"/> - - <xsl:template match="/tp:spec"> - <xsl:if test="$mixed-case-prefix = ''"> - <xsl:message terminate="yes"> - <xsl:text>mixed-case-prefix param must be set </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:text>/* Generated from: </xsl:text> - <xsl:value-of select="tp:title"/> - <xsl:if test="tp:version"> - <xsl:text> version </xsl:text> - <xsl:value-of select="tp:version"/> - </xsl:if> - <xsl:text> </xsl:text> - <xsl:for-each select="tp:copyright"> - <xsl:value-of select="."/> - <xsl:text> </xsl:text> - </xsl:for-each> - <xsl:text> </xsl:text> - <xsl:value-of select="tp:license"/> - <xsl:value-of select="tp:docstring"/> - <xsl:text> */ </xsl:text> - <xsl:apply-templates/> - </xsl:template> - -</xsl:stylesheet> - -<!-- vim:set sw=2 sts=2 et noai noci: --> diff --git a/tools/doc-generator.xsl b/tools/doc-generator.xsl deleted file mode 100644 index 76fc9696..00000000 --- a/tools/doc-generator.xsl +++ /dev/null @@ -1,1199 +0,0 @@ -<!-- Generate HTML documentation from the Telepathy specification. -The master copy of this stylesheet is in the Telepathy spec repository - -please make any changes there. - -Copyright (C) 2006-2008 Collabora Limited - -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 ---> - -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - xmlns:html="http://www.w3.org/1999/xhtml" - exclude-result-prefixes="tp html"> - <!--Don't move the declaration of the HTML namespace up here — XMLNSs - don't work ideally in the presence of two things that want to use the - absence of a prefix, sadly. --> - - <xsl:param name="allow-undefined-interfaces" select="false()"/> - - <xsl:template match="html:* | @*" mode="html"> - <xsl:copy> - <xsl:apply-templates mode="html"/> - </xsl:copy> - </xsl:template> - - <xsl:template match="tp:type" mode="html"> - <xsl:call-template name="tp-type"> - <xsl:with-param name="tp-type" select="string(.)"/> - </xsl:call-template> - </xsl:template> - - <!-- tp:dbus-ref: reference a D-Bus interface, signal, method or property --> - <xsl:template match="tp:dbus-ref" mode="html"> - <xsl:variable name="name"> - <xsl:choose> - <xsl:when test="@namespace"> - <xsl:value-of select="@namespace"/> - <xsl:text>.</xsl:text> - </xsl:when> - </xsl:choose> - <xsl:value-of select="string(.)"/> - </xsl:variable> - - <xsl:choose> - <xsl:when test="//interface[@name=$name] - or //interface/method[concat(../@name, '.', @name)=$name] - or //interface/signal[concat(../@name, '.', @name)=$name] - or //interface/property[concat(../@name, '.', @name)=$name] - or //interface[@name=concat($name, '.DRAFT')] - or //interface/method[ - concat(../@name, '.', @name)=concat($name, '.DRAFT')] - or //interface/signal[ - concat(../@name, '.', @name)=concat($name, '.DRAFT')] - or //interface/property[ - concat(../@name, '.', @name)=concat($name, '.DRAFT')] - "> - <a xmlns="http://www.w3.org/1999/xhtml" href="#{$name}"> - <xsl:value-of select="string(.)"/> - </a> - </xsl:when> - - <xsl:when test="$allow-undefined-interfaces"> - <span xmlns="http://www.w3.org/1999/xhtml" title="defined elsewhere"> - <xsl:value-of select="string(.)"/> - </span> - </xsl:when> - - <xsl:otherwise> - <xsl:message terminate="yes"> - <xsl:text>ERR: cannot find D-Bus interface, method, </xsl:text> - <xsl:text>signal or property called '</xsl:text> - <xsl:value-of select="$name"/> - <xsl:text>' </xsl:text> - </xsl:message> - </xsl:otherwise> - </xsl:choose> - </xsl:template> - - <!-- tp:member-ref: reference a property of the current interface --> - <xsl:template match="tp:member-ref" mode="html"> - <xsl:variable name="prefix" select="concat(ancestor::interface/@name, - '.')"/> - <xsl:variable name="name" select="string(.)"/> - - <xsl:if test="not(ancestor::interface)"> - <xsl:message terminate="yes"> - <xsl:text>ERR: Cannot use tp:member-ref when not in an</xsl:text> - <xsl:text> <interface> </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:choose> - <xsl:when test="ancestor::interface/signal[@name=$name]"/> - <xsl:when test="ancestor::interface/method[@name=$name]"/> - <xsl:when test="ancestor::interface/property[@name=$name]"/> - <xsl:otherwise> - <xsl:message terminate="yes"> - <xsl:text>ERR: interface </xsl:text> - <xsl:value-of select="ancestor::interface/@name"/> - <xsl:text> has no signal/method/property called </xsl:text> - <xsl:value-of select="$name"/> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:otherwise> - </xsl:choose> - - <a xmlns="http://www.w3.org/1999/xhtml" href="#{$prefix}{$name}"> - <xsl:value-of select="$name"/> - </a> - </xsl:template> - - <xsl:template match="*" mode="identity"> - <xsl:copy> - <xsl:apply-templates mode="identity"/> - </xsl:copy> - </xsl:template> - - <xsl:template match="tp:docstring"> - <xsl:apply-templates mode="html"/> - </xsl:template> - - <xsl:template match="tp:added"> - <p class="added" xmlns="http://www.w3.org/1999/xhtml">Added in - version <xsl:value-of select="@version"/>. - <xsl:apply-templates select="node()" mode="html"/></p> - </xsl:template> - - <xsl:template match="tp:changed"> - <xsl:choose> - <xsl:when test="node()"> - <p class="changed" xmlns="http://www.w3.org/1999/xhtml">Changed in - version <xsl:value-of select="@version"/>: - <xsl:apply-templates select="node()" mode="html"/></p> - </xsl:when> - <xsl:otherwise> - <p class="changed">Changed in version - <xsl:value-of select="@version"/></p> - </xsl:otherwise> - </xsl:choose> - </xsl:template> - - <xsl:template match="tp:deprecated"> - <p class="deprecated" xmlns="http://www.w3.org/1999/xhtml">Deprecated - since version <xsl:value-of select="@version"/>. - <xsl:apply-templates select="node()" mode="html"/></p> - </xsl:template> - - <xsl:template match="tp:rationale" mode="html"> - <div xmlns="http://www.w3.org/1999/xhtml" class="rationale"> - <xsl:apply-templates select="node()" mode="html"/> - </div> - </xsl:template> - - <xsl:template match="tp:errors"> - <h1 xmlns="http://www.w3.org/1999/xhtml">Errors</h1> - <xsl:apply-templates/> - </xsl:template> - - <xsl:template match="tp:generic-types"> - <h1 xmlns="http://www.w3.org/1999/xhtml">Generic types</h1> - <xsl:call-template name="do-types"/> - </xsl:template> - - <xsl:template name="do-types"> - <xsl:if test="tp:simple-type"> - <h2 xmlns="http://www.w3.org/1999/xhtml">Simple types</h2> - <xsl:apply-templates select="tp:simple-type"/> - </xsl:if> - - <xsl:if test="tp:enum"> - <h2 xmlns="http://www.w3.org/1999/xhtml">Enumerated types:</h2> - <xsl:apply-templates select="tp:enum"/> - </xsl:if> - - <xsl:if test="tp:flags"> - <h2 xmlns="http://www.w3.org/1999/xhtml">Sets of flags:</h2> - <xsl:apply-templates select="tp:flags"/> - </xsl:if> - - <xsl:if test="tp:struct"> - <h2 xmlns="http://www.w3.org/1999/xhtml">Structure types</h2> - <xsl:apply-templates select="tp:struct"/> - </xsl:if> - - <xsl:if test="tp:mapping"> - <h2 xmlns="http://www.w3.org/1999/xhtml">Mapping types</h2> - <xsl:apply-templates select="tp:mapping"/> - </xsl:if> - - <xsl:if test="tp:external-type"> - <h2 xmlns="http://www.w3.org/1999/xhtml">Types defined elsewhere</h2> - <dl><xsl:apply-templates select="tp:external-type"/></dl> - </xsl:if> - </xsl:template> - - <xsl:template match="tp:error"> - <h2 xmlns="http://www.w3.org/1999/xhtml"><a name="{concat(../@namespace, '.', translate(@name, ' ', ''))}"></a><xsl:value-of select="concat(../@namespace, '.', translate(@name, ' ', ''))"/></h2> - <xsl:apply-templates select="tp:docstring"/> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - </xsl:template> - - <xsl:template match="/tp:spec/tp:copyright"> - <div xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates mode="text"/> - </div> - </xsl:template> - <xsl:template match="/tp:spec/tp:license"> - <div xmlns="http://www.w3.org/1999/xhtml" class="license"> - <xsl:apply-templates mode="html"/> - </div> - </xsl:template> - - <xsl:template match="tp:copyright"/> - <xsl:template match="tp:license"/> - - <xsl:template match="interface"> - <h1 xmlns="http://www.w3.org/1999/xhtml"><a name="{@name}"></a><xsl:value-of select="@name"/></h1> - - <xsl:if test="@tp:causes-havoc"> - <p xmlns="http://www.w3.org/1999/xhtml" class="causes-havoc"> - This interface is <xsl:value-of select="@tp:causes-havoc"/> - and is likely to cause havoc to your API/ABI if bindings are generated. - Don't include it in libraries that care about compatibility. - </p> - </xsl:if> - - <xsl:if test="tp:requires"> - <p>Implementations of this interface must also implement:</p> - <ul xmlns="http://www.w3.org/1999/xhtml"> - <xsl:for-each select="tp:requires"> - <li><code><a href="#{@interface}"><xsl:value-of select="@interface"/></a></code></li> - </xsl:for-each> - </ul> - </xsl:if> - - <xsl:apply-templates select="tp:docstring" /> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - - <xsl:choose> - <xsl:when test="method"> - <h2 xmlns="http://www.w3.org/1999/xhtml">Methods:</h2> - <xsl:apply-templates select="method"/> - </xsl:when> - <xsl:otherwise> - <p xmlns="http://www.w3.org/1999/xhtml">Interface has no methods.</p> - </xsl:otherwise> - </xsl:choose> - - <xsl:choose> - <xsl:when test="signal"> - <h2 xmlns="http://www.w3.org/1999/xhtml">Signals:</h2> - <xsl:apply-templates select="signal"/> - </xsl:when> - <xsl:otherwise> - <p xmlns="http://www.w3.org/1999/xhtml">Interface has no signals.</p> - </xsl:otherwise> - </xsl:choose> - - <xsl:choose> - <xsl:when test="tp:property"> - <h2 xmlns="http://www.w3.org/1999/xhtml">Telepathy Properties:</h2> - <p xmlns="http://www.w3.org/1999/xhtml">Accessed using the - <a href="#org.freedesktop.Telepathy.Properties">Telepathy - Properties</a> interface.</p> - <dl xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="tp:property"/> - </dl> - </xsl:when> - <xsl:otherwise> - <p xmlns="http://www.w3.org/1999/xhtml">Interface has no Telepathy - properties.</p> - </xsl:otherwise> - </xsl:choose> - - <xsl:choose> - <xsl:when test="property"> - <h2 xmlns="http://www.w3.org/1999/xhtml">D-Bus core Properties:</h2> - <p xmlns="http://www.w3.org/1999/xhtml">Accessed using the - org.freedesktop.DBus.Properties interface.</p> - <dl xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="property"/> - </dl> - </xsl:when> - <xsl:otherwise> - <p xmlns="http://www.w3.org/1999/xhtml">Interface has no D-Bus core - properties.</p> - </xsl:otherwise> - </xsl:choose> - - <xsl:call-template name="do-types"/> - - </xsl:template> - - <xsl:template match="tp:flags"> - - <xsl:if test="not(@name) or @name = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @name on a tp:flags type </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:if test="not(@type) or @type = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @type on tp:flags type</xsl:text> - <xsl:value-of select="@name"/> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:if> - - <h3> - <a name="type-{@name}"> - <xsl:value-of select="@name"/> - </a> - </h3> - <xsl:apply-templates select="tp:docstring" /> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - <dl xmlns="http://www.w3.org/1999/xhtml"> - <xsl:variable name="value-prefix"> - <xsl:choose> - <xsl:when test="@value-prefix"> - <xsl:value-of select="@value-prefix"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@name"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:for-each select="tp:flag"> - <dt xmlns="http://www.w3.org/1999/xhtml"><code><xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/></code></dt> - <xsl:choose> - <xsl:when test="tp:docstring"> - <dd xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="tp:docstring" /> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - </dd> - </xsl:when> - <xsl:otherwise> - <dd xmlns="http://www.w3.org/1999/xhtml">(Undocumented)</dd> - </xsl:otherwise> - </xsl:choose> - </xsl:for-each> - </dl> - </xsl:template> - - <xsl:template match="tp:enum"> - - <xsl:if test="not(@name) or @name = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @name on a tp:enum type </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:if test="not(@type) or @type = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @type on tp:enum type</xsl:text> - <xsl:value-of select="@name"/> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:if> - - <h3 xmlns="http://www.w3.org/1999/xhtml"> - <a name="type-{@name}"> - <xsl:value-of select="@name"/> - </a> - </h3> - <xsl:apply-templates select="tp:docstring" /> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - <dl xmlns="http://www.w3.org/1999/xhtml"> - <xsl:variable name="value-prefix"> - <xsl:choose> - <xsl:when test="@value-prefix"> - <xsl:value-of select="@value-prefix"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="@name"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - <xsl:for-each select="tp:enumvalue"> - <dt xmlns="http://www.w3.org/1999/xhtml"><code><xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/></code></dt> - <xsl:choose> - <xsl:when test="tp:docstring"> - <dd xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="tp:docstring" /> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - </dd> - </xsl:when> - <xsl:otherwise> - <dd xmlns="http://www.w3.org/1999/xhtml">(Undocumented)</dd> - </xsl:otherwise> - </xsl:choose> - </xsl:for-each> - </dl> - </xsl:template> - - <xsl:template match="property"> - - <xsl:if test="not(parent::interface)"> - <xsl:message terminate="yes"> - <xsl:text>ERR: property </xsl:text> - <xsl:value-of select="@name"/> - <xsl:text> does not have an interface as parent </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:if test="not(@name) or @name = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @name on a property of </xsl:text> - <xsl:value-of select="../@name"/> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:if test="not(@type) or @type = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @type on property </xsl:text> - <xsl:value-of select="concat(../@name, '.', @name)"/> - <xsl:text>: '</xsl:text> - <xsl:value-of select="@access"/> - <xsl:text>' </xsl:text> - </xsl:message> - </xsl:if> - - <dt xmlns="http://www.w3.org/1999/xhtml"> - <a name="{concat(../@name, '.', @name)}"> - <code><xsl:value-of select="@name"/></code> - </a> - <xsl:text> − </xsl:text> - <code><xsl:value-of select="@type"/></code> - <xsl:call-template name="parenthesized-tp-type"/> - <xsl:text>, </xsl:text> - <xsl:choose> - <xsl:when test="@access = 'read'"> - <xsl:text>read-only</xsl:text> - </xsl:when> - <xsl:when test="@access = 'write'"> - <xsl:text>write-only</xsl:text> - </xsl:when> - <xsl:when test="@access = 'readwrite'"> - <xsl:text>read/write</xsl:text> - </xsl:when> - <xsl:otherwise> - <xsl:message terminate="yes"> - <xsl:text>ERR: unknown or missing value for </xsl:text> - <xsl:text>@access on property </xsl:text> - <xsl:value-of select="concat(../@name, '.', @name)"/> - <xsl:text>: '</xsl:text> - <xsl:value-of select="@access"/> - <xsl:text>' </xsl:text> - </xsl:message> - </xsl:otherwise> - </xsl:choose> - </dt> - <dd xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="tp:docstring"/> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - </dd> - </xsl:template> - - <xsl:template match="tp:property"> - <dt xmlns="http://www.w3.org/1999/xhtml"> - <xsl:if test="@name"> - <code><xsl:value-of select="@name"/></code> − - </xsl:if> - <code><xsl:value-of select="@type"/></code> - </dt> - <dd xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="tp:docstring"/> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - </dd> - </xsl:template> - - <xsl:template match="tp:mapping"> - <div xmlns="http://www.w3.org/1999/xhtml" class="struct"> - <h3> - <a name="type-{@name}"> - <xsl:value-of select="@name"/> - </a> − a{ - <xsl:for-each select="tp:member"> - <xsl:value-of select="@type"/> - <xsl:text>: </xsl:text> - <xsl:value-of select="@name"/> - <xsl:if test="position() != last()"> → </xsl:if> - </xsl:for-each> - } - </h3> - <div class="docstring"> - <xsl:apply-templates select="tp:docstring"/> - <xsl:if test="string(@array-name) != ''"> - <p>In bindings that need a separate name, arrays of - <xsl:value-of select="@name"/> should be called - <xsl:value-of select="@array-name"/>.</p> - </xsl:if> - </div> - <div> - <h4>Members</h4> - <dl> - <xsl:apply-templates select="tp:member" mode="members-in-docstring"/> - </dl> - </div> - </div> - </xsl:template> - - <xsl:template match="tp:docstring" mode="in-index"/> - - <xsl:template match="tp:simple-type | tp:enum | tp:flags | tp:external-type" - mode="in-index"> - − <xsl:value-of select="@type"/> - </xsl:template> - - <xsl:template match="tp:simple-type"> - - <xsl:if test="not(@name) or @name = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @name on a tp:simple-type </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:if test="not(@type) or @type = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @type on tp:simple-type</xsl:text> - <xsl:value-of select="@name"/> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:if> - - <div xmlns="http://www.w3.org/1999/xhtml" class="simple-type"> - <h3> - <a name="type-{@name}"> - <xsl:value-of select="@name"/> - </a> − <xsl:value-of select="@type"/> - </h3> - <div class="docstring"> - <xsl:apply-templates select="tp:docstring"/> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - </div> - </div> - </xsl:template> - - <xsl:template match="tp:external-type"> - - <xsl:if test="not(@name) or @name = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @name on a tp:external-type </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:if test="not(@type) or @type = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @type on tp:external-type</xsl:text> - <xsl:value-of select="@name"/> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:if> - - <div xmlns="http://www.w3.org/1999/xhtml" class="external-type"> - <dt> - <a name="type-{@name}"> - <xsl:value-of select="@name"/> - </a> − <xsl:value-of select="@type"/> - </dt> - <dd>Defined by: <xsl:value-of select="@from"/></dd> - </div> - </xsl:template> - - <xsl:template match="tp:struct" mode="in-index"> - − ( <xsl:for-each select="tp:member"> - <xsl:value-of select="@type"/> - <xsl:if test="position() != last()">, </xsl:if> - </xsl:for-each> ) - </xsl:template> - - <xsl:template match="tp:mapping" mode="in-index"> - − a{ <xsl:for-each select="tp:member"> - <xsl:value-of select="@type"/> - <xsl:if test="position() != last()"> → </xsl:if> - </xsl:for-each> } - </xsl:template> - - <xsl:template match="tp:struct"> - <div xmlns="http://www.w3.org/1999/xhtml" class="struct"> - <h3> - <a name="type-{@name}"> - <xsl:value-of select="@name"/> - </a> − ( - <xsl:for-each select="tp:member"> - <xsl:value-of select="@type"/> - <xsl:text>: </xsl:text> - <xsl:value-of select="@name"/> - <xsl:if test="position() != last()">, </xsl:if> - </xsl:for-each> - ) - </h3> - <div class="docstring"> - <xsl:apply-templates select="tp:docstring"/> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - </div> - <xsl:choose> - <xsl:when test="string(@array-name) != ''"> - <p>In bindings that need a separate name, arrays of - <xsl:value-of select="@name"/> should be called - <xsl:value-of select="@array-name"/>.</p> - </xsl:when> - <xsl:otherwise> - <p>Arrays of <xsl:value-of select="@name"/> don't generally - make sense.</p> - </xsl:otherwise> - </xsl:choose> - <div> - <h4>Members</h4> - <dl> - <xsl:apply-templates select="tp:member" mode="members-in-docstring"/> - </dl> - </div> - </div> - </xsl:template> - - <xsl:template match="method"> - - <xsl:if test="not(parent::interface)"> - <xsl:message terminate="yes"> - <xsl:text>ERR: method </xsl:text> - <xsl:value-of select="@name"/> - <xsl:text> does not have an interface as parent </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:if test="not(@name) or @name = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @name on a method of </xsl:text> - <xsl:value-of select="../@name"/> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:for-each select="arg"> - <xsl:if test="not(@type) or @type = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: an arg of method </xsl:text> - <xsl:value-of select="concat(../../@name, '.', ../@name)"/> - <xsl:text> has no type</xsl:text> - </xsl:message> - </xsl:if> - <xsl:choose> - <xsl:when test="@direction='in'"> - <xsl:if test="not(@name) or @name = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: an 'in' arg of method </xsl:text> - <xsl:value-of select="concat(../../@name, '.', ../@name)"/> - <xsl:text> has no name</xsl:text> - </xsl:message> - </xsl:if> - </xsl:when> - <xsl:when test="@direction='out'"> - <!-- FIXME: This is commented out until someone with a lot of time - on their hands goes through the spec adding names to all the "out" - arguments - - <xsl:if test="not(@name) or @name = ''"> - <xsl:message terminate="no"> - <xsl:text>INFO: an 'out' arg of method </xsl:text> - <xsl:value-of select="concat(../../@name, '.', ../@name)"/> - <xsl:text> has no name</xsl:text> - </xsl:message> - </xsl:if>--> - </xsl:when> - <xsl:otherwise> - <xsl:message terminate="yes"> - <xsl:text>ERR: an arg of method </xsl:text> - <xsl:value-of select="concat(../../@name, '.', ../@name)"/> - <xsl:text> has direction neither 'in' nor 'out'</xsl:text> - </xsl:message> - </xsl:otherwise> - </xsl:choose> - </xsl:for-each> - - <div xmlns="http://www.w3.org/1999/xhtml" class="method"> - <h3 xmlns="http://www.w3.org/1999/xhtml"> - <a name="{concat(../@name, concat('.', @name))}"> - <xsl:value-of select="@name"/> - </a> ( - <xsl:for-each xmlns="" select="arg[@direction='in']"> - <xsl:value-of select="@type"/>: <xsl:value-of select="@name"/> - <xsl:if test="position() != last()">, </xsl:if> - </xsl:for-each> - ) → - <xsl:choose> - <xsl:when test="arg[@direction='out']"> - <xsl:for-each xmlns="" select="arg[@direction='out']"> - <xsl:value-of select="@type"/> - <xsl:if test="position() != last()">, </xsl:if> - </xsl:for-each> - </xsl:when> - <xsl:otherwise>nothing</xsl:otherwise> - </xsl:choose> - </h3> - <div xmlns="http://www.w3.org/1999/xhtml" class="docstring"> - <xsl:apply-templates select="tp:docstring" /> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - </div> - - <xsl:if test="arg[@direction='in']"> - <div xmlns="http://www.w3.org/1999/xhtml"> - <h4>Parameters</h4> - <dl xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="arg[@direction='in']" - mode="parameters-in-docstring"/> - </dl> - </div> - </xsl:if> - - <xsl:if test="arg[@direction='out']"> - <div xmlns="http://www.w3.org/1999/xhtml"> - <h4>Returns</h4> - <dl xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="arg[@direction='out']" - mode="returns-in-docstring"/> - </dl> - </div> - </xsl:if> - - <xsl:if test="tp:possible-errors"> - <div xmlns="http://www.w3.org/1999/xhtml"> - <h4>Possible errors</h4> - <dl xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="tp:possible-errors/tp:error"/> - </dl> - </div> - </xsl:if> - - </div> - </xsl:template> - - <xsl:template name="tp-type"> - <xsl:param name="tp-type"/> - <xsl:param name="type"/> - - <xsl:variable name="single-type"> - <xsl:choose> - <xsl:when test="contains($tp-type, '[]')"> - <xsl:value-of select="substring-before($tp-type, '[]')"/> - </xsl:when> - <xsl:otherwise> - <xsl:value-of select="$tp-type"/> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - - <xsl:variable name="type-of-tp-type"> - <xsl:if test="contains($tp-type, '[]')"> - <!-- one 'a', plus one for each [ after the [], and delete all ] --> - <xsl:value-of select="concat('a', - translate(substring-after($tp-type, '[]'), '[]', 'a'))"/> - </xsl:if> - - <xsl:choose> - <xsl:when test="//tp:simple-type[@name=$single-type]"> - <xsl:value-of select="string(//tp:simple-type[@name=$single-type]/@type)"/> - </xsl:when> - <xsl:when test="//tp:struct[@name=$single-type]"> - <xsl:text>(</xsl:text> - <xsl:for-each select="//tp:struct[@name=$single-type]/tp:member"> - <xsl:value-of select="@type"/> - </xsl:for-each> - <xsl:text>)</xsl:text> - </xsl:when> - <xsl:when test="//tp:enum[@name=$single-type]"> - <xsl:value-of select="string(//tp:enum[@name=$single-type]/@type)"/> - </xsl:when> - <xsl:when test="//tp:flags[@name=$single-type]"> - <xsl:value-of select="string(//tp:flags[@name=$single-type]/@type)"/> - </xsl:when> - <xsl:when test="//tp:mapping[@name=$single-type]"> - <xsl:text>a{</xsl:text> - <xsl:for-each select="//tp:mapping[@name=$single-type]/tp:member"> - <xsl:value-of select="@type"/> - </xsl:for-each> - <xsl:text>}</xsl:text> - </xsl:when> - <xsl:when test="//tp:external-type[@name=$single-type]"> - <xsl:value-of select="string(//tp:external-type[@name=$single-type]/@type)"/> - </xsl:when> - <xsl:otherwise> - <xsl:message terminate="yes"> - <xsl:text>ERR: Unable to find type '</xsl:text> - <xsl:value-of select="$tp-type"/> - <xsl:text>' </xsl:text> - </xsl:message> - </xsl:otherwise> - </xsl:choose> - </xsl:variable> - - <xsl:if test="string($type) != '' and - string($type-of-tp-type) != string($type)"> - <xsl:message terminate="yes"> - <xsl:text>ERR: tp:type '</xsl:text> - <xsl:value-of select="$tp-type"/> - <xsl:text>' has D-Bus type '</xsl:text> - <xsl:value-of select="$type-of-tp-type"/> - <xsl:text>' but has been used with type='</xsl:text> - <xsl:value-of select="$type"/> - <xsl:text>' </xsl:text> - </xsl:message> - </xsl:if> - - <a href="#type-{$single-type}"><xsl:value-of select="$tp-type"/></a> - - </xsl:template> - - <xsl:template name="parenthesized-tp-type"> - <xsl:if test="@tp:type"> - <xsl:text> (</xsl:text> - <xsl:call-template name="tp-type"> - <xsl:with-param name="tp-type" select="@tp:type"/> - <xsl:with-param name="type" select="@type"/> - </xsl:call-template> - <xsl:text>)</xsl:text> - </xsl:if> - </xsl:template> - - <xsl:template match="tp:member" mode="members-in-docstring"> - <dt xmlns="http://www.w3.org/1999/xhtml"> - <code><xsl:value-of select="@name"/></code> − - <code><xsl:value-of select="@type"/></code> - <xsl:call-template name="parenthesized-tp-type"/> - </dt> - <dd xmlns="http://www.w3.org/1999/xhtml"> - <xsl:choose> - <xsl:when test="tp:docstring"> - <xsl:apply-templates select="tp:docstring" /> - </xsl:when> - <xsl:otherwise> - <em>(undocumented)</em> - </xsl:otherwise> - </xsl:choose> - </dd> - </xsl:template> - - <xsl:template match="arg" mode="parameters-in-docstring"> - <dt xmlns="http://www.w3.org/1999/xhtml"> - <code><xsl:value-of select="@name"/></code> − - <code><xsl:value-of select="@type"/></code> - <xsl:call-template name="parenthesized-tp-type"/> - </dt> - <dd xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="tp:docstring" /> - </dd> - </xsl:template> - - <xsl:template match="arg" mode="returns-in-docstring"> - <dt xmlns="http://www.w3.org/1999/xhtml"> - <xsl:if test="@name"> - <code><xsl:value-of select="@name"/></code> − - </xsl:if> - <code><xsl:value-of select="@type"/></code> - <xsl:call-template name="parenthesized-tp-type"/> - </dt> - <dd xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="tp:docstring"/> - </dd> - </xsl:template> - - <xsl:template match="tp:possible-errors/tp:error"> - <dt xmlns="http://www.w3.org/1999/xhtml"> - <code><xsl:value-of select="@name"/></code> - </dt> - <dd xmlns="http://www.w3.org/1999/xhtml"> - <xsl:variable name="name" select="@name"/> - <xsl:choose> - <xsl:when test="tp:docstring"> - <xsl:apply-templates select="tp:docstring"/> - </xsl:when> - <xsl:when test="//tp:errors/tp:error[concat(../@namespace, '.', translate(@name, ' ', ''))=$name]/tp:docstring"> - <xsl:apply-templates select="//tp:errors/tp:error[concat(../@namespace, '.', translate(@name, ' ', ''))=$name]/tp:docstring"/> <em xmlns="http://www.w3.org/1999/xhtml">(generic description)</em> - </xsl:when> - <xsl:otherwise> - (Undocumented.) - </xsl:otherwise> - </xsl:choose> - </dd> - </xsl:template> - - <xsl:template match="signal"> - - <xsl:if test="not(parent::interface)"> - <xsl:message terminate="yes"> - <xsl:text>ERR: signal </xsl:text> - <xsl:value-of select="@name"/> - <xsl:text> does not have an interface as parent </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:if test="not(@name) or @name = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: missing @name on a signal of </xsl:text> - <xsl:value-of select="../@name"/> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:if> - - <xsl:for-each select="arg"> - <xsl:if test="not(@type) or @type = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: an arg of signal </xsl:text> - <xsl:value-of select="concat(../../@name, '.', ../@name)"/> - <xsl:text> has no type</xsl:text> - </xsl:message> - </xsl:if> - <xsl:if test="not(@name) or @name = ''"> - <xsl:message terminate="yes"> - <xsl:text>ERR: an arg of signal </xsl:text> - <xsl:value-of select="concat(../../@name, '.', ../@name)"/> - <xsl:text> has no name</xsl:text> - </xsl:message> - </xsl:if> - <xsl:choose> - <xsl:when test="not(@direction)"/> - <xsl:when test="@direction='in'"> - <xsl:message terminate="no"> - <xsl:text>INFO: an arg of signal </xsl:text> - <xsl:value-of select="concat(../../@name, '.', ../@name)"/> - <xsl:text> has unnecessary direction 'in'</xsl:text> - </xsl:message> - </xsl:when> - <xsl:otherwise> - <xsl:message terminate="yes"> - <xsl:text>ERR: an arg of signal </xsl:text> - <xsl:value-of select="concat(../../@name, '.', ../@name)"/> - <xsl:text> has direction other than 'in'</xsl:text> - </xsl:message> - </xsl:otherwise> - </xsl:choose> - </xsl:for-each> - - <div xmlns="http://www.w3.org/1999/xhtml" class="signal"> - <h3 xmlns="http://www.w3.org/1999/xhtml"> - <a name="{concat(../@name, concat('.', @name))}"> - <xsl:value-of select="@name"/> - </a> ( - <xsl:for-each xmlns="" select="arg"> - <xsl:value-of select="@type"/>: <xsl:value-of select="@name"/> - <xsl:if test="position() != last()">, </xsl:if> - </xsl:for-each> - )</h3> - - <div xmlns="http://www.w3.org/1999/xhtml" class="docstring"> - <xsl:apply-templates select="tp:docstring"/> - <xsl:apply-templates select="tp:added"/> - <xsl:apply-templates select="tp:changed"/> - <xsl:apply-templates select="tp:deprecated"/> - </div> - - <xsl:if test="arg"> - <div xmlns="http://www.w3.org/1999/xhtml"> - <h4>Parameters</h4> - <dl xmlns="http://www.w3.org/1999/xhtml"> - <xsl:apply-templates select="arg" mode="parameters-in-docstring"/> - </dl> - </div> - </xsl:if> - </div> - </xsl:template> - - <xsl:output method="xml" indent="no" encoding="ascii" - omit-xml-declaration="yes" - doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" - doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" /> - - <xsl:template match="/tp:spec"> - <html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <title> - <xsl:value-of select="tp:title"/> - <xsl:if test="tp:version"> - <xsl:text> version </xsl:text> - <xsl:value-of select="tp:version"/> - </xsl:if> - </title> - <style type="text/css"> - - body { - font-family: sans-serif; - margin: 2em; - height: 100%; - font-size: 1.2em; - } - h1 { - padding-top: 5px; - padding-bottom: 5px; - font-size: 1.6em; - background: #dadae2; - } - h2 { - font-size: 1.3em; - } - h3 { - font-size: 1.2em; - } - a:link, a:visited, a:link:hover, a:visited:hover { - font-weight: bold; - } - .topbox { - padding-top: 10px; - padding-left: 10px; - border-bottom: black solid 1px; - padding-bottom: 10px; - background: #dadae2; - font-size: 2em; - font-weight: bold; - color: #5c5c5c; - } - .topnavbox { - padding-left: 10px; - padding-top: 5px; - padding-bottom: 5px; - background: #abacba; - border-bottom: black solid 1px; - font-size: 1.2em; - } - .topnavbox a{ - color: black; - font-weight: normal; - } - .sidebar { - float: left; - /* width:9em; - border-right:#abacba solid 1px; - border-left: #abacba solid 1px; - height:100%; */ - border: #abacba solid 1px; - padding-left: 10px; - margin-left: 10px; - padding-right: 10px; - margin-right: 10px; - color: #5d5d5d; - background: #dadae2; - } - .sidebar a { - text-decoration: none; - border-bottom: #e29625 dotted 1px; - color: #e29625; - font-weight: normal; - } - .sidebar h1 { - font-size: 1.2em; - color: black; - } - .sidebar ul { - padding-left: 25px; - padding-bottom: 10px; - border-bottom: #abacba solid 1px; - } - .sidebar li { - padding-top: 2px; - padding-bottom: 2px; - } - .sidebar h2 { - font-style:italic; - font-size: 0.81em; - padding-left: 5px; - padding-right: 5px; - font-weight: normal; - } - .date { - font-size: 0.6em; - float: right; - font-style: italic; - } - .method, .signal, .property { - margin-left: 1em; - margin-right: 4em; - } - .rationale { - font-style: italic; - border-left: 0.25em solid #808080; - padding-left: 0.5em; - } - - .added { - color: #006600; - background: #ffffff; - } - .deprecated { - color: #ff0000; - background: #ffffff; - } - table, tr, td, th { - border: 1px solid #666; - } - - </style> - </head> - <body> - <h1 class="topbox"> - <xsl:value-of select="tp:title" /> - </h1> - <xsl:if test="tp:version"> - <h2>Version <xsl:value-of select="string(tp:version)"/></h2> - </xsl:if> - <xsl:apply-templates select="tp:copyright"/> - <xsl:apply-templates select="tp:license"/> - <xsl:apply-templates select="tp:docstring"/> - - <h2>Interfaces</h2> - <ul> - <xsl:for-each select="//node/interface"> - <li><code><a href="#{@name}"><xsl:value-of select="@name"/></a></code></li> - </xsl:for-each> - </ul> - - <xsl:apply-templates select="//node"/> - <xsl:apply-templates select="tp:generic-types"/> - <xsl:apply-templates select="tp:errors"/> - - <h1>Index</h1> - <h2>Index of interfaces</h2> - <ul> - <xsl:for-each select="//node/interface"> - <li><code><a href="#{@name}"><xsl:value-of select="@name"/></a></code></li> - </xsl:for-each> - </ul> - <h2>Index of types</h2> - <ul> - <xsl:for-each select="//tp:simple-type | //tp:enum | //tp:flags | //tp:mapping | //tp:struct | //tp:external-type"> - <xsl:sort select="@name"/> - <li> - <code> - <a href="#type-{@name}"> - <xsl:value-of select="@name"/> - </a> - </code> - <xsl:apply-templates mode="in-index" select="."/> - </li> - </xsl:for-each> - </ul> - </body> - </html> - </xsl:template> - - <xsl:template match="node"> - <xsl:apply-templates /> - </xsl:template> - - <xsl:template match="text()"> - <xsl:if test="normalize-space(.) != ''"> - <xsl:message terminate="yes"> - <xsl:text>Stray text: {{{</xsl:text> - <xsl:value-of select="." /> - <xsl:text>}}} </xsl:text> - </xsl:message> - </xsl:if> - </xsl:template> - - <xsl:template match="*"> - <xsl:message terminate="yes"> - <xsl:text>Unrecognised element: {</xsl:text> - <xsl:value-of select="namespace-uri(.)" /> - <xsl:text>}</xsl:text> - <xsl:value-of select="local-name(.)" /> - <xsl:text> </xsl:text> - </xsl:message> - </xsl:template> -</xsl:stylesheet> - -<!-- vim:set sw=2 sts=2 et: --> diff --git a/tools/glib-blocking-client-gen.py b/tools/glib-blocking-client-gen.py deleted file mode 100644 index c20dcd4a..00000000 --- a/tools/glib-blocking-client-gen.py +++ /dev/null @@ -1,1014 +0,0 @@ -#!/usr/bin/python - -# glib-client-gen.py: "I Can't Believe It's Not dbus-binding-tool" -# -# Generate GLib client wrappers from the Telepathy specification. -# The master copy of this program is in the telepathy-glib repository - -# please make any changes there. -# -# Copyright (C) 2006-2008 Collabora Ltd. <http://www.collabora.co.uk/> -# -# 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 - -import sys -import os.path -import xml.dom.minidom -from getopt import gnu_getopt - -from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \ - camelcase_to_lower, get_docstring - - -NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - -class Generator(object): - - def __init__(self, dom, prefix, basename, opts): - self.dom = dom - self.__header = [] - self.__body = [] - - self.prefix_lc = prefix.lower() - self.prefix_uc = prefix.upper() - self.prefix_mc = prefix.replace('_', '') - self.basename = basename - self.group = opts.get('--group', None) - self.iface_quark_prefix = opts.get('--iface-quark-prefix', None) - self.proxy_cls = opts.get('--subclass', 'TpProxy') + ' *' - self.proxy_arg = opts.get('--subclass', 'void') + ' *' - self.proxy_assert = opts.get('--subclass-assert', 'TP_IS_PROXY') - self.proxy_doc = ('A #%s or subclass' - % opts.get('--subclass', 'TpProxy')) - if self.proxy_arg == 'void *': - self.proxy_arg = 'gpointer ' - - def h(self, s): - self.__header.append(s) - - def b(self, s): - self.__body.append(s) - - def get_iface_quark(self): - assert self.iface_dbus is not None - assert self.iface_uc is not None - if self.iface_quark_prefix is None: - return 'g_quark_from_static_string (\"%s\")' % self.iface_dbus - else: - return '%s_%s' % (self.iface_quark_prefix, self.iface_uc) - - def do_signal(self, iface, signal): - iface_lc = iface.lower() - - member = signal.getAttribute('name') - member_lc = camelcase_to_lower(member) - member_uc = member_lc.upper() - - arg_count = 0 - args = [] - out_args = [] - - for arg in signal.getElementsByTagName('arg'): - name = arg.getAttribute('name') - type = arg.getAttribute('type') - tp_type = arg.getAttribute('tp:type') - - if not name: - name = 'arg%u' % arg_count - arg_count += 1 - else: - name = 'arg_%s' % name - - info = type_to_gtype(type) - args.append((name, info, tp_type, arg)) - - callback_name = ('%s_%s_signal_callback_%s' - % (self.prefix_lc, iface_lc, member_lc)) - collect_name = ('_%s_%s_collect_args_of_%s' - % (self.prefix_lc, iface_lc, member_lc)) - invoke_name = ('_%s_%s_invoke_callback_for_%s' - % (self.prefix_lc, iface_lc, member_lc)) - - # Example: - # - # typedef void (*tp_cli_connection_signal_callback_new_channel) - # (TpConnection *proxy, const gchar *arg_object_path, - # const gchar *arg_channel_type, guint arg_handle_type, - # guint arg_handle, gboolean arg_suppress_handler, - # gpointer user_data, GObject *weak_object); - - self.b('/**') - self.b(' * %s:' % callback_name) - self.b(' * @proxy: The proxy on which %s_%s_connect_to_%s ()' - % (self.prefix_lc, iface_lc, member_lc)) - self.b(' * was called') - - for arg in args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' * @%s: <![CDATA[%s]]>' % (name, - get_docstring(elt) or '(Undocumented)')) - - self.b(' * @user_data: User-supplied data') - self.b(' * @weak_object: User-supplied weakly referenced object') - self.b(' *') - self.b(' * Represents the signature of a callback for the signal %s.' - % member) - self.b(' */') - self.h('typedef void (*%s) (%sproxy,' - % (callback_name, self.proxy_cls)) - - for arg in args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.h(' %s%s%s,' % (const, ctype, name)) - - self.h(' gpointer user_data, GObject *weak_object);') - - if args: - self.b('static void') - self.b('%s (DBusGProxy *proxy,' % collect_name) - - for arg in args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.b(' %s%s%s,' % (const, ctype, name)) - - self.b(' TpProxySignalConnection *sc)') - self.b('{') - self.b(' GValueArray *args = g_value_array_new (%d);' % len(args)) - self.b(' GValue blank = { 0 };') - self.b(' guint i;') - self.b('') - self.b(' g_value_init (&blank, G_TYPE_INT);') - self.b('') - self.b(' for (i = 0; i < %d; i++)' % len(args)) - self.b(' g_value_array_append (args, &blank);') - self.b('') - - for i, arg in enumerate(args): - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' g_value_unset (args->values + %d);' % i) - self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) - - if gtype == 'G_TYPE_STRING': - self.b(' g_value_set_string (args->values + %d, %s);' - % (i, name)) - elif marshaller == 'BOXED': - self.b(' g_value_set_boxed (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_UCHAR': - self.b(' g_value_set_uchar (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_BOOLEAN': - self.b(' g_value_set_boolean (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_INT': - self.b(' g_value_set_int (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_UINT': - self.b(' g_value_set_uint (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_INT64': - self.b(' g_value_set_int (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_UINT64': - self.b(' g_value_set_uint (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_DOUBLE': - self.b(' g_value_set_double (args->values + %d, %s);' - % (i, name)) - else: - assert False, ("Don't know how to put %s in a GValue" - % gtype) - self.b('') - - self.b(' tp_proxy_signal_connection_v0_take_results (sc, args);') - self.b('}') - - self.b('static void') - self.b('%s (TpProxy *tpproxy,' % invoke_name) - self.b(' GError *error,') - self.b(' GValueArray *args,') - self.b(' GCallback generic_callback,') - self.b(' gpointer user_data,') - self.b(' GObject *weak_object)') - self.b('{') - self.b(' %s callback =' % callback_name) - self.b(' (%s) generic_callback;' % callback_name) - self.b('') - self.b(' if (callback != NULL)') - self.b(' callback (g_object_ref (tpproxy),') - - # FIXME: factor out into a function - for i, arg in enumerate(args): - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - if marshaller == 'BOXED': - self.b(' g_value_get_boxed (args->values + %d),' % i) - elif gtype == 'G_TYPE_STRING': - self.b(' g_value_get_string (args->values + %d),' % i) - elif gtype == 'G_TYPE_UCHAR': - self.b(' g_value_get_uchar (args->values + %d),' % i) - elif gtype == 'G_TYPE_BOOLEAN': - self.b(' g_value_get_boolean (args->values + %d),' % i) - elif gtype == 'G_TYPE_UINT': - self.b(' g_value_get_uint (args->values + %d),' % i) - elif gtype == 'G_TYPE_INT': - self.b(' g_value_get_int (args->values + %d),' % i) - elif gtype == 'G_TYPE_UINT64': - self.b(' g_value_get_uint64 (args->values + %d),' % i) - elif gtype == 'G_TYPE_INT64': - self.b(' g_value_get_int64 (args->values + %d),' % i) - elif gtype == 'G_TYPE_DOUBLE': - self.b(' g_value_get_double (args->values + %d),' % i) - else: - assert False, "Don't know how to get %s from a GValue" % gtype - - self.b(' user_data,') - self.b(' weak_object);') - self.b('') - - if len(args) > 0: - self.b(' g_value_array_free (args);') - else: - self.b(' if (args != NULL)') - self.b(' g_value_array_free (args);') - self.b('') - - self.b(' g_object_unref (tpproxy);') - self.b('}') - - # Example: - # - # TpProxySignalConnection * - # tp_cli_connection_connect_to_new_channel - # (TpConnection *proxy, - # tp_cli_connection_signal_callback_new_channel callback, - # gpointer user_data, - # GDestroyNotify destroy); - # - # destroy is invoked when the signal becomes disconnected. This - # is either because the signal has been disconnected explicitly - # by the user, because the TpProxy has become invalid and - # emitted the 'invalidated' signal, or because the weakly referenced - # object has gone away. - - self.b('/**') - self.b(' * %s_%s_connect_to_%s:' - % (self.prefix_lc, iface_lc, member_lc)) - self.b(' * @proxy: %s' % self.proxy_doc) - self.b(' * @callback: Callback to be called when the signal is') - self.b(' * received') - self.b(' * @user_data: User-supplied data for the callback') - self.b(' * @destroy: Destructor for the user-supplied data, which') - self.b(' * will be called when this signal is disconnected, or') - self.b(' * before this function returns %NULL') - self.b(' * @weak_object: A #GObject which will be weakly referenced; ') - self.b(' * if it is destroyed, this callback will automatically be') - self.b(' * disconnected') - self.b(' * @error: If not %NULL, used to raise an error if %NULL is') - self.b(' * returned') - self.b(' *') - self.b(' * Connect a handler to the signal %s.' % member) - self.b(' *') - self.b(' * <![CDATA[%s]]>' - % (get_docstring(signal) or '(Undocumented)')) - self.b(' *') - self.b(' * Returns: a #TpProxySignalConnection containing all of the') - self.b(' * above, which can be used to disconnect the signal; or') - self.b(' * %NULL if the proxy does not have the desired interface') - self.b(' * or has become invalid.') - self.b(' */') - self.h('TpProxySignalConnection *%s_%s_connect_to_%s (%sproxy,' - % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) - self.h(' %s callback,' % callback_name) - self.h(' gpointer user_data,') - self.h(' GDestroyNotify destroy,') - self.h(' GObject *weak_object,') - self.h(' GError **error);') - - self.b('TpProxySignalConnection *') - self.b('%s_%s_connect_to_%s (%sproxy,' - % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) - self.b(' %s callback,' % callback_name) - self.b(' gpointer user_data,') - self.b(' GDestroyNotify destroy,') - self.b(' GObject *weak_object,') - self.b(' GError **error)') - self.b('{') - self.b(' GType expected_types[%d] = {' % (len(args) + 1)) - - for arg in args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' %s,' % gtype) - - self.b(' G_TYPE_INVALID };') - self.b('') - self.b(' g_return_val_if_fail (%s (proxy), NULL);' - % self.proxy_assert) - self.b(' g_return_val_if_fail (callback != NULL, NULL);') - self.b('') - self.b(' return tp_proxy_signal_connection_v0_new ((TpProxy *) proxy,') - self.b(' %s, \"%s\",' % (self.get_iface_quark(), member)) - self.b(' expected_types,') - - if args: - self.b(' G_CALLBACK (%s),' % collect_name) - else: - self.b(' NULL, /* no args => no collector function */') - - self.b(' %s,' % invoke_name) - self.b(' G_CALLBACK (callback), user_data, destroy,') - self.b(' weak_object, error);') - self.b('}') - self.b('') - - self.h('') - - def do_method(self, iface, method): - iface_lc = iface.lower() - - member = method.getAttribute('name') - member_lc = camelcase_to_lower(member) - member_uc = member_lc.upper() - - in_count = 0 - ret_count = 0 - in_args = [] - out_args = [] - - for arg in method.getElementsByTagName('arg'): - name = arg.getAttribute('name') - direction = arg.getAttribute('direction') - type = arg.getAttribute('type') - tp_type = arg.getAttribute('tp:type') - - if direction != 'out': - if not name: - name = 'in%u' % in_count - in_count += 1 - else: - name = 'in_%s' % name - else: - if not name: - name = 'out%u' % ret_count - ret_count += 1 - else: - name = 'out_%s' % name - - info = type_to_gtype(type) - if direction != 'out': - in_args.append((name, info, tp_type, arg)) - else: - out_args.append((name, info, tp_type, arg)) - - # Async reply callback type - - # Example: - # void (*tp_cli_properties_interface_callback_for_get_properties) - # (TpProxy *proxy, - # const GPtrArray *out0, - # const GError *error, - # gpointer user_data, - # GObject *weak_object); - - self.b('/**') - self.b(' * %s_%s_callback_for_%s:' - % (self.prefix_lc, iface_lc, member_lc)) - self.b(' * @proxy: the proxy on which the call was made') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' * @%s: Used to return an \'out\' argument if @error is ' - '%%NULL: <![CDATA[%s]]>' - % (name, get_docstring(elt) or '(Undocumented)')) - - self.b(' * @error: %NULL on success, or an error on failure') - self.b(' * @user_data: user-supplied data') - self.b(' * @weak_object: user-supplied object') - self.b(' *') - self.b(' * Signature of the callback called when a %s method call' - % member) - self.b(' * succeeds or fails.') - self.b(' */') - - callback_name = '%s_%s_callback_for_%s' % (self.prefix_lc, iface_lc, - member_lc) - - self.h('typedef void (*%s) (%sproxy,' - % (callback_name, self.proxy_cls)) - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - const = pointer and 'const ' or '' - - self.h(' %s%s%s,' % (const, ctype, name)) - - self.h(' const GError *error, gpointer user_data,') - self.h(' GObject *weak_object);') - self.h('') - - # Async callback implementation - - invoke_callback = '_%s_%s_invoke_callback_%s' % (self.prefix_lc, - iface_lc, - member_lc) - - collect_callback = '_%s_%s_collect_callback_%s' % (self.prefix_lc, - iface_lc, - member_lc) - - # The callback called by dbus-glib; this ends the call and collects - # the results into a GValueArray. - self.b('static void') - self.b('%s (DBusGProxy *proxy,' % collect_callback) - self.b(' DBusGProxyCall *call,') - self.b(' gpointer user_data)') - self.b('{') - self.b(' GError *error = NULL;') - - if len(out_args) > 0: - self.b(' GValueArray *args;') - self.b(' GValue blank = { 0 };') - self.b(' guint i;') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' %s%s;' % (ctype, name)) - - self.b('') - self.b(' dbus_g_proxy_end_call (proxy, call, &error,') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' %s, &%s,' % (gtype, name)) - - self.b(' G_TYPE_INVALID);') - - if len(out_args) == 0: - self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,' - 'NULL);') - else: - self.b('') - self.b(' if (error != NULL)') - self.b(' {') - self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,') - self.b(' NULL);') - self.b(' return;') - self.b(' }') - self.b('') - self.b(' args = g_value_array_new (%d);' % len(out_args)) - self.b(' g_value_init (&blank, G_TYPE_INT);') - self.b('') - self.b(' for (i = 0; i < %d; i++)' % len(out_args)) - self.b(' g_value_array_append (args, &blank);') - - for i, arg in enumerate(out_args): - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b('') - self.b(' g_value_unset (args->values + %d);' % i) - self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) - - if gtype == 'G_TYPE_STRING': - self.b(' g_value_take_string (args->values + %d, %s);' - % (i, name)) - elif marshaller == 'BOXED': - self.b(' g_value_take_boxed (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_UCHAR': - self.b(' g_value_set_uchar (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_BOOLEAN': - self.b(' g_value_set_boolean (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_INT': - self.b(' g_value_set_int (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_UINT': - self.b(' g_value_set_uint (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_INT64': - self.b(' g_value_set_int (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_UINT64': - self.b(' g_value_set_uint (args->values + %d, %s);' - % (i, name)) - elif gtype == 'G_TYPE_DOUBLE': - self.b(' g_value_set_double (args->values + %d, %s);' - % (i, name)) - else: - assert False, ("Don't know how to put %s in a GValue" - % gtype) - - self.b(' tp_proxy_pending_call_v0_take_results (user_data, ' - 'NULL, args);') - - self.b('}') - - self.b('static void') - self.b('%s (TpProxy *self,' % invoke_callback) - self.b(' GError *error,') - self.b(' GValueArray *args,') - self.b(' GCallback generic_callback,') - self.b(' gpointer user_data,') - self.b(' GObject *weak_object)') - self.b('{') - self.b(' %s callback = (%s) generic_callback;' - % (callback_name, callback_name)) - self.b('') - self.b(' if (error != NULL)') - self.b(' {') - self.b(' callback ((%s) self,' % self.proxy_cls) - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - if marshaller == 'BOXED' or pointer: - self.b(' NULL,') - elif gtype == 'G_TYPE_DOUBLE': - self.b(' 0.0,') - else: - self.b(' 0,') - - self.b(' error, user_data, weak_object);') - self.b(' g_error_free (error);') - self.b(' return;') - self.b(' }') - - self.b(' callback ((%s) self,' % self.proxy_cls) - - # FIXME: factor out into a function - for i, arg in enumerate(out_args): - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - if marshaller == 'BOXED': - self.b(' g_value_get_boxed (args->values + %d),' % i) - elif gtype == 'G_TYPE_STRING': - self.b(' g_value_get_string (args->values + %d),' % i) - elif gtype == 'G_TYPE_UCHAR': - self.b(' g_value_get_uchar (args->values + %d),' % i) - elif gtype == 'G_TYPE_BOOLEAN': - self.b(' g_value_get_boolean (args->values + %d),' % i) - elif gtype == 'G_TYPE_UINT': - self.b(' g_value_get_uint (args->values + %d),' % i) - elif gtype == 'G_TYPE_INT': - self.b(' g_value_get_int (args->values + %d),' % i) - elif gtype == 'G_TYPE_UINT64': - self.b(' g_value_get_uint64 (args->values + %d),' % i) - elif gtype == 'G_TYPE_INT64': - self.b(' g_value_get_int64 (args->values + %d),' % i) - elif gtype == 'G_TYPE_DOUBLE': - self.b(' g_value_get_double (args->values + %d),' % i) - else: - assert False, "Don't know how to get %s from a GValue" % gtype - - self.b(' error, user_data, weak_object);') - self.b('') - - if len(out_args) > 0: - self.b(' g_value_array_free (args);') - else: - self.b(' if (args != NULL)') - self.b(' g_value_array_free (args);') - - self.b('}') - self.b('') - - # Async stub - - # Example: - # TpProxyPendingCall * - # tp_cli_properties_interface_call_get_properties - # (gpointer proxy, - # gint timeout_ms, - # const GArray *in_properties, - # tp_cli_properties_interface_callback_for_get_properties callback, - # gpointer user_data, - # GDestroyNotify *destructor); - - self.h('TpProxyPendingCall *%s_%s_call_%s (%sproxy,' - % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) - self.h(' gint timeout_ms,') - - self.b('/**') - self.b(' * %s_%s_call_%s:' - % (self.prefix_lc, iface_lc, member_lc)) - self.b(' * @proxy: the #TpProxy') - self.b(' * @timeout_ms: the timeout in milliseconds, or -1 to use the') - self.b(' * default') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' * @%s: Used to pass an \'in\' argument: <![CDATA[%s]]>' - % (name, get_docstring(elt) or '(Undocumented)')) - - self.b(' * @callback: called when the method call succeeds or fails') - self.b(' * @user_data: user-supplied data passed to the callback') - self.b(' * @destroy: called with the user_data as argument, after the') - self.b(' * call has succeeded, failed or been cancelled') - self.b(' * @weak_object: A #GObject which will be weakly referenced; ') - self.b(' * if it is destroyed, this callback will automatically be') - self.b(' * disconnected') - self.b(' *') - self.b(' * Start a %s method call.' % member) - self.b(' *') - self.b(' * <![CDATA[%s]]>' - % (get_docstring(method) or '(Undocumented)')) - self.b(' *') - self.b(' * Returns: a #TpProxyPendingCall representing the call in') - self.b(' * progress. It is borrowed from the object, and will become') - self.b(' * invalid when the callback is called, the call is') - self.b(' * cancelled or the #TpProxy becomes invalid.') - self.b(' */') - self.b('TpProxyPendingCall *\n%s_%s_call_%s (%sproxy,' - % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) - self.b(' gint timeout_ms,') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.h(' %s%s%s,' % (const, ctype, name)) - self.b(' %s%s%s,' % (const, ctype, name)) - - self.h(' %s callback,' % callback_name) - self.h(' gpointer user_data,') - self.h(' GDestroyNotify destroy,') - self.h(' GObject *weak_object);') - self.h('') - - self.b(' %s callback,' % callback_name) - self.b(' gpointer user_data,') - self.b(' GDestroyNotify destroy,') - self.b(' GObject *weak_object)') - self.b('{') - self.b(' GError *error = NULL;') - self.b(' GQuark interface = %s;' % self.get_iface_quark()) - self.b(' DBusGProxy *iface;') - self.b('') - self.b(' g_return_val_if_fail (%s (proxy), NULL);' - % self.proxy_assert) - self.b('') - self.b(' iface = tp_proxy_borrow_interface_by_id (') - self.b(' (TpProxy *) proxy,') - self.b(' interface, &error);') - self.b('') - self.b(' if (iface == NULL)') - self.b(' {') - self.b(' if (callback != NULL)') - self.b(' callback (proxy,') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - if pointer: - self.b(' NULL,') - else: - self.b(' 0,') - - self.b(' error, user_data, weak_object);') - self.b(' g_error_free (error);') - self.b(' return NULL;') - self.b(' }') - self.b('') - self.b(' if (callback == NULL)') - self.b(' {') - self.b(' dbus_g_proxy_call_no_reply (iface, "%s",' % member) - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.b(' %s, %s,' % (gtype, name)) - - self.b(' G_TYPE_INVALID);') - self.b(' return NULL;') - self.b(' }') - self.b(' else') - self.b(' {') - self.b(' TpProxyPendingCall *data;') - self.b('') - self.b(' data = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') - self.b(' interface, "%s", iface,' % member) - self.b(' %s,' % invoke_callback) - self.b(' G_CALLBACK (callback), user_data, destroy,') - self.b(' weak_object, FALSE);') - self.b(' tp_proxy_pending_call_v0_take_pending_call (data,') - self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') - self.b(' "%s",' % member) - self.b(' %s,' % collect_callback) - self.b(' data,') - self.b(' tp_proxy_pending_call_v0_completed,') - self.b(' timeout_ms,') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.b(' %s, %s,' % (gtype, name)) - - self.b(' G_TYPE_INVALID));') - self.b('') - self.b(' return data;') - self.b(' }') - self.b('}') - self.b('') - - # Non reentrant blocking calls - # Example: - # gboolean tp_cli_properties_interface_do_get_properties - # (gpointer proxy, - # gint timeout_ms, - # const GArray *in_properties, - # GPtrArray **out0, - # GError **error); - - self.h('gboolean %s_%s_do_%s (%sproxy,' - % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) - self.h(' gint timeout_ms,') - - self.b('/**') - self.b(' * %s_%s_do_%s:' % (self.prefix_lc, iface_lc, member_lc)) - self.b(' * @proxy: %s' % self.proxy_doc) - self.b(' * @timeout_ms: Timeout in milliseconds, or -1 for default') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' * @%s: Used to pass an \'in\' argument: <![CDATA[%s]]>' - % (name, get_docstring(elt) or '(Undocumented)')) - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' * @%s: Used to return an \'out\' argument if %%TRUE is ' - 'returned: <![CDATA[%s]]>' - % (name, get_docstring(elt) or '(Undocumented)')) - - self.b(' * @error: If not %NULL, used to return errors if %FALSE ') - self.b(' * is returned') - self.b(' *') - self.b(' * Call the method %s and block' % member) - self.b(' * until it returns.') - self.b(' *') - self.b(' * <![CDATA[%s]]>' - % (get_docstring(method) or '(Undocumented)')) - self.b(' *') - self.b(' * Returns: TRUE on success, FALSE and sets @error on error') - self.b(' */') - self.b('gboolean\n%s_%s_do_%s (%sproxy,' - % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) - self.b(' gint timeout_ms,') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.h(' %s%s%s,' % (const, ctype, name)) - self.b(' %s%s%s,' % (const, ctype, name)) - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.h(' %s*%s,' % (ctype, name)) - self.b(' %s*%s,' % (ctype, name)) - - self.h(' GError **error);') - self.h('') - - self.b(' GError **error)') - self.b('{') - self.b(' DBusGProxy *iface;') - self.b(' GQuark interface = %s;' % self.get_iface_quark()) - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' %si_%s;' % (ctype, name)) - self.b('') - self.b(' g_return_val_if_fail (%s (proxy), FALSE);' - % self.proxy_assert) - self.b('') - self.b(' iface = tp_proxy_borrow_interface_by_id') - self.b(' ((TpProxy *) proxy, interface, error);') - self.b('') - self.b(' if (iface == NULL)') - self.b(' return FALSE;') - self.b('') - self.b(' if (dbus_g_proxy_call_with_timeout (iface,') - self.b(' "%s",' % member) - self.b(' timeout_ms,') - self.b(' error,') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.b(' %s, %s,' % (gtype, name)) - - self.b(' G_TYPE_INVALID,') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' %s, &i_%s,' % (gtype, name)) - self.b(' G_TYPE_INVALID))') - self.b(' {') - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' *%s = i_%s;' % (name, name)) - self.b(' return TRUE;') - self.b(' }') - self.b(' else') - self.b(' return FALSE;') - self.b('}') - self.b('') - - # leave a gap for the end of the method - self.b('') - self.h('') - - def do_signal_add(self, signal): - marshaller_items = [] - gtypes = [] - - for i in signal.getElementsByTagName('arg'): - name = i.getAttribute('name') - type = i.getAttribute('type') - info = type_to_gtype(type) - # type, GType, STRING, is a pointer - gtypes.append(info[1]) - - self.b(' dbus_g_proxy_add_signal (proxy, "%s",' - % signal.getAttribute('name')) - for gtype in gtypes: - self.b(' %s,' % gtype) - self.b(' G_TYPE_INVALID);') - - def do_interface(self, node): - ifaces = node.getElementsByTagName('interface') - assert len(ifaces) == 1 - iface = ifaces[0] - name = node.getAttribute('name').replace('/', '') - - self.iface = name - self.iface_lc = name.lower() - self.iface_uc = name.upper() - self.iface_mc = name.replace('_', '') - self.iface_dbus = iface.getAttribute('name') - - signals = node.getElementsByTagName('signal') - methods = node.getElementsByTagName('method') - - self.b('static inline void') - self.b('%s_add_signals_for_%s (DBusGProxy *proxy)' - % (self.prefix_lc, name.lower())) - self.b('{') - - for signal in signals: - self.do_signal_add(signal) - - self.b('}') - self.b('') - self.b('') - - for signal in signals: - self.do_signal(name, signal) - - for method in methods: - self.do_method(name, method) - - self.iface_dbus = None - - def __call__(self): - - self.h('G_BEGIN_DECLS') - self.h('') - - self.b('/* We don\'t want gtkdoc scanning this file, it\'ll get') - self.b(' * confused by seeing function definitions, so mark it as: */') - self.b('/*<private_header>*/') - self.b('') - - nodes = self.dom.getElementsByTagName('node') - nodes.sort(cmp_by_name) - - for node in nodes: - self.do_interface(node) - - if self.group is not None: - - self.b('/*') - self.b(' * %s_%s_add_signals:' % (self.prefix_lc, self.group)) - self.b(' * @self: the #TpProxy') - self.b(' * @quark: a quark whose string value is the interface') - self.b(' * name whose signals should be added') - self.b(' * @proxy: the D-Bus proxy to which to add the signals') - self.b(' * @unused: not used for anything') - self.b(' *') - self.b(' * Tell dbus-glib that @proxy has the signatures of all') - self.b(' * signals on the given interface, if it\'s one we') - self.b(' * support.') - self.b(' *') - self.b(' * This function should be used as a signal handler for') - self.b(' * #TpProxy::interface-added.') - self.b(' */') - self.b('static void') - self.b('%s_%s_add_signals (TpProxy *self,' - % (self.prefix_lc, self.group)) - self.b(' guint quark,') - self.b(' DBusGProxy *proxy,') - self.b(' gpointer unused)') - - self.b('{') - - for node in nodes: - iface = node.getElementsByTagName('interface')[0] - self.iface_dbus = iface.getAttribute('name') - name = node.getAttribute('name').replace('/', '').lower() - self.iface_uc = name.upper() - self.b(' if (quark == %s)' % self.get_iface_quark()) - self.b(' %s_add_signals_for_%s (proxy);' - % (self.prefix_lc, name)) - - self.b('}') - self.b('') - - self.h('G_END_DECLS') - self.h('') - - open(self.basename + '.h', 'w').write('\n'.join(self.__header)) - open(self.basename + '-body.h', 'w').write('\n'.join(self.__body)) - - -def types_to_gtypes(types): - return [type_to_gtype(t)[1] for t in types] - - -if __name__ == '__main__': - options, argv = gnu_getopt(sys.argv[1:], '', - ['group=', 'subclass=', 'subclass-assert=', - 'iface-quark-prefix=']) - - opts = {} - - for option, value in options: - opts[option] = value - - dom = xml.dom.minidom.parse(argv[0]) - - Generator(dom, argv[1], argv[2], opts)() diff --git a/tools/glib-client-gen.py b/tools/glib-client-gen.py deleted file mode 100644 index b7edbc0d..00000000 --- a/tools/glib-client-gen.py +++ /dev/null @@ -1,1217 +0,0 @@ -#!/usr/bin/python - -# glib-client-gen.py: "I Can't Believe It's Not dbus-binding-tool" -# -# Generate GLib client wrappers from the Telepathy specification. -# The master copy of this program is in the telepathy-glib repository - -# please make any changes there. -# -# Copyright (C) 2006-2008 Collabora Ltd. <http://www.collabora.co.uk/> -# -# 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 - -import sys -import os.path -import xml.dom.minidom -from getopt import gnu_getopt - -from libtpcodegen import file_set_contents, key_by_name, u -from libglibcodegen import (Signature, type_to_gtype, - get_docstring, xml_escape, get_deprecated, copy_into_gvalue, - move_into_gvalue) - -NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - -class Generator(object): - - def __init__(self, dom, prefix, basename, opts): - self.dom = dom - self.__header = [] - self.__body = [] - self.__docs = [] - - self.prefix_lc = prefix.lower() - self.prefix_uc = prefix.upper() - self.prefix_mc = prefix.replace('_', '') - self.basename = basename - self.group = opts.get('--group', None) - self.iface_quark_prefix = opts.get('--iface-quark-prefix', None) - self.tp_proxy_api = tuple(map(int, - opts.get('--tp-proxy-api', '0').split('.'))) - self.proxy_cls = opts.get('--subclass', 'TpProxy') + ' *' - self.proxy_arg = opts.get('--subclass', 'void') + ' *' - self.proxy_assert = opts.get('--subclass-assert', 'TP_IS_PROXY') - self.proxy_doc = ('A #%s or subclass' - % opts.get('--subclass', 'TpProxy')) - if self.proxy_arg == 'void *': - self.proxy_arg = 'gpointer ' - - self.reentrant_symbols = set() - try: - filename = opts['--generate-reentrant'] - with open(filename, 'r') as f: - for line in f.readlines(): - self.reentrant_symbols.add(line.strip()) - except KeyError: - pass - - self.deprecate_reentrant = opts.get('--deprecate-reentrant', None) - self.deprecation_attribute = opts.get('--deprecation-attribute', - 'G_GNUC_DEPRECATED') - - self.guard = opts.get('--guard', None) - - def h(self, s): - self.__header.append(s) - - def b(self, s): - self.__body.append(s) - - def d(self, s): - self.__docs.append(s) - - def get_iface_quark(self): - assert self.iface_dbus is not None - assert self.iface_uc is not None - if self.iface_quark_prefix is None: - return 'g_quark_from_static_string (\"%s\")' % self.iface_dbus - else: - return '%s_%s' % (self.iface_quark_prefix, self.iface_uc) - - def do_signal(self, iface, signal): - iface_lc = iface.lower() - - member = signal.getAttribute('name') - member_lc = signal.getAttribute('tp:name-for-bindings') - if member != member_lc.replace('_', ''): - raise AssertionError('Signal %s tp:name-for-bindings (%s) does ' - 'not match' % (member, member_lc)) - member_lc = member_lc.lower() - member_uc = member_lc.upper() - - arg_count = 0 - args = [] - out_args = [] - - for arg in signal.getElementsByTagName('arg'): - name = arg.getAttribute('name') - type = arg.getAttribute('type') - tp_type = arg.getAttribute('tp:type') - - if not name: - name = 'arg%u' % arg_count - arg_count += 1 - else: - name = 'arg_%s' % name - - info = type_to_gtype(type) - args.append((name, info, tp_type, arg)) - - callback_name = ('%s_%s_signal_callback_%s' - % (self.prefix_lc, iface_lc, member_lc)) - collect_name = ('_%s_%s_collect_args_of_%s' - % (self.prefix_lc, iface_lc, member_lc)) - invoke_name = ('_%s_%s_invoke_callback_for_%s' - % (self.prefix_lc, iface_lc, member_lc)) - - # Example: - # - # typedef void (*tp_cli_connection_signal_callback_new_channel) - # (TpConnection *proxy, const gchar *arg_object_path, - # const gchar *arg_channel_type, guint arg_handle_type, - # guint arg_handle, gboolean arg_suppress_handler, - # gpointer user_data, GObject *weak_object); - - self.d('/**') - self.d(' * %s:' % callback_name) - self.d(' * @proxy: The proxy on which %s_%s_connect_to_%s ()' - % (self.prefix_lc, iface_lc, member_lc)) - self.d(' * was called') - - for arg in args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - docs = get_docstring(elt) or '(Undocumented)' - - if ctype == 'guint ' and tp_type != '': - docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) - - self.d(' * @%s: %s' % (name, xml_escape(docs))) - - self.d(' * @user_data: User-supplied data') - self.d(' * @weak_object: User-supplied weakly referenced object') - self.d(' *') - self.d(' * Represents the signature of a callback for the signal %s.' - % member) - self.d(' */') - self.d('') - - self.h('typedef void (*%s) (%sproxy,' - % (callback_name, self.proxy_cls)) - - for arg in args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.h(' %s%s%s,' % (const, ctype, name)) - - self.h(' gpointer user_data, GObject *weak_object);') - - if args: - self.b('static void') - self.b('%s (DBusGProxy *proxy G_GNUC_UNUSED,' % collect_name) - - for arg in args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.b(' %s%s%s,' % (const, ctype, name)) - - self.b(' TpProxySignalConnection *sc)') - self.b('{') - self.b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') - self.b(' GValueArray *args = g_value_array_new (%d);' % len(args)) - self.b(' GValue blank = { 0 };') - self.b(' guint i;') - self.b('') - self.b(' g_value_init (&blank, G_TYPE_INT);') - self.b('') - self.b(' for (i = 0; i < %d; i++)' % len(args)) - self.b(' g_value_array_append (args, &blank);') - self.b(' G_GNUC_END_IGNORE_DEPRECATIONS') - self.b('') - - for i, arg in enumerate(args): - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' g_value_unset (args->values + %d);' % i) - self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) - - self.b(' ' + copy_into_gvalue('args->values + %d' % i, - gtype, marshaller, name)) - self.b('') - - self.b(' tp_proxy_signal_connection_v0_take_results (sc, args);') - self.b('}') - - self.b('static void') - self.b('%s (TpProxy *tpproxy,' % invoke_name) - self.b(' GError *error G_GNUC_UNUSED,') - self.b(' GValueArray *args,') - self.b(' GCallback generic_callback,') - self.b(' gpointer user_data,') - self.b(' GObject *weak_object)') - self.b('{') - self.b(' %s callback =' % callback_name) - self.b(' (%s) generic_callback;' % callback_name) - self.b('') - self.b(' if (callback != NULL)') - self.b(' callback (g_object_ref (tpproxy),') - - # FIXME: factor out into a function - for i, arg in enumerate(args): - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - if marshaller == 'BOXED': - self.b(' g_value_get_boxed (args->values + %d),' % i) - elif gtype == 'G_TYPE_STRING': - self.b(' g_value_get_string (args->values + %d),' % i) - elif gtype == 'G_TYPE_UCHAR': - self.b(' g_value_get_uchar (args->values + %d),' % i) - elif gtype == 'G_TYPE_BOOLEAN': - self.b(' g_value_get_boolean (args->values + %d),' % i) - elif gtype == 'G_TYPE_UINT': - self.b(' g_value_get_uint (args->values + %d),' % i) - elif gtype == 'G_TYPE_INT': - self.b(' g_value_get_int (args->values + %d),' % i) - elif gtype == 'G_TYPE_UINT64': - self.b(' g_value_get_uint64 (args->values + %d),' % i) - elif gtype == 'G_TYPE_INT64': - self.b(' g_value_get_int64 (args->values + %d),' % i) - elif gtype == 'G_TYPE_DOUBLE': - self.b(' g_value_get_double (args->values + %d),' % i) - else: - assert False, "Don't know how to get %s from a GValue" % gtype - - self.b(' user_data,') - self.b(' weak_object);') - self.b('') - - self.b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') - if len(args) > 0: - self.b(' g_value_array_free (args);') - else: - self.b(' if (args != NULL)') - self.b(' g_value_array_free (args);') - self.b('') - self.b(' G_GNUC_END_IGNORE_DEPRECATIONS') - - self.b(' g_object_unref (tpproxy);') - self.b('}') - - # Example: - # - # TpProxySignalConnection * - # tp_cli_connection_connect_to_new_channel - # (TpConnection *proxy, - # tp_cli_connection_signal_callback_new_channel callback, - # gpointer user_data, - # GDestroyNotify destroy); - # - # destroy is invoked when the signal becomes disconnected. This - # is either because the signal has been disconnected explicitly - # by the user, because the TpProxy has become invalid and - # emitted the 'invalidated' signal, or because the weakly referenced - # object has gone away. - - self.d('/**') - self.d(' * %s_%s_connect_to_%s:' - % (self.prefix_lc, iface_lc, member_lc)) - self.d(' * @proxy: %s' % self.proxy_doc) - self.d(' * @callback: Callback to be called when the signal is') - self.d(' * received') - self.d(' * @user_data: User-supplied data for the callback') - self.d(' * @destroy: Destructor for the user-supplied data, which') - self.d(' * will be called when this signal is disconnected, or') - self.d(' * before this function returns %NULL') - self.d(' * @weak_object: A #GObject which will be weakly referenced; ') - self.d(' * if it is destroyed, this callback will automatically be') - self.d(' * disconnected') - self.d(' * @error: If not %NULL, used to raise an error if %NULL is') - self.d(' * returned') - self.d(' *') - self.d(' * Connect a handler to the signal %s.' % member) - self.d(' *') - self.d(' * %s' % xml_escape(get_docstring(signal) or '(Undocumented)')) - self.d(' *') - self.d(' * Returns: a #TpProxySignalConnection containing all of the') - self.d(' * above, which can be used to disconnect the signal; or') - self.d(' * %NULL if the proxy does not have the desired interface') - self.d(' * or has become invalid.') - self.d(' */') - self.d('') - - self.h('TpProxySignalConnection *%s_%s_connect_to_%s (%sproxy,' - % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) - self.h(' %s callback,' % callback_name) - self.h(' gpointer user_data,') - self.h(' GDestroyNotify destroy,') - self.h(' GObject *weak_object,') - self.h(' GError **error);') - self.h('') - - self.b('TpProxySignalConnection *') - self.b('%s_%s_connect_to_%s (%sproxy,' - % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) - self.b(' %s callback,' % callback_name) - self.b(' gpointer user_data,') - self.b(' GDestroyNotify destroy,') - self.b(' GObject *weak_object,') - self.b(' GError **error)') - self.b('{') - self.b(' GType expected_types[%d] = {' % (len(args) + 1)) - - for arg in args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' %s,' % gtype) - - self.b(' G_TYPE_INVALID };') - self.b('') - self.b(' g_return_val_if_fail (%s (proxy), NULL);' - % self.proxy_assert) - self.b(' g_return_val_if_fail (callback != NULL, NULL);') - self.b('') - self.b(' return tp_proxy_signal_connection_v0_new ((TpProxy *) proxy,') - self.b(' %s, \"%s\",' % (self.get_iface_quark(), member)) - self.b(' expected_types,') - - if args: - self.b(' G_CALLBACK (%s),' % collect_name) - else: - self.b(' NULL, /* no args => no collector function */') - - self.b(' %s,' % invoke_name) - self.b(' G_CALLBACK (callback), user_data, destroy,') - self.b(' weak_object, error);') - self.b('}') - self.b('') - - def do_method(self, iface, method): - iface_lc = iface.lower() - - member = method.getAttribute('name') - member_lc = method.getAttribute('tp:name-for-bindings') - if member != member_lc.replace('_', ''): - raise AssertionError('Method %s tp:name-for-bindings (%s) does ' - 'not match' % (member, member_lc)) - member_lc = member_lc.lower() - member_uc = member_lc.upper() - - in_count = 0 - ret_count = 0 - in_args = [] - out_args = [] - - for arg in method.getElementsByTagName('arg'): - name = arg.getAttribute('name') - direction = arg.getAttribute('direction') - type = arg.getAttribute('type') - tp_type = arg.getAttribute('tp:type') - - if direction != 'out': - if not name: - name = 'in%u' % in_count - in_count += 1 - else: - name = 'in_%s' % name - else: - if not name: - name = 'out%u' % ret_count - ret_count += 1 - else: - name = 'out_%s' % name - - info = type_to_gtype(type) - if direction != 'out': - in_args.append((name, info, tp_type, arg)) - else: - out_args.append((name, info, tp_type, arg)) - - # Async reply callback type - - # Example: - # void (*tp_cli_properties_interface_callback_for_get_properties) - # (TpProxy *proxy, - # const GPtrArray *out0, - # const GError *error, - # gpointer user_data, - # GObject *weak_object); - - self.d('/**') - self.d(' * %s_%s_callback_for_%s:' - % (self.prefix_lc, iface_lc, member_lc)) - self.d(' * @proxy: the proxy on which the call was made') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - docs = xml_escape(get_docstring(elt) or '(Undocumented)') - - if ctype == 'guint ' and tp_type != '': - docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) - - self.d(' * @%s: Used to return an \'out\' argument if @error is ' - '%%NULL: %s' - % (name, docs)) - - self.d(' * @error: %NULL on success, or an error on failure') - self.d(' * @user_data: user-supplied data') - self.d(' * @weak_object: user-supplied object') - self.d(' *') - self.d(' * Signature of the callback called when a %s method call' - % member) - self.d(' * succeeds or fails.') - - deprecated = method.getElementsByTagName('tp:deprecated') - if deprecated: - d = deprecated[0] - self.d(' *') - self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) - - self.d(' */') - self.d('') - - callback_name = '%s_%s_callback_for_%s' % (self.prefix_lc, iface_lc, - member_lc) - - self.h('typedef void (*%s) (%sproxy,' - % (callback_name, self.proxy_cls)) - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - const = pointer and 'const ' or '' - - self.h(' %s%s%s,' % (const, ctype, name)) - - self.h(' const GError *error, gpointer user_data,') - self.h(' GObject *weak_object);') - self.h('') - - # Async callback implementation - - invoke_callback = '_%s_%s_invoke_callback_%s' % (self.prefix_lc, - iface_lc, - member_lc) - - collect_callback = '_%s_%s_collect_callback_%s' % (self.prefix_lc, - iface_lc, - member_lc) - - # The callback called by dbus-glib; this ends the call and collects - # the results into a GValueArray. - self.b('static void') - self.b('%s (DBusGProxy *proxy,' % collect_callback) - self.b(' DBusGProxyCall *call,') - self.b(' gpointer user_data)') - self.b('{') - self.b(' GError *error = NULL;') - - if len(out_args) > 0: - self.b(' GValueArray *args;') - self.b(' GValue blank = { 0 };') - self.b(' guint i;') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - # "We handle variants specially; the caller is expected to - # have already allocated storage for them". Thanks, - # dbus-glib... - if gtype == 'G_TYPE_VALUE': - self.b(' GValue *%s = g_new0 (GValue, 1);' % name) - else: - self.b(' %s%s;' % (ctype, name)) - - self.b('') - self.b(' dbus_g_proxy_end_call (proxy, call, &error,') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - if gtype == 'G_TYPE_VALUE': - self.b(' %s, %s,' % (gtype, name)) - else: - self.b(' %s, &%s,' % (gtype, name)) - - self.b(' G_TYPE_INVALID);') - - if len(out_args) == 0: - self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,' - 'NULL);') - else: - self.b('') - self.b(' if (error != NULL)') - self.b(' {') - self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,') - self.b(' NULL);') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - if gtype == 'G_TYPE_VALUE': - self.b(' g_free (%s);' % name) - - self.b(' return;') - self.b(' }') - self.b('') - self.b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') - self.b(' args = g_value_array_new (%d);' % len(out_args)) - self.b(' g_value_init (&blank, G_TYPE_INT);') - self.b('') - self.b(' for (i = 0; i < %d; i++)' % len(out_args)) - self.b(' g_value_array_append (args, &blank);') - self.b(' G_GNUC_END_IGNORE_DEPRECATIONS') - - for i, arg in enumerate(out_args): - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b('') - self.b(' g_value_unset (args->values + %d);' % i) - self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) - - self.b(' ' + move_into_gvalue('args->values + %d' % i, - gtype, marshaller, name)) - - self.b(' tp_proxy_pending_call_v0_take_results (user_data, ' - 'NULL, args);') - - self.b('}') - - self.b('static void') - self.b('%s (TpProxy *self,' % invoke_callback) - self.b(' GError *error,') - self.b(' GValueArray *args,') - self.b(' GCallback generic_callback,') - self.b(' gpointer user_data,') - self.b(' GObject *weak_object)') - self.b('{') - self.b(' %s callback = (%s) generic_callback;' - % (callback_name, callback_name)) - self.b('') - self.b(' if (error != NULL)') - self.b(' {') - self.b(' callback ((%s) self,' % self.proxy_cls) - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - if marshaller == 'BOXED' or pointer: - self.b(' NULL,') - elif gtype == 'G_TYPE_DOUBLE': - self.b(' 0.0,') - else: - self.b(' 0,') - - self.b(' error, user_data, weak_object);') - self.b(' g_error_free (error);') - self.b(' return;') - self.b(' }') - - self.b(' callback ((%s) self,' % self.proxy_cls) - - # FIXME: factor out into a function - for i, arg in enumerate(out_args): - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - if marshaller == 'BOXED': - self.b(' g_value_get_boxed (args->values + %d),' % i) - elif gtype == 'G_TYPE_STRING': - self.b(' g_value_get_string (args->values + %d),' % i) - elif gtype == 'G_TYPE_UCHAR': - self.b(' g_value_get_uchar (args->values + %d),' % i) - elif gtype == 'G_TYPE_BOOLEAN': - self.b(' g_value_get_boolean (args->values + %d),' % i) - elif gtype == 'G_TYPE_UINT': - self.b(' g_value_get_uint (args->values + %d),' % i) - elif gtype == 'G_TYPE_INT': - self.b(' g_value_get_int (args->values + %d),' % i) - elif gtype == 'G_TYPE_UINT64': - self.b(' g_value_get_uint64 (args->values + %d),' % i) - elif gtype == 'G_TYPE_INT64': - self.b(' g_value_get_int64 (args->values + %d),' % i) - elif gtype == 'G_TYPE_DOUBLE': - self.b(' g_value_get_double (args->values + %d),' % i) - else: - assert False, "Don't know how to get %s from a GValue" % gtype - - self.b(' error, user_data, weak_object);') - self.b('') - - self.b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') - if len(out_args) > 0: - self.b(' g_value_array_free (args);') - else: - self.b(' if (args != NULL)') - self.b(' g_value_array_free (args);') - self.b(' G_GNUC_END_IGNORE_DEPRECATIONS') - - self.b('}') - self.b('') - - # Async stub - - # Example: - # TpProxyPendingCall * - # tp_cli_properties_interface_call_get_properties - # (gpointer proxy, - # gint timeout_ms, - # const GArray *in_properties, - # tp_cli_properties_interface_callback_for_get_properties callback, - # gpointer user_data, - # GDestroyNotify *destructor); - - self.h('TpProxyPendingCall *%s_%s_call_%s (%sproxy,' - % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) - self.h(' gint timeout_ms,') - - self.d('/**') - self.d(' * %s_%s_call_%s:' - % (self.prefix_lc, iface_lc, member_lc)) - self.d(' * @proxy: the #TpProxy') - self.d(' * @timeout_ms: the timeout in milliseconds, or -1 to use the') - self.d(' * default') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - docs = xml_escape(get_docstring(elt) or '(Undocumented)') - - if ctype == 'guint ' and tp_type != '': - docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) - - self.d(' * @%s: Used to pass an \'in\' argument: %s' - % (name, docs)) - - self.d(' * @callback: called when the method call succeeds or fails;') - self.d(' * may be %NULL to make a "fire and forget" call with no ') - self.d(' * reply tracking') - self.d(' * @user_data: user-supplied data passed to the callback;') - self.d(' * must be %NULL if @callback is %NULL') - self.d(' * @destroy: called with the user_data as argument, after the') - self.d(' * call has succeeded, failed or been cancelled;') - self.d(' * must be %NULL if @callback is %NULL') - self.d(' * @weak_object: If not %NULL, a #GObject which will be ') - self.d(' * weakly referenced; if it is destroyed, this call ') - self.d(' * will automatically be cancelled. Must be %NULL if ') - self.d(' * @callback is %NULL') - self.d(' *') - self.d(' * Start a %s method call.' % member) - self.d(' *') - self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)')) - self.d(' *') - self.d(' * Returns: a #TpProxyPendingCall representing the call in') - self.d(' * progress. It is borrowed from the object, and will become') - self.d(' * invalid when the callback is called, the call is') - self.d(' * cancelled or the #TpProxy becomes invalid.') - - deprecated = method.getElementsByTagName('tp:deprecated') - if deprecated: - d = deprecated[0] - self.d(' *') - self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) - - self.d(' */') - self.d('') - - self.b('TpProxyPendingCall *\n%s_%s_call_%s (%sproxy,' - % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) - self.b(' gint timeout_ms,') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.h(' %s%s%s,' % (const, ctype, name)) - self.b(' %s%s%s,' % (const, ctype, name)) - - self.h(' %s callback,' % callback_name) - self.h(' gpointer user_data,') - self.h(' GDestroyNotify destroy,') - self.h(' GObject *weak_object);') - self.h('') - - self.b(' %s callback,' % callback_name) - self.b(' gpointer user_data,') - self.b(' GDestroyNotify destroy,') - self.b(' GObject *weak_object)') - self.b('{') - self.b(' GError *error = NULL;') - self.b(' GQuark interface = %s;' % self.get_iface_quark()) - self.b(' DBusGProxy *iface;') - self.b('') - self.b(' g_return_val_if_fail (%s (proxy), NULL);' - % self.proxy_assert) - self.b(' g_return_val_if_fail (callback != NULL || ' - 'user_data == NULL, NULL);') - self.b(' g_return_val_if_fail (callback != NULL || ' - 'destroy == NULL, NULL);') - self.b(' g_return_val_if_fail (callback != NULL || ' - 'weak_object == NULL, NULL);') - self.b('') - self.b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') - self.b(' iface = tp_proxy_borrow_interface_by_id (') - self.b(' (TpProxy *) proxy,') - self.b(' interface, &error);') - self.b(' G_GNUC_END_IGNORE_DEPRECATIONS') - self.b('') - self.b(' if (iface == NULL)') - self.b(' {') - self.b(' if (callback != NULL)') - self.b(' callback (proxy,') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - if pointer: - self.b(' NULL,') - else: - self.b(' 0,') - - self.b(' error, user_data, weak_object);') - self.b('') - self.b(' if (destroy != NULL)') - self.b(' destroy (user_data);') - self.b('') - self.b(' g_error_free (error);') - self.b(' return NULL;') - self.b(' }') - self.b('') - self.b(' if (callback == NULL)') - self.b(' {') - self.b(' dbus_g_proxy_call_no_reply (iface, "%s",' % member) - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.b(' %s, %s,' % (gtype, name)) - - self.b(' G_TYPE_INVALID);') - self.b(' return NULL;') - self.b(' }') - self.b(' else') - self.b(' {') - self.b(' TpProxyPendingCall *data;') - self.b('') - self.b(' data = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') - self.b(' interface, "%s", iface,' % member) - self.b(' %s,' % invoke_callback) - self.b(' G_CALLBACK (callback), user_data, destroy,') - self.b(' weak_object, FALSE);') - self.b(' tp_proxy_pending_call_v0_take_pending_call (data,') - self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') - self.b(' "%s",' % member) - self.b(' %s,' % collect_callback) - self.b(' data,') - self.b(' tp_proxy_pending_call_v0_completed,') - self.b(' timeout_ms,') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.b(' %s, %s,' % (gtype, name)) - - self.b(' G_TYPE_INVALID));') - self.b('') - self.b(' return data;') - self.b(' }') - self.b('}') - self.b('') - - self.do_method_reentrant(method, iface_lc, member, member_lc, - in_args, out_args, collect_callback) - - # leave a gap for the end of the method - self.d('') - self.b('') - self.h('') - - def do_method_reentrant(self, method, iface_lc, member, member_lc, in_args, - out_args, collect_callback): - # Reentrant blocking calls - # Example: - # gboolean tp_cli_properties_interface_run_get_properties - # (gpointer proxy, - # gint timeout_ms, - # const GArray *in_properties, - # GPtrArray **out0, - # GError **error, - # GMainLoop **loop); - - run_method_name = '%s_%s_run_%s' % (self.prefix_lc, iface_lc, member_lc) - if run_method_name not in self.reentrant_symbols: - return - - self.b('typedef struct {') - self.b(' GMainLoop *loop;') - self.b(' GError **error;') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' %s*%s;' % (ctype, name)) - - self.b(' unsigned success:1;') - self.b(' unsigned completed:1;') - self.b('} _%s_%s_run_state_%s;' - % (self.prefix_lc, iface_lc, member_lc)) - - reentrant_invoke = '_%s_%s_finish_running_%s' % (self.prefix_lc, - iface_lc, - member_lc) - - self.b('static void') - self.b('%s (TpProxy *self G_GNUC_UNUSED,' % reentrant_invoke) - self.b(' GError *error,') - self.b(' GValueArray *args,') - self.b(' GCallback unused G_GNUC_UNUSED,') - self.b(' gpointer user_data G_GNUC_UNUSED,') - self.b(' GObject *unused2 G_GNUC_UNUSED)') - self.b('{') - self.b(' _%s_%s_run_state_%s *state = user_data;' - % (self.prefix_lc, iface_lc, member_lc)) - self.b('') - self.b(' state->success = (error == NULL);') - self.b(' state->completed = TRUE;') - self.b(' g_main_loop_quit (state->loop);') - self.b('') - self.b(' if (error != NULL)') - self.b(' {') - self.b(' if (state->error != NULL)') - self.b(' *state->error = error;') - self.b(' else') - self.b(' g_error_free (error);') - self.b('') - self.b(' return;') - self.b(' }') - self.b('') - - for i, arg in enumerate(out_args): - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.b(' if (state->%s != NULL)' % name) - if marshaller == 'BOXED': - self.b(' *state->%s = g_value_dup_boxed (' - 'args->values + %d);' % (name, i)) - elif marshaller == 'STRING': - self.b(' *state->%s = g_value_dup_string ' - '(args->values + %d);' % (name, i)) - elif marshaller in ('UCHAR', 'BOOLEAN', 'INT', 'UINT', - 'INT64', 'UINT64', 'DOUBLE'): - self.b(' *state->%s = g_value_get_%s (args->values + %d);' - % (name, marshaller.lower(), i)) - else: - assert False, "Don't know how to copy %s" % gtype - - self.b('') - - self.b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') - if len(out_args) > 0: - self.b(' g_value_array_free (args);') - else: - self.b(' if (args != NULL)') - self.b(' g_value_array_free (args);') - self.b(' G_GNUC_END_IGNORE_DEPRECATIONS') - - self.b('}') - self.b('') - - if self.deprecate_reentrant: - self.h('#ifndef %s' % self.deprecate_reentrant) - - self.h('gboolean %s (%sproxy,' - % (run_method_name, self.proxy_arg)) - self.h(' gint timeout_ms,') - - self.d('/**') - self.d(' * %s:' % run_method_name) - self.d(' * @proxy: %s' % self.proxy_doc) - self.d(' * @timeout_ms: Timeout in milliseconds, or -1 for default') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - docs = xml_escape(get_docstring(elt) or '(Undocumented)') - - if ctype == 'guint ' and tp_type != '': - docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) - - self.d(' * @%s: Used to pass an \'in\' argument: %s' - % (name, docs)) - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.d(' * @%s: Used to return an \'out\' argument if %%TRUE is ' - 'returned: %s' - % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) - - self.d(' * @error: If not %NULL, used to return errors if %FALSE ') - self.d(' * is returned') - self.d(' * @loop: If not %NULL, set before re-entering ') - self.d(' * the main loop, to point to a #GMainLoop ') - self.d(' * which can be used to cancel this call with ') - self.d(' * g_main_loop_quit(), causing a return of ') - self.d(' * %FALSE with @error set to %TP_DBUS_ERROR_CANCELLED') - self.d(' *') - self.d(' * Call the method %s and run the main loop' % member) - self.d(' * until it returns. Before calling this method, you must') - self.d(' * add a reference to any borrowed objects you need to keep,') - self.d(' * and generally ensure that everything is in a consistent') - self.d(' * state.') - self.d(' *') - self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)')) - self.d(' *') - self.d(' * Returns: TRUE on success, FALSE and sets @error on error') - - deprecated = method.getElementsByTagName('tp:deprecated') - if deprecated: - d = deprecated[0] - self.d(' *') - self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) - - self.d(' */') - self.d('') - - self.b('gboolean\n%s (%sproxy,' - % (run_method_name, self.proxy_arg)) - self.b(' gint timeout_ms,') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.h(' %s%s%s,' % (const, ctype, name)) - self.b(' %s%s%s,' % (const, ctype, name)) - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - self.h(' %s*%s,' % (ctype, name)) - self.b(' %s*%s,' % (ctype, name)) - - self.h(' GError **error,') - - if self.deprecate_reentrant: - self.h(' GMainLoop **loop) %s;' % self.deprecation_attribute) - self.h('#endif /* not %s */' % self.deprecate_reentrant) - else: - self.h(' GMainLoop **loop);') - - self.h('') - - self.b(' GError **error,') - self.b(' GMainLoop **loop)') - self.b('{') - self.b(' DBusGProxy *iface;') - self.b(' GQuark interface = %s;' % self.get_iface_quark()) - self.b(' TpProxyPendingCall *pc;') - self.b(' _%s_%s_run_state_%s state = {' - % (self.prefix_lc, iface_lc, member_lc)) - self.b(' NULL /* loop */, error,') - - for arg in out_args: - name, info, tp_type, elt = arg - - self.b(' %s,' % name) - - self.b(' FALSE /* completed */, FALSE /* success */ };') - self.b('') - self.b(' g_return_val_if_fail (%s (proxy), FALSE);' - % self.proxy_assert) - self.b('') - self.b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') - self.b(' iface = tp_proxy_borrow_interface_by_id') - self.b(' ((TpProxy *) proxy, interface, error);') - self.b(' G_GNUC_END_IGNORE_DEPRECATIONS') - self.b('') - self.b(' if (iface == NULL)') - self.b(' return FALSE;') - self.b('') - self.b(' state.loop = g_main_loop_new (NULL, FALSE);') - self.b('') - self.b(' pc = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') - self.b(' interface, "%s", iface,' % member) - self.b(' %s,' % reentrant_invoke) - self.b(' NULL, &state, NULL, NULL, TRUE);') - self.b('') - self.b(' if (loop != NULL)') - self.b(' *loop = state.loop;') - self.b('') - self.b(' tp_proxy_pending_call_v0_take_pending_call (pc,') - self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') - self.b(' "%s",' % member) - self.b(' %s,' % collect_callback) - self.b(' pc,') - self.b(' tp_proxy_pending_call_v0_completed,') - self.b(' timeout_ms,') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - self.b(' %s, %s,' % (gtype, name)) - - self.b(' G_TYPE_INVALID));') - self.b('') - self.b(' if (!state.completed)') - self.b(' g_main_loop_run (state.loop);') - self.b('') - self.b(' if (!state.completed)') - self.b(' tp_proxy_pending_call_cancel (pc);') - self.b('') - self.b(' if (loop != NULL)') - self.b(' *loop = NULL;') - self.b('') - self.b(' g_main_loop_unref (state.loop);') - self.b('') - self.b(' return state.success;') - self.b('}') - self.b('') - - def do_signal_add(self, signal): - marshaller_items = [] - gtypes = [] - - for i in signal.getElementsByTagName('arg'): - name = i.getAttribute('name') - type = i.getAttribute('type') - info = type_to_gtype(type) - # type, GType, STRING, is a pointer - gtypes.append(info[1]) - - self.b(' dbus_g_proxy_add_signal (proxy, "%s",' - % signal.getAttribute('name')) - for gtype in gtypes: - self.b(' %s,' % gtype) - self.b(' G_TYPE_INVALID);') - - def do_interface(self, node): - ifaces = node.getElementsByTagName('interface') - assert len(ifaces) == 1 - iface = ifaces[0] - name = node.getAttribute('name').replace('/', '') - - self.iface = name - self.iface_lc = name.lower() - self.iface_uc = name.upper() - self.iface_mc = name.replace('_', '') - self.iface_dbus = iface.getAttribute('name') - - signals = node.getElementsByTagName('signal') - methods = node.getElementsByTagName('method') - - if signals: - self.b('static inline void') - self.b('%s_add_signals_for_%s (DBusGProxy *proxy)' - % (self.prefix_lc, name.lower())) - self.b('{') - - if self.tp_proxy_api >= (0, 7, 6): - self.b(' if (!tp_proxy_dbus_g_proxy_claim_for_signal_adding ' - '(proxy))') - self.b(' return;') - - for signal in signals: - self.do_signal_add(signal) - - self.b('}') - self.b('') - self.b('') - - for signal in signals: - self.do_signal(name, signal) - - for method in methods: - self.do_method(name, method) - - self.iface_dbus = None - - def __call__(self): - - if self.guard is not None: - self.h('#ifndef %s' % self.guard) - self.h('#define %s' % self.guard) - self.h('') - - self.h('G_BEGIN_DECLS') - self.h('') - - self.b('/* We don\'t want gtkdoc scanning this file, it\'ll get') - self.b(' * confused by seeing function definitions, so mark it as: */') - self.b('/*<private_header>*/') - self.b('') - - nodes = self.dom.getElementsByTagName('node') - nodes.sort(key=key_by_name) - - for node in nodes: - self.do_interface(node) - - if self.group is not None: - - self.b('/*') - self.b(' * %s_%s_add_signals:' % (self.prefix_lc, self.group)) - self.b(' * @self: the #TpProxy') - self.b(' * @quark: a quark whose string value is the interface') - self.b(' * name whose signals should be added') - self.b(' * @proxy: the D-Bus proxy to which to add the signals') - self.b(' * @unused: not used for anything') - self.b(' *') - self.b(' * Tell dbus-glib that @proxy has the signatures of all') - self.b(' * signals on the given interface, if it\'s one we') - self.b(' * support.') - self.b(' *') - self.b(' * This function should be used as a signal handler for') - self.b(' * #TpProxy::interface-added.') - self.b(' */') - self.b('static void') - self.b('%s_%s_add_signals (TpProxy *self G_GNUC_UNUSED,' - % (self.prefix_lc, self.group)) - self.b(' guint quark,') - self.b(' DBusGProxy *proxy,') - self.b(' gpointer unused G_GNUC_UNUSED)') - - self.b('{') - - for node in nodes: - iface = node.getElementsByTagName('interface')[0] - self.iface_dbus = iface.getAttribute('name') - signals = node.getElementsByTagName('signal') - if not signals: - continue - name = node.getAttribute('name').replace('/', '').lower() - self.iface_uc = name.upper() - self.b(' if (quark == %s)' % self.get_iface_quark()) - self.b(' %s_add_signals_for_%s (proxy);' - % (self.prefix_lc, name)) - - self.b('}') - self.b('') - - self.h('G_END_DECLS') - self.h('') - - if self.guard is not None: - self.h('#endif /* defined (%s) */' % self.guard) - self.h('') - - file_set_contents(self.basename + '.h', u('\n').join(self.__header).encode('utf-8')) - file_set_contents(self.basename + '-body.h', u('\n').join(self.__body).encode('utf-8')) - file_set_contents(self.basename + '-gtk-doc.h', u('\n').join(self.__docs).encode('utf-8')) - -def types_to_gtypes(types): - return [type_to_gtype(t)[1] for t in types] - - -if __name__ == '__main__': - options, argv = gnu_getopt(sys.argv[1:], '', - ['group=', 'subclass=', 'subclass-assert=', - 'iface-quark-prefix=', 'tp-proxy-api=', - 'generate-reentrant=', 'deprecate-reentrant=', - 'deprecation-attribute=', 'guard=']) - - opts = {} - - for option, value in options: - opts[option] = value - - dom = xml.dom.minidom.parse(argv[0]) - - Generator(dom, argv[1], argv[2], opts)() diff --git a/tools/glib-client-marshaller-gen.py b/tools/glib-client-marshaller-gen.py deleted file mode 100644 index cd9823bd..00000000 --- a/tools/glib-client-marshaller-gen.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/python - -import sys -import xml.dom.minidom -from string import ascii_letters, digits - - -from libglibcodegen import signal_to_marshal_name - - -NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - -class Generator(object): - - def __init__(self, dom, prefix): - self.dom = dom - self.marshallers = {} - self.prefix = prefix - - def do_signal(self, signal): - marshaller = signal_to_marshal_name(signal, self.prefix) - - assert '__' in marshaller - rhs = marshaller.split('__', 1)[1].split('_') - - self.marshallers[marshaller] = rhs - - def __call__(self): - signals = self.dom.getElementsByTagName('signal') - - for signal in signals: - self.do_signal(signal) - - print('void') - print('%s_register_dbus_glib_marshallers (void)' % self.prefix) - print('{') - - all = list(self.marshallers.keys()) - all.sort() - for marshaller in all: - rhs = self.marshallers[marshaller] - - print(' dbus_g_object_register_marshaller (') - print(' g_cclosure_marshal_generic,') - print(' G_TYPE_NONE, /* return */') - for type in rhs: - print(' G_TYPE_%s,' % type.replace('VOID', 'NONE')) - print(' G_TYPE_INVALID);') - - print('}') - - -def types_to_gtypes(types): - return [type_to_gtype(t)[1] for t in types] - -if __name__ == '__main__': - argv = sys.argv[1:] - dom = xml.dom.minidom.parse(argv[0]) - - Generator(dom, argv[1])() diff --git a/tools/glib-ginterface-gen.py b/tools/glib-ginterface-gen.py deleted file mode 100644 index c0ce20dd..00000000 --- a/tools/glib-ginterface-gen.py +++ /dev/null @@ -1,832 +0,0 @@ -#!/usr/bin/python - -# glib-ginterface-gen.py: service-side interface generator -# -# Generate dbus-glib 0.x service GInterfaces from the Telepathy specification. -# The master copy of this program is in the telepathy-glib repository - -# please make any changes there. -# -# Copyright (C) 2006, 2007 Collabora Limited -# -# 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 - -import sys -import os.path -import xml.dom.minidom - -from libtpcodegen import file_set_contents, key_by_name, u -from libglibcodegen import Signature, type_to_gtype, \ - NS_TP, dbus_gutils_wincaps_to_uscore - - -NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - -def get_emits_changed(node): - try: - return [ - annotation.getAttribute('value') - for annotation in node.getElementsByTagName('annotation') - if annotation.getAttribute('name') == 'org.freedesktop.DBus.Property.EmitsChangedSignal' - ][0] - except IndexError: - return None - -class Generator(object): - - def __init__(self, dom, prefix, basename, signal_marshal_prefix, - headers, end_headers, not_implemented_func, - allow_havoc): - self.dom = dom - self.__header = [] - self.__body = [] - self.__docs = [] - - assert prefix.endswith('_') - assert not signal_marshal_prefix.endswith('_') - - # The main_prefix, sub_prefix thing is to get: - # FOO_ -> (FOO_, _) - # FOO_SVC_ -> (FOO_, _SVC_) - # but - # FOO_BAR/ -> (FOO_BAR_, _) - # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_) - - if '/' in prefix: - main_prefix, sub_prefix = prefix.upper().split('/', 1) - prefix = prefix.replace('/', '_') - else: - main_prefix, sub_prefix = prefix.upper().split('_', 1) - - self.MAIN_PREFIX_ = main_prefix + '_' - self._SUB_PREFIX_ = '_' + sub_prefix - - self.Prefix_ = prefix - self.Prefix = prefix.replace('_', '') - self.prefix_ = prefix.lower() - self.PREFIX_ = prefix.upper() - - self.basename = basename - self.signal_marshal_prefix = signal_marshal_prefix - self.headers = headers - self.end_headers = end_headers - self.not_implemented_func = not_implemented_func - self.allow_havoc = allow_havoc - - def h(self, s): - self.__header.append(s) - - def b(self, s): - self.__body.append(s) - - def d(self, s): - self.__docs.append(s) - - def do_node(self, node): - node_name = node.getAttribute('name').replace('/', '') - node_name_mixed = self.node_name_mixed = node_name.replace('_', '') - node_name_lc = self.node_name_lc = node_name.lower() - node_name_uc = self.node_name_uc = node_name.upper() - - interfaces = node.getElementsByTagName('interface') - assert len(interfaces) == 1, interfaces - interface = interfaces[0] - self.iface_name = interface.getAttribute('name') - - tmp = interface.getAttribute('tp:implement-service') - if tmp == "no": - return - - tmp = interface.getAttribute('tp:causes-havoc') - if tmp and not self.allow_havoc: - raise AssertionError('%s is %s' % (self.iface_name, tmp)) - - iface_emits_changed = get_emits_changed(interface) - - self.b('static const DBusGObjectInfo _%s%s_object_info;' - % (self.prefix_, node_name_lc)) - self.b('') - - methods = interface.getElementsByTagName('method') - signals = interface.getElementsByTagName('signal') - properties = interface.getElementsByTagName('property') - # Don't put properties in dbus-glib glue - glue_properties = [] - - self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed)) - self.b(' GTypeInterface parent_class;') - for method in methods: - self.b(' %s %s;' % self.get_method_impl_names(method)) - self.b('};') - self.b('') - - if signals: - self.b('enum {') - for signal in signals: - self.b(' %s,' % self.get_signal_const_entry(signal)) - self.b(' N_%s_SIGNALS' % node_name_uc) - self.b('};') - self.b('static guint %s_signals[N_%s_SIGNALS] = {0};' - % (node_name_lc, node_name_uc)) - self.b('') - - self.b('static void %s%s_base_init (gpointer klass);' - % (self.prefix_, node_name_lc)) - self.b('') - - self.b('GType') - self.b('%s%s_get_type (void)' - % (self.prefix_, node_name_lc)) - self.b('{') - self.b(' static GType type = 0;') - self.b('') - self.b(' if (G_UNLIKELY (type == 0))') - self.b(' {') - self.b(' static const GTypeInfo info = {') - self.b(' sizeof (%s%sClass),' % (self.Prefix, node_name_mixed)) - self.b(' %s%s_base_init, /* base_init */' - % (self.prefix_, node_name_lc)) - self.b(' NULL, /* base_finalize */') - self.b(' NULL, /* class_init */') - self.b(' NULL, /* class_finalize */') - self.b(' NULL, /* class_data */') - self.b(' 0,') - self.b(' 0, /* n_preallocs */') - self.b(' NULL /* instance_init */') - self.b(' };') - self.b('') - self.b(' type = g_type_register_static (G_TYPE_INTERFACE,') - self.b(' "%s%s", &info, 0);' % (self.Prefix, node_name_mixed)) - self.b(' }') - self.b('') - self.b(' return type;') - self.b('}') - self.b('') - - self.d('/**') - self.d(' * %s%s:' % (self.Prefix, node_name_mixed)) - self.d(' *') - self.d(' * Dummy typedef representing any implementation of this ' - 'interface.') - self.d(' */') - - self.h('typedef struct _%s%s %s%s;' - % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed)) - self.h('') - - self.d('/**') - self.d(' * %s%sClass:' % (self.Prefix, node_name_mixed)) - self.d(' *') - self.d(' * The class of %s%s.' % (self.Prefix, node_name_mixed)) - - if methods: - self.d(' *') - self.d(' * In a full implementation of this interface (i.e. all') - self.d(' * methods implemented), the interface initialization') - self.d(' * function used in G_IMPLEMENT_INTERFACE() would') - self.d(' * typically look like this:') - self.d(' *') - self.d(' * <programlisting>') - self.d(' * static void') - self.d(' * implement_%s (gpointer klass,' % self.node_name_lc) - self.d(' * gpointer unused G_GNUC_UNUSED)') - self.d(' * {') - self.d(' * #define IMPLEMENT(x) %s%s_implement_##x (\\' - % (self.prefix_, self.node_name_lc)) - self.d(' * klass, my_object_##x)') - - for method in methods: - class_member_name = method.getAttribute('tp:name-for-bindings') - class_member_name = class_member_name.lower() - self.d(' * IMPLEMENT (%s);' % class_member_name) - - self.d(' * #undef IMPLEMENT') - self.d(' * }') - self.d(' * </programlisting>') - else: - self.d(' * This interface has no D-Bus methods, so an') - self.d(' * implementation can typically pass %NULL to') - self.d(' * G_IMPLEMENT_INTERFACE() as the interface') - self.d(' * initialization function.') - - self.d(' */') - self.d('') - - self.h('typedef struct _%s%sClass %s%sClass;' - % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed)) - self.h('') - self.h('GType %s%s_get_type (void);' - % (self.prefix_, node_name_lc)) - - gtype = self.current_gtype = \ - self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc - classname = self.Prefix + node_name_mixed - - self.h('#define %s \\\n (%s%s_get_type ())' - % (gtype, self.prefix_, node_name_lc)) - self.h('#define %s%s(obj) \\\n' - ' (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))' - % (self.PREFIX_, node_name_uc, gtype, classname)) - self.h('#define %sIS%s%s(obj) \\\n' - ' (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))' - % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype)) - self.h('#define %s%s_GET_CLASS(obj) \\\n' - ' (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))' - % (self.PREFIX_, node_name_uc, gtype, classname)) - self.h('') - self.h('') - - base_init_code = [] - - for method in methods: - self.do_method(method) - - for signal in signals: - base_init_code.extend(self.do_signal(signal)) - - self.b('static inline void') - self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)' - % (self.prefix_, node_name_lc)) - self.b('{') - - if properties: - self.b(' static TpDBusPropertiesMixinPropInfo properties[%d] = {' - % (len(properties) + 1)) - - for m in properties: - access = m.getAttribute('access') - assert access in ('read', 'write', 'readwrite') - - if access == 'read': - flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ' - elif access == 'write': - flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE' - else: - flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | ' - 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE') - - prop_emits_changed = get_emits_changed(m) - - if prop_emits_changed is None: - prop_emits_changed = iface_emits_changed - - if prop_emits_changed == 'true': - flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED' - elif prop_emits_changed == 'invalidates': - flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED' - - self.b(' { 0, %s, "%s", 0, NULL, NULL }, /* %s */' - % (flags, m.getAttribute('type'), m.getAttribute('name'))) - - self.b(' { 0, 0, NULL, 0, NULL, NULL }') - self.b(' };') - self.b(' static TpDBusPropertiesMixinIfaceInfo interface =') - self.b(' { 0, properties, NULL, NULL };') - self.b('') - - - self.b(' dbus_g_object_type_install_info (%s%s_get_type (),' - % (self.prefix_, node_name_lc)) - self.b(' &_%s%s_object_info);' - % (self.prefix_, node_name_lc)) - self.b('') - - if properties: - self.b(' interface.dbus_interface = g_quark_from_static_string ' - '("%s");' % self.iface_name) - - for i, m in enumerate(properties): - self.b(' properties[%d].name = g_quark_from_static_string ("%s");' - % (i, m.getAttribute('name'))) - self.b(' properties[%d].type = %s;' - % (i, type_to_gtype(m.getAttribute('type'))[1])) - - self.b(' tp_svc_interface_set_dbus_properties_info (%s, &interface);' - % self.current_gtype) - - self.b('') - - for s in base_init_code: - self.b(s) - self.b('}') - - self.b('static void') - self.b('%s%s_base_init (gpointer klass)' - % (self.prefix_, node_name_lc)) - self.b('{') - self.b(' static gboolean initialized = FALSE;') - self.b('') - self.b(' if (!initialized)') - self.b(' {') - self.b(' initialized = TRUE;') - self.b(' %s%s_base_init_once (klass);' - % (self.prefix_, node_name_lc)) - self.b(' }') - # insert anything we need to do per implementation here - self.b('}') - - self.h('') - - self.b('static const DBusGMethodInfo _%s%s_methods[] = {' - % (self.prefix_, node_name_lc)) - - method_blob, offsets = self.get_method_glue(methods) - - for method, offset in zip(methods, offsets): - self.do_method_glue(method, offset) - - if len(methods) == 0: - # empty arrays are a gcc extension, so put in a dummy member - self.b(" { NULL, NULL, 0 }") - - self.b('};') - self.b('') - - self.b('static const DBusGObjectInfo _%s%s_object_info = {' - % (self.prefix_, node_name_lc)) - self.b(' 0,') # version - self.b(' _%s%s_methods,' % (self.prefix_, node_name_lc)) - self.b(' %d,' % len(methods)) - self.b('"' + method_blob.replace('\0', '\\0') + '",') - self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",') - self.b('"' + - self.get_property_glue(glue_properties).replace('\0', '\\0') + - '",') - self.b('};') - self.b('') - - self.node_name_mixed = None - self.node_name_lc = None - self.node_name_uc = None - - def get_method_glue(self, methods): - info = [] - offsets = [] - - for method in methods: - offsets.append(len(''.join(info))) - - info.append(self.iface_name + '\0') - info.append(method.getAttribute('name') + '\0') - - info.append('A\0') # async - - counter = 0 - for arg in method.getElementsByTagName('arg'): - out = arg.getAttribute('direction') == 'out' - - name = arg.getAttribute('name') - if not name: - assert out - name = 'arg%u' % counter - counter += 1 - - info.append(name + '\0') - - if out: - info.append('O\0') - else: - info.append('I\0') - - if out: - info.append('F\0') # not const - info.append('N\0') # not error or return - info.append(arg.getAttribute('type') + '\0') - - info.append('\0') - - return ''.join(info) + '\0', offsets - - def do_method_glue(self, method, offset): - lc_name = method.getAttribute('tp:name-for-bindings') - if method.getAttribute('name') != lc_name.replace('_', ''): - raise AssertionError('Method %s tp:name-for-bindings (%s) does ' - 'not match' % (method.getAttribute('name'), lc_name)) - lc_name = lc_name.lower() - - marshaller = 'g_cclosure_marshal_generic' - wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name - - self.b(" { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset)) - - def get_signal_glue(self, signals): - info = [] - - for signal in signals: - info.append(self.iface_name) - info.append(signal.getAttribute('name')) - - return '\0'.join(info) + '\0\0' - - # the implementation can be the same - get_property_glue = get_signal_glue - - def get_method_impl_names(self, method): - dbus_method_name = method.getAttribute('name') - - class_member_name = method.getAttribute('tp:name-for-bindings') - if dbus_method_name != class_member_name.replace('_', ''): - raise AssertionError('Method %s tp:name-for-bindings (%s) does ' - 'not match' % (dbus_method_name, class_member_name)) - class_member_name = class_member_name.lower() - - stub_name = (self.prefix_ + self.node_name_lc + '_' + - class_member_name) - return (stub_name + '_impl', class_member_name + '_cb') - - def do_method(self, method): - assert self.node_name_mixed is not None - - in_class = [] - - # Examples refer to Thing.DoStuff (su) -> ii - - # DoStuff - dbus_method_name = method.getAttribute('name') - # do_stuff - class_member_name = method.getAttribute('tp:name-for-bindings') - if dbus_method_name != class_member_name.replace('_', ''): - raise AssertionError('Method %s tp:name-for-bindings (%s) does ' - 'not match' % (dbus_method_name, class_member_name)) - class_member_name = class_member_name.lower() - - # void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint, - # DBusGMethodInvocation *); - stub_name = (self.prefix_ + self.node_name_lc + '_' + - class_member_name) - # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *, - # const char *, guint, DBusGMethodInvocation); - impl_name = stub_name + '_impl' - # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *, - # gint, gint); - ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' + - class_member_name) - - # Gather arguments - in_args = [] - out_args = [] - for i in method.getElementsByTagName('arg'): - name = i.getAttribute('name') - direction = i.getAttribute('direction') or 'in' - dtype = i.getAttribute('type') - - assert direction in ('in', 'out') - - if name: - name = direction + '_' + name - elif direction == 'in': - name = direction + str(len(in_args)) - else: - name = direction + str(len(out_args)) - - ctype, gtype, marshaller, pointer = type_to_gtype(dtype) - - if pointer: - ctype = 'const ' + ctype - - struct = (ctype, name) - - if direction == 'in': - in_args.append(struct) - else: - out_args.append(struct) - - # Implementation type declaration (in header, docs separated) - self.d('/**') - self.d(' * %s:' % impl_name) - self.d(' * @self: The object implementing this interface') - for (ctype, name) in in_args: - self.d(' * @%s: %s (FIXME, generate documentation)' - % (name, ctype)) - self.d(' * @context: Used to return values or throw an error') - self.d(' *') - self.d(' * The signature of an implementation of the D-Bus method') - self.d(' * %s on interface %s.' % (dbus_method_name, self.iface_name)) - self.d(' */') - - self.h('typedef void (*%s) (%s%s *self,' - % (impl_name, self.Prefix, self.node_name_mixed)) - for (ctype, name) in in_args: - self.h(' %s%s,' % (ctype, name)) - self.h(' DBusGMethodInvocation *context);') - - # Class member (in class definition) - in_class.append(' %s %s;' % (impl_name, class_member_name)) - - # Stub definition (in body only - it's static) - self.b('static void') - self.b('%s (%s%s *self,' - % (stub_name, self.Prefix, self.node_name_mixed)) - for (ctype, name) in in_args: - self.b(' %s%s,' % (ctype, name)) - self.b(' DBusGMethodInvocation *context)') - self.b('{') - self.b(' %s impl = (%s%s_GET_CLASS (self)->%s_cb);' - % (impl_name, self.PREFIX_, self.node_name_uc, class_member_name)) - self.b('') - self.b(' if (impl != NULL)') - tmp = ['self'] + [name for (ctype, name) in in_args] + ['context'] - self.b(' {') - self.b(' (impl) (%s);' % ',\n '.join(tmp)) - self.b(' }') - self.b(' else') - self.b(' {') - if self.not_implemented_func: - self.b(' %s (context);' % self.not_implemented_func) - else: - self.b(' GError e = { DBUS_GERROR, ') - self.b(' DBUS_GERROR_UNKNOWN_METHOD,') - self.b(' "Method not implemented" };') - self.b('') - self.b(' dbus_g_method_return_error (context, &e);') - self.b(' }') - self.b('}') - self.b('') - - # Implementation registration (in both header and body) - self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);' - % (self.prefix_, self.node_name_lc, class_member_name, - self.Prefix, self.node_name_mixed, impl_name)) - - self.d('/**') - self.d(' * %s%s_implement_%s:' - % (self.prefix_, self.node_name_lc, class_member_name)) - self.d(' * @klass: A class whose instances implement this interface') - self.d(' * @impl: A callback used to implement the %s D-Bus method' - % dbus_method_name) - self.d(' *') - self.d(' * Register an implementation for the %s method in the vtable' - % dbus_method_name) - self.d(' * of an implementation of this interface. To be called from') - self.d(' * the interface init function.') - self.d(' */') - - self.b('void') - self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)' - % (self.prefix_, self.node_name_lc, class_member_name, - self.Prefix, self.node_name_mixed, impl_name)) - self.b('{') - self.b(' klass->%s_cb = impl;' % class_member_name) - self.b('}') - self.b('') - - # Return convenience function (static inline, in header) - self.d('/**') - self.d(' * %s:' % ret_name) - self.d(' * @context: The D-Bus method invocation context') - for (ctype, name) in out_args: - self.d(' * @%s: %s (FIXME, generate documentation)' - % (name, ctype)) - self.d(' *') - self.d(' * Return successfully by calling dbus_g_method_return().') - self.d(' * This inline function exists only to provide type-safety.') - self.d(' */') - self.d('') - - tmp = (['DBusGMethodInvocation *context'] + - [ctype + name for (ctype, name) in out_args]) - self.h('static inline') - self.h('/* this comment is to stop gtkdoc realising this is static */') - self.h(('void %s (' % ret_name) + (',\n '.join(tmp)) + ');') - self.h('static inline void') - self.h(('%s (' % ret_name) + (',\n '.join(tmp)) + ')') - self.h('{') - tmp = ['context'] + [name for (ctype, name) in out_args] - self.h(' dbus_g_method_return (' + ',\n '.join(tmp) + ');') - self.h('}') - self.h('') - - return in_class - - def get_signal_const_entry(self, signal): - assert self.node_name_uc is not None - return ('SIGNAL_%s_%s' - % (self.node_name_uc, signal.getAttribute('name'))) - - def do_signal(self, signal): - assert self.node_name_mixed is not None - - in_base_init = [] - - # for signal: Thing::StuffHappened (s, u) - # we want to emit: - # void tp_svc_thing_emit_stuff_happened (gpointer instance, - # const char *arg0, guint arg1); - - dbus_name = signal.getAttribute('name') - - ugly_name = signal.getAttribute('tp:name-for-bindings') - if dbus_name != ugly_name.replace('_', ''): - raise AssertionError('Signal %s tp:name-for-bindings (%s) does ' - 'not match' % (dbus_name, ugly_name)) - - stub_name = (self.prefix_ + self.node_name_lc + '_emit_' + - ugly_name.lower()) - - const_name = self.get_signal_const_entry(signal) - - # Gather arguments - args = [] - for i in signal.getElementsByTagName('arg'): - name = i.getAttribute('name') - dtype = i.getAttribute('type') - tp_type = i.getAttribute('tp:type') - - if name: - name = 'arg_' + name - else: - name = 'arg' + str(len(args)) - - ctype, gtype, marshaller, pointer = type_to_gtype(dtype) - - if pointer: - ctype = 'const ' + ctype - - struct = (ctype, name, gtype) - args.append(struct) - - tmp = (['gpointer instance'] + - [ctype + name for (ctype, name, gtype) in args]) - - self.h(('void %s (' % stub_name) + (',\n '.join(tmp)) + ');') - - # FIXME: emit docs - - self.d('/**') - self.d(' * %s:' % stub_name) - self.d(' * @instance: The object implementing this interface') - for (ctype, name, gtype) in args: - self.d(' * @%s: %s (FIXME, generate documentation)' - % (name, ctype)) - self.d(' *') - self.d(' * Type-safe wrapper around g_signal_emit to emit the') - self.d(' * %s signal on interface %s.' - % (dbus_name, self.iface_name)) - self.d(' */') - - self.b('void') - self.b(('%s (' % stub_name) + (',\n '.join(tmp)) + ')') - self.b('{') - self.b(' g_assert (instance != NULL);') - self.b(' g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));' - % (self.current_gtype)) - tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name), - '0'] + [name for (ctype, name, gtype) in args]) - self.b(' g_signal_emit (' + ',\n '.join(tmp) + ');') - self.b('}') - self.b('') - - signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_', - '-') - - self.d('/**') - self.d(' * %s%s::%s:' - % (self.Prefix, self.node_name_mixed, signal_name)) - self.d(' * @self: an object') - for (ctype, name, gtype) in args: - self.d(' * @%s: %s (FIXME, generate documentation)' - % (name, ctype)) - self.d(' *') - self.d(' * The %s D-Bus signal is emitted whenever ' - 'this GObject signal is.' % dbus_name) - self.d(' */') - self.d('') - - in_base_init.append(' %s_signals[%s] =' - % (self.node_name_lc, const_name)) - in_base_init.append(' g_signal_new ("%s",' % signal_name) - in_base_init.append(' G_OBJECT_CLASS_TYPE (klass),') - in_base_init.append(' G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,') - in_base_init.append(' 0,') - in_base_init.append(' NULL, NULL,') - in_base_init.append(' g_cclosure_marshal_generic,') - in_base_init.append(' G_TYPE_NONE,') - tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args] - in_base_init.append(' %s);' % ',\n '.join(tmp)) - in_base_init.append('') - - return in_base_init - - def have_properties(self, nodes): - for node in nodes: - interface = node.getElementsByTagName('interface')[0] - if interface.getElementsByTagName('property'): - return True - return False - - def __call__(self): - nodes = self.dom.getElementsByTagName('node') - nodes.sort(key=key_by_name) - - self.h('#include <glib-object.h>') - self.h('#include <dbus/dbus-glib.h>') - - for header in self.headers: - self.h('#include %s' % header) - self.h('') - - self.h('') - self.h('G_BEGIN_DECLS') - self.h('') - - self.b('#include "%s.h"' % self.basename) - self.b('') - - for node in nodes: - self.do_node(node) - - self.h('') - self.h('G_END_DECLS') - - self.b('') - for header in self.end_headers: - self.b('#include %s' % header) - - self.h('') - self.b('') - file_set_contents(self.basename + '.h', u('\n').join(self.__header).encode('utf-8')) - file_set_contents(self.basename + '.c', u('\n').join(self.__body).encode('utf-8')) - file_set_contents(self.basename + '-gtk-doc.h', u('\n').join(self.__docs).encode('utf-8')) - -def cmdline_error(): - print("""\ -usage: - gen-ginterface [OPTIONS] xmlfile Prefix_ -options: - --include='<header.h>' (may be repeated) - --include='"header.h"' (ditto) - --include-end='"header.h"' (ditto) - Include extra headers in the generated .c file - --signal-marshal-prefix='prefix' - Use the given prefix on generated signal marshallers (default is - prefix.lower()). - --filename='BASENAME' - Set the basename for the output files (default is prefix.lower() - + 'ginterfaces') - --not-implemented-func='symbol' - Set action when methods not implemented in the interface vtable are - called. symbol must have signature - void symbol (DBusGMethodInvocation *context) - and return some sort of "not implemented" error via - dbus_g_method_return_error (context, ...) -""") - sys.exit(1) - - -if __name__ == '__main__': - from getopt import gnu_getopt - - options, argv = gnu_getopt(sys.argv[1:], '', - ['filename=', 'signal-marshal-prefix=', - 'include=', 'include-end=', - 'allow-unstable', - 'not-implemented-func=']) - - try: - prefix = argv[1] - except IndexError: - cmdline_error() - - basename = prefix.lower() + 'ginterfaces' - signal_marshal_prefix = prefix.lower().rstrip('_') - headers = [] - end_headers = [] - not_implemented_func = '' - allow_havoc = False - - for option, value in options: - if option == '--filename': - basename = value - elif option == '--signal-marshal-prefix': - signal_marshal_prefix = value - elif option == '--include': - if value[0] not in '<"': - value = '"%s"' % value - headers.append(value) - elif option == '--include-end': - if value[0] not in '<"': - value = '"%s"' % value - end_headers.append(value) - elif option == '--not-implemented-func': - not_implemented_func = value - elif option == '--allow-unstable': - allow_havoc = True - - try: - dom = xml.dom.minidom.parse(argv[0]) - except IndexError: - cmdline_error() - - Generator(dom, prefix, basename, signal_marshal_prefix, headers, - end_headers, not_implemented_func, allow_havoc)() diff --git a/tools/glib-gtypes-generator.py b/tools/glib-gtypes-generator.py deleted file mode 100644 index 1477bd37..00000000 --- a/tools/glib-gtypes-generator.py +++ /dev/null @@ -1,304 +0,0 @@ -#!/usr/bin/python - -# Generate GLib GInterfaces from the Telepathy specification. -# The master copy of this program is in the telepathy-glib repository - -# please make any changes there. -# -# Copyright (C) 2006, 2007 Collabora Limited -# -# 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 - -import sys -import xml.dom.minidom - -from libtpcodegen import file_set_contents, u -from libglibcodegen import escape_as_identifier, \ - get_docstring, \ - NS_TP, \ - Signature, \ - type_to_gtype, \ - xml_escape - - -def types_to_gtypes(types): - return [type_to_gtype(t)[1] for t in types] - - -class GTypesGenerator(object): - def __init__(self, dom, output, mixed_case_prefix): - self.dom = dom - self.Prefix = mixed_case_prefix - self.PREFIX_ = self.Prefix.upper() + '_' - self.prefix_ = self.Prefix.lower() + '_' - - self.header = [] - self.body = [] - self.docs = [] - self.output = output - - for f in (self.header, self.body, self.docs): - f.append('/* Auto-generated, do not edit.\n *\n' - ' * This file may be distributed under the same terms\n' - ' * as the specification from which it was generated.\n' - ' */\n\n') - - # keys are e.g. 'sv', values are the key escaped - self.need_mappings = {} - # keys are the contents of the struct (e.g. 'sssu'), values are the - # key escaped - self.need_structs = {} - # keys are the contents of the struct (e.g. 'sssu'), values are the - # key escaped - self.need_struct_arrays = {} - - # keys are the contents of the array (unlike need_struct_arrays!), - # values are the key escaped - self.need_other_arrays = {} - - def h(self, code): - self.header.append(code) - - def c(self, code): - self.body.append(code) - - def d(self, code): - self.docs.append(code) - - def do_mapping_header(self, mapping): - members = mapping.getElementsByTagNameNS(NS_TP, 'member') - assert len(members) == 2 - - impl_sig = ''.join([elt.getAttribute('type') - for elt in members]) - - esc_impl_sig = escape_as_identifier(impl_sig) - - name = (self.PREFIX_ + 'HASH_TYPE_' + - mapping.getAttribute('name').upper()) - impl = self.prefix_ + 'type_dbus_hash_' + esc_impl_sig - - docstring = get_docstring(mapping) or '(Undocumented)' - - self.d('/**\n * %s:\n *\n' % name.strip()) - self.d(' * %s\n' % xml_escape(docstring)) - self.d(' *\n') - self.d(' * This macro expands to a call to a function\n') - self.d(' * that returns the #GType of a #GHashTable\n') - self.d(' * appropriate for representing a D-Bus\n') - self.d(' * dictionary of signature\n') - self.d(' * <literal>a{%s}</literal>.\n' % impl_sig) - self.d(' *\n') - - key, value = members - - self.d(' * Keys (D-Bus type <literal>%s</literal>,\n' - % key.getAttribute('type')) - tp_type = key.getAttributeNS(NS_TP, 'type') - if tp_type: - self.d(' * type <literal>%s</literal>,\n' % tp_type) - self.d(' * named <literal>%s</literal>):\n' - % key.getAttribute('name')) - docstring = get_docstring(key) or '(Undocumented)' - self.d(' * %s\n' % xml_escape(docstring)) - self.d(' *\n') - - self.d(' * Values (D-Bus type <literal>%s</literal>,\n' - % value.getAttribute('type')) - tp_type = value.getAttributeNS(NS_TP, 'type') - if tp_type: - self.d(' * type <literal>%s</literal>,\n' % tp_type) - self.d(' * named <literal>%s</literal>):\n' - % value.getAttribute('name')) - docstring = get_docstring(value) or '(Undocumented)' - self.d(' * %s\n' % xml_escape(docstring)) - self.d(' *\n') - - self.d(' */\n') - - self.h('#define %s (%s ())\n\n' % (name, impl)) - self.need_mappings[impl_sig] = esc_impl_sig - - array_name = mapping.getAttribute('array-name') - if array_name: - gtype_name = self.PREFIX_ + 'ARRAY_TYPE_' + array_name.upper() - contents_sig = 'a{' + impl_sig + '}' - esc_contents_sig = escape_as_identifier(contents_sig) - impl = self.prefix_ + 'type_dbus_array_of_' + esc_contents_sig - self.d('/**\n * %s:\n\n' % gtype_name) - self.d(' * Expands to a call to a function\n') - self.d(' * that returns the #GType of a #GPtrArray\n') - self.d(' * of #%s.\n' % name) - self.d(' */\n\n') - - self.h('#define %s (%s ())\n\n' % (gtype_name, impl)) - self.need_other_arrays[contents_sig] = esc_contents_sig - - def do_struct_header(self, struct): - members = struct.getElementsByTagNameNS(NS_TP, 'member') - impl_sig = ''.join([elt.getAttribute('type') for elt in members]) - esc_impl_sig = escape_as_identifier(impl_sig) - - name = (self.PREFIX_ + 'STRUCT_TYPE_' + - struct.getAttribute('name').upper()) - impl = self.prefix_ + 'type_dbus_struct_' + esc_impl_sig - docstring = struct.getElementsByTagNameNS(NS_TP, 'docstring') - if docstring: - docstring = docstring[0].toprettyxml() - if docstring.startswith('<tp:docstring>'): - docstring = docstring[14:] - if docstring.endswith('</tp:docstring>\n'): - docstring = docstring[:-16] - if docstring.strip() in ('<tp:docstring/>', ''): - docstring = '(Undocumented)' - else: - docstring = '(Undocumented)' - self.d('/**\n * %s:\n\n' % name) - self.d(' * %s\n' % xml_escape(docstring)) - self.d(' *\n') - self.d(' * This macro expands to a call to a function\n') - self.d(' * that returns the #GType of a #GValueArray\n') - self.d(' * appropriate for representing a D-Bus struct\n') - self.d(' * with signature <literal>(%s)</literal>.\n' - % impl_sig) - self.d(' *\n') - - for i, member in enumerate(members): - self.d(' * Member %d (D-Bus type ' - '<literal>%s</literal>,\n' - % (i, member.getAttribute('type'))) - tp_type = member.getAttributeNS(NS_TP, 'type') - if tp_type: - self.d(' * type <literal>%s</literal>,\n' % tp_type) - self.d(' * named <literal>%s</literal>):\n' - % member.getAttribute('name')) - docstring = get_docstring(member) or '(Undocumented)' - self.d(' * %s\n' % xml_escape(docstring)) - self.d(' *\n') - - self.d(' */\n\n') - - self.h('#define %s (%s ())\n\n' % (name, impl)) - - array_name = struct.getAttribute('array-name') - if array_name != '': - array_name = (self.PREFIX_ + 'ARRAY_TYPE_' + array_name.upper()) - impl = self.prefix_ + 'type_dbus_array_' + esc_impl_sig - self.d('/**\n * %s:\n\n' % array_name) - self.d(' * Expands to a call to a function\n') - self.d(' * that returns the #GType of a #GPtrArray\n') - self.d(' * of #%s.\n' % name) - self.d(' */\n\n') - - self.h('#define %s (%s ())\n\n' % (array_name, impl)) - self.need_struct_arrays[impl_sig] = esc_impl_sig - - self.need_structs[impl_sig] = esc_impl_sig - - def __call__(self): - mappings = self.dom.getElementsByTagNameNS(NS_TP, 'mapping') - structs = self.dom.getElementsByTagNameNS(NS_TP, 'struct') - - for mapping in mappings: - self.do_mapping_header(mapping) - - for sig in self.need_mappings: - self.h('GType %stype_dbus_hash_%s (void);\n\n' % - (self.prefix_, self.need_mappings[sig])) - self.c('GType\n%stype_dbus_hash_%s (void)\n{\n' % - (self.prefix_, self.need_mappings[sig])) - self.c(' static GType t = 0;\n\n') - self.c(' if (G_UNLIKELY (t == 0))\n') - # FIXME: translate sig into two GTypes - items = tuple(Signature(sig)) - gtypes = types_to_gtypes(items) - self.c(' t = dbus_g_type_get_map ("GHashTable", ' - '%s, %s);\n' % (gtypes[0], gtypes[1])) - self.c(' return t;\n') - self.c('}\n\n') - - for struct in structs: - self.do_struct_header(struct) - - for sig in self.need_structs: - self.h('GType %stype_dbus_struct_%s (void);\n\n' % - (self.prefix_, self.need_structs[sig])) - self.c('GType\n%stype_dbus_struct_%s (void)\n{\n' % - (self.prefix_, self.need_structs[sig])) - self.c(' static GType t = 0;\n\n') - self.c(' if (G_UNLIKELY (t == 0))\n') - self.c(' t = dbus_g_type_get_struct ("GValueArray",\n') - items = tuple(Signature(sig)) - gtypes = types_to_gtypes(items) - for gtype in gtypes: - self.c(' %s,\n' % gtype) - self.c(' G_TYPE_INVALID);\n') - self.c(' return t;\n') - self.c('}\n\n') - - for sig in self.need_struct_arrays: - self.h('GType %stype_dbus_array_%s (void);\n\n' % - (self.prefix_, self.need_struct_arrays[sig])) - self.c('GType\n%stype_dbus_array_%s (void)\n{\n' % - (self.prefix_, self.need_struct_arrays[sig])) - self.c(' static GType t = 0;\n\n') - self.c(' if (G_UNLIKELY (t == 0))\n') - self.c(' t = dbus_g_type_get_collection ("GPtrArray", ' - '%stype_dbus_struct_%s ());\n' % - (self.prefix_, self.need_struct_arrays[sig])) - self.c(' return t;\n') - self.c('}\n\n') - - for sig in self.need_other_arrays: - self.h('GType %stype_dbus_array_of_%s (void);\n\n' % - (self.prefix_, self.need_other_arrays[sig])) - self.c('GType\n%stype_dbus_array_of_%s (void)\n{\n' % - (self.prefix_, self.need_other_arrays[sig])) - self.c(' static GType t = 0;\n\n') - self.c(' if (G_UNLIKELY (t == 0))\n') - - if sig[:2] == 'a{' and sig[-1:] == '}': - # array of mappings - self.c(' t = dbus_g_type_get_collection (' - '"GPtrArray", ' - '%stype_dbus_hash_%s ());\n' % - (self.prefix_, escape_as_identifier(sig[2:-1]))) - elif sig[:2] == 'a(' and sig[-1:] == ')': - # array of arrays of struct - self.c(' t = dbus_g_type_get_collection (' - '"GPtrArray", ' - '%stype_dbus_array_%s ());\n' % - (self.prefix_, escape_as_identifier(sig[2:-1]))) - elif sig[:1] == 'a': - # array of arrays of non-struct - self.c(' t = dbus_g_type_get_collection (' - '"GPtrArray", ' - '%stype_dbus_array_of_%s ());\n' % - (self.prefix_, escape_as_identifier(sig[1:]))) - else: - raise AssertionError("array of '%s' not supported" % sig) - - self.c(' return t;\n') - self.c('}\n\n') - - file_set_contents(self.output + '.h', u('').join(self.header).encode('utf-8')) - file_set_contents(self.output + '-body.h', u('').join(self.body).encode('utf-8')) - file_set_contents(self.output + '-gtk-doc.h', u('').join(self.docs).encode('utf-8')) - -if __name__ == '__main__': - argv = sys.argv[1:] - - dom = xml.dom.minidom.parse(argv[0]) - - GTypesGenerator(dom, argv[1], argv[2])() diff --git a/tools/glib-interfaces-body-generator.xsl b/tools/glib-interfaces-body-generator.xsl deleted file mode 100644 index caff8917..00000000 --- a/tools/glib-interfaces-body-generator.xsl +++ /dev/null @@ -1,47 +0,0 @@ -<!-- Stylesheet to extract C interface names from the Telepathy spec. -The master copy of this stylesheet is in telepathy-glib - please make any -changes there. - -Copyright (C) 2006, 2007 Collabora Limited - -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 Street, Fifth Floor, Boston, MA 02110-1301, USA. ---> - -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - exclude-result-prefixes="tp"> - - <xsl:import href="c-interfaces-generator.xsl"/> - - <xsl:template match="interface"> - <xsl:text>GQuark </xsl:text> - <xsl:value-of select="$prefix"/> - <xsl:text>_iface_quark_</xsl:text> - <xsl:value-of select="translate(../@name, concat($upper, '/'), $lower)"/> - <xsl:text> (void) { </xsl:text> - <xsl:text> static GQuark quark = 0; </xsl:text> - <xsl:text> if (G_UNLIKELY (quark == 0)) </xsl:text> - <xsl:text> { </xsl:text> - <xsl:text> quark = g_quark_from_static_string ("</xsl:text> - <xsl:value-of select="@name"/> - <xsl:text>"); </xsl:text> - <xsl:text> } </xsl:text> - <xsl:text> return quark; </xsl:text> - <xsl:text>} </xsl:text> - </xsl:template> - -</xsl:stylesheet> - -<!-- vim:set sw=2 sts=2 et noai noci: --> diff --git a/tools/glib-interfaces-generator.xsl b/tools/glib-interfaces-generator.xsl deleted file mode 100644 index e703c407..00000000 --- a/tools/glib-interfaces-generator.xsl +++ /dev/null @@ -1,55 +0,0 @@ -<!-- Stylesheet to extract C interface names from the Telepathy spec. -The master copy of this stylesheet is in telepathy-glib - please make any -changes there. - -Copyright (C) 2006, 2007 Collabora Limited - -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 Street, Fifth Floor, Boston, MA 02110-1301, USA. ---> - -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - exclude-result-prefixes="tp"> - - <xsl:import href="c-interfaces-generator.xsl"/> - - <xsl:template match="interface"> - <xsl:apply-imports/> - - <xsl:text>/** * </xsl:text> - <xsl:value-of select="$PREFIX"/> - <xsl:text>_IFACE_QUARK_</xsl:text> - <xsl:value-of select="translate(../@name, concat($lower, '/'), $upper)"/> - <xsl:text>: * * Expands to a call to a function that </xsl:text> - <xsl:text>returns a quark for the interface name "</xsl:text> - <xsl:value-of select="@name"/> - <xsl:text>" */ #define </xsl:text> - <xsl:value-of select="$PREFIX"/> - <xsl:text>_IFACE_QUARK_</xsl:text> - <xsl:value-of select="translate(../@name, concat($lower, '/'), $upper)"/> - <xsl:text> \ (</xsl:text> - <xsl:value-of select="$prefix"/> - <xsl:text>_iface_quark_</xsl:text> - <xsl:value-of select="translate(../@name, concat($upper, '/'), $lower)"/> - <xsl:text> ()) GQuark </xsl:text> - <xsl:value-of select="$prefix"/> - <xsl:text>_iface_quark_</xsl:text> - <xsl:value-of select="translate(../@name, concat($upper, '/'), $lower)"/> - <xsl:text> (void); </xsl:text> - </xsl:template> - -</xsl:stylesheet> - -<!-- vim:set sw=2 sts=2 et noai noci: --> diff --git a/tools/identity.xsl b/tools/identity.xsl deleted file mode 100644 index 6630f84d..00000000 --- a/tools/identity.xsl +++ /dev/null @@ -1,7 +0,0 @@ -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> - <xsl:template match="@*|node()"> - <xsl:copy> - <xsl:apply-templates select="@*|node()"/> - </xsl:copy> - </xsl:template> -</xsl:stylesheet> diff --git a/tools/lcov.am b/tools/lcov.am index a5feb727..298e0914 100644 --- a/tools/lcov.am +++ b/tools/lcov.am @@ -8,7 +8,7 @@ lcov-report: --remove @top_builddir@/lcov.info.tmp \ 'doc/reference/*/*-scan.c' rm @top_builddir@/lcov.info.tmp - $(mkdir_p) @top_builddir@/lcov.html + $(MKDIR_P) @top_builddir@/lcov.html git_commit=`GIT_DIR=@top_srcdir@/.git git log -1 --pretty=format:%h 2>/dev/null`;\ genhtml --title "@PACKAGE_STRING@ $$git_commit" \ --output-directory @top_builddir@/lcov.html lcov.info diff --git a/tools/libglibcodegen.py b/tools/libglibcodegen.py deleted file mode 100644 index 5c76f07d..00000000 --- a/tools/libglibcodegen.py +++ /dev/null @@ -1,216 +0,0 @@ -"""Library code for GLib/D-Bus-related code generation. - -The master copy of this library is in the telepathy-glib repository - -please make any changes there. -""" - -# Copyright (C) 2006-2008 Collabora Limited -# -# 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 - - -from libtpcodegen import NS_TP, \ - Signature, \ - cmp_by_name, \ - escape_as_identifier, \ - get_by_path, \ - get_descendant_text, \ - get_docstring, \ - xml_escape, \ - get_deprecated - -def dbus_gutils_wincaps_to_uscore(s): - """Bug-for-bug compatible Python port of _dbus_gutils_wincaps_to_uscore - which gets sequences of capital letters wrong in the same way. - (e.g. in Telepathy, SendDTMF -> send_dt_mf) - """ - ret = '' - for c in s: - if c >= 'A' and c <= 'Z': - length = len(ret) - if length > 0 and (length < 2 or ret[length-2] != '_'): - ret += '_' - ret += c.lower() - else: - ret += c - return ret - - -def signal_to_marshal_type(signal): - """ - return a list of strings indicating the marshalling type for this signal. - """ - - mtype=[] - for i in signal.getElementsByTagName("arg"): - name =i.getAttribute("name") - type = i.getAttribute("type") - mtype.append(type_to_gtype(type)[2]) - - return mtype - - -_glib_marshallers = ['VOID', 'BOOLEAN', 'CHAR', 'UCHAR', 'INT', - 'STRING', 'UINT', 'LONG', 'ULONG', 'ENUM', 'FLAGS', 'FLOAT', - 'DOUBLE', 'STRING', 'PARAM', 'BOXED', 'POINTER', 'OBJECT', - 'UINT_POINTER'] - - -def signal_to_marshal_name(signal, prefix): - - mtype = signal_to_marshal_type(signal) - if len(mtype): - name = '_'.join(mtype) - else: - name = 'VOID' - - if name in _glib_marshallers: - return 'g_cclosure_marshal_VOID__' + name - else: - return prefix + '_marshal_VOID__' + name - - -def method_to_glue_marshal_name(method, prefix): - - mtype = [] - for i in method.getElementsByTagName("arg"): - if i.getAttribute("direction") != "out": - type = i.getAttribute("type") - mtype.append(type_to_gtype(type)[2]) - - mtype.append('POINTER') - - name = '_'.join(mtype) - - if name in _glib_marshallers: - return 'g_cclosure_marshal_VOID__' + name - else: - return prefix + '_marshal_VOID__' + name - - -def type_to_gtype(s): - if s == 'y': #byte - return ("guchar ", "G_TYPE_UCHAR","UCHAR", False) - elif s == 'b': #boolean - return ("gboolean ", "G_TYPE_BOOLEAN","BOOLEAN", False) - elif s == 'n': #int16 - return ("gint ", "G_TYPE_INT","INT", False) - elif s == 'q': #uint16 - return ("guint ", "G_TYPE_UINT","UINT", False) - elif s == 'i': #int32 - return ("gint ", "G_TYPE_INT","INT", False) - elif s == 'u': #uint32 - return ("guint ", "G_TYPE_UINT","UINT", False) - elif s == 'x': #int64 - return ("gint64 ", "G_TYPE_INT64","INT64", False) - elif s == 't': #uint64 - return ("guint64 ", "G_TYPE_UINT64","UINT64", False) - elif s == 'd': #double - return ("gdouble ", "G_TYPE_DOUBLE","DOUBLE", False) - elif s == 's': #string - return ("gchar *", "G_TYPE_STRING", "STRING", True) - elif s == 'g': #signature - FIXME - return ("gchar *", "DBUS_TYPE_G_SIGNATURE", "STRING", True) - elif s == 'o': #object path - return ("gchar *", "DBUS_TYPE_G_OBJECT_PATH", "BOXED", True) - elif s == 'v': #variant - return ("GValue *", "G_TYPE_VALUE", "BOXED", True) - elif s == 'as': #array of strings - return ("gchar **", "G_TYPE_STRV", "BOXED", True) - elif s == 'ay': #byte array - return ("GArray *", - "dbus_g_type_get_collection (\"GArray\", G_TYPE_UCHAR)", "BOXED", - True) - elif s == 'au': #uint array - return ("GArray *", "DBUS_TYPE_G_UINT_ARRAY", "BOXED", True) - elif s == 'ai': #int array - return ("GArray *", "DBUS_TYPE_G_INT_ARRAY", "BOXED", True) - elif s == 'ax': #int64 array - return ("GArray *", "DBUS_TYPE_G_INT64_ARRAY", "BOXED", True) - elif s == 'at': #uint64 array - return ("GArray *", "DBUS_TYPE_G_UINT64_ARRAY", "BOXED", True) - elif s == 'ad': #double array - return ("GArray *", "DBUS_TYPE_G_DOUBLE_ARRAY", "BOXED", True) - elif s == 'ab': #boolean array - return ("GArray *", "DBUS_TYPE_G_BOOLEAN_ARRAY", "BOXED", True) - elif s == 'ao': #object path array - return ("GPtrArray *", - 'dbus_g_type_get_collection ("GPtrArray",' - ' DBUS_TYPE_G_OBJECT_PATH)', - "BOXED", True) - elif s == 'a{ss}': #hash table of string to string - return ("GHashTable *", "DBUS_TYPE_G_STRING_STRING_HASHTABLE", "BOXED", False) - elif s[:2] == 'a{': #some arbitrary hash tables - if s[2] not in ('y', 'b', 'n', 'q', 'i', 'u', 's', 'o', 'g'): - raise Exception("can't index a hashtable off non-basic type " + s) - first = type_to_gtype(s[2]) - second = type_to_gtype(s[3:-1]) - return ("GHashTable *", "(dbus_g_type_get_map (\"GHashTable\", " + first[1] + ", " + second[1] + "))", "BOXED", False) - elif s[:2] in ('a(', 'aa'): # array of structs or arrays, recurse - gtype = type_to_gtype(s[1:])[1] - return ("GPtrArray *", "(dbus_g_type_get_collection (\"GPtrArray\", "+gtype+"))", "BOXED", True) - elif s[:1] == '(': #struct - gtype = "(dbus_g_type_get_struct (\"GValueArray\", " - for subsig in Signature(s[1:-1]): - gtype = gtype + type_to_gtype(subsig)[1] + ", " - gtype = gtype + "G_TYPE_INVALID))" - return ("GValueArray *", gtype, "BOXED", True) - - # we just don't know .. - raise Exception("don't know the GType for " + s) - -def move_into_gvalue(gvaluep, gtype, marshaller, name): - if gtype == 'G_TYPE_STRING': - return 'g_value_take_string (%s, %s);' % (gvaluep, name) - elif marshaller == 'BOXED': - return 'g_value_take_boxed (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_UCHAR': - return 'g_value_set_uchar (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_BOOLEAN': - return 'g_value_set_boolean (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_INT': - return 'g_value_set_int (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_UINT': - return 'g_value_set_uint (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_INT64': - return 'g_value_set_int (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_UINT64': - return 'g_value_set_uint64 (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_DOUBLE': - return 'g_value_set_double (%s, %s);' % (gvaluep, name) - else: - raise AssertionError("Don't know how to put %s in a GValue" % gtype) - -def copy_into_gvalue(gvaluep, gtype, marshaller, name): - if gtype == 'G_TYPE_STRING': - return 'g_value_set_string (%s, %s);' % (gvaluep, name) - elif marshaller == 'BOXED': - return 'g_value_set_boxed (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_UCHAR': - return 'g_value_set_uchar (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_BOOLEAN': - return 'g_value_set_boolean (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_INT': - return 'g_value_set_int (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_UINT': - return 'g_value_set_uint (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_INT64': - return 'g_value_set_int (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_UINT64': - return 'g_value_set_uint64 (%s, %s);' % (gvaluep, name) - elif gtype == 'G_TYPE_DOUBLE': - return 'g_value_set_double (%s, %s);' % (gvaluep, name) - else: - raise AssertionError("Don't know how to put %s in a GValue" % gtype) diff --git a/tools/libtpcodegen.py b/tools/libtpcodegen.py deleted file mode 100644 index 99de6634..00000000 --- a/tools/libtpcodegen.py +++ /dev/null @@ -1,247 +0,0 @@ -"""Library code for language-independent D-Bus-related code generation. - -The master copy of this library is in the telepathy-glib repository - -please make any changes there. -""" - -# Copyright (C) 2006-2008 Collabora Limited -# -# 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 - -import os -import sys -from string import ascii_letters, digits - - -NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - -_ASCII_ALNUM = ascii_letters + digits - -if sys.version_info[0] >= 3: - def u(s): - """Return s, which must be a str literal with no non-ASCII characters. - This is like a more restricted form of the Python 2 u'' syntax. - """ - return s.encode('ascii').decode('ascii') -else: - def u(s): - """Return a Unicode version of s, which must be a str literal - (a bytestring) in which each byte is an ASCII character. - This is like a more restricted form of the u'' syntax. - """ - return s.decode('ascii') - -def file_set_contents(filename, contents): - try: - os.remove(filename) - except OSError: - pass - try: - os.remove(filename + '.tmp') - except OSError: - pass - - open(filename + '.tmp', 'wb').write(contents) - os.rename(filename + '.tmp', filename) - -def cmp_by_name(node1, node2): - return cmp(node1.getAttributeNode("name").nodeValue, - node2.getAttributeNode("name").nodeValue) - -def key_by_name(node): - return node.getAttributeNode("name").nodeValue - -def escape_as_identifier(identifier): - """Escape the given string to be a valid D-Bus object path or service - name component, using a reversible encoding to ensure uniqueness. - - The reversible encoding is as follows: - - * The empty string becomes '_' - * Otherwise, each non-alphanumeric character is replaced by '_' plus - two lower-case hex digits; the same replacement is carried out on - the first character, if it's a digit - """ - # '' -> '_' - if not identifier: - return '_' - - # A bit of a fast path for strings which are already OK. - # We deliberately omit '_' because, for reversibility, that must also - # be escaped. - if (identifier.strip(_ASCII_ALNUM) == '' and - identifier[0] in ascii_letters): - return identifier - - # The first character may not be a digit - if identifier[0] not in ascii_letters: - ret = ['_%02x' % ord(identifier[0])] - else: - ret = [identifier[0]] - - # Subsequent characters may be digits or ASCII letters - for c in identifier[1:]: - if c in _ASCII_ALNUM: - ret.append(c) - else: - ret.append('_%02x' % ord(c)) - - return ''.join(ret) - - -def get_by_path(element, path): - branches = path.split('/') - branch = branches[0] - - # Is the current branch an attribute, if so, return the attribute value - if branch[0] == '@': - return element.getAttribute(branch[1:]) - - # Find matching children for the branch - children = [] - if branch == '..': - children.append(element.parentNode) - else: - for x in element.childNodes: - if x.localName == branch: - children.append(x) - - ret = [] - # If this is not the last path element, recursively gather results from - # children - if len(branches) > 1: - for x in children: - add = get_by_path(x, '/'.join(branches[1:])) - if isinstance(add, list): - ret += add - else: - return add - else: - ret = children - - return ret - - -def get_docstring(element): - docstring = None - for x in element.childNodes: - if x.namespaceURI == NS_TP and x.localName == 'docstring': - docstring = x - if docstring is not None: - docstring = docstring.toxml().replace('\n', ' ').strip() - if docstring.startswith('<tp:docstring>'): - docstring = docstring[14:].lstrip() - if docstring.endswith('</tp:docstring>'): - docstring = docstring[:-15].rstrip() - if docstring in ('<tp:docstring/>', ''): - docstring = '' - return docstring - -def get_deprecated(element): - text = [] - for x in element.childNodes: - if hasattr(x, 'data'): - text.append(x.data.replace('\n', ' ').strip()) - else: - # This caters for tp:dbus-ref elements, but little else. - if x.childNodes and hasattr(x.childNodes[0], 'data'): - text.append(x.childNodes[0].data.replace('\n', ' ').strip()) - return ' '.join(text) - -def get_descendant_text(element_or_elements): - if not element_or_elements: - return '' - if isinstance(element_or_elements, list): - return ''.join(map(get_descendant_text, element_or_elements)) - parts = [] - for x in element_or_elements.childNodes: - if x.nodeType == x.TEXT_NODE: - parts.append(x.nodeValue) - elif x.nodeType == x.ELEMENT_NODE: - parts.append(get_descendant_text(x)) - else: - pass - return ''.join(parts) - - -class _SignatureIter: - """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we - can run genginterface in a limited environment with only Python - (like Scratchbox). - """ - def __init__(self, string): - self.remaining = string - - def next(self): - return self.__next__() - - def __next__(self): - if self.remaining == '': - raise StopIteration - - signature = self.remaining - block_depth = 0 - block_type = None - end = len(signature) - - for marker in range(0, end): - cur_sig = signature[marker] - - if cur_sig == 'a': - pass - elif cur_sig == '{' or cur_sig == '(': - if block_type == None: - block_type = cur_sig - - if block_type == cur_sig: - block_depth = block_depth + 1 - - elif cur_sig == '}': - if block_type == '{': - block_depth = block_depth - 1 - - if block_depth == 0: - end = marker - break - - elif cur_sig == ')': - if block_type == '(': - block_depth = block_depth - 1 - - if block_depth == 0: - end = marker - break - - else: - if block_depth == 0: - end = marker - break - - end = end + 1 - self.remaining = signature[end:] - return Signature(signature[0:end]) - - -class Signature(str): - """A string, iteration over which is by D-Bus single complete types - rather than characters. - """ - def __iter__(self): - return _SignatureIter(self) - - -def xml_escape(s): - s = s.replace('&', '&').replace("'", ''').replace('"', '"') - return s.replace('<', '<').replace('>', '>') diff --git a/tools/spec-to-introspect.xsl b/tools/spec-to-introspect.xsl deleted file mode 100644 index 992efc73..00000000 --- a/tools/spec-to-introspect.xsl +++ /dev/null @@ -1,26 +0,0 @@ -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - xmlns:mc="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - exclude-result-prefixes="mc"> - - <xsl:template match="*"> - <xsl:copy> - <xsl:for-each select="@*"> - <xsl:if test="not(starts-with(name(), 'mc:'))"> - <xsl:copy/> - </xsl:if> - </xsl:for-each> - <xsl:apply-templates/> - </xsl:copy> - </xsl:template> - - <xsl:template match="mc:*"/> - <xsl:template match="text()"/> - - <xsl:output method="xml" indent="yes" encoding="UTF-8" - omit-xml-declaration="no" - doctype-system="http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" - doctype-public="-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" /> - -</xsl:stylesheet> - -<!-- vim:set sw=2 sts=2 et: --> diff --git a/util/Makefile.am b/util/Makefile.am index f506dd22..4a5a6016 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -37,7 +37,7 @@ Android.mk: Makefile.am -:EXECUTABLE mc-tool -:TAGS eng debug \ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ -:SOURCES $(mc_tool_SOURCES) \ - -:CFLAGS $(DEFS) $(CFLAGS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + -:CFLAGS $(DEFS) $(CFLAGS) $(DEFAULT_INCLUDES) \ $(AM_CFLAGS) \ -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) \ -:LDFLAGS $(LDADD) \ diff --git a/xml/Account_Interface_Conditions.xml b/xml/Account_Interface_Conditions.xml deleted file mode 100644 index eb384826..00000000 --- a/xml/Account_Interface_Conditions.xml +++ /dev/null @@ -1,99 +0,0 @@ -<?xml version="1.0" ?> -<node name="/Account_Interface_Conditions" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - <tp:copyright>Copyright (C) 2008 Nokia Corporation</tp:copyright> - <tp:license xmlns="http://www.w3.org/1999/xhtml"> - <p>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.</p> - -<p>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.</p> - -<p>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 Street, Fifth Floor, Boston, MA 02110-1301, USA.</p> - </tp:license> - <interface name="com.nokia.Account.Interface.Conditions"> - <tp:requires interface="org.freedesktop.Telepathy.Account"/> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>The Conditions interface specifies under what conditions an account - can be put online. When the conditions are met and the - ConnectAutomatically property on the account is set to True, then the - account will automatically try to connect. On the other hand, if the - conditions are not met, the account won't attempt to connect, not even if - requested so.</p> - - <tp:rationale> - <p>The main goal is to bind accounts to connectivity: most TCP and UDP - based protocols require a network route to be present, while link-local - does not; some protocol might be specifically designed to work only over - bluetooth, in which case we don't want to attempt a connection if - bluetooth is not available. Then, some accounts might work only in certain - VPNs.</p> - - <p>There is also the case of user preferences: one might want to activate - a certain account only when he is at home (in that case, he could set a - rule on the network ESSID) or when he is not at work (this requires the - possibility of negating rules).</p> - - <p>The proposed format of a rule is (name, value), where name is a string - (which is typically a connectivity parameter, such as "ip-address", - "vpn-name", "essid", maybe prefixed with some namespacing convention) - and value is the value the parameter must assume for the rule to be met. - The value, it will be specified as a string, but we want to be - able to specify multiple alternatives (for instance a rule might be - "connect if ESSID is HOME or WORK"), as well as negative matches. The - proposed syntax is simple yet expressive enough.</p> - - </tp:rationale> - - <p>Conditions consists of a list of rules, which take the form of a - struct <code>(name, value)</code>, where:</p> - <ul> - <li><code>name</code> is a string which names the rule</li> - <li><code>value</code> is the value</li> - </ul> - </tp:docstring> - - <property name="Condition" type="a{ss}" tp:type="String_String_Map" - access="readwrite"> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>The condition is a list of rules, consisting of a name and of a value, - both strings. - The syntax for the value is:</p> - <code> - value ::= [logic] (":" singlevalue | "[" listvalue)<br /> - logic ::= ("!" | <possibly other operators>)<br /> - singlevalue ::= <possibly any ASCII string><br /> - listvalue ::= separator list<br /> - separator ::= <any non-blank ASCII character><br /> - list ::= singlevaluenonsep (separator singlevaluenonsep)*<br /> - singlevaluenonsep ::= <ASCII string not containing the separator character><br /> - </code> - - <p>Some examples of values:</p> - <code> - :my_essid_name<br /> - !:my_essid_name<br /> - [,my_essid_name,his_essid_name<br /> - ![,my_essid_name,his_essid_name<br /> - </code> - - <tp:rationale> - <p> - Questions:</p> - <ul> - <li>Should it be a(ss) or a{ss}?</li> - <li>Should we specify namespacing rules for the name?</li> - <li>Should we allow wildcards in the values?</li> - </ul> - </tp:rationale> - </tp:docstring> - </property> - - </interface> -</node> -<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/xml/Account_Interface_External_Password_Storage.xml b/xml/Account_Interface_External_Password_Storage.xml deleted file mode 100644 index 5bd1bfce..00000000 --- a/xml/Account_Interface_External_Password_Storage.xml +++ /dev/null @@ -1,58 +0,0 @@ -<?xml version="1.0" ?> -<node name="/Account_Interface_External_Password_Storage" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - - <tp:copyright>Copyright © 2011 Collabora Ltd.</tp:copyright> - <tp:license xmlns="http://www.w3.org/1999/xhtml"> - <p>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.</p> - - <p>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.</p> - - <p>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 Street, Fifth Floor, Boston, MA - 02110-1301, USA.</p> - </tp:license> - - <interface name="org.freedesktop.Telepathy.Account.Interface.ExternalPasswordStorage.DRAFT" - tp:causes-havoc="experimental"> - <tp:added version="0.21.10">(draft 1)</tp:added> - <tp:requires interface="org.freedesktop.Telepathy.Account"/> - - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>An interface for Accounts whose passwords are stored externally and - SHOULD NOT be stored by either the - <tp:dbus-ref namespace="ofdT">AccountManager</tp:dbus-ref> nor any - <tp:dbus-ref namespace="ofdT.Channel.Type">ServerAuthentication</tp:dbus-ref> - handler.</p> - - <p>This interface SHOULD only appear on accounts for which the - related Connection Manager implements - <tp:dbus-ref namespace="ofdT">ConnectionManager.Interface.AccountStorage.DRAFT</tp:dbus-ref>.</p> - </tp:docstring> - - <method name="ForgetPassword" tp:name-for-bindings="Forget_Password"> - <tp:docstring> - Clears any saved password associated with this account. - </tp:docstring> - </method> - - <property name="PasswordSaved" - tp:name-for-bindings="Password_Saved" - type="b" access="read"> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>Indicates whether the account has a saved password or not.</p> - - <p>Change notification for this property is provided by the - standard D-Bus <code>PropertiesChanged</code> signal.</p> - </tp:docstring> - </property> - - </interface> -</node> diff --git a/xml/Account_Interface_Hidden.xml b/xml/Account_Interface_Hidden.xml deleted file mode 100644 index cb001917..00000000 --- a/xml/Account_Interface_Hidden.xml +++ /dev/null @@ -1,65 +0,0 @@ -<?xml version="1.0" ?> -<node name="/Account_Interface_Hidden" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - - <tp:copyright>Copyright © 2010 Collabora Ltd.</tp:copyright> - <tp:license xmlns="http://www.w3.org/1999/xhtml"> - <p>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.</p> - - <p>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.</p> - - <p>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 Street, Fifth Floor, Boston, MA - 02110-1301, USA.</p> - </tp:license> - - <interface name="org.freedesktop.Telepathy.Account.Interface.Hidden.DRAFT1" - tp:causes-havoc="outrageous"> - <tp:added version="0.21.10">(draft 1)</tp:added> - - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>An interface for flagging certain accounts as hidden, so that they do - not appear in the account manager's standard lists of accounts. - Accounts whose <tp:member-ref>Hidden</tp:member-ref> property is - <code>True</code> are intended for non-interactive use (by - non-user-visible services), and appear on the <tp:dbus-ref - namespace='ofdT'>AccountManager.Interface.Hidden.DRAFT1</tp:dbus-ref> - interface; in all other respects, they behave like any other - account.</p> - - <tp:rationale> - <p>XMPP, in particular, is increasingly used for purposes other than - instant messaging and VoIP. For instance, extensions exist for - inter-device bookmark synchronization.</p> - - <p>While obviously these services could re-use connections intended for - instant messaging, in some cases you might want to use a different - account. (Perhaps your bookmark sync provider is not your IM - provider.) This API allows such auxiliary accounts to exist in - Telepathy, while not being displayed in standard user interfaces for - IM, VoIP, and friends.</p> - </tp:rationale> - </tp:docstring> - - <property name="Hidden" tp:name-for-bindings="Hidden" - type="b" access="read" tp:immutable='aye'> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>If <code>True</code>, this account is intended for non-interactive - use, and thus should not be presented to the user. It will not appear - in properties and signals on the main <tp:dbus-ref - namespace='ofdT'>AccountManager</tp:dbus-ref> interface; instead, it - will show up on <tp:dbus-ref - namespace='ofdT'>AccountManager.Interface.Hidden.DRAFT1</tp:dbus-ref>.</p> - </tp:docstring> - </property> - - </interface> -</node> -<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/xml/Account_Manager_Interface_Hidden.xml b/xml/Account_Manager_Interface_Hidden.xml deleted file mode 100644 index 284eb642..00000000 --- a/xml/Account_Manager_Interface_Hidden.xml +++ /dev/null @@ -1,100 +0,0 @@ -<?xml version="1.0" ?> -<node name="/Account_Manager_Interface_Hidden" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - <tp:copyright>Copyright © 2010 Collabora Ltd.</tp:copyright> - <tp:copyright>Copyright © 2010 Nokia Corporation</tp:copyright> - <tp:license xmlns="http://www.w3.org/1999/xhtml"> -<p>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.</p> - -<p>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.</p> - -<p>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 Street, Fifth Floor, Boston, MA 02110-1301, USA. -</p> - </tp:license> - <interface - name="org.freedesktop.Telepathy.AccountManager.Interface.Hidden.DRAFT1" - tp:causes-havoc='kind of sketchy'> - <tp:requires interface='org.freedesktop.Telepathy.AccountManager'/> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>This interface lists accounts whose <tp:dbus-ref - namespace='ofdT.Account.Interface.Hidden.DRAFT1'>Hidden</tp:dbus-ref> - property is <code>True</code>.</p> - </tp:docstring> - <tp:added version="0.21.10">first draft</tp:added> - - <property name="ValidHiddenAccounts" type="ao" access="read" - tp:name-for-bindings="Valid_Hidden_Accounts"> - <tp:docstring> - A list of valid (complete, usable) <tp:dbus-ref - namespace="org.freedesktop.Telepathy">Account</tp:dbus-ref>s intended - exclusively for noninteractive applications. These accounts are not - included in <tp:dbus-ref - namespace='ofdT'>AccountManager.ValidAccounts</tp:dbus-ref>. Change - notification is via - <tp:member-ref>HiddenAccountValidityChanged</tp:member-ref>. - </tp:docstring> - </property> - - <property name="InvalidHiddenAccounts" type="ao" access="read" - tp:name-for-bindings="Invalid_Hidden_Accounts"> - <tp:docstring> - A list of incomplete or otherwise unusable <tp:dbus-ref - namespace="org.freedesktop.Telepathy">Account</tp:dbus-ref>s intended - exclusively for noninteractive applications. Change notification is via - <tp:member-ref>HiddenAccountValidityChanged</tp:member-ref>. - </tp:docstring> - </property> - - <signal name="HiddenAccountRemoved" - tp:name-for-bindings="Hidden_Account_Removed"> - <tp:docstring> - The given account has been removed from - <tp:member-ref>ValidHiddenAccounts</tp:member-ref> or - <tp:member-ref>InvalidHiddenAccounts</tp:member-ref>. - </tp:docstring> - - <arg name="Account" type="o"> - <tp:docstring> - An Account, which must not be used any more. - </tp:docstring> - </arg> - </signal> - - <signal name="HiddenAccountValidityChanged" - tp:name-for-bindings="Hidden_Account_Validity_Changed"> - <tp:docstring> - The validity of the given account has changed. New magic - accounts are also indicated by this signal, as an account validity - change (usually to True) on an account that did not previously exist. - - <tp:rationale> - This is effectively change notification for the valid and invalid - accounts lists. - </tp:rationale> - </tp:docstring> - - <arg name="Account" type="o"> - <tp:docstring> - An <tp:dbus-ref - namespace="org.freedesktop.Telepathy">Account</tp:dbus-ref>. - </tp:docstring> - </arg> - - <arg name="Valid" type="b"> - <tp:docstring> - True if the account is now valid. - </tp:docstring> - </arg> - </signal> - - </interface> -</node> -<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/xml/Channel_Dispatcher_Interface_Messages_DRAFT.xml b/xml/Channel_Dispatcher_Interface_Messages_DRAFT.xml deleted file mode 100644 index 61a398b7..00000000 --- a/xml/Channel_Dispatcher_Interface_Messages_DRAFT.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" ?> -<node name="/Channel_Dispatcher_Interface_Messages_Draft" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - - <tp:copyright>Copyright (C) 2011 Collabora Ltd.</tp:copyright> - <tp:copyright>Copyright (C) 2011 Nokia Corporation</tp:copyright> - <tp:license xmlns="http://www.w3.org/1999/xhtml"> - <p>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.</p> - - <p>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.</p> - - <p>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 Street, Fifth Floor, Boston, MA 02110-1301, - USA.</p> - </tp:license> - - <interface - name="org.freedesktop.Telepathy.ChannelDispatcher.Interface.Messages.DRAFT" - tp:causes-havoc="not yet final"> - - <tp:requires interface="org.freedesktop.Telepathy.ChannelDispatcher"/> - - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p> - This interface allows DBus clients to use the ChannelDispatcher to - send one-off text messages to a contact, identified by account and - target ID, without requiring the caller to handle channels or be - the primary message UI. - </p> - - <tp:rationale> - <p> - This enables entities other than the main UI to send messages - to contacts. - </p> - </tp:rationale> - </tp:docstring> - - <method name="SendMessage" tp:name-for-bindings="Send_Message"> - <arg direction="in" name="Account" type="o"/> - <arg direction="in" name="TargetID" type="s"/> - <arg direction="in" name="Message" type="aa{sv}" - tp:type="Message_Part[]"/> - <arg direction="in" name="Flags" type="u"/> - <arg direction="out" name="Token" type="s"/> - </method> - - </interface> -</node> - diff --git a/xml/Connection_Manager_Interface_Account_Storage.xml b/xml/Connection_Manager_Interface_Account_Storage.xml deleted file mode 100644 index 2f4f4bf7..00000000 --- a/xml/Connection_Manager_Interface_Account_Storage.xml +++ /dev/null @@ -1,120 +0,0 @@ -<?xml version="1.0" ?> -<node name="/Connection_Manager_Interface_Account_Storage" - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - - <tp:copyright>Copyright © 2011 Collabora Ltd.</tp:copyright> - <tp:license xmlns="http://www.w3.org/1999/xhtml"> - <p>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.</p> - - <p>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.</p> - - <p>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 Street, Fifth Floor, Boston, MA - 02110-1301, USA.</p> - </tp:license> - - <interface name="org.freedesktop.Telepathy.ConnectionManager.Interface.AccountStorage.DRAFT" - tp:causes-havoc="experimental"> - <tp:added version="0.21.10">(draft 1)</tp:added> - <tp:requires interface="org.freedesktop.Telepathy.ConnectionManager"/> - - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>An interface for connection managers that store account details - internally. At the moment this consists only of storing an account's - credentials, but other functionality may be added in the future.</p> - - <p><tp:dbus-ref namespace="ofdT">Account</tp:dbus-ref> objects - representing accounts on a connection manager that implements this - interface should implement the - <tp:dbus-ref namespace="ofdT.Account.Interface">ExternalPasswordStorage.DRAFT</tp:dbus-ref> - interface.</p> - </tp:docstring> - - <tp:flags name="Account_Flags" value-prefix="Account_Flag" type="u"> - <tp:docstring> - A set of flags representing the status of the Account stored in the - Connection Manager. - </tp:docstring> - - <tp:flag suffix="Credentials_Stored" value="1"> - <tp:docstring> - The associated account has its authentication credentials (password) - stored in the connection manager - </tp:docstring> - </tp:flag> - </tp:flags> - - <tp:mapping name="Account_Flags_Map" array-name="Account_Flags_Map_List"> - <tp:docstring>A mapping from Account_Ids to account flags. - </tp:docstring> - <tp:member type="s" name="Account_Id"/> - <tp:member type="u" tp:type="Account_Flags" name="Flags"/> - </tp:mapping> - - <property name="Accounts" - tp:name-for-bindings="Accounts" - type="a{su}" tp:type="Account_Flags_Map" access="read"> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>The set of Accounts stored in this Connection Manager, and flags - indicating their status.</p> - - <p>Change notification for this property is provided by the standard - D-Bus <code>PropertiesChanged</code> signal.</p> - </tp:docstring> - </property> - - <method name="ForgetCredentials" tp:name-for-bindings="Forget_Credentials"> - <tp:docstring> - Clears any saved credentials associated with the specified Account_Id. - Any other saved data related to the account will be unaffected. - </tp:docstring> - - <arg direction="in" name="Account_Id" - type="s"> - <tp:docstring> - An account id as returned from - <tp:dbus-ref namespace="ofdT">Protocol.IdentifyAccount</tp:dbus-ref>. - </tp:docstring> - </arg> - - <tp:possible-errors> - <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> - <tp:docstring> - The account id is invalid. - </tp:docstring> - </tp:error> - </tp:possible-errors> - </method> - - <method name="RemoveAccount" tp:name-for-bindings="Remove_Account"> - <tp:docstring> - Completely removes all data associated with an account from the - connection manager's internal storage. - </tp:docstring> - - <arg direction="in" name="Account_Id" - type="s"> - <tp:docstring> - An account id as returned from - <tp:dbus-ref namespace="ofdT">Protocol.IdentifyAccount</tp:dbus-ref>. - </tp:docstring> - </arg> - - <tp:possible-errors> - <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> - <tp:docstring> - The account id is invalid. - </tp:docstring> - </tp:error> - </tp:possible-errors> - </method> - - </interface> -</node> diff --git a/xml/Makefile.am b/xml/Makefile.am deleted file mode 100644 index 50ef1d51..00000000 --- a/xml/Makefile.am +++ /dev/null @@ -1,33 +0,0 @@ -tools_dir = $(top_srcdir)/tools - -XSLTFLAGS = --nonet --novalid -DROP_NAMESPACE = sed -e 's@xmlns:tp="http://telepathy\.freedesktop\.org/wiki/DbusSpec.extensions-v0"@@g' -DROP_TPTYPE = sed -e 's@tp:type="[^"]*"@@g' - -SPECS = \ - Account_Manager_Interface_Hidden.xml \ - Account_Interface_Conditions.xml \ - Account_Interface_External_Password_Storage.xml \ - Account_Interface_Hidden.xml \ - Connection_Manager_Interface_Account_Storage.xml \ - Channel_Dispatcher_Interface_Messages_DRAFT.xml - - -SPECS_GEN = ${SPECS:%.xml=_gen/introspect-%.xml} - -all-local: $(SPECS_GEN) - -_gen/introspect-%.xml: %.xml $(tools_dir)/spec-to-introspect.xsl - $(AM_V_at)$(mkdir_p) _gen - $(AM_V_GEN)$(XSLTPROC) $(XSLTFLAGS) $(tools_dir)/spec-to-introspect.xsl $< \ - | $(DROP_NAMESPACE) > $@ - -clean-local: - rm -f $(SPECS_GEN) - -EXTRA_DIST = \ - all.xml \ - nmc5.xml \ - generic-types.xml \ - telepathy-types.xml \ - $(SPECS) diff --git a/xml/all.xml b/xml/all.xml deleted file mode 100644 index cb7661f9..00000000 --- a/xml/all.xml +++ /dev/null @@ -1,35 +0,0 @@ -<tp:spec - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - xmlns:xi="http://www.w3.org/2001/XInclude"> - -<tp:copyright>Copyright (C) 2008 Nokia Corporation</tp:copyright> - -<xi:include href="nmc5.xml"/> - -<tp:generic-types> - <tp:external-type name="Connection_Status" type="u" - from="Telepathy specification (Connection)"/> - <tp:external-type name="Connection_Status_Reason" type="u" - from="Telepathy specification (Connection)"/> - <tp:external-type name="Connection_Presence_Type" type="u" - from="Telepathy specification (Connection)"/> - <tp:external-type name="Connection_Manager_Name" type="s" - from="Telepathy specification (ConnectionManager)"/> - <tp:external-type name="Contact_Handle" type="u" - from="Telepathy specification (Connection)"/> - <tp:external-type name="Protocol" type="s" - from="Telepathy specification (ConnectionManager)"/> - <tp:external-type name="Simple_Presence" type="(uss)" - from="Telepathy specification (SimplePresence)"/> - <tp:external-type name="Conn_Mgr_Param_Flags" type="u" - from="Telepathy specification (ConnectionManager)"/> - <tp:external-type name="Requestable_Channel_Class" type="(a{sv}as)" - from="Telepathy specification (Requests)"/> - <tp:external-type name="Handler_Capability_Token" type="s" - from="Telepathy specification (Handler)"/> -</tp:generic-types> - -<xi:include href="generic-types.xml"/> -<xi:include href="telepathy-types.xml"/> - -</tp:spec> diff --git a/xml/generic-types.xml b/xml/generic-types.xml deleted file mode 100644 index 5b0d6ab9..00000000 --- a/xml/generic-types.xml +++ /dev/null @@ -1,214 +0,0 @@ -<tp:generic-types - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - - <tp:simple-type name="Unix_Timestamp" type="u"> - <tp:docstring>An unsigned 32-bit integer representing time as the number - of seconds elapsed since the Unix epoch - (1970-01-01T00:00:00Z)</tp:docstring> - </tp:simple-type> - - <tp:simple-type name="Unix_Timestamp64" type="x"> - <tp:docstring>An signed 64-bit integer representing time as the number - of seconds elapsed since the Unix epoch - (1970-01-01T00:00:00Z); negative for times before the epoch</tp:docstring> - - <tp:rationale>The Text interface is the only user of Unix_Timestamp so - far, and we'd like to be Y2038 compatible in future - interfaces.</tp:rationale> - </tp:simple-type> - - <tp:simple-type name="DBus_Bus_Name" type="s" - array-name="DBus_Bus_Name_List"> - <tp:docstring>A string representing a D-Bus bus name - either a well-known - name like "org.freedesktop.Telepathy.MissionControl" or a unique name - like ":1.123"</tp:docstring> - </tp:simple-type> - - <tp:simple-type name="DBus_Well_Known_Name" type="s" - array-name="DBus_Well_Known_Name_List"> - <tp:docstring>A string representing a D-Bus well-known - name like "org.freedesktop.Telepathy.MissionControl".</tp:docstring> - </tp:simple-type> - - <tp:simple-type name="DBus_Unique_Name" type="s" - array-name="DBus_Unique_Name_List"> - <tp:docstring>A string representing a D-Bus unique name, such as - ":1.123"</tp:docstring> - </tp:simple-type> - - <tp:simple-type name="DBus_Interface" type="s" - array-name="DBus_Interface_List"> - <tp:docstring>An ASCII string representing a D-Bus interface - two or more - elements separated by dots, where each element is a non-empty - string of ASCII letters, digits and underscores, not starting with - a digit. The maximum total length is 255 characters. For example, - "org.freedesktop.DBus.Peer".</tp:docstring> - </tp:simple-type> - - <tp:simple-type name="DBus_Error_Name" type="s"> - <tp:docstring>An ASCII string representing a D-Bus error. This is - syntactically the same as a <tp:type>DBus_Interface</tp:type>, but the - meaning is different.</tp:docstring> - </tp:simple-type> - - <tp:simple-type name="DBus_Signature" type="s"> - <tp:docstring>A string representing a D-Bus signature - (the 'g' type isn't used because of poor interoperability, particularly - with dbus-glib)</tp:docstring> - </tp:simple-type> - - <tp:simple-type name="DBus_Member" type="s"> - <tp:docstring>An ASCII string representing a D-Bus method, signal - or property name - a non-empty string of ASCII letters, digits and - underscores, not starting with a digit, with a maximum length of 255 - characters. For example, "Ping".</tp:docstring> - </tp:simple-type> - - <tp:simple-type name="DBus_Qualified_Member" type="s" - array-name="DBus_Qualified_Member_List"> - <tp:docstring>A string representing the full name of a D-Bus method, - signal or property, consisting of a DBus_Interface, followed by - a dot, followed by a DBus_Member. For example, - "org.freedesktop.DBus.Peer.Ping".</tp:docstring> - </tp:simple-type> - - <tp:mapping name="Qualified_Property_Value_Map" - array-name="Qualified_Property_Value_Map_List"> - <tp:docstring>A mapping from strings representing D-Bus - properties (by their namespaced names) to their values.</tp:docstring> - <tp:member type="s" name="Key" tp:type="DBus_Qualified_Member"> - <tp:docstring> - A D-Bus interface name, followed by a dot and a D-Bus property name. - </tp:docstring> - </tp:member> - <tp:member type="v" name="Value"> - <tp:docstring> - The value of the property. - </tp:docstring> - </tp:member> - </tp:mapping> - - <tp:mapping name="String_Variant_Map" array-name="String_Variant_Map_List"> - <tp:docstring>A mapping from strings to variants representing extra - key-value pairs.</tp:docstring> - <tp:member type="s" name="Key"/> - <tp:member type="v" name="Value"/> - </tp:mapping> - - <tp:mapping name="String_String_Map" array-name="String_String_Map_List"> - <tp:docstring>A mapping from strings to strings representing extra - key-value pairs.</tp:docstring> - <tp:member type="s" name="Key"/> - <tp:member type="s" name="Value"/> - </tp:mapping> - - <tp:struct name="Socket_Address_IP" array-name="Socket_Address_IP_List"> - <tp:docstring>An IP address and port.</tp:docstring> - <tp:member type="s" name="Address"> - <tp:docstring>Either a dotted-quad IPv4 address literal as for - <tp:type>Socket_Address_IPv4</tp:type>, or an RFC2373 IPv6 address - as for <tp:type>Socket_Address_IPv6</tp:type>. - </tp:docstring> - </tp:member> - <tp:member type="q" name="Port"> - <tp:docstring>The TCP or UDP port number.</tp:docstring> - </tp:member> - </tp:struct> - - <tp:struct name="Socket_Address_IPv4"> - <tp:docstring>An IPv4 address and port.</tp:docstring> - <tp:member type="s" name="Address"> - <tp:docstring>A dotted-quad IPv4 address literal: four ASCII decimal - numbers, each between 0 and 255 inclusive, e.g. - "192.168.0.1".</tp:docstring> - </tp:member> - <tp:member type="q" name="Port"> - <tp:docstring>The TCP or UDP port number.</tp:docstring> - </tp:member> - </tp:struct> - - <tp:struct name="Socket_Address_IPv6"> - <tp:docstring>An IPv6 address and port.</tp:docstring> - <tp:member type="s" name="Address"> - <tp:docstring>An IPv6 address literal as specified by RFC2373 - section 2.2, e.g. "2001:DB8::8:800:200C:4171".</tp:docstring> - </tp:member> - <tp:member type="q" name="Port"> - <tp:docstring>The TCP or UDP port number.</tp:docstring> - </tp:member> - </tp:struct> - - <tp:struct name="Socket_Netmask_IPv4"> - <tp:docstring>An IPv4 network or subnet.</tp:docstring> - <tp:member type="s" name="Address"> - <tp:docstring>A dotted-quad IPv4 address literal: four ASCII decimal - numbers, each between 0 and 255 inclusive, e.g. - "192.168.0.1".</tp:docstring> - </tp:member> - <tp:member type="y" name="Prefix_Length"> - <tp:docstring>The number of leading bits of the address that must - match, for this netmask to be considered to match an - address.</tp:docstring> - </tp:member> - </tp:struct> - - <tp:struct name="Socket_Netmask_IPv6"> - <tp:docstring>An IPv6 network or subnet.</tp:docstring> - <tp:member type="s" name="Address"> - <tp:docstring>An IPv6 address literal as specified by RFC2373 - section 2.2, e.g. "2001:DB8::8:800:200C:4171".</tp:docstring> - </tp:member> - <tp:member type="y" name="Prefix_Length"> - <tp:docstring>The number of leading bits of the address that must - match, for this netmask to be considered to match an - address.</tp:docstring> - </tp:member> - </tp:struct> - - <tp:simple-type name="User_Action_Timestamp" type="x"> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>The time at which an user action occurred. This type has the 2 - following special values:</p> - - <p>0: the action doesn't involve any user action. Clients - SHOULD avoid stealing focus when presenting the channel.</p> - - <p>MAX_INT64: clients SHOULD behave as though the user action happened - at the current time, e.g. a client MAY request that its window gains - focus. - </p> - - <tp:rationale> - <p>This can be used by clients that can't know the X server time like - command line applications for example.</p> - </tp:rationale> - - <p>For all the other values it corresponds to the time of the user - action. Clients SHOULD use this for focus-stealing prevention, - if applicable. - Note that the time is dependant on the local - environment and so is not necessarily a wall-clock time. - For example in an X environment it's expected to be the X timestamp - of events. - This corresponds to the _NET_WM_USER_TIME property in - <a href="http://standards.freedesktop.org/wm-spec/wm-spec-latest.html">EWMH</a>.</p> - </tp:docstring> - </tp:simple-type> - - <tp:mapping name="Object_Immutable_Properties_Map" - array-name="Object_Immutable_Properties_Map_List"> - <tp:docstring>A mapping from object path to the immutable properties of - the object.</tp:docstring> - <tp:member type="o" name="Path"> - <tp:docstring> - The object path of an object - </tp:docstring> - </tp:member> - <tp:member type="a{sv}" name="Immutable_Properties" tp:type="Qualified_Property_Value_Map"> - <tp:docstring> - The immutable properties of the object - </tp:docstring> - </tp:member> - </tp:mapping> - -</tp:generic-types> diff --git a/xml/nmc5.xml b/xml/nmc5.xml deleted file mode 100644 index 130fd3f3..00000000 --- a/xml/nmc5.xml +++ /dev/null @@ -1,14 +0,0 @@ -<tp:spec - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" - xmlns:xi="http://www.w3.org/2001/XInclude"> - -<tp:copyright>Copyright (C) 2008 Nokia Corporation</tp:copyright> - -<xi:include href="Account_Interface_Conditions.xml"/> -<xi:include href="Account_Interface_External_Password_Storage.xml"/> -<xi:include href="Account_Interface_Hidden.xml"/> -<xi:include href="Account_Manager_Interface_Hidden.xml"/> - -<xi:include href="Connection_Manager_Interface_Account_Storage.xml"/> - -</tp:spec> diff --git a/xml/telepathy-types.xml b/xml/telepathy-types.xml deleted file mode 100644 index 8225aa03..00000000 --- a/xml/telepathy-types.xml +++ /dev/null @@ -1,96 +0,0 @@ -<tp:telepathy-types - xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> - - <tp:mapping name="Channel_Class" array-name="Channel_Class_List"> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>Mapping representing a class of channels that can be requested - from a connection manager, can be handled by a user interface, - are supported by a contact, etc.</p> - - <p>Classes of channel are identified by the fixed values of - a subset of their properties.</p> - - <p>Channel classes SHOULD always include the keys - <tp:dbus-ref>org.freedesktop.Telepathy.Channel.ChannelType</tp:dbus-ref> - and - <tp:dbus-ref>org.freedesktop.Telepathy.Channel.TargetHandleType</tp:dbus-ref>. - </p> - </tp:docstring> - - <tp:member type="s" name="Key" tp:type="DBus_Qualified_Member"> - <tp:docstring> - A D-Bus interface name, followed by a dot and a D-Bus property name. - </tp:docstring> - </tp:member> - - <tp:member type="v" name="Value"> - <tp:docstring> - The value of the property. - </tp:docstring> - </tp:member> - </tp:mapping> - - <tp:struct name="Channel_Details" array-name="Channel_Details_List"> - <tp:added version="0.17.11">(as stable API)</tp:added> - - <tp:docstring> - Enough details of a channel that clients can work out how to dispatch - or handle it. - </tp:docstring> - - <tp:member name="Channel" type="o"> - <tp:docstring> - The object path of the channel. - </tp:docstring> - </tp:member> - - <tp:member name="Properties" type="a{sv}" - tp:type="Qualified_Property_Value_Map"> - <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> - <p>Properties of the channel.</p> - - <p>Connection managers MUST NOT include properties in this mapping - if their values can change. Clients MUST ignore properties - that appear in this mapping if their values can change.</p> - - <tp:rationale> - <p>If properties that could change were included, the following - race condition would be likely to exist in some cases:</p> - - <ul> - <li>NewChannels or Get("Channels") includes a property P with - value V1</li> - <li>Client creates a proxy object for the channel</li> - <li>The value of P changes to V2</li> - <li>Client connects to PChanged signal</li> - <li>Client should call Get("P") or GetAll here, to avoid the - race, but client's author has forgotten to do so</li> - <li>Proxy object thinks P == V1, but actually P == V2</li> - </ul> - - <p>We've taken the opportunity to make the API encourage the - client author to get it right. Where possible, we intend that - properties whose value will be used in channel dispatching - or other "early" processing will be defined so that they are - immutable (can never change).</p> - </tp:rationale> - - <p>Each dictionary MUST contain the keys - <tp:dbus-ref>org.freedesktop.Telepathy.Channel.ChannelType</tp:dbus-ref>, - <tp:dbus-ref>org.freedesktop.Telepathy.Channel.TargetHandleType</tp:dbus-ref>, - <tp:dbus-ref>org.freedesktop.Telepathy.Channel.TargetHandle</tp:dbus-ref> - and - <tp:dbus-ref>org.freedesktop.Telepathy.Channel.TargetID</tp:dbus-ref>. - </p> - <!-- FIXME: maybe also Requested, InitiatorHandle, - InitiatorID once they leave the FUTURE pseudo-interface --> - - <tp:rationale> - <p>We expect these to be crucial to the channel-dispatching - process.</p> - </tp:rationale> - </tp:docstring> - </tp:member> - </tp:struct> - -</tp:telepathy-types> |