diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | NEWS | 1329 | ||||
-rwxr-xr-x | autogen.sh | 22 | ||||
-rw-r--r-- | configure.ac | 35 | ||||
-rw-r--r-- | docs/reference/telepathy-glib-sections.txt | 58 | ||||
-rw-r--r-- | examples/extensions/Makefile.am | 26 | ||||
-rw-r--r-- | m4/Makefile.am | 3 | ||||
-rw-r--r-- | m4/shave.m4 | 77 | ||||
-rw-r--r-- | shave-libtool.in | 69 | ||||
-rw-r--r-- | shave.in | 79 | ||||
-rw-r--r-- | telepathy-glib/Makefile.am | 36 | ||||
-rw-r--r-- | telepathy-glib/account-internal.h | 39 | ||||
-rw-r--r-- | telepathy-glib/account-manager-internal.h | 40 | ||||
-rw-r--r-- | telepathy-glib/account-manager.c | 1385 | ||||
-rw-r--r-- | telepathy-glib/account-manager.h | 38 | ||||
-rw-r--r-- | telepathy-glib/account.c | 2626 | ||||
-rw-r--r-- | telepathy-glib/account.h | 118 | ||||
-rw-r--r-- | telepathy-glib/signals-marshal.list | 4 | ||||
-rw-r--r-- | telepathy-glib/telepathy-glib-uninstalled.pc.in | 2 | ||||
-rw-r--r-- | telepathy-glib/telepathy-glib.pc.in | 2 | ||||
-rw-r--r-- | telepathy-glib/versions/0.9.0.abi | 56 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/dbus/Makefile.am | 9 | ||||
-rw-r--r-- | tests/dbus/account-manager.c | 23 | ||||
-rw-r--r-- | tests/dbus/account.c | 101 | ||||
-rw-r--r-- | tools/Makefile.am | 20 |
26 files changed, 4538 insertions, 1664 deletions
diff --git a/.gitignore b/.gitignore index 6675ac5e6..f5e676da5 100644 --- a/.gitignore +++ b/.gitignore @@ -70,5 +70,3 @@ tests/test-* tests/tools/actual.h tests/tools/actual-body.h tools/telepathy-glib-env -shave -shave-libtool @@ -1,10 +1,16 @@ -telepathy-glib 0.8.1 (UNRELEASED) +telepathy-glib 0.9.1 (UNRELEASED) ================================= -The "TpConnectionManager was not my finest hour" release. - Fixes: +* Corrected the GLib dependency to 2.20 (this was also needed for 0.9.0, + but that fact was undocumented) (smcv) + +* Corrected the error message given when a write-only D-Bus property + is read (Pekka Pessi) + +* Work around GLib 2.20 being less const-correct than 2.22 (jonny) + * fd.o #23853: if a connection manager is discovered not to be running while TpConnectionManager has a ListProtocols call in-flight, then a new instance of the CM starts up and replies to that call, don't crash with an assertion @@ -22,8 +28,28 @@ Fixes: if they lack the SECRET flag, as was already done when reading .manager files (smcv) -* Corrected the error message given when a write-only D-Bus property - is read (Pekka Pessi) +telepathy-glib 0.9.0 (2009-09-28) +================================= + +The "purging all the lies" release. + +Dependencies: + +* GLib 2.20 is now required. +* telepathy-glib now links to GIO as well as GLib and GObject (in practice + they're packaged together, and we already depended on a new enough GLib + version that it would come with GIO). + +Enhancements: + +* TpAccountManager, TpAccount: add convenience API similar to libempathy's + (jonny, with contributions from wjt/danni/sjoerd/smcv) + +* telepathy-glib now uses Automake 1.11's "silent rules" feature for + kernel-style output; as a result, we no longer use shave. If you were + previously using --enable-shave to get prettier output, use + --enable-silent-rules instead, and upgrade to Automake >= 1.11 if you will + be altering the build system. (jonny) telepathy-glib 0.8.0 (2009-09-24) ================================= @@ -61,1296 +87,3 @@ Changes since 0.7.37: * spec: update from 0.17.28 to 0.18.0 (no real changes) (smcv) * ContactList example CM: fix a crash during shutdown (andrunko) * StreamedMedia example CM: check for direction changes correctly (andrunko) - -telepathy-glib 0.7.37 (2009-09-14) -================================== - -The "Lego Batman" release. - -Enhancements: - -* Update to telepathy-spec 0.17.28 - * add ContactCapabilities interface - * add InitialVideo, InitialAudio, ImmutableStreams properties to - StreamedMedia - * add TP_CHANNEL_CALL_STATE_FLAG_IN_PROGRESS - -* Set the debug domain for telepathy-glib to tp-glib/$category, e.g. - tp-glib/connection for TpConnection and TpBaseConnection (jonny) - -Fixes: - -* fd.o #23843: make tp_debug_sender_log_handler with exclude=NULL include - everything, rather than nothing (smcv) - -telepathy-glib 0.7.36 (2009-09-03) -================================== - -The "yes, it's a phone" release. - -Enhancements: - -* Add TpDebugSender, along with the lower-level TpSvcDebug interface that it - uses (jonny) - -* Clarify when tp_proxy_signal_connection_disconnect is safe to call (smcv) - -* Add support for simulating DTMF and Hold support in the 'callable' example - connection manager (andrunko) - -* fd.o #21327: truncate the ChangeLog at version 0.6 to stop it being so huge, - and force ISO date format (wjt) - -Fixes: - -* If the bus name for a Connection would be too long, hash the unique name - provided by the CM, instead of crashing (daf) - -* fd.o #23524: don't introspect a TpConnectionManager twice in parallel, - avoiding an assertion failure (wjt) - -* TpChannel: don't disconnect from a TpProxySignalConnection that's already - gone, fixing a crash (smcv) - -* tp_connection_manager_idle_read_manager_file: avoid using the - TpConnectionManager after it may have been disposed (sjoerd) - -* TpBaseConnection: Allow GetInterfaces() to succeed before CONNECTED (smcv) - -telepathy-glib 0.7.35 (2009-08-18) -================================== - -The "dance to the sound of sirens" release. - -API changes: - -* fd.o #17751: TpBaseConnection no longer guarantees that the self-handle is - set to 0 when the state changes to DISCONNECTED; instead, the self-handle - remains valid until the connection is disposed, so that connection managers - can use it during teardown. (cassidy) - - This change will cause assertion failures on disconnection in - telepathy-sofiasip older than 0.5.17, and in telepathy-gabble older than - 0.7.9. - -Enhancements: - -* Add tp_dbus_daemon_list_names() and tp_dbus_daemon_list_activatable_names(), - which are more efficient (and more const-correct) versions of the - corresponding autogenerated tp_cli_dbus_daemon methods (smcv) - -* Support the Hold interface in the "callable" example CM (andrunko) - -Fixes: - -* Finally make tp_dbus_daemon_watch_name_owner bind to NameOwnerChanged - selectively, for reduced wakeups. To take advantage of this, you should - avoid using all functions that start with tp_cli_dbus_daemon_; file bugs - to request more replacement functions in the tp_dbus_daemon namespace. (smcv) - -* Fix a memory leak in tp_dbus_daemon_watch_name_owner (smcv) - -* fd.o #9812: TpDynamicHandleRepo: use tp_dbus_daemon_watch_name_owner for - reduced wakeups (smcv) - -* In TpDynamicHandleRepo, if a client holds handles then immediately crashes, - release those handles as soon as we find out that it's gone (smcv) - -* fd.o #22957: fix compilation in some non-gcc compilers - (based on a patch from Elaine Xiong) - -* As per telepathy-spec 0.17.27, represent connection status reason NAME_IN_USE - as TP_ERROR_CONNECTION_REPLACED or TP_ERROR_ALREADY_CONNECTED (as - appropriate), not as TP_ERROR_NOT_YOURS, so that NotYours can safely be given - special handling (smcv) - -telepathy-glib 0.7.34 (2009-08-16) -================================== - -The "secret lack of example detected" release. - -Enhancements: - -* Updated to telepathy-spec 0.17.27 (smcv) - - generate code for Connection.Interface.Location - - omit Debug interface from code-generation for now: it will be enabled in a - future release when we have reference client- and service-side - implementations (possibly called TpDebugReceiver and TpDebugSender) - - add more errors - - generate code for Handler.Capabilities property - -* Add macros for static assertions (tp_verify, tp_verify_true, - tp_verify_statement) to util.h (smcv, adapted from gnulib) - -* Make the allocation model for tp_dbus_properties_mixin_make_properties_hash - part of our ABI guarantee (smcv) - -* Add a GType macro TP_ARRAY_TYPE_OBJECT_PATH_LIST (wjt) - -Fixes: - -* fd.o #23164: don't unref a NULL DBusMessage (wjt) - -* Get tp_intset_iter_init and tp_intset_iter_reset into the documentation (smcv) - -* Make omitted CM parameters that have a default indistinguishable from - explicitly-passed parameters with that value, removing the need to specify - the default in the Connection as well (sjoerd) - -* fd.o #22889: reduce introspection round-trips (wjt) - -* Fix header guard in debug.h (dilinger) - -* fd.o #18091: for interfaces with D-Bus methods, document how to write a - complete interface init function (smcv) - -* Fix a rare crash when a TpProxyPendingCall is cancelled before the - DBusGProxyCall is provided (smcv) - -telepathy-glib 0.7.33 (2009-06-26) -================================== - -The "please be careful when handling the sun" release. - -Fixes: - -* Fixed a test failure in test-params-cm on certain architectures, including - powerpc and sparc (smcv) - -* Fixed an assertion failure in TpConnection if StatusChanged to CONNECTED is - followed immdiately by GetStatus returning CONNECTED (smcv) - -telepathy-glib 0.7.32 (2009-06-12) -================================== - -The "cake wars" release. - -Enhancements: - -* fd.o #22208: Updated to telepathy-spec 0.17.26 (smcv) - -* Added generated service-side bindings for AccountManager, Account, - ChannelDispatcher, ChannelDispatchOperation, ChannelRequest and Client (smcv) - -* Added minimal versions of TpAccountManager, TpAccount, - TpChannelDispatcher, TpChannelDispatchOperation, TpChannelRequest and - TpClient (smcv) - -* fd.o #22205: Added support for deprecating or removing _run_ methods in code - generation (smcv) - -* Added flymake support (jonnylamb) - -* fd.o #22230: Added support for byte ('y') parameters to CMs (smcv) - -Fixes: - -* Made sure _gen/error-str.h and .c end with a newline, fixing compilation with - certain compilers (smcv) - -* fd.o #19741: Documented client-side handle reference counting (wjt) - -* fd.o #21977: Removed duplicate mention of media-interfaces.h in Makefile.am - (Patryk Zawadzki) - -* fd.o #22121: made tp_channel_dispose properly idempotent (wjt) - -* Removed dead code for introspecting alias flags (smcv) - -* fd.o #22182: Fixed a potential use-after-free in the callable example CM - (smcv) - -Release notes for projects using code generation: - -* After updating to this version of glib-client-gen.py, _run_ methods will no - longer be generated by default. To get the behaviour of older versions, run - it with the new --generate-reentrant option; to get the methods present - but deprecated, use an option like - --deprecate-reentrant=FOO_DISABLE_DEPRECATED (which means the _run_ methods - are marked with G_GNUC_DEPRECATED, and will not be available at all if - FOO_DISABLE_DEPRECATED is defined). - -telepathy-glib 0.7.31 (2009-05-27) -================================== - -The "this beer tastes of Stilton!" release. - -Enhancements: - -* Added TP_ERROR_STR_* constants corresponding to the members of the - TpError enum, and tp_error_get_dbus_name() mapping the latter to the - former (cassidy/wjt) - -* Updated to telepathy-spec 0.17.25, adding ConnectionRefused, ConnectionFailed - and ConnectionLost errors, and the new-style StreamTube and DBusTube channel - types (cassidy) - -* Made tp_g_key_file_get_int64 and _uint64 into public API for Mission - Control's benefit (smcv) - -Fixes: - -* Changed the dbus_daemon member of TpProxy to be removed in dispose, rather - than just after the invalidated signal (smcv) - -* Fixed a null pointer dereference if a TpConnectionManager is resurrected - during dispose (smcv) - -* Fixed a use-after-free when the user-supplied object in - tp_connection_manager_call_when_ready is destroyed after the CM becomes - ready (smcv) - -* Fixed tp_dbus_daemon_request_name, tp_dbus_daemon_release_name and - _tp_dbus_daemon_get_name_owner to fail gracefully if the TpDBusDaemon has - been invalidated (smcv) - -telepathy-glib 0.7.30 (2009-05-18) -================================== - -The "slowing down but with a sense of speeding up" release. - -API changes: - -* tp_proxy_add_interface_by_id() may not be called on an invalidated proxy. (It - never made sense to do this, but now telepathy-glib will complain if you do.) - -Enhancements: - -* Added tp_dbus_daemon_request_name() and tp_dbus_daemon_release_name(). (Fixes - fd.o#21771.) - -* Update to telepathy-spec 0.17.23, adding the Terminated error. - -* Added optional support for Shave, to give kernel-style pretty make output. - (This is disabled by default; to enable it, pass --enable-shave to configure.) - (Jonny Lamb) - -* Channel introspection will now fail more quickly when the channel is - invalidated. - -Fixes: - -* Generated GInterfaces now install type information sooner, fixing a bug where - object construction could make dbus-glib assert. (Ross Burton) - -* fd.o#21792: tp_presence_mixin_simple_presence_fill_contact_attributes asserts - if get_contact_statuses returns NULL. - -telepathy-glib 0.7.29 (2009-04-03) -================================== - -The "sense of adventure working perfectly and sensing no adventures" release. - -Enhancements: - -* TpBaseConnectionManager, tp_cm_param_setter_offset: support parameters of - various extra types - object path, double, 64-bit signed and unsigned - integer, array of strings, array of bytes - and add regression tests - -* TpContact: enhance documentation to spell out that change notification is - via GObject::notify::alias etc. - -* tp_asv_new(), a shorthand for creating hash tables from strings to - slice-allocated GValues, along with tp_asv_set_* and tp_asv_take_* helpers to - add entries to such hash tables. (Fixes fd.o#20942) - -telepathy-glib 0.7.28 (2009-03-24) -================================== - -The "a surprisingly meaty courgette" release. - -API changes: - -* Use the prefix for D-Bus errors specified by telepathy-spec, not something - telepathy-glib made up (changing .Errors to the correct .Error). This was - a regression in telepathy-glib 0.7.1. - - Expected impact of this change: - * Connection managers will emit the correct errors as soon as the - telepathy-glib shared library is upgraded - * Clients that use TpConnection, TpChannel etc. will expect the correct - errors as soon as the shared library is upgraded - * Clients that match errors by string value using TP_ERROR_PREFIX will have - to be recompiled against the new telepathy-glib, but should have sane - fallback behaviour to treat the correct error strings as an unknown error - -Enhancements: - -* Update to telepathy-spec 0.17.22, adding - TP_ARRAY_TYPE_SOCKET_ADDRESS_IP_LIST, TP_ARRAY_TYPE_STRING_STRING_MAP_LIST, - TP_STRUCT_TYPE_SOCKET_ADDRESS_IP - -* Extend the 'callable' example connection manager to support simulated - incoming calls, support simulated contacts who never answer, are busy or - terminate the call, and comply with telepathy-spec 0.17.22 - -Fixes: - -* fd.o #20729: csh (chatroom) example connection manager: comply with - telepathy-spec 0.17.21 by always allowing the user to leave the Group - -* Cancel GetParameters call if the CM crashes, fixing an assertion failure - in tp_connection_manager_continue_introspection - -telepathy-glib 0.7.27 (2009-03-17) -================================== - -The "sliced woot" release. - -Enhancements: - -* fd.o #18220: make it much less verbose to construct string/variant maps - (a{sv}) by adding functions like tp_g_value_slice_new_uint(), which are a - shortcut for a sequence like tp_g_value_slice_new (G_TYPE_UINT) followed by - g_value_set_uint - -* Make example connection managers installable (they might be useful for - testing UIs - use ./configure --enable-installed-examples if you want them) - -* Add an example connection manager that has dummy StreamedMedia channels - -* Add an example connection manager that has dummy ContactList channels - vaguely resembling the XMPP roster - -* Update to telepathy-spec 0.17.21 (adding - TP_CHANNEL_GROUP_FLAG_MESSAGE_DEPART and - TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_ICE_UDP) - -Fixes: - -* fd.o #20646: in TpChannel, when adding contacts to one of the Group sets, - remove them from both of the others - -* Use AM_PATH_PYTHON to detect the Python version - -* In TpGroupMixin, let implementations allow the self-handle to be removed - regardless of whether the CAN_REMOVE flag is set. This is meant to be how - you remove yourself gracefully from a chatroom or streamed media call. - -* fd.o #20165: fix a use-after-free in tp_base_connection_register() that - causes connection managers to assert if they have not already called - tp_dbus_daemon_dup() or tp_run_connection_manager() (regression in 0.7.26 - which broke the telepathy-qt4 regression tests) - -* Fix some memory leaks when making TpContact objects, when holding handles in - TpContactsMixin, when filling contact attributes in TpPresenceMixin, - when setting simple presence, and in the regression tests - -* Update the valgrind suppressions file to cope with new telepathy-glib code, - GTest, and glibc 2.9 - -* Fix compilation with gtkdoc 1.11, which is stricter about the contents of - Since: annotations, and parses more comments - -telepathy-glib 0.7.26 (2009-02-16) -================================== - -The "Leffe Radieuse" release. - -Enhancements: - -* Add tp_connection_manager_call_when_ready() - -* When constructing TpConnectionManager, if the .manager file is missing - or can't be read, always try to activate the connection manager (in - practice, this is what's wanted) - -* fd.o #18291: when listing connection managers, wait for them to all be ready - -* fd.o #18056: add method-based accessors for TpConnectionManager members - -* fd.o #17519: track the Connection's SelfHandle property in TpConnection - -* Add tp_dbus_daemon_dup(), convenient API to share a starter-bus connection - -* Generate GTypes for arrays of mappings, and improve use of the - type-generation functions - -* fd.o #19907: Add tp_intset_new_containing(), a convenient factory for sets - with exactly one member - -Deprecations: - -* Deprecate tp_get_bus_proxy() - -Fixes: - -* Reduce our reliance on making a DBusGProxy for the dbus-daemon (working - towards avoiding overly-broad NameOwnerChanged matches) - -* fd.o #18832 (partially addressed): document that tp_get_bus() can call - exit() and why, and discourage it in processes that aren't totally reliant - on D-Bus - -* fd.o #18207: reliably emit TpConnectionManager::got-info signal (although - tp_connection_manager_call_when_ready() should be used instead) - -* Fix some assertion failures in TpConnectionManager when .manager files - contain surprising types - -* fd.o #19054: Parse all currently supported types in .manager files - -* fd.o #20096: don't consider InvalidHandle to be fatal for - tp_connection_get_contacts_by_id (spec 0.17.18 compliance) - -Miscellaneous: - -* Add regression test coverage for TpConnectionManager - -telepathy-glib 0.7.25 (2009-01-30) -================================== - -The "Paradise Lost" release. - -Fixes: - -* fd.o #17588: don't break ABI if errors are re-ordered in a future spec - version - -* fd.o #19688: don't assert when getting contacts by ID if none of the - identifiers supplied are valid - -* TpPresenceMixin: don't allow setting statuses that are flagged as not - settable on the user themselves - -* TpPresenceMixin: never allow statuses of type OFFLINE, UNKNOWN or ERROR to - be set on the user themselves, and warn if the connection manager got this - wrong - -* Fix a memory leak in tp_presence_mixin_get_statuses() (sorry Jonathon, - your patch got lost during the release process...) - -telepathy-glib 0.7.24 (2009-01-28) -================================== - -The "Divide By Cucumber Error" release. - -API changes: - -* When a TpConnection is invalidated due to disconnection, the error will now - be chosen from the TP_ERRORS domain in most cases. Previously, the - TP_ERROR_DISCONNECTED domain was used. This change is necessary to support - the extensible error reporting introduced in spec 0.17.19. - -Enhancements: - -* Update to telepathy-spec 0.17.19 - - Many new errors in the TP_ERRORS domain - - Connection.ConnectionError signal for extensible error reporting - -* Add a client binding for the extensible error reporting provided - by the ConnectionError signal - -* Use a simple Python implementation of XInclude, rather than xsltproc, for - easier Windows porting - -Fixes: - -* Add File Transfer to the documentation - -telepathy-glib 0.7.23 (2009-01-20) -================================== - -The "new in version 0.7.1, and allegedly also in 0.7.21" release. - -Enhancements: - -* Updated to spec version 0.17.18 - - Added the File Transfer channel type - - Added TpRichPresenceAccessControlType and - TP_STRUCT_TYPE_RICH_PRESENCE_ACCESS_CONTROL - - RequestHandles raises NotImplemented for bad handle types or InvalidHandle - for bad identifiers (fd.o #19609) - - MediaStreamHandler has a CodecsUpdated method - - Methods automatically generated by telepathy-glib now have names - for all "out" arguments, which should make the documentation easier to use - -* Use tp:name-for-bindings to construct the C name for D-Bus methods etc., - which will result in better C function naming for future D-Bus interfaces - like DBusTube - -Fixes: - -* Remove symbols from 0.7.21.abi that were already in 0.7.1.abi (GNU ld - seems to respond by giving them the older version, so no harm was done - to the ABI, but it broke the Debian packages) - -* Make make-version-script.py fail if that ever happens again - -* Annotate things added in 0.7.21 with the correct "Since:" indicator - -* Correct the documentation for tp_list_connection_names() - -telepathy-glib 0.7.22 (2009-01-13) -================================== - -The "TP_STRUCT_TYPE_BROWN_PAPER_BAG" release. - -Fixes: - -* Accept message=NULL in tp_group_mixin_change_members, as documented and true - in the past, rather than crashing. - -telepathy-glib 0.7.21 (2009-01-12) -================================== - -The "TP_HASH_TYPE_PINT_HOBGOBLIN_MOUTH_MAP" release. - -Enhancements: - -* Updated to spec version 0.17.17 - - Added TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP and - TP_HASH_TYPE_MESSAGE_PART_CONTENT_MAP - -* (Finally) merged TpMessageMixin, which can be used in place of TpTextMixin to - implement the Messages interface on Text channels. - -* The examples have been made more exemplary, using TpChannelManager in place - of TpChannelFactoryIface, implementing Destroyable and respawning 1-1 text - channels which are closed with pending messages. - (fd.o #17632) - -* Added a TP_COMPILER_WARNINGS macro to simplify choosing compiler warnings in - configure.ac, and forked a version of AS_COMPILER_FLAG that supports C++ so - that TP_COMPILER_WARNINGS can be used for C++ projects (such as - telepathy-qt4). Other projects using telepathy-glib's warnings might want to - copy m4/tp-compiler-*.m4 and use TP_COMPILER_WARNINGS. - -* Added support to TpGroupMixin for emitting the MembersChangedDetailed signal - (fd.o #19050 and #19052), and to TpChannel for listening to it when possible - (fd.o #19051). - -* Added tp_channel_get_identifier - -* Added support for parsing the dbus-property CM parameter flag (introduced in - spec 0.17.16) from .manager files (fd.o #19053). - -Fixes: - -* Various Win32 portability fixes (from Sunil Mohan Adapa on fd.o #19461). - -* fd.o #19101: tp_connection_get_contacts_by_id() crashes - -telepathy-glib 0.7.20 (2008-12-14) -================================== - -The "xfs_freeze stole the night" release. - -Enhancements: - -* Updated to spec version 0.17.16 - - Generate code for the Messages interface, which is now undrafted. - -Fixes: - -* fd.o #18845: don't throw a critical error from TpConnection if the - corresponding CM falls off the bus. - -* fd.o #18926: avoid using a non-top-level GLib header - -telepathy-glib 0.7.19 (2008-12-01) -================================== - -The "fast path" release. - -Enhancements: - -* TpContact now has a fast path using the Contacts interface to reduce - D-Bus round-trips, if supported - -* tp_connection_get_contact_attributes integrates the Contacts interface - with TpConnection's handle reference tracking at a lower level - -* TpChannel now tracks its immutable properties (as provided by - Requests.NewChannels, Requests.CreateChannel and Requests.EnsureChannel), - can be constructed from a dictionary of immutable properties - (tp_channel_new_from_properties), and has a fast path using GetAll to reduce - round trips (if supported); when constructed from a dictionary of immutable - properties, non-Group channels should become 'ready' in a single round-trip - (fd.o #17427) - -* Only use the Properties mixin in there are properties defined - -Fixes: - -* fd.o #15092: mixins in a superclass should now work correctly in subclasses - -* fd.o #18151: tp_base_connection_dispose asserts if there are two connections to the same account - -telepathy-glib 0.7.18 (2008-11-03) -================================== - -The "320 GB" release. - -Enhancements: - -* Update to spec 0.17.14 - * test, and generate code for, the Destroyable interface - * add support for the SCROLLBACK and RESCUED message flags in Text - * add tp_text_mixin_set_rescued(), for CMs to call when respawning a channel, - and tp_text_mixin_receive_with_flags(), for CMs to call when receiving - scrollback messages - * update echo example CM's Text support to spec 0.17.14 - * CreateChannel etc. return before NewChannels, which is emitted before - NewChannel - -* Add TpContact, an object representing a contact, with inspection of various - attributes (e.g. alias, avatar token and SimplePresence) - -Fixes: - -* In TpContactsMixin, always return info from interface TP_IFACE_CONNECTION - even if the client didn't ask for it, since the spec says we should - -* Add a simple regression test for TpContactsMixin - -* Use unsigned int (rather than gboolean, which is signed!) for bitfields - -* Fix a memory leak when a TpChannel with the Group interface is freed - -* Fix a memory leak in tp_connection_unref_handles when no handles are released - -* Fix some memory leaks in the regression tests - -* Re-enable the coding style check and fix various things it complained about - -telepathy-glib 0.7.17 (2008-10-14) -================================== - -The "inexplicable bonus pizza" release. - -Enhancements: - -* Updated to spec 0.17.13, which adds Requested, InitiatorHandle and - InitiatorID properties to Channel - -* Updated two example CMs (echo and channelspecific) to spec 0.17.13 too - -* Added handle reference-counting helpers, which should be used instead - of using the HoldHandles, RequestHandles and ReleaseHandles D-Bus API - directly - -* Added C accessors for TpProxy's read-only properties, and for - TpConnection's connection-ready property - -* Added some infrastructure for test coverage analysis using lcov - -* Moved some of the release/checking machinery to tools/telepathy.am - for easy pasting into other Telepathy projects - -* The first time TP_ERRORS is used, the error domain is now automatically - registered with dbus-glib - -* Added sanity checks (g_return_if_fail/g_critical) to TpConnection, - TpProxy, TpDBusDaemon and tp_dbus_check_* public API - -Fixes: - -* tp_handle_is_valid and tp_handles_are_valid raise InvalidHandle on failure, - not InvalidArgument (numerous methods in telepathy-spec require - InvalidHandle to be raised, so this should make most CMs more - spec-compliant) - -* The test and example CMs raise NotAvailable on syntactically incorrect - strings, rather than InvalidArgument (which is not spec-compliant) - -* The example valgrind suppressions file now works if libdbus was not - installed in /usr, or if glibc is version 2.7 - -* fd.o #17502: fixed documentation of the #include for TpDBusPropertiesMixin - -* Fixed a memory leak in TpContactsMixin - -telepathy-glib 0.7.16 (2008-09-26) -================================== - -The "could you say that again? I was looking at that bee" release. - -Dependencies: - -* To use --enable-gtk-doc you must now have at least gtkdoc 1.10 - -Enhancements: - -* Updated to spec 0.17.12, mainly featuring EnsureChannel - -* We now support EnsureChannel on the Requests interface - to implement this, - put a suitable function pointer in TpChannelManagerIface::ensure_channel - -* Channel factories' RequestChannel implementations no longer need to - validate handles - TpBaseConnection now does this automatically - -* Added a function to compare presence types in order of "availability" - -Fixes: - -* The gtkdoc now documents GInterfaces' signals and properties (fd.o #16995, - fd.o #17308) - -* TpBaseConnection::self_handle is unreffed and cleared slightly later, - for the benefit of channel managers that want to use it in their - status-changed(Disconnected) callback - -* Fixed a compiler warning on platforms with daemon(3) in their libc - -* TpChannelManager can no longer be crashed by asking for unsupported - handle types - -telepathy-glib 0.7.15 (2008-09-18) -================================== - -The "plumbing" release. - -Dependencies: - -* GLib and GObject must be at least version 2.16 (this was accidentally the - case in 0.7.14 too, but the dependency is now official). (fd.o #17213) - -Deprecations: - -* TpBaseConnection implementations should use the new method - tp_base_connection_set_self_handle instead of setting the self_handle member - directly. To comply with spec 0.17.10, if the self-handle changes after the - status becomes CONNECTED, tp_base_connection_set_self_handle *must* be used. - -Enhancements: - -* Updated to specification 0.17.10, which includes the SelfHandleChanged signal - and SelfHandle property... - -* ... and then to specification 0.17.11, which includes the stable Requests - interface with the CreateChannel method (but not EnsureChannel, which is - planned for a future spec.). - -* Added support for the Requests interface to TpBaseConnection, using the new - interfaces TpChannelManager and TpExportableChannel (which are intended to - replace TpChannelFactoryIface and TpChannelIface). - -* Added some utility functions: tp_dbus_properties_mixin_make_properties_hash, - tp_strv_contains - -* with-session-bus.sh (used for the tests) optionally records dbus-monitor - output - -* telepathy-glib-uninstalled.pc is generated in source builds. It's now - easier to compile dependent projects against an uninstalled copy of - telepathy-glib >= 0.7.15, like this: - - PKG_CONFIG_PATH=$HOME/src/telepathy-glib/telepathy-glib ./autogen.sh - - (if $HOME/src/telepathy-glib is the directory containing this file) - -Fixes: - -* TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED no longer causes compiler warnings - when used from a C++ source file - -* glib-client-gen.py generates correct code for 64-bit unsigned integers - -Release notes for projects using code generation: - -* If you generate client-side code, update glib-client-gen.py to avoid wrong - code generation for unsigned 64-bit integer arguments. This will cause your - project to require telepathy-glib >= 0.7.3 - -* If you use a copy of with-session-bus.sh for regression tests, consider - updating it to add support for logging dbus-monitor - -telepathy-glib 0.7.14 (2008-08-19) -================================== - -The "M'era Luna" release. - -Enhancements: - -* Updated to specification 0.17.9 - - Connection.Interface.Contacts interface ("the inspectotron"), which allows - mass contact-handle holding and inspection in a single round-trip - - Channel.TargetID property, for further round-trip reduction - -* Added TpContactsMixin, a generic implementation of the Contacts interface - -* Moved source code control from darcs to git (see README for details) - -* Added tp_connection_get_status() convenience accessor for status and - status-reason properties - -* Altered tp_debug_divert_messages() to support a "+" prefix to filenames, - which changes the mode from truncate to append: you can now set something - like GABBLE_LOGFILE="+gabble.log" to append to an existing log - -* Updated AUTHORS - -telepathy-glib 0.7.13 (2008-07-29) -================================== - -The "presence made easy" release. - -Enhancements: - -* TpPresenceMixin implements the new SimplePresence interface - -* The spec text and doc-generator.xsl have been updated - -* The coding-style checks have been removed - -* A couple of supporting functions for Requests API development have - been added (tp_text_mixin_has_pending_messages and - tp_dbus_properties_mixin_get) - -Release notes for projects using code generation: - -* We now ship the more pedantic doc-generator.xsl from telepathy-spec 0.17.8: - - you'll probably need to clean up your spec markup! - - set the allow-undefined-interfaces XSLT parameter to a true value (e.g. - run xsltproc with --param allow-undefined-interfaces "true()") if you are - compiling documentation for interfaces that are not self-contained - (e.g. Telepathy extensions that reference the main Telepathy spec) - -* If you're trying to use Telepathy coding style, upgrading the coding-style - checks is recommended, but might require you to make code changes - -telepathy-glib 0.7.12 (2008-07-21) -================================== - -The "Channel.Interface.Useful" release. - -Enhancements: - -* TpChannel and TpConnection can be subclassed (fd.o #14828, #14829) - -* TpChannel has various convenience methods like tp_channel_get_channel_type(), - which can be used as an alternative to its GObject properties - -* Internally, TpChannel tries to use D-Bus core Properties (the GetAll - method) to reduce round-trips; if that fails, it will automatically fall - back to a series of normal method calls - -* TpChannel tracks the Group interface automatically (fd.o #14180) - -* tp_asv_size() has been added to the a{sv} convenience API, which now has - its own file in the documentation - -Fixes: - -* For the moment, TpDBusPropertiesMixin raises Telepathy errors rather than - D-Bus core errors on failure, due to fd.o #16776 in dbus-glib causing an - assertion when DBUS_GERROR errors are raised - -* TpConnectionManager no longer causes a crash if destroyed before it has - read the .manager file (fd.o #16774, thanks to Sunil Mohan Adapa for the - patch) - -Dependencies: - -* We no longer support automake 1.8.x, since even Maemo has had 1.9 for a - while. We don't yet use any automake 1.9 features, but we will no longer - test with 1.8 unless someone explains to me why a 4 year old version is - still relevant :-) - -telepathy-glib 0.7.11 (2008-07-02) -================================== - -The "design is hard" release. - -This is mainly a bugfix release. - -Fixes: - -* fd.o #16307: in TpConnection, don't assert when a connection goes CONNECTED - while a GetStatus call is pending - -* In TpDBusPropertiesMixin: return properly if Get, Set or GetAll are called - on nonexistent interfaces or properties; if the wrong type is passed to Set, - coerce it to the right type (if possible) and actually use the right type; - and if the coercion fails, don't leak memory - -* In libglibcodegen (code generation), use the right GType for arrays of - object-path - -Enhancements: - -* Use Python rather than XSLT for code generation, for improved maintainability - -Release notes for projects using code generation: - -* All the XSLT (except doc-generator.xsl and identity.xsl) has been rewritten - in Python. It should produce identical results, but be careful when updating! - -* You must update libglibcodegen to this version if there are arrays of - object-path in your API, otherwise it just won't work - -telepathy-glib 0.7.10 (2008-06-06) -================================== - -The "properties everywhere" release. - -Again, this version mainly contains infrastructure to support future APIs, -like the planned Requests API. - -Enhancements: - -* Update to telepathy-spec 0.17.7 - - Channel gained immutable/read-only ChannelType, TargetHandleType, - TargetHandle and Interfaces properties - -* TpGroupMixin now supports the Group properties introduced in the previous - version - -* Added an example connection manager with channel-specific handles in - chatrooms - -telepathy-glib 0.7.9 (2008-05-30) -================================= - -The "scaffolding" release. - -This version mostly contains infrastructure to support APIs that are coming -soon, but haven't got through the review process yet. - -Enhancements: - -* Update to telepathy-spec 0.17.5 - - RoomList gained a read-only Server property - - Text gained TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT, - TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT to support the future - Messages and DeliveryReporting interfaces - -* Update to telepathy-spec 0.17.6 - - Group properties (but the group mixin does not yet implement them) - - Group HandleOwnersChanged, SelfHandleChanged - - TP_HASH_TYPE_CHANNEL_OWNER_MAP - -* New functions tp_asv_get_uint32() etc. to make it easier to use a{sv} - hash tables (implemented in dbus-glib as a GHashTable of gchar * => GValue *) - -* Enhance TpDBusPropertiesMixin so that mixins can provide properties more - easily - -* Start porting the remaining XSLT to Python for better maintainability - -Fixes: - -* TpProxy: Avoid a misleading debug message every time a pending call - completes, and probably fix detection of crashing services - -* tp_dbus_check_valid_interface_name: correctly detect that a name with a - dot followed by a digit is invalid - -* Add a Valgrind suppression for SELinux-related ld.so initialization (not our - problem) - -Release notes for projects using code-generation: - -* When you update tools copied from telepathy-glib, be aware that - libglibcodegen.py now depends on libtpcodegen.py, and that some of the - formerly-XSLT tools have been replaced by Python versions - -telepathy-glib 0.7.8 (2008-05-09) -================================= - -The "Hold, unheld" release - -Enhancements: - -* Update to telepathy-spec 0.17.4 - - add Hold API to code-generation - -Fixes: - -* Make coding-style check less fragile - -Recommended updates for projects using check-coding-style.mk: - -* Update check-coding-style.mk - -* In every Makefile where check-coding-style.mk is used, make check-local - depend on check-coding-style (this is no longer done automatically) - -telepathy-glib 0.7.7 (2008-05-02) -================================= - -The "Old Trip" release. - -API changes: - -* It is an error to pass non-NULL user_data, weak_object or destroy arguments - when making an asynchronous method call with callback == NULL (i.e. - ignoring the reply) - doing this would make no sense - -Enhancements: - -* Implement tp_connection_call_when_ready, tp_channel_call_when_ready - - fully async variants of the existing run_when_ready API (fd.o #15300) - -* Documentation: divide up the service-side Channel interfaces by topic, - in the same way the client-side ones were already divided up - -* TpTextMixin: drop the futile attempt to limit memory consumption. We were - doing it wrong, and a typical connection manager has so many ways it can - be induced to consume memory that trying to guard against this particular - case by truncating or dropping messages seems likely to cause more problems - than it fixes. - -* TpTextMixin: save a malloc/free cycle in GetMessageTypes - -* TpGroupMixin: don't emit GroupFlagsChanged(0, 0) - -* Log a message when disconnected from the D-Bus session bus - -* Improve ABI-checking functionality so symbols only have to be whitelisted - just before a release - -Fixes: - -* TpBaseConnection: don't return from Disconnect() until disconnection has - fully completed (fd.o #15796) - -* libglibcodegen.py: generate correct bindings for arrays of object-path (ao) - -* glib-client-gen.py: cope correctly with Unicode in the spec - -* doc-generator.xsl: update from telepathy-spec to cope with arrays of mappings - -* When a channel's connection becomes invalidated, don't warn if the - resulting invalidation causes the channel to be freed (fd.o #15644) - -* In tp_handle_lookup() on a dynamic handle repository, if the ID is valid - but there is no handle, raise the error NotAvailable, instead of returning - 0 with no error set (fd.o #15387) - -* When asynchronous method calls are made on an unsupported interface, call - the user-supplied destroy() callback on the user_data (fd.o #15530) - -* TpTextMixin: fix a memory leak in ListPendingMessages - -* Use automake 1.8 if available, to verify that we can still build on such - an old version - -* When running under valgrind with the recommended suppressions, don't - complain about libc dlopen initialization caused by the handle-leak debug - code - -Recommended updates for projects using code-generation: - -* Update libglibcodegen.py to avoid a crash if arrays of object-path appear - in your API in future - -* Update glib-client-gen.py to avoid fd.o #15530; this will cause your project - to require telepathy-glib >= 0.7.3 - -telepathy-glib 0.7.6 (2008-04-03) -================================= - -The "hold the assertions" release. - -API changes: - -* API was added to fix fd.o #15325. Projects that add extension interfaces - to TpChannel or TpConnection might suffer from assertion failures if those - interfaces are later added to telepathy-glib, unless they follow the - recommendations below. Projects that do not use the telepathy-glib code - generation tools are unaffected. - -* Related to the above, extending TpDBusDaemon, TpMediaStreamHandler, - TpMediaSessionHandler and TpConnectionManager is officially not supported - at this time. - -Enhancements: - -* Updated to specification 0.17.3 - - supports the CallState interface, for receiving notifications of a - remote contact or device's state (currently supported states are: ringing, - queued, placed us on hold) - - MediaStreamHandler supports some new Hold-related API - - the Hold interface is *not* yet supported, use private code-generation - (as seen in telepathy-sofiasip) for now - -Fixes: - -* Doesn't assert in client code when a connection's GetInterfaces method fails - (fd.o #15306) - -* The build process works on platforms without -Wl,-O1 and - -Wl,--version-script, such as Mac OS X (fd.o #15026) - -* test-handle-set now runs under a temporary session bus, so the tests can work - in platforms without working D-Bus autolaunch - -* Code generation uses G_GNUC_UNUSED where necessary, so you can use the - generated code in projects with stricter warning flags than telepathy-glib - itself - -* A couple of unterminated argument lists when initializing the allowed - message types for the text mixin have been fixed - -* glib-client-gen.py optionally inserts a guard against assertion failures - caused by a local extension overriding an interface that is also now provided - by telepathy-glib (fd.o #15325) - -* glib-client-gen.py has a mechanism to allow its output to be made - backwards-compatible: specify --tp-proxy-api=x.y.z where x.y.z is the oldest - version of telepathy-glib you check for in configure.ac (it must be at - least 0.7.6) - -Recommended updates for projects using telepathy-glib code generation: - -* Update glib-client-gen.py from telepathy-glib, and invoke it with - the extra argument "--tp-proxy-api=0.7.6", to fix #15325 (this will make - the generated code require telepathy-glib 0.7.6) - -* Before calling tp_proxy_or_subclass_hook_on_interface_add for an existing - class, call tp_proxy_init_known_interfaces, - tp_connection_init_known_interfaces or tp_channel_init_known_interfaces. - -telepathy-glib 0.7.5 (2008-03-07) -================================= - -Enhancements: - -* Updated to specification 0.17.2 - - GType-generating functions for Media_Session_Handler_Info - - Added TP_CONN_MGR_PARAM_FLAG_SECRET - -Fixes: - -* No longer crashes if a proxy is invalidated while still invoking signal - callbacks (fd.o #14854) - -* Compilation now works on architectures with function descriptors, like ppc64 - (fd.o #14852, thanks Brian Pepple) - -* TpChannel no longer crashes if GetInterfaces() returns an error or an - empty list (fd.o #14855) - -* Linking examples no longer fails in environments where .la files exist for - system libraries - -telepathy-glib 0.7.4 (2008-03-04) -================================= - -Fixes: - -* Refactoring and life-cycle fixes in TpProxy, including some assertions - during method-call and signal-connection cancellation (fd.o #14750; fixes a - crash in development versions of Mission Control) - -* Maps "_" back to "-" in protocol names seen in bus names (fd.o #14667; - fixes telepathy-inspector 0.5.1 not displaying Salut connections) - -* Does not leak the GError when connecting to signals on an unsupported - interface (fd.o #14746) - -* Builds with more warnings by default, for additional strictness - -* Improvements to example client code: - - make example_cli_init() safe to call multiple times - - generate signals-marshal.list in a way that avoids more gcc warnings - -Recommended updates for projects using telepathy-glib code generation: - -* Update glib-client-gen.py from telepathy-glib to fix #14746 (this will make - the generated code require telepathy-glib 0.7.3) -* Consider updating *_cli_init() and extensions/Makefile.am to resemble the - examples - -telepathy-glib 0.7.3 (2008-02-20) -================================= - -Requirements: - -* pkg-config 0.21 is now required - -* gtkdoc 1.8 is recommended - -Enhancements: - -* Supports org.freedesktop.DBus.Properties with a mixin for services, and - extensions to auto-generated code - -Fixes: - -* Fixed a crash that could occur when pending calls on otherwise unreferenced - proxies are cancelled, e.g. during teardown in telepathy-stream-engine - (fd.o #14576) - -* Correct client-side bindings are generated for methods that return a variant - (e.g. o.fd.DBus.Properties.Get) fixing potential crashes in clients - -* tp_dbus_check_valid_object_path allows "/" and disallows "//", not the - reverse - -* Partially works around some gtkdoc 1.7 bugs (if your gtkdoc is 1.7 you'll - still lose the "Telepathy protocol enumerations" section) - -* Uses pkg-config's Requires.private feature to reduce shared library - interdependencies - -telepathy-glib 0.7.2 (2008-02-11) -================================= - -* Fixed incorrect GValue code that caused Empathy to assert on - Room -> Join New... - -* Added support for the D-Bus core Properties, Introspect and Peer interfaces - -telepathy-glib 0.7.1 (2008-01-04) -================================= - -This is a major feature release, adding client-side code which continues the -process of making libtelepathy obsolete. - -* Implements specification 0.17.1 - - it is a fatal error for TpBaseConnectionManagerClass::cm_dbus_name not to - conform to the specification (briefly: it must match - /[A-Za-z_][A-Za-z0-9_]+/) - - connections cannot be opened for protocols not matching - /[A-Za-z-][A-Za-z0-9-]+/ - -* Initial client-side code added: - - TpProxy, a much more capable version of DBusGProxy (it more closely - resembles a dbus-python ProxyObject, with one object per connection, - channel etc. rather than one per interface) - - auto-generation machinery for client call wrappers (namespaced tp_cli_*) - - TpDBusDaemon, a TpProxy subclass to talk to the bus daemon - - TpChannel, a channel (replaces libtelepathy's TpChan) - - TpConnection, a connection (replaces libtelepathy's TpConn) - - TpConnectionManager, a connection manager (replaces libtelepathy's - TpConnMgr) - - TpMediaStreamHandler and TpMediaSessionHandler, media signalling helpers - - Correct algorithms for listing connection managers, listing connections, - and reading .manager files (as documented in the 0.17.1 spec) - -* Examples extended: - - various simple client examples - - an "echo" connection manager - - an extended connection manager and client (implementing a hypothetical - Hats interface), to illustrate how to support experimental or extension - interfaces in services and clients - -* Miscellaneous: - - tp_debug_divert_messages (adapted from Gabble) - - tp_debug_timestamped_log_handler (adapted from Gabble) - - tp_cm_param_setter_offset now supports byte-array params - -* Versioned symbols - - versions are of the form TELEPATHY_GLIB_x.y.z - - unreleased development builds use versions containing _UNRELEASED - to force relinking against a proper version - -telepathy-glib 0.7.0 (2007-11-22) -================================= - -This is the first release from the 0.7.0 development branch, and -prepares the way for the client-side code I'm going to be merging soon. - -* Enums etc. updated to specification 0.17.0 -* Improved support for connection managers whose parameters aren't known - at compile time (*coughhazecough* :-) -* Build system refactoring to make it less weird -* GType-generating functions and macros in gtypes.h cache the results of calls - to dbus-glib type lookup functions, and give data types convenient names -* Functions and macros to generate GQuarks for interface names (which will - be used extensively by client code in future) -* The beginning of an examples/ directory, containing the simplest possible - example connection manager (it doesn't support any protocols) -* Dependencies increased to libdbus 0.93, dbus-glib 0.73, glib 2.10 diff --git a/autogen.sh b/autogen.sh index f92b907d9..939aa7d8e 100755 --- a/autogen.sh +++ b/autogen.sh @@ -5,13 +5,14 @@ gtkdocize if test -n "$AUTOMAKE"; then : # don't override an explicit user request -elif automake-1.9 --version >/dev/null 2>/dev/null && \ - aclocal-1.9 --version >/dev/null 2>/dev/null; then - # If we have automake-1.9, use it. This helps to ensure that our build - # system doesn't accidentally grow automake-1.10 dependencies. - AUTOMAKE=automake-1.9 +elif automake-1.11 --version >/dev/null 2>/dev/null && \ + aclocal-1.11 --version >/dev/null 2>/dev/null; then + # If we have automake-1.11, use it. This is the oldest version (=> least + # likely to introduce undeclared dependencies) that will give us + # --enable-silent-rules support. + AUTOMAKE=automake-1.11 export AUTOMAKE - ACLOCAL=aclocal-1.9 + ACLOCAL=aclocal-1.11 export ACLOCAL fi @@ -28,15 +29,6 @@ for arg in $*; do esac done -# Workaround for gtk-doc + shave + libtool 1.x -# See http://git.lespiau.name/cgit/shave/tree/README#n83 -sed -e 's#) --mode=compile#) --tag=CC --mode=compile#' gtk-doc.make \ - > gtk-doc.temp \ - && mv gtk-doc.temp gtk-doc.make -sed -e 's#) --mode=link#) --tag=CC --mode=link#' gtk-doc.make \ - > gtk-doc.temp \ - && mv gtk-doc.temp gtk-doc.make - if test $run_configure = true; then ./configure "$@" fi diff --git a/configure.ac b/configure.ac index 8c84fd039..24b055720 100644 --- a/configure.ac +++ b/configure.ac @@ -10,16 +10,25 @@ AC_PREREQ([2.59]) # set nano_version to 1 m4_define([tp_glib_major_version], [0]) -m4_define([tp_glib_minor_version], [8]) +m4_define([tp_glib_minor_version], [9]) m4_define([tp_glib_micro_version], [0]) m4_define([tp_glib_nano_version], [1]) -# Do not add API/ABI in this branch (which means current and age stay at 30). -# If library source has changed since last release, increment revision. - -m4_define([tp_glib_lt_current], [30]) -m4_define([tp_glib_lt_revision], [1]) -m4_define([tp_glib_lt_age], [30]) +# If library source has changed since last release, increment revision +# If interfaces have been added, removed or changed since last release, +# increment current and set revision to 0 +# If interfaces have been added since last release, increment age +# If interfaces have been removed since last release, set age to 0 +# +# If interfaces have been added, release builds will fail unless you add a new +# file like telepathy-glib/versions/0.7.3.abi to add them to the official ABI. +# This also forces binaries built against devel versions to be rebuilt +# (we don't guarantee that we won't add ABI then remove it again, if it was +# never seen in a release). + +m4_define([tp_glib_lt_current], [31]) +m4_define([tp_glib_lt_revision], [0]) +m4_define([tp_glib_lt_age], [31]) # Some magic m4_define([tp_glib_base_version], @@ -36,6 +45,8 @@ AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.9 -Wno-portability]) AM_CONFIG_HEADER(config.h) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) + dnl check for tools AC_PROG_CC AC_PROG_CC_STDC @@ -139,8 +150,8 @@ dnl (srcdir != builddir) AM_CONDITIONAL([OUT_OF_TREE_BUILD], [test "z$ac_srcdir" != z.]) dnl Check for Glib -PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.16, gobject-2.0 >= 2.16]) - +PKG_CHECK_MODULES(GLIB, + [glib-2.0 >= 2.20, gobject-2.0 >= 2.20, gio-2.0 >= 2.20]) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) @@ -185,8 +196,6 @@ if test -z "$NM"; then fi AM_CONDITIONAL([HAVE_LD_VERSION_SCRIPT], [test $HAVE_LD_VERSION_SCRIPT = yes]) -SHAVE_INIT(.) - AC_OUTPUT( Makefile \ docs/Makefile \ docs/reference/Makefile \ @@ -210,7 +219,5 @@ AC_OUTPUT( Makefile \ tests/dbus/Makefile \ tests/tools/Makefile \ tools/Makefile \ - m4/Makefile \ - shave \ - shave-libtool + m4/Makefile ) diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index 34de9a7a1..2c1df5eb2 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -3027,6 +3027,49 @@ TpAccount TpAccountClass tp_account_new tp_account_init_known_interfaces +tp_account_parse_object_path +tp_account_get_connection +tp_account_ensure_connection +tp_account_get_display_name +tp_account_get_connection_manager +tp_account_get_protocol +tp_account_get_icon_name +tp_account_set_enabled_async +tp_account_set_enabled_finish +tp_account_reconnect_async +tp_account_reconnect_finish +tp_account_is_enabled +tp_account_is_valid +tp_account_update_parameters_async +tp_account_update_parameters_finish +tp_account_remove_async +tp_account_remove_finish +tp_account_set_display_name_async +tp_account_set_display_name_finish +tp_account_set_icon_name_async +tp_account_set_icon_name_finish +tp_account_request_presence_async +tp_account_request_presence_finish +tp_account_get_connect_automatically +tp_account_set_connect_automatically_async +tp_account_set_connect_automatically_finish +tp_account_get_has_been_online +tp_account_get_connection_status +tp_account_get_current_presence +tp_account_get_requested_presence +tp_account_get_parameters +tp_account_get_nickname +tp_account_set_nickname_async +tp_account_set_nickname_finish +tp_account_get_avatar_async +tp_account_get_avatar_finish +<SUBSECTION> +TP_ACCOUNT_FEATURE_CORE +tp_account_is_prepared +tp_account_prepare_async +tp_account_prepare_finish +<SUBSECTION Private> +tp_account_get_feature_quark_core <SUBSECTION> tp_cli_account_callback_for_reconnect tp_cli_account_call_reconnect @@ -3061,6 +3104,21 @@ TpAccountManager TpAccountManagerClass tp_account_manager_new tp_account_manager_init_known_interfaces +tp_account_manager_dup +tp_account_manager_create_account_async +tp_account_manager_create_account_finish +tp_account_manager_ensure_account +tp_account_manager_get_valid_accounts +tp_account_manager_get_most_available_presence +tp_account_manager_set_all_requested_presences +tp_account_manager_enable_restart +<SUBSECTION> +TP_ACCOUNT_MANAGER_FEATURE_CORE +tp_account_manager_is_prepared +tp_account_manager_prepare_async +tp_account_manager_prepare_finish +<SUBSECTION Private> +tp_account_manager_get_feature_quark_core <SUBSECTION> tp_cli_account_manager_callback_for_create_account tp_cli_account_manager_call_create_account diff --git a/examples/extensions/Makefile.am b/examples/extensions/Makefile.am index 2053010f9..ad15f475b 100644 --- a/examples/extensions/Makefile.am +++ b/examples/extensions/Makefile.am @@ -9,8 +9,6 @@ tools_dir = $(top_srcdir)/tools -include $(tools_dir)/shave.mk - AM_CFLAGS = \ $(ERROR_CFLAGS) \ $(DBUS_CFLAGS) \ @@ -68,43 +66,43 @@ XSLTPROCFLAGS = --nonet --novalid _gen/all.xml: all.xml $(wildcard *.xml) $(tools_dir)/xincludator.py $(mkdir_p) _gen - $(QUIET_GEN)$(PYTHON) $(tools_dir)/xincludator.py $< > $@ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/xincludator.py $< > $@ extensions.html: _gen/all.xml $(tools_dir)/doc-generator.xsl - $(QUIET_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) \ + $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) \ $(tools_dir)/doc-generator.xsl \ $< > $@ _gen/gtypes.h _gen/gtypes-body.h: _gen/all.xml \ $(top_srcdir)/tools/glib-gtypes-generator.py - $(QUIET_GEN)$(PYTHON) $(top_srcdir)/tools/glib-gtypes-generator.py \ + $(AM_V_GEN)$(PYTHON) $(top_srcdir)/tools/glib-gtypes-generator.py \ $< _gen/gtypes Example _gen/signals-marshal.list: _gen/all.xml \ $(tools_dir)/glib-signals-marshal-gen.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-signals-marshal-gen.py $< > $@ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-signals-marshal-gen.py $< > $@ _gen/signals-marshal.h: _gen/signals-marshal.list Makefile.am - $(QUIET_GEN)$(GLIB_GENMARSHAL) --header --prefix=_example_ext_marshal $< > $@ + $(AM_V_GEN)$(GLIB_GENMARSHAL) --header --prefix=_example_ext_marshal $< > $@ _gen/signals-marshal.c: _gen/signals-marshal.list Makefile.am - $(QUIET_GEN){ echo '#include "_gen/signals-marshal.h"' && \ + $(AM_V_GEN){ echo '#include "_gen/signals-marshal.h"' && \ $(GLIB_GENMARSHAL) --body --prefix=_example_ext_marshal $< ; } > $@ _gen/register-dbus-glib-marshallers-body.h: _gen/all.xml \ $(tools_dir)/glib-client-marshaller-gen.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-client-marshaller-gen.py $< \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-marshaller-gen.py $< \ _example_ext > $@ _gen/enums.h: _gen/all.xml \ $(tools_dir)/c-constants-gen.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/c-constants-gen.py \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/c-constants-gen.py \ Example \ $< > $@ _gen/interfaces-body.h _gen/interfaces.h: _gen/all.xml \ $(tools_dir)/glib-interfaces-gen.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-interfaces-gen.py \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-interfaces-gen.py \ Example _gen/interfaces-body.h _gen/interfaces.h $< # Generated files which must be generated per "category". Each TpProxy @@ -113,11 +111,11 @@ _gen/interfaces-body.h _gen/interfaces.h: _gen/all.xml \ _gen/connection.xml: connection.xml $(wildcard *.xml) $(tools_dir)/xincludator.py $(mkdir_p) _gen - $(QUIET_GEN)$(PYTHON) $(tools_dir)/xincludator.py $< > $@ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/xincludator.py $< > $@ _gen/cli-connection-body.h _gen/cli-connection.h: _gen/connection.xml \ $(tools_dir)/glib-client-gen.py Makefile.am - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-client-gen.py \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-gen.py \ --group=connection \ --subclass=TpConnection \ --subclass-assert=TP_IS_CONNECTION \ @@ -128,7 +126,7 @@ _gen/cli-connection-body.h _gen/cli-connection.h: _gen/connection.xml \ _gen/svc-connection.c _gen/svc-connection.h: _gen/connection.xml \ $(tools_dir)/glib-ginterface-gen.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \ --filename=_gen/svc-connection \ --signal-marshal-prefix=_example_ext \ --include='<telepathy-glib/dbus.h>' \ diff --git a/m4/Makefile.am b/m4/Makefile.am index e72117c91..e04d0cb39 100644 --- a/m4/Makefile.am +++ b/m4/Makefile.am @@ -3,5 +3,4 @@ compiler.m4 \ gtk-doc.m4 \ linker.m4 \ tp-compiler-flag.m4 \ -tp-compiler-warnings.m4 \ -shave.m4 +tp-compiler-warnings.m4 diff --git a/m4/shave.m4 b/m4/shave.m4 deleted file mode 100644 index 0a3509e59..000000000 --- a/m4/shave.m4 +++ /dev/null @@ -1,77 +0,0 @@ -dnl Make automake/libtool output more friendly to humans -dnl Damien Lespiau <damien.lespiau@gmail.com> -dnl -dnl SHAVE_INIT([shavedir],[default_mode]) -dnl -dnl shavedir: the directory where the shave scripts are, it defaults to -dnl $(top_builddir) -dnl default_mode: (enable|disable) default shave mode. This parameter -dnl controls shave's behaviour when no option has been -dnl given to configure. It defaults to disable. -dnl -dnl * SHAVE_INIT should be called late in your configure.(ac|in) file (just -dnl before AC_CONFIG_FILE/AC_OUTPUT is perfect. This macro rewrites CC and -dnl LIBTOOL, you don't want the configure tests to have these variables -dnl re-defined. -dnl * This macro requires GNU make's -s option. - -AC_DEFUN([_SHAVE_ARG_ENABLE], -[ - AC_ARG_ENABLE([shave], - AS_HELP_STRING( - [--enable-shave], - [use shave to make the build pretty [[default=$1]]]),, - [enable_shave=$1] - ) -]) - -AC_DEFUN([SHAVE_INIT], -[ - dnl you can tweak the default value of enable_shave - m4_if([$2], [enable], [_SHAVE_ARG_ENABLE(yes)], [_SHAVE_ARG_ENABLE(no)]) - - if test x"$enable_shave" = xyes; then - dnl where can we find the shave scripts? - m4_if([$1],, - [shavedir="$ac_pwd"], - [shavedir="$ac_pwd/$1"]) - AC_SUBST(shavedir) - - dnl make is now quiet - AC_SUBST([MAKEFLAGS], [-s]) - AC_SUBST([AM_MAKEFLAGS], ['`test -z $V && echo -s`']) - - dnl we need sed - AC_CHECK_PROG(SED,sed,sed,false) - - dnl substitute libtool - SHAVE_SAVED_LIBTOOL=$LIBTOOL - LIBTOOL="${SHELL} ${shavedir}/shave-libtool '${SHAVE_SAVED_LIBTOOL}'" - AC_SUBST(LIBTOOL) - - dnl substitute cc/cxx - SHAVE_SAVED_CC=$CC - SHAVE_SAVED_CXX=$CXX - SHAVE_SAVED_FC=$FC - SHAVE_SAVED_F77=$F77 - SHAVE_SAVED_OBJC=$OBJC - CC="${SHELL} ${shavedir}/shave cc ${SHAVE_SAVED_CC}" - CXX="${SHELL} ${shavedir}/shave cxx ${SHAVE_SAVED_CXX}" - FC="${SHELL} ${shavedir}/shave fc ${SHAVE_SAVED_FC}" - F77="${SHELL} ${shavedir}/shave f77 ${SHAVE_SAVED_F77}" - OBJC="${SHELL} ${shavedir}/shave objc ${SHAVE_SAVED_OBJC}" - AC_SUBST(CC) - AC_SUBST(CXX) - AC_SUBST(FC) - AC_SUBST(F77) - AC_SUBST(OBJC) - - V=@ - else - V=1 - fi - Q='$(V:1=)' - AC_SUBST(V) - AC_SUBST(Q) -]) - diff --git a/shave-libtool.in b/shave-libtool.in deleted file mode 100644 index 1f3a720c1..000000000 --- a/shave-libtool.in +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/sh - -# we need sed -SED=@SED@ -if test -z "$SED" ; then -SED=sed -fi - -lt_unmangle () -{ - last_result=`echo $1 | $SED -e 's#.libs/##' -e 's#[0-9a-zA-Z_\-\.]*_la-##'` -} - -# the real libtool to use -LIBTOOL="$1" -shift - -# if 1, don't print anything, the underlaying wrapper will do it -pass_though=0 - -# scan the arguments, keep the right ones for libtool, and discover the mode -preserved_args= -while test "$#" -gt 0; do - opt="$1" - shift - - case $opt in - --mode=*) - mode=`echo $opt | $SED -e 's/[-_a-zA-Z0-9]*=//'` - preserved_args="$preserved_args $opt" - ;; - -o) - lt_output="$1" - preserved_args="$preserved_args $opt" - ;; - *) - preserved_args="$preserved_args $opt" - ;; - esac -done - -case "$mode" in -compile) - # shave will be called and print the actual CC/CXX/LINK line - preserved_args="$preserved_args --shave-mode=$mode" - pass_though=1 - ;; -link) - preserved_args="$preserved_args --shave-mode=$mode" - Q=" LINK " - ;; -*) - # let's u - # echo "*** libtool: Unimplemented mode: $mode, fill a bug report" - ;; -esac - -lt_unmangle "$lt_output" -output=$last_result - -if test -z $V; then - if test $pass_though -eq 0; then - echo "$Q$output" - fi - $LIBTOOL --silent $preserved_args -else - echo $LIBTOOL $preserved_args - $LIBTOOL $preserved_args -fi diff --git a/shave.in b/shave.in deleted file mode 100644 index 5c16f27ae..000000000 --- a/shave.in +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/sh - -# we need sed -SED=@SED@ -if test -z "$SED" ; then -SED=sed -fi - -lt_unmangle () -{ - last_result=`echo $1 | $SED -e 's#.libs/##' -e 's#[0-9a-zA-Z_\-\.]*_la-##'` -} - -# the tool to wrap (cc, cxx, ar, ranlib, ..) -tool="$1" -shift - -# the reel tool (to call) -REEL_TOOL="$1" -shift - -pass_through=0 -preserved_args= -while test "$#" -gt 0; do - opt="$1" - shift - - case $opt in - --shave-mode=*) - mode=`echo $opt | $SED -e 's/[-_a-zA-Z0-9]*=//'` - ;; - -o) - lt_output="$1" - preserved_args="$preserved_args $opt" - ;; - *) - preserved_args="$preserved_args $opt" - ;; - esac -done - -# mode=link is handled in the libtool wrapper -case "$mode,$tool" in -link,*) - pass_through=1 - ;; -*,cxx) - Q=" CXX " - ;; -*,cc) - Q=" CC " - ;; -*,fc) - Q=" FC " - ;; -*,f77) - Q=" F77 " - ;; -*,objc) - Q=" OBJC " - ;; -*,*) - # should not happen - Q=" CC " - ;; -esac - -lt_unmangle "$lt_output" -output=$last_result - -if test -z $V; then - if test $pass_through -eq 0; then - echo "$Q$output" - fi - $REEL_TOOL $preserved_args -else - echo $REEL_TOOL $preserved_args - $REEL_TOOL $preserved_args -fi diff --git a/telepathy-glib/Makefile.am b/telepathy-glib/Makefile.am index 7a65ee509..a29b71555 100644 --- a/telepathy-glib/Makefile.am +++ b/telepathy-glib/Makefile.am @@ -1,4 +1,3 @@ -include $(top_srcdir)/tools/shave.mk include $(top_srcdir)/tools/flymake.mk tpgincludedir=$(includedir)/telepathy-1.0/telepathy-glib @@ -40,7 +39,8 @@ ABI_LISTS = \ versions/0.7.34.abi \ versions/0.7.35.abi \ versions/0.7.36.abi \ - versions/0.7.37.abi + versions/0.7.37.abi \ + versions/0.9.0.abi EXTRA_DIST = \ $(ABI_LISTS) \ @@ -96,7 +96,7 @@ endif _gen/version-script.txt: $(ABI_LISTS) _gen/abi.txt Makefile.am \ $(top_srcdir)/tools/make-version-script.py - $(QUIET_GEN)$(PYTHON) $(top_srcdir)/tools/make-version-script.py \ + $(AM_V_GEN)$(PYTHON) $(top_srcdir)/tools/make-version-script.py \ --symbols=_gen/abi.txt $(MAKE_VERSION_SCRIPT_FLAGS) \ $(ABI_LISTS:%=$(srcdir)/%) > $@ $(PYTHON) $(top_srcdir)/tools/make-version-script.py \ @@ -114,7 +114,7 @@ _gen/abi.txt: libtelepathy-glib-internal.la Makefile.am grep " [DT] " < _gen/abi.nm > _gen/abi.funcs cut -d" " -f3 < _gen/abi.funcs > _gen/abi.funcnames grep "^tp" < _gen/abi.funcnames > _gen/abi.tpfuncnames - $(QUIET_GEN)sort -u < _gen/abi.tpfuncnames > $@ + $(AM_V_GEN)sort -u < _gen/abi.tpfuncnames > $@ libtelepathy_glib_la_LDFLAGS += \ $(VERSION_SCRIPT_ARG)=_gen/version-script.txt @@ -280,7 +280,9 @@ check-local: check-coding-style libtelepathy_glib_internal_la_LIBADD = $(ALL_LIBS) libtelepathy_glib_internal_la_SOURCES = \ account.c \ + account-internal.h \ account-manager.c \ + account-manager-internal.h \ base-connection.c \ base-connection-manager.c \ channel.c \ @@ -360,60 +362,60 @@ _gen/stable-stamp: $(wildcard *.xml) _gen/spec-stamp touch $@ _gen/stable-spec.xml: stable-interfaces.xml _gen/stable-stamp $(tools_dir)/xincludator.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/xincludator.py $< > $@ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/xincludator.py $< > $@ # Things generated from the whole spec at once _gen/gtypes.h _gen/gtypes-body.h: _gen/stable-spec.xml \ $(tools_dir)/glib-gtypes-generator.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-gtypes-generator.py \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-gtypes-generator.py \ _gen/stable-spec.xml \ _gen/gtypes Tp _gen/telepathy-enums.h: _gen/stable-spec.xml \ $(tools_dir)/c-constants-gen.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/c-constants-gen.py \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/c-constants-gen.py \ Tp \ $< > $@ _gen/interfaces-body.h _gen/telepathy-interfaces.h: _gen/stable-spec.xml \ $(tools_dir)/glib-interfaces-gen.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-interfaces-gen.py \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-interfaces-gen.py \ Tp _gen/interfaces-body.h _gen/telepathy-interfaces.h $< _gen/register-dbus-glib-marshallers-body.h: _gen/stable-spec.xml \ $(tools_dir)/glib-client-marshaller-gen.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-client-marshaller-gen.py $< _tp > $@ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-marshaller-gen.py $< _tp > $@ _gen/tp-signals-marshal.list: $(tools_dir)/glib-signals-marshal-gen.py \ _gen/stable-spec.xml - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-signals-marshal-gen.py \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-signals-marshal-gen.py \ _gen/stable-spec.xml > $@ _gen/signals-marshal.list: signals-marshal.list _gen/tp-signals-marshal.list - $(QUIET_GEN)sort -u $^ > $@ + $(AM_V_GEN)sort -u $^ > $@ _gen/signals-marshal.h: _gen/signals-marshal.list Makefile.am - $(QUIET_GEN)$(GLIB_GENMARSHAL) --header --prefix=_tp_marshal $< > $@ + $(AM_V_GEN)$(GLIB_GENMARSHAL) --header --prefix=_tp_marshal $< > $@ _gen/signals-marshal.c: _gen/signals-marshal.list Makefile.am - $(QUIET_GEN){ echo '#include "_gen/signals-marshal.h"' && \ + $(AM_V_GEN){ echo '#include "_gen/signals-marshal.h"' && \ $(GLIB_GENMARSHAL) --body --prefix=_tp_marshal $< ; } > $@ _gen/error-str.h _gen/error-str.c: _gen/stable-spec.xml \ $(tools_dir)/glib-errors-str-gen.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-errors-str-gen.py \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-errors-str-gen.py \ _gen/error-str $< # Things generated per interface _gen/tp-spec-%.xml: %.xml _gen/spec-stamp $(tools_dir)/xincludator.py - $(QUIET_GEN)$(PYTHON) $(tools_dir)/xincludator.py $< > $@ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/xincludator.py $< > $@ _gen/tp-svc-%.c _gen/tp-svc-%.h: _gen/tp-spec-%.xml \ $(tools_dir)/glib-ginterface-gen.py \ Makefile.am - $(QUIET_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \ + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \ --filename=_gen/tp-svc-$* \ --signal-marshal-prefix=_tp \ --include='<telepathy-glib/dbus.h>' \ @@ -424,7 +426,7 @@ _gen/tp-svc-%.c _gen/tp-svc-%.h: _gen/tp-spec-%.xml \ _gen/tp-cli-%-body.h _gen/tp-cli-%.h: _gen/tp-spec-%.xml \ $(tools_dir)/glib-client-gen.py \ Makefile.am - $(QUIET_GEN)set -e; \ + $(AM_V_GEN)set -e; \ subclass= ; \ subclass_assert= ; \ case $* in \ diff --git a/telepathy-glib/account-internal.h b/telepathy-glib/account-internal.h new file mode 100644 index 000000000..2fbf071f5 --- /dev/null +++ b/telepathy-glib/account-internal.h @@ -0,0 +1,39 @@ +/* + * TpAccount - proxy for a Telepathy account (internals) + * + * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2009 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 TP_ACCOUNT_INTERNAL_H +#define TP_ACCOUNT_INTERNAL_H + +#include <telepathy-glib/account.h> + +G_BEGIN_DECLS + +const GQuark * _tp_account_get_requested_features (TpAccount *account); + +const GQuark * _tp_account_get_actual_features (TpAccount *account); + +const GQuark * _tp_account_get_missing_features (TpAccount *account); + +void _tp_account_refresh_properties (TpAccount *account); + +G_END_DECLS + +#endif diff --git a/telepathy-glib/account-manager-internal.h b/telepathy-glib/account-manager-internal.h new file mode 100644 index 000000000..18dbeef75 --- /dev/null +++ b/telepathy-glib/account-manager-internal.h @@ -0,0 +1,40 @@ +/* + * TpAccountManager - proxy for a Telepathy account manager (internals) + * + * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2009 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 TP_ACCOUNT_MANAGER_INTERNAL_H +#define TP_ACCOUNT_MANAGER_INTERNAL_H + +#include <telepathy-glib/account-manager.h> + +G_BEGIN_DECLS + +const GQuark * _tp_account_manager_get_requested_features ( + TpAccountManager *manager); + +const GQuark * _tp_account_manager_get_actual_features ( + TpAccountManager *manager); + +const GQuark * _tp_account_manager_get_missing_features ( + TpAccountManager *manager); + +G_END_DECLS + +#endif diff --git a/telepathy-glib/account-manager.c b/telepathy-glib/account-manager.c index e147358ce..59b6fa94c 100644 --- a/telepathy-glib/account-manager.c +++ b/telepathy-glib/account-manager.c @@ -19,13 +19,17 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "telepathy-glib/account-manager.h" +#include "telepathy-glib/account-manager-internal.h" +#include "telepathy-glib/account-internal.h" -#include <telepathy-glib/dbus.h> #include <telepathy-glib/defs.h> -#include <telepathy-glib/errors.h> +#include <telepathy-glib/gtypes.h> #include <telepathy-glib/interfaces.h> #include <telepathy-glib/proxy-subclass.h> +#include <telepathy-glib/util.h> + +#include "telepathy-glib/account-manager.h" +#include "telepathy-glib/_gen/signals-marshal.h" #define DEBUG_FLAG TP_DEBUG_ACCOUNTS #include "telepathy-glib/debug-internal.h" @@ -51,14 +55,6 @@ * their configuration, places accounts online on request, and manipulates * accounts' presence, nicknames and avatars. * - * This proxy is usable but incomplete: GObject signals and accessors for the - * D-Bus properties will be added in a later version of telepathy-glib, along - * with a mechanism similar to tp_connection_call_when_ready(). - * - * Until suitable convenience methods are implemented, the generic - * tp_cli_dbus_properties_call_get_all() method can be used to get the D-Bus - * properties. - * * Since: 0.7.32 */ @@ -69,29 +65,594 @@ */ struct _TpAccountManagerPrivate { - gpointer dummy; + /* (owned) object path -> (reffed) TpAccount */ + GHashTable *accounts; + gboolean dispose_run; + + /* most available presence */ + TpAccount *most_available_account; + + TpConnectionPresenceType most_available_presence; + gchar *most_available_status; + gchar *most_available_status_message; + + /* requested presence, could be different + * from the actual one. */ + TpConnectionPresenceType requested_presence; + gchar *requested_status; + gchar *requested_status_message; + + GHashTable *create_results; + + /* Features */ + GList *features; + GList *callbacks; + GArray *requested_features; + GArray *actual_features; + GArray *missing_features; +}; + +typedef struct { + GQuark name; + gboolean ready; +} TpAccountManagerFeature; + +typedef struct { + GSimpleAsyncResult *result; + const GQuark *features; +} TpAccountManagerFeatureCallback; + +#define MC5_BUS_NAME "org.freedesktop.Telepathy.MissionControl5" + +enum { + ACCOUNT_VALIDITY_CHANGED, + ACCOUNT_REMOVED, + ACCOUNT_ENABLED, + ACCOUNT_DISABLED, + MOST_AVAILABLE_PRESENCE_CHANGED, + LAST_SIGNAL }; +static guint signals[LAST_SIGNAL]; + G_DEFINE_TYPE (TpAccountManager, tp_account_manager, TP_TYPE_PROXY); +/** + * TP_ACCOUNT_MANAGER_FEATURE_CORE: + * + * Expands to a call to a function that returns a quark for the "core" feature + * on a #TpAccountManager. + * + * When this feature is prepared, the list of accounts have been retrieved and + * are available for use, and change-notification has been set up. + * + * One can ask for a feature to be prepared using the + * tp_account_manager_prepare_async() function, and waiting for it to callback. + * + * Since: 0.9.0 + */ + +/** + * tp_account_manager_get_feature_quark_core: + * + * <!-- --> + * + * Returns: the quark used for representing the core feature of a + * #TpAccountManager + * + * Since: 0.9.0 + */ +GQuark +tp_account_manager_get_feature_quark_core (void) +{ + return g_quark_from_static_string ("tp-account-manager-feature-core"); +} + +static const GQuark * +_tp_account_manager_get_known_features (void) +{ + static GQuark features[1] = { 0 }; + + if (G_UNLIKELY (features[0] == 0)) + { + features[0] = TP_ACCOUNT_MANAGER_FEATURE_CORE; + } + + return features; +} + +static TpAccountManagerFeature * +_tp_account_manager_get_feature (TpAccountManager *self, + GQuark feature) +{ + TpAccountManagerPrivate *priv = self->priv; + GList *l; + + for (l = priv->features; l != NULL; l = l->next) + { + TpAccountManagerFeature *f = l->data; + + if (f->name == feature) + return f; + } + + return NULL; +} + +static gboolean +_tp_account_manager_feature_in_array (GQuark feature, + const GArray *array) +{ + const GQuark *c = (const GQuark *) array->data; + + for (; *c != 0; c++) + { + if (*c == feature) + return TRUE; + } + + return FALSE; +} + +static gboolean +_tp_account_manager_check_features (TpAccountManager *self, + const GQuark *features) +{ + const GQuark *f; + TpAccountManagerFeature *feat; + + for (f = features; f != NULL && *f != 0; f++) + { + feat = _tp_account_manager_get_feature (self, *f); + + /* features which are NULL (ie. don't exist) are always considered as + * being ready, except in _is_prepared when it doesn't make sense to + * return TRUE. */ + if (feat != NULL && !feat->ready) + return FALSE; + } + + /* Special-case core: no other feature is ready unless core itself is + * ready. */ + feat = _tp_account_manager_get_feature (self, + TP_ACCOUNT_MANAGER_FEATURE_CORE); + if (!feat->ready) + return FALSE; + + return TRUE; +} + +static void +_tp_account_manager_become_ready (TpAccountManager *self, + GQuark feature) +{ + TpAccountManagerPrivate *priv = self->priv; + TpAccountManagerFeature *f = NULL; + GList *l, *remove = NULL; + + f = _tp_account_manager_get_feature (self, feature); + + g_assert (f != NULL); + + if (f->ready) + return; + + f->ready = TRUE; + + if (!_tp_account_manager_feature_in_array (feature, priv->actual_features)) + g_array_append_val (priv->actual_features, feature); + + /* First, find which callbacks are satisfied and add those items + * from the remove list. */ + l = priv->callbacks; + while (l != NULL) + { + GList *c = l; + TpAccountManagerFeatureCallback *cb = l->data; + + l = l->next; + + if (_tp_account_manager_check_features (self, cb->features)) + { + priv->callbacks = g_list_remove_link (priv->callbacks, c); + remove = g_list_concat (c, remove); + } + } + + /* Next, complete these callbacks */ + for (l = remove; l != NULL; l = l->next) + { + TpAccountManagerFeatureCallback *cb = l->data; + + g_simple_async_result_complete (cb->result); + g_object_unref (cb->result); + g_slice_free (TpAccountManagerFeatureCallback, cb); + } + + g_list_free (remove); +} + static void tp_account_manager_init (TpAccountManager *self) { - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_ACCOUNT_MANAGER, + TpAccountManagerPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_ACCOUNT_MANAGER, TpAccountManagerPrivate); + + self->priv = priv; + + priv->most_available_presence = TP_CONNECTION_PRESENCE_TYPE_UNSET; + + priv->accounts = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_object_unref); + + priv->create_results = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +_tp_account_manager_start_mc5 (TpDBusDaemon *bus) +{ + TpProxy *mc5_proxy; + + /* trigger MC5 starting */ + mc5_proxy = g_object_new (TP_TYPE_PROXY, + "dbus-daemon", bus, + "dbus-connection", tp_proxy_get_dbus_connection (TP_PROXY (bus)), + "bus-name", MC5_BUS_NAME, + "object-path", "/", + NULL); + + tp_cli_dbus_peer_call_ping (mc5_proxy, -1, NULL, NULL, NULL, NULL); + + g_object_unref (mc5_proxy); +} + +static void +_tp_account_manager_name_owner_cb (TpDBusDaemon *proxy, + const gchar *name, + const gchar *new_owner, + gpointer user_data) +{ + DEBUG ("Name owner changed for %s, new name: %s", name, new_owner); + + if (new_owner == NULL || new_owner[0] == '\0') + { + /* MC5 quit or crashed for some reason, let's start it again */ + _tp_account_manager_start_mc5 (proxy); + return; + } +} + +static void +_tp_account_manager_validity_changed_cb (TpAccountManager *proxy, + const gchar *path, + gboolean valid, + gpointer user_data, + GObject *weak_object) +{ + TpAccountManager *manager = TP_ACCOUNT_MANAGER (weak_object); + TpAccountManagerPrivate *priv = manager->priv; + TpAccount *account; + + account = tp_account_manager_ensure_account (manager, path); + + g_object_ref (account); + + if (!valid) + g_hash_table_remove (priv->accounts, path); + + g_signal_emit (manager, signals[ACCOUNT_VALIDITY_CHANGED], 0, + account, valid); + + g_object_unref (account); +} + +static void +_tp_account_manager_ensure_all_accounts (TpAccountManager *manager, + GPtrArray *valid_accounts, + GPtrArray *invalid_accounts) +{ + guint i, missing_accounts; + GHashTableIter iter; + TpAccountManagerPrivate *priv = manager->priv; + gpointer value; + TpAccount *account; + gboolean found_in_valid = FALSE; + gboolean found_in_invalid = FALSE; + const gchar *name; + + /* ensure all accounts coming from MC5 first */ + for (i = 0; i < valid_accounts->len; i++) + { + name = g_ptr_array_index (valid_accounts, i); + + account = tp_account_manager_ensure_account (manager, name); + _tp_account_refresh_properties (account); + } + + missing_accounts = g_hash_table_size (priv->accounts) - valid_accounts->len; + + if (missing_accounts > 0) + { + /* look for accounts we have and the TpAccountManager doesn't, + * and remove them from our cache. */ + + DEBUG ("%d missing accounts", missing_accounts); + + g_hash_table_iter_init (&iter, priv->accounts); + + while (g_hash_table_iter_next (&iter, NULL, &value) && missing_accounts > 0) + { + account = value; + + /* look for this account in the valid accounts array */ + for (i = 0; i < valid_accounts->len; i++) + { + name = g_ptr_array_index (valid_accounts, i); + + if (!tp_strdiff (name, tp_proxy_get_object_path (account))) + { + found_in_valid = TRUE; + break; + } + } + + if (!found_in_valid) + { + /* look for this account in the invalid accounts array */ + for (i = 0; i < invalid_accounts->len; i++) + { + name = g_ptr_array_index (invalid_accounts, i); + + if (!tp_strdiff (name, tp_proxy_get_object_path (account))) + { + found_in_invalid = TRUE; + break; + } + } + + if (found_in_invalid) + { + DEBUG ("Account %s's validity changed", + tp_proxy_get_object_path (account)); + + _tp_account_manager_validity_changed_cb (manager, + tp_proxy_get_object_path (account), FALSE, NULL, + G_OBJECT (manager)); + } + else + { + DEBUG ("Account %s was not found, remove it from the cache", + tp_proxy_get_object_path (account)); + + g_object_ref (account); + g_hash_table_iter_remove (&iter); + g_signal_emit (manager, signals[ACCOUNT_REMOVED], 0, account); + g_object_unref (account); + } + + missing_accounts--; + } + + found_in_valid = FALSE; + found_in_invalid = FALSE; + } + } +} + +static void +_tp_account_manager_update_most_available_presence (TpAccountManager *manager) +{ + TpAccountManagerPrivate *priv = manager->priv; + TpConnectionPresenceType presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE; + TpAccount *account = NULL; + GHashTableIter iter; + gpointer value; + + /* this presence is equal to the presence of the account with the + * highest availability */ + + g_hash_table_iter_init (&iter, priv->accounts); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + TpAccount *a = TP_ACCOUNT (value); + TpConnectionPresenceType p; + + p = tp_account_get_current_presence (a, NULL, NULL); + + if (tp_connection_presence_type_cmp_availability (p, presence) > 0) + { + account = a; + presence = p; + } + } + + priv->most_available_account = account; + g_free (priv->most_available_status); + g_free (priv->most_available_status_message); + + if (account == NULL) + { + priv->most_available_presence = presence; + priv->most_available_status = NULL; + priv->most_available_status_message = NULL; + return; + } + + priv->most_available_presence = tp_account_get_current_presence (account, + &(priv->most_available_status), &(priv->most_available_status_message)); + + DEBUG ("Updated most available presence to: %s (%d) \"%s\"", + priv->most_available_status, priv->most_available_presence, + priv->most_available_status_message); } static void -tp_account_manager_constructed (GObject *object) +_tp_account_manager_check_core_ready (TpAccountManager *manager) +{ + TpAccountManagerPrivate *priv = manager->priv; + GHashTableIter iter; + gpointer value; + + if (tp_account_manager_is_prepared (manager, TP_ACCOUNT_MANAGER_FEATURE_CORE)) + return; + + g_hash_table_iter_init (&iter, priv->accounts); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + TpAccount *account = TP_ACCOUNT (value); + + if (!tp_account_is_prepared (account, TP_ACCOUNT_FEATURE_CORE)) + return; + } + + /* Rerequest most available presence on the initial set of accounts for cases + * where a most available presence was requested before the manager was ready + */ + if (priv->requested_presence != TP_CONNECTION_PRESENCE_TYPE_UNSET) + { + tp_account_manager_set_all_requested_presences (manager, + priv->requested_presence, priv->requested_status, + priv->requested_status_message); + } + + _tp_account_manager_update_most_available_presence (manager); + + _tp_account_manager_become_ready (manager, TP_ACCOUNT_MANAGER_FEATURE_CORE); +} + +static void +_tp_account_manager_got_all_cb (TpProxy *proxy, + GHashTable *properties, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpAccountManager *manager = TP_ACCOUNT_MANAGER (weak_object); + GPtrArray *valid_accounts; + GPtrArray *invalid_accounts; + + if (error != NULL) + { + DEBUG ("Failed to get account manager properties: %s", error->message); + return; + } + + valid_accounts = tp_asv_get_boxed (properties, "ValidAccounts", + TP_ARRAY_TYPE_OBJECT_PATH_LIST); + + invalid_accounts = tp_asv_get_boxed (properties, "InvalidAccounts", + TP_ARRAY_TYPE_OBJECT_PATH_LIST); + + if (valid_accounts != NULL && invalid_accounts != NULL) + _tp_account_manager_ensure_all_accounts (manager, valid_accounts, + invalid_accounts); + + _tp_account_manager_check_core_ready (manager); +} + +static void +_tp_account_manager_constructed (GObject *object) { TpAccountManager *self = TP_ACCOUNT_MANAGER (object); void (*chain_up) (GObject *) = ((GObjectClass *) tp_account_manager_parent_class)->constructed; + TpAccountManagerPrivate *priv = self->priv; + guint i; + const GQuark *known_features; if (chain_up != NULL) chain_up (object); g_return_if_fail (tp_proxy_get_dbus_daemon (self) != NULL); + + priv->features = NULL; + priv->callbacks = NULL; + + priv->requested_features = g_array_new (TRUE, FALSE, sizeof (GQuark)); + priv->actual_features = g_array_new (TRUE, FALSE, sizeof (GQuark)); + priv->missing_features = g_array_new (TRUE, FALSE, sizeof (GQuark)); + + known_features = _tp_account_manager_get_known_features (); + + /* Fill features list. */ + for (i = 0; i < G_N_ELEMENTS (known_features); i++) + { + TpAccountManagerFeature *feature; + feature = g_slice_new0 (TpAccountManagerFeature); + feature->name = known_features[i]; + feature->ready = FALSE; + priv->features = g_list_prepend (priv->features, feature); + } + + tp_cli_account_manager_connect_to_account_validity_changed (self, + _tp_account_manager_validity_changed_cb, NULL, + NULL, G_OBJECT (self), NULL); + + tp_cli_dbus_properties_call_get_all (self, -1, TP_IFACE_ACCOUNT_MANAGER, + _tp_account_manager_got_all_cb, NULL, NULL, G_OBJECT (self)); +} + +static void +_tp_account_manager_feature_free (gpointer data, + gpointer user_data) +{ + g_slice_free (TpAccountManagerFeature, data); +} + +static void +_tp_account_manager_finalize (GObject *object) +{ + TpAccountManager *manager = TP_ACCOUNT_MANAGER (object); + TpAccountManagerPrivate *priv = manager->priv; + + g_free (priv->most_available_status); + g_free (priv->most_available_status_message); + + g_free (priv->requested_status); + g_free (priv->requested_status_message); + + g_list_foreach (priv->features, _tp_account_manager_feature_free, NULL); + g_list_free (priv->features); + priv->features = NULL; + + /* GSimpleAsyncResult keeps a ref to the source GObject, so this list + * should be empty. */ + g_assert_cmpuint (g_list_length (priv->callbacks), ==, 0); + g_list_free (priv->callbacks); + priv->callbacks = NULL; + + g_array_free (priv->requested_features, TRUE); + g_array_free (priv->actual_features, TRUE); + g_array_free (priv->missing_features, TRUE); + + G_OBJECT_CLASS (tp_account_manager_parent_class)->finalize (object); +} + +static void +_tp_account_manager_dispose (GObject *object) +{ + TpAccountManager *self = TP_ACCOUNT_MANAGER (object); + TpAccountManagerPrivate *priv = self->priv; + + if (priv->dispose_run) + return; + + priv->dispose_run = TRUE; + + /* GSimpleAsyncResult keeps a ref to the source GObject, so this hash + * table should be empty. */ + g_assert_cmpuint (g_hash_table_size (priv->create_results), ==, 0); + g_hash_table_destroy (priv->create_results); + priv->create_results = NULL; + + g_hash_table_destroy (priv->accounts); + + tp_dbus_daemon_cancel_name_owner_watch (tp_proxy_get_dbus_daemon (self), + TP_ACCOUNT_MANAGER_BUS_NAME, _tp_account_manager_name_owner_cb, self); + + G_OBJECT_CLASS (tp_account_manager_parent_class)->dispose (object); } static void @@ -102,10 +663,113 @@ tp_account_manager_class_init (TpAccountManagerClass *klass) g_type_class_add_private (klass, sizeof (TpAccountManagerPrivate)); - object_class->constructed = tp_account_manager_constructed; + object_class->constructed = _tp_account_manager_constructed; + object_class->finalize = _tp_account_manager_finalize; + object_class->dispose = _tp_account_manager_dispose; proxy_class->interface = TP_IFACE_QUARK_ACCOUNT_MANAGER; tp_account_manager_init_known_interfaces (); + + /** + * TpAccountManager::account-validity-changed: + * @manager: a #TpAccountManager + * @account: a #TpAccount + * @valid: %TRUE if the account is now valid + * + * Emitted when the validity on @account changes. @account is not guaranteed + * to be ready when this signal is emitted. + * + * Since: 0.9.0 + */ + signals[ACCOUNT_VALIDITY_CHANGED] = g_signal_new ("account-validity-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _tp_marshal_VOID__OBJECT_BOOLEAN, + G_TYPE_NONE, + 2, TP_TYPE_ACCOUNT, G_TYPE_BOOLEAN); + + /** + * TpAccountManager::account-removed: + * @manager: a #TpAccountManager + * @account: a #TpAccount + * + * Emitted when an account is removed from @manager. + * + * Since: 0.9.0 + */ + signals[ACCOUNT_REMOVED] = g_signal_new ("account-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, TP_TYPE_ACCOUNT); + + /** + * TpAccountManager::account-enabled: + * @manager: a #TpAccountManager + * @account: a #TpAccount + * + * Emitted when an account from @manager is enabled. + * + * Note that the returned #TpAccount @account is not guaranteed to have any + * features pre-prepared, including %TP_ACCOUNT_FEATURE_CORE. + * + * Since: 0.9.0 + */ + signals[ACCOUNT_ENABLED] = g_signal_new ("account-enabled", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, TP_TYPE_ACCOUNT); + + /** + * TpAccountManager::account-disabled. + * @manager: a #TpAccountManager + * @account: a #TpAccount + * + * Emitted when an account from @manager is disabled. + * + * Since: 0.9.0 + */ + signals[ACCOUNT_DISABLED] = g_signal_new ("account-disabled", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, TP_TYPE_ACCOUNT); + + /** + * TpAccountManager::most-available-presence-changed: + * @manager: a #TpAccountManager + * @account: a #TpAccount + * @presence: new presence type + * @status: new status + * @message: new status message + * + * Emitted when the most available presence on @manager changes. + * + * Since: 0.9.0 + */ + signals[MOST_AVAILABLE_PRESENCE_CHANGED] = + g_signal_new ("most-available-presence-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _tp_marshal_VOID__UINT_STRING_STRING, + G_TYPE_NONE, + 3, G_TYPE_UINT, /* Presence type */ + G_TYPE_STRING, /* status */ + G_TYPE_STRING); /* stauts message*/ } /** @@ -143,7 +807,12 @@ tp_account_manager_init_known_interfaces (void) * tp_account_manager_new: * @bus_daemon: Proxy for the D-Bus daemon * - * Convenience function to create a new account manager proxy. + * Convenience function to create a new account manager proxy. The returned + * #TpAccountManager is not guaranteed to be ready on return. + * + * Use tp_account_manager_dup() instead if you want an account manager proxy + * on the starter or session bus (which is almost always the right thing for + * Telepathy). * * Returns: a new reference to an account manager proxy */ @@ -152,14 +821,686 @@ tp_account_manager_new (TpDBusDaemon *bus_daemon) { TpAccountManager *self; - g_return_val_if_fail (bus_daemon != NULL, NULL); + g_return_val_if_fail (TP_IS_DBUS_DAEMON (bus_daemon), NULL); self = TP_ACCOUNT_MANAGER (g_object_new (TP_TYPE_ACCOUNT_MANAGER, - "dbus-daemon", bus_daemon, - "dbus-connection", ((TpProxy *) bus_daemon)->dbus_connection, - "bus-name", TP_ACCOUNT_MANAGER_BUS_NAME, - "object-path", TP_ACCOUNT_MANAGER_OBJECT_PATH, - NULL)); + "dbus-daemon", bus_daemon, + "dbus-connection", ((TpProxy *) bus_daemon)->dbus_connection, + "bus-name", TP_ACCOUNT_MANAGER_BUS_NAME, + "object-path", TP_ACCOUNT_MANAGER_OBJECT_PATH, + NULL)); return self; } + +static gpointer starter_account_manager_proxy = NULL; + +/** + * tp_account_manager_dup: + * + * Returns an account manager proxy on the D-Bus daemon on which this + * process was activated (if it was launched by D-Bus service activation), or + * the session bus (otherwise). + * + * The returned #TpAccountManager is cached; the same #TpAccountManager object + * will be returned by this function repeatedly, as long as at least one + * reference exists. Note that the returned #TpAccountManager is not guaranteed + * to be ready on return. + * + * Returns: an account manager proxy on the starter or session bus, or %NULL + * if it wasn't possible to get a dbus daemon proxy for the + * appropriate bus + * + * Since: 0.9.0 + */ +TpAccountManager * +tp_account_manager_dup (void) +{ + TpDBusDaemon *dbus; + + if (starter_account_manager_proxy != NULL) + return g_object_ref (starter_account_manager_proxy); + + dbus = tp_dbus_daemon_dup (NULL); + + if (dbus == NULL) + return NULL; + + starter_account_manager_proxy = tp_account_manager_new (dbus); + g_assert (starter_account_manager_proxy != NULL); + g_object_add_weak_pointer (starter_account_manager_proxy, + &starter_account_manager_proxy); + + return starter_account_manager_proxy; +} + +static void +_tp_account_manager_account_enabled_cb (TpAccount *account, + GParamSpec *spec, + gpointer manager) +{ + TpAccountManager *self = TP_ACCOUNT_MANAGER (manager); + + if (tp_account_is_enabled (account)) + g_signal_emit (self, signals[ACCOUNT_ENABLED], 0, account); + else + g_signal_emit (self, signals[ACCOUNT_DISABLED], 0, account); +} + +static void +_tp_account_manager_account_presence_changed_cb (TpAccount *account, + TpConnectionPresenceType presence, + const gchar *status, + const gchar *status_message, + gpointer user_data) +{ + TpAccountManager *manager = TP_ACCOUNT_MANAGER (user_data); + TpAccountManagerPrivate *priv = manager->priv; + + if (tp_connection_presence_type_cmp_availability (presence, + priv->most_available_presence) > 0) + { + priv->most_available_account = account; + + priv->most_available_presence = presence; + + g_free (priv->most_available_status); + priv->most_available_status = g_strdup (status); + + g_free (priv->most_available_status_message); + priv->most_available_status_message = g_strdup (status_message); + + goto signal; + } + else if (priv->most_available_account == account) + { + _tp_account_manager_update_most_available_presence (manager); + goto signal; + } + + return; +signal: + g_signal_emit (manager, signals[MOST_AVAILABLE_PRESENCE_CHANGED], 0, + priv->most_available_presence, priv->most_available_status, + priv->most_available_status_message); +} + +static void +_tp_account_manager_account_invalidated_cb (TpProxy *proxy, + guint domain, + gint code, + gchar *message, + gpointer user_data) +{ + TpAccountManager *manager = TP_ACCOUNT_MANAGER (user_data); + TpAccountManagerPrivate *priv = manager->priv; + TpAccount *account = TP_ACCOUNT (proxy); + + /* We only want to deal with accounts being removed here. */ + if (domain != TP_DBUS_ERRORS || code != TP_DBUS_ERROR_OBJECT_REMOVED) + return; + + g_object_ref (account); + g_hash_table_remove (priv->accounts, + tp_proxy_get_object_path (account)); + + g_signal_emit (manager, signals[ACCOUNT_REMOVED], 0, account); + g_object_unref (account); +} + +static void +_tp_account_manager_account_ready_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + TpAccountManager *manager = TP_ACCOUNT_MANAGER (user_data); + TpAccountManagerPrivate *priv = manager->priv; + TpAccount *account = TP_ACCOUNT (source_object); + GSimpleAsyncResult *result; + + if (!tp_account_prepare_finish (account, res, NULL)) + return; + + /* see if there's any pending callbacks for this account */ + result = g_hash_table_lookup (priv->create_results, account); + if (result != NULL) + { + g_simple_async_result_set_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (result), account, NULL); + + g_simple_async_result_complete (result); + + g_hash_table_remove (priv->create_results, account); + g_object_unref (result); + } + + g_signal_connect (account, "notify::enabled", + G_CALLBACK (_tp_account_manager_account_enabled_cb), manager); + + g_signal_connect (account, "presence-changed", + G_CALLBACK (_tp_account_manager_account_presence_changed_cb), manager); + + g_signal_connect (account, "invalidated", + G_CALLBACK (_tp_account_manager_account_invalidated_cb), manager); + + _tp_account_manager_check_core_ready (manager); +} + +/** + * tp_account_manager_ensure_account: + * @manager: a #TpAccountManager + * @path: the object path for an account + * + * Lookup an account in the account manager @manager. If the desired account + * has already been ensured then the same object will be returned, otherwise + * it will create a new #TpAccount and add it to @manager. As a result, if + * @manager thinks that the account doesn't exist, this will still add it to + * @manager to avoid races. Note that the returned #TpAccount is not guaranteed + * to be ready on return. + * + * The caller must keep a ref to the returned object using g_object_ref() if + * it is to be kept. + * + * Returns: a new #TpAccount at @path + * + * Since: 0.9.0 + */ +TpAccount * +tp_account_manager_ensure_account (TpAccountManager *manager, + const gchar *path) +{ + TpAccountManagerPrivate *priv; + TpAccount *account; + GQuark fs[] = { TP_ACCOUNT_FEATURE_CORE, 0 }; + + g_return_val_if_fail (TP_IS_ACCOUNT_MANAGER (manager), NULL); + g_return_val_if_fail (path != NULL, NULL); + + priv = manager->priv; + + account = g_hash_table_lookup (priv->accounts, path); + if (account != NULL) + return account; + + account = tp_account_new (tp_proxy_get_dbus_daemon (manager), path, NULL); + g_hash_table_insert (priv->accounts, g_strdup (path), account); + + tp_account_prepare_async (account, fs, _tp_account_manager_account_ready_cb, + manager); + + return account; +} + +/** + * tp_account_manager_get_valid_accounts: + * @manager: a #TpAccountManager + * + * Returns a newly allocated #GList of valid accounts in @manager. The list + * must be freed with g_list_free() after used. None of the accounts in the + * returned list are guaranteed to be ready. + * + * Note that the #TpAccount<!-- -->s in the returned #GList are not reffed + * before returning from this function. One could ref every item in the list + * like the following example: + * |[ + * GList *accounts; + * account = tp_account_manager_get_valid_accounts (manager); + * g_list_foreach (accounts, (GFunc) g_object_ref, NULL); + * ]| + * + * The list of valid accounts returned is not guaranteed to have been retrieved + * until %TP_ACCOUNT_MANAGER_FEATURE_CORE is prepared + * (tp_account_manager_prepare_async() has returned). Until this feature has + * been prepared, an empty list (%NULL) will be returned. + * + * Returns: a newly allocated #GList of valid accounts in @manager + * + * Since: 0.9.0 + */ +GList * +tp_account_manager_get_valid_accounts (TpAccountManager *manager) +{ + TpAccountManagerPrivate *priv; + GList *ret; + + g_return_val_if_fail (TP_IS_ACCOUNT_MANAGER (manager), NULL); + + priv = manager->priv; + + ret = g_hash_table_get_values (priv->accounts); + + return ret; +} + +/** + * tp_account_manager_set_all_requested_presences: + * @manager: a #TpAccountManager + * @type: a presence type to request + * @status: a status to request + * @message: a status message to request + * + * Iterates through the accounts in @manager and requests the presence + * (@type, @status and @message). Note that the presence requested here is + * merely a request, and if might not be satisfiable. + * + * You can find the most available presence across all accounts by calling + * tp_account_manager_get_most_available_presence(). + * + * Setting a requested presence on all accounts will have no effect + * until tp_account_manager_prepare_async() has finished. + * + * Since: 0.9.0 + */ +void +tp_account_manager_set_all_requested_presences (TpAccountManager *manager, + TpConnectionPresenceType type, + const gchar *status, + const gchar *message) +{ + TpAccountManagerPrivate *priv; + GHashTableIter iter; + gpointer value; + + g_return_if_fail (TP_IS_ACCOUNT_MANAGER (manager)); + + priv = manager->priv; + + DEBUG ("request most available presence, type: %d, status: %s, message: %s", + type, status, message); + + g_hash_table_iter_init (&iter, priv->accounts); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + TpAccount *account = TP_ACCOUNT (value); + + if (tp_account_is_prepared (account, TP_ACCOUNT_FEATURE_CORE)) + tp_account_request_presence_async (account, type, status, message, + NULL, NULL); + } + + /* save the requested presence, to use it in case we create new accounts or + * some accounts become ready. */ + priv->requested_presence = type; + + if (tp_strdiff (priv->requested_status, status)) + { + g_free (priv->requested_status); + priv->requested_status = g_strdup (status); + } + + if (tp_strdiff (priv->requested_status_message, message)) + { + g_free (priv->requested_status_message); + priv->requested_status_message = g_strdup (message); + } +} + +/** + * tp_account_manager_get_most_available_presence: + * @manager: a #TpAccountManager + * @status: a string to fill with the actual status + * @message: a string to fill with the actual status message + * + * Gets the most available presence over all accounts in @manager. This + * function does not average presences across all accounts, but it merely + * finds the "most available" presence. As a result, there is a guarantee + * that there exists at least one account in @manager with the returned + * presence. + * + * If no accounts are enabled or valid the output will be + * (%TP_CONNECTION_PRESENCE_TYPE_OFFLINE, "offline", ""). + * + * The return value of this function is not guaranteed to have been retrieved + * until tp_account_manager_prepare_async() has finished; until then, the + * value will be the same as if no accounts are enabled or valid. + * + * Returns: the most available presence across all accounts + * + * Since: 0.9.0 + */ + +TpConnectionPresenceType +tp_account_manager_get_most_available_presence (TpAccountManager *manager, + gchar **status, + gchar **message) +{ + TpAccountManagerPrivate *priv; + + g_return_val_if_fail (TP_IS_ACCOUNT_MANAGER (manager), + TP_CONNECTION_PRESENCE_TYPE_UNSET); + + priv = manager->priv; + + if (status != NULL) + *status = g_strdup (priv->most_available_status); + + if (message != NULL) + *message = g_strdup (priv->most_available_status_message); + + return priv->most_available_presence; +} + +static void +_tp_account_manager_created_cb (TpAccountManager *proxy, + const gchar *account_path, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpAccountManager *manager = TP_ACCOUNT_MANAGER (weak_object); + TpAccountManagerPrivate *priv = manager->priv; + GSimpleAsyncResult *my_res = user_data; + TpAccount *account; + + if (error != NULL) + { + g_simple_async_result_set_from_error (my_res, (GError *) error); + g_simple_async_result_complete (my_res); + g_object_unref (my_res); + + return; + } + + account = tp_account_manager_ensure_account (manager, account_path); + + g_hash_table_insert (priv->create_results, account, my_res); +} + +/** + * tp_account_manager_create_account_async: + * @manager: a #TpAccountManager + * @connection_manager: the name of a connection manager + * @protocol: the name of a protocol + * @display_name: the display name for the account + * @parameters: parameters for the new account + * @properties: properties for the new account + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous create of an account on the account manager + * @manager. When the operation is finished, @callback will be called. You can + * then call tp_account_manager_create_account_finish() to get the result of + * the operation. + * + * @callback will only be called when the newly created #TpAccount has the + * %TP_ACCOUNT_FEATURE_CORE feature ready on it, so when calling + * tp_account_manager_create_account_finish(), one can guarantee this feature + * will be ready. + * + * Since: 0.9.0 + */ +void +tp_account_manager_create_account_async (TpAccountManager *manager, + const gchar *connection_manager, + const gchar *protocol, + const gchar *display_name, + GHashTable *parameters, + GHashTable *properties, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + + g_return_if_fail (TP_IS_ACCOUNT_MANAGER (manager)); + g_return_if_fail (connection_manager != NULL); + g_return_if_fail (protocol != NULL); + g_return_if_fail (display_name != NULL); + g_return_if_fail (parameters != NULL); + g_return_if_fail (properties != NULL); + g_return_if_fail (TP_IS_ACCOUNT_MANAGER (manager)); + + res = g_simple_async_result_new (G_OBJECT (manager), callback, user_data, + tp_account_manager_create_account_finish); + + tp_cli_account_manager_call_create_account (manager, + -1, connection_manager, protocol, display_name, parameters, + properties, _tp_account_manager_created_cb, res, NULL, + G_OBJECT (manager)); +} + +/** + * tp_account_manager_create_account_finish: + * @manager: a #TpAccountManager + * @result: a #GAsyncResult + * @error: a #GError to be filled + * + * Finishes an async create account operation, and returns a new #TpAccount + * object, with the %TP_ACCOUNT_FEATURE_CORE feature ready on it. + * + * Returns: a new #TpAccount which was just created on success, otherwise + * %NULL + * + * Since: 0.9.0 + */ +TpAccount * +tp_account_manager_create_account_finish (TpAccountManager *manager, + GAsyncResult *result, + GError **error) +{ + TpAccount *retval; + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT_MANAGER (manager), NULL); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), + error)) + return NULL; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (manager), tp_account_manager_create_account_finish), NULL); + + retval = TP_ACCOUNT (g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (result))); + + return retval; +} + +/** + * tp_account_manager_is_prepared: + * @manager: a #TpAccountManager + * @feature: a feature which is required + * @error: a #GError to fill + * + * <!-- --> + * + * Returns: %TRUE whether @feature is ready on @manager, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_manager_is_prepared (TpAccountManager *manager, + GQuark feature) +{ + TpAccountManagerFeature *f; + + g_return_val_if_fail (TP_IS_ACCOUNT_MANAGER (manager), FALSE); + + if (tp_proxy_get_invalidated (manager) != NULL) + return FALSE; + + f = _tp_account_manager_get_feature (manager, feature); + + if (f == NULL) + return FALSE; + + return f->ready; +} + +/** + * tp_account_manager_prepare_async: + * @manager: a #TpAccountManager + * @features: a 0-terminated list of features, or %NULL + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous preparation of @manager with the features specified + * by @features. When the operation is finished, @callback will be called. You + * can then call tp_account_manager_prepare_finish() to get the result of the + * operation. + * + * If @features is %NULL, then @callback will be called when the implied + * %TP_ACCOUNT_FEATURE_CORE feature is ready. + * + * If %NULL is given to @callback, then no callback will be called when the + * operation is finished. Instead, it will simply set @features on @manager. + * Note that if @callback is %NULL, then @user_data must also be %NULL. + * + * Since: 0.9.0 + */ +void +tp_account_manager_prepare_async (TpAccountManager *manager, + const GQuark *features, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TpAccountManagerPrivate *priv; + GSimpleAsyncResult *result; + const GQuark *f; + + g_return_if_fail (TP_IS_ACCOUNT_MANAGER (manager)); + + priv = manager->priv; + + /* In this object, there are no features which are activatable (core is + * forced on you). They'd be activated here though. */ + + for (f = features; f != NULL && *f != 0; f++) + { + /* Only add features to requested which exist on this object and are not + * already in the list. */ + if (_tp_account_manager_get_feature (manager, *f) != NULL + && _tp_account_manager_feature_in_array (*f, priv->requested_features)) + g_array_append_val (priv->requested_features, *f); + } + + if (callback == NULL) + return; + + result = g_simple_async_result_new (G_OBJECT (manager), + callback, user_data, tp_account_manager_prepare_finish); + + if (_tp_account_manager_check_features (manager, features)) + { + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + } + else + { + TpAccountManagerFeatureCallback *cb; + + cb = g_slice_new0 (TpAccountManagerFeatureCallback); + cb->result = result; + cb->features = features; + priv->callbacks = g_list_prepend (priv->callbacks, cb); + } +} + +/** + * tp_account_manager_prepare_finish: + * @manager: a #TpAccountManager + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async preparation of the account manager @manager. + * + * Returns: %TRUE if the preparation was successful, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_manager_prepare_finish (TpAccountManager *manager, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT_MANAGER (manager), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (manager),tp_account_manager_prepare_finish), FALSE); + + return TRUE; +} + +/** + * _tp_account_manager_get_requested_features: + * @manager: a #TpAccountManager + * + * <!-- --> + * + * Returns: a 0-terminated list of requested features on @manager + * + * Since: 0.9.0 + */ +const GQuark * +_tp_account_manager_get_requested_features (TpAccountManager *manager) +{ + g_return_val_if_fail (TP_IS_ACCOUNT_MANAGER (manager), NULL); + + return (const GQuark *) manager->priv->requested_features->data; +} + +/** + * _tp_account_manager_get_actual_features: + * @manager: a #TpAccountManager + * + * <!-- --> + * + * Returns: a 0-terminated list of actual features on @manager + * + * Since: 0.9.0 + */ +const GQuark * +_tp_account_manager_get_actual_features (TpAccountManager *manager) +{ + g_return_val_if_fail (TP_IS_ACCOUNT_MANAGER (manager), NULL); + + return (const GQuark *) manager->priv->actual_features->data; +} + +/** + * _tp_account_manager_get_missing_features: + * @manager: a #TpAccountManager + * + * <!-- --> + * + * Returns: a 0-terminated list of missing features on @manager + * + * Since: 0.9.0 + */ +const GQuark * +_tp_account_manager_get_missing_features (TpAccountManager *manager) +{ + g_return_val_if_fail (TP_IS_ACCOUNT_MANAGER (manager), NULL); + + return (const GQuark *) manager->priv->missing_features->data; +} + +/** + * tp_account_manager_enable_restart: + * @manager: a #TpAccountManager + * + * Enable autostarting the account manager D-Bus service. This means + * that the account manager will be restarted if it disappears from + * the bus. + */ +void +tp_account_manager_enable_restart (TpAccountManager *manager) +{ + g_return_if_fail (TP_IS_ACCOUNT_MANAGER (manager)); + + tp_dbus_daemon_watch_name_owner (tp_proxy_get_dbus_daemon (manager), + TP_ACCOUNT_MANAGER_BUS_NAME, _tp_account_manager_name_owner_cb, + manager, NULL); + + _tp_account_manager_start_mc5 (tp_proxy_get_dbus_daemon (manager)); +} diff --git a/telepathy-glib/account-manager.h b/telepathy-glib/account-manager.h index f96321ac7..f117cd2f4 100644 --- a/telepathy-glib/account-manager.h +++ b/telepathy-glib/account-manager.h @@ -22,6 +22,7 @@ #ifndef TP_ACCOUNT_MANAGER_H #define TP_ACCOUNT_MANAGER_H +#include <telepathy-glib/account.h> #include <telepathy-glib/proxy.h> #include <telepathy-glib/dbus.h> @@ -63,10 +64,47 @@ GType tp_account_manager_get_type (void); (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TYPE_ACCOUNT_MANAGER, \ TpAccountManagerClass)) +#define TP_ACCOUNT_MANAGER_FEATURE_CORE \ + tp_account_manager_get_feature_quark_core () + +GQuark tp_account_manager_get_feature_quark_core (void) G_GNUC_CONST; + TpAccountManager *tp_account_manager_new (TpDBusDaemon *bus_daemon); +TpAccountManager *tp_account_manager_dup (void); + void tp_account_manager_init_known_interfaces (void); +TpAccount *tp_account_manager_ensure_account (TpAccountManager *manager, + const gchar *path); + +GList *tp_account_manager_get_valid_accounts (TpAccountManager *manager); + +void tp_account_manager_set_all_requested_presences (TpAccountManager *manager, + TpConnectionPresenceType type, const gchar *status, const gchar *message); + +TpConnectionPresenceType tp_account_manager_get_most_available_presence ( + TpAccountManager *manager, gchar **status, gchar **message); + +void tp_account_manager_create_account_async (TpAccountManager *manager, + const gchar *connection_manager, const gchar *protocol, + const gchar *display_name, GHashTable *parameters, GHashTable *properties, + GAsyncReadyCallback callback, gpointer user_data); + +TpAccount * tp_account_manager_create_account_finish ( + TpAccountManager *manager, GAsyncResult *result, GError **error); + +gboolean tp_account_manager_is_prepared (TpAccountManager *manager, + GQuark feature); + +void tp_account_manager_prepare_async (TpAccountManager *manager, + const GQuark *features, GAsyncReadyCallback callback, gpointer user_data); + +gboolean tp_account_manager_prepare_finish (TpAccountManager *manager, + GAsyncResult *result, GError **error); + +void tp_account_manager_enable_restart (TpAccountManager *manager); + G_END_DECLS #include <telepathy-glib/_gen/tp-cli-account-manager.h> diff --git a/telepathy-glib/account.c b/telepathy-glib/account.c index 3285c5991..830e29534 100644 --- a/telepathy-glib/account.c +++ b/telepathy-glib/account.c @@ -19,17 +19,23 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <string.h> + +#include "telepathy-glib/account-internal.h" #include "telepathy-glib/account.h" #include <telepathy-glib/dbus.h> #include <telepathy-glib/defs.h> #include <telepathy-glib/errors.h> +#include <telepathy-glib/gtypes.h> #include <telepathy-glib/interfaces.h> #include <telepathy-glib/proxy-subclass.h> +#include <telepathy-glib/util.h> #define DEBUG_FLAG TP_DEBUG_ACCOUNTS #include "telepathy-glib/debug-internal.h" +#include "telepathy-glib/_gen/signals-marshal.h" #include "telepathy-glib/_gen/tp-cli-account-body.h" /** @@ -56,19 +62,9 @@ * with the domain %TP_DBUS_ERRORS and the error code * %TP_DBUS_ERROR_OBJECT_REMOVED. * - * This proxy is usable but very incomplete: accessors for the - * Account's D-Bus properties will be added in a later version of - * telepathy-glib, along with a mechanism similar to - * tp_connection_call_when_ready(). - * - * Most operations performed on an Account are done via D-Bus properties. - * Until convenience methods for this are implemented, use of the generic - * tp_cli_dbus_properties_call_get_all() and tp_cli_dbus_properties_call_set() - * methods is recommended. - * - * Other useful auto-generated method wrappers on an Account include - * tp_cli_account_call_remove(), tp_cli_account_call_update_parameters() and - * tp_cli_account_call_reconnect(). + * One can connect to the #GObject::notify signal to get change notifications + * for many of the properties on this object. Refer to each property's + * documentation for whether it can be used in this way. * * Since: 0.7.32 */ @@ -80,56 +76,728 @@ */ struct _TpAccountPrivate { - gpointer dummy; + gboolean dispose_has_run; + + TpConnection *connection; + gchar *connection_object_path; + + TpConnectionStatus connection_status; + TpConnectionStatusReason reason; + + TpConnectionPresenceType presence; + gchar *status; + gchar *message; + + TpConnectionPresenceType requested_presence; + gchar *requested_status; + gchar *requested_message; + + gboolean connect_automatically; + gboolean has_been_online; + + gchar *nickname; + + gboolean enabled; + gboolean valid; + gboolean removed; + + gchar *cm_name; + gchar *proto_name; + gchar *icon_name; + + gchar *display_name; + + GHashTable *parameters; + + /* Features. */ + GList *features; + GList *callbacks; + GArray *requested_features; + GArray *actual_features; + GArray *missing_features; }; +typedef struct { + GQuark name; + gboolean ready; +} TpAccountFeature; + +typedef struct { + GSimpleAsyncResult *result; + const GQuark *features; +} TpAccountFeatureCallback; + G_DEFINE_TYPE (TpAccount, tp_account, TP_TYPE_PROXY); +/* signals */ +enum { + STATUS_CHANGED, + PRESENCE_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +/* properties */ +enum { + PROP_ENABLED = 1, + PROP_CURRENT_PRESENCE_TYPE, + PROP_CURRENT_STATUS, + PROP_CURRENT_STATUS_MESSAGE, + PROP_CONNECTION_STATUS, + PROP_CONNECTION_STATUS_REASON, + PROP_CONNECTION, + PROP_DISPLAY_NAME, + PROP_CONNECTION_MANAGER, + PROP_PROTOCOL, + PROP_ICON_NAME, + PROP_CONNECT_AUTOMATICALLY, + PROP_HAS_BEEN_ONLINE, + PROP_VALID, + PROP_REQUESTED_PRESENCE_TYPE, + PROP_REQUESTED_STATUS, + PROP_REQUESTED_STATUS_MESSAGE, + PROP_NICKNAME +}; + +/** + * TP_ACCOUNT_FEATURE_CORE: + * + * Expands to a call to a function that returns a quark for the "core" feature + * on a #TpAccount. + * + * When this feature is prepared, the basic properties of the Account have + * been retrieved and are available for use, and change-notification has been + * set up. + * + * One can ask for a feature to be prepared using the + * tp_account_prepare_async() function, and waiting for it to callback. + * + * Since: 0.9.0 + */ + +/** + * tp_account_get_feature_quark_core: + * + * <!-- --> + * + * Returns: the quark used for representing the core feature of a + * #TpAccount + * + * Since: 0.9.0 + */ +GQuark +tp_account_get_feature_quark_core (void) +{ + return g_quark_from_static_string ("tp-account-feature-core"); +} + +static const GQuark * +_tp_account_get_known_features (void) +{ + static GQuark features[1] = { 0 }; + + if (G_UNLIKELY (features[0] == 0)) + { + features[0] = TP_ACCOUNT_FEATURE_CORE; + } + + return features; +} + static void tp_account_init (TpAccount *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_ACCOUNT, TpAccountPrivate); + + self->priv->connection_status = TP_CONNECTION_STATUS_DISCONNECTED; +} + +static TpAccountFeature * +_tp_account_get_feature (TpAccount *self, + GQuark feature) +{ + TpAccountPrivate *priv = self->priv; + GList *l; + + for (l = priv->features; l != NULL; l = l->next) + { + TpAccountFeature *f = l->data; + + if (f->name == feature) + return f; + } + + return NULL; +} + +static gboolean +_tp_account_feature_in_array (GQuark feature, + const GArray *array) +{ + const GQuark *c = (const GQuark *) array->data; + + for (; *c != 0; c++) + { + if (*c == feature) + return TRUE; + } + + return FALSE; +} + +static gboolean +_tp_account_check_features (TpAccount *self, + const GQuark *features) +{ + const GQuark *f; + TpAccountFeature *feat; + + for (f = features; f != NULL && *f != 0; f++) + { + feat = _tp_account_get_feature (self, *f); + + /* features which are NULL (ie. don't exist) are always considered as + * being ready, except in _is_prepared when it doesn't make sense to + * return TRUE. */ + if (feat != NULL && !feat->ready) + return FALSE; + } + + /* Special-case core: no other feature is ready unless core itself is + * ready. */ + feat = _tp_account_get_feature (self, TP_ACCOUNT_FEATURE_CORE); + if (!feat->ready) + return FALSE; + + return TRUE; +} + +static void +_tp_account_become_ready (TpAccount *self, + GQuark feature) +{ + TpAccountPrivate *priv = self->priv; + TpAccountFeature *f = NULL; + GList *l, *remove = NULL; + + f = _tp_account_get_feature (self, feature); + + g_assert (f != NULL); + + if (f->ready) + return; + + f->ready = TRUE; + + if (!_tp_account_feature_in_array (feature, priv->actual_features)) + g_array_append_val (priv->actual_features, feature); + + /* First, find which callbacks are satisfied and add those items + * from the remove list. */ + l = priv->callbacks; + while (l != NULL) + { + GList *c = l; + TpAccountFeatureCallback *cb = l->data; + + l = l->next; + + if (_tp_account_check_features (self, cb->features)) + { + priv->callbacks = g_list_remove_link (priv->callbacks, c); + remove = g_list_concat (c, remove); + } + } + + /* Next, complete these callbacks */ + for (l = remove; l != NULL; l = l->next) + { + TpAccountFeatureCallback *cb = l->data; + + g_simple_async_result_complete (cb->result); + g_object_unref (cb->result); + g_slice_free (TpAccountFeatureCallback, cb); + } + + g_list_free (remove); } static void -tp_account_removed_cb (TpAccount *self, +_tp_account_removed_cb (TpAccount *self, gpointer unused G_GNUC_UNUSED, GObject *object G_GNUC_UNUSED) { GError e = { TP_DBUS_ERRORS, TP_DBUS_ERROR_OBJECT_REMOVED, - "Account removed" }; + "Account removed" }; + + if (self->priv->removed) + return; + + self->priv->removed = TRUE; tp_proxy_invalidate ((TpProxy *) self, &e); } static void -tp_account_constructed (GObject *object) +_tp_account_free_connection (TpAccount *account) +{ + TpAccountPrivate *priv = account->priv; + + if (priv->connection == NULL) + return; + + g_object_unref (priv->connection); + priv->connection = NULL; +} + +static void +_tp_account_set_connection (TpAccount *account, + const gchar *path) +{ + TpAccountPrivate *priv = account->priv; + + if (priv->connection != NULL) + { + const gchar *current; + + current = tp_proxy_get_object_path (priv->connection); + if (!tp_strdiff (current, path)) + return; + } + + _tp_account_free_connection (account); + + if (tp_strdiff ("/", path)) + { + GError *error = NULL; + priv->connection = tp_connection_new (tp_proxy_get_dbus_daemon (account), + NULL, path, &error); + + if (priv->connection == NULL) + { + DEBUG ("Failed to create a new TpConnection: %s", + error->message); + g_error_free (error); + } + } + + g_object_notify (G_OBJECT (account), "connection"); +} + +static void +_tp_account_update (TpAccount *account, + GHashTable *properties) +{ + TpAccountPrivate *priv = account->priv; + GValueArray *arr; + TpConnectionStatus old_s = priv->connection_status; + gboolean presence_changed = FALSE; + + if (g_hash_table_lookup (properties, "ConnectionStatus") != NULL) + priv->connection_status = + tp_asv_get_uint32 (properties, "ConnectionStatus", NULL); + + + if (g_hash_table_lookup (properties, "ConnectionStatusReason") != NULL) + priv->reason = tp_asv_get_int32 (properties, + "ConnectionStatusReason", NULL); + + if (g_hash_table_lookup (properties, "CurrentPresence") != NULL) + { + presence_changed = TRUE; + arr = tp_asv_get_boxed (properties, "CurrentPresence", + TP_STRUCT_TYPE_SIMPLE_PRESENCE); + priv->presence = g_value_get_uint (g_value_array_get_nth (arr, 0)); + + g_free (priv->status); + priv->status = g_value_dup_string (g_value_array_get_nth (arr, 1)); + + g_free (priv->message); + priv->message = g_value_dup_string (g_value_array_get_nth (arr, 2)); + } + + if (g_hash_table_lookup (properties, "RequestedPresence") != NULL) + { + arr = tp_asv_get_boxed (properties, "RequestedPresence", + TP_STRUCT_TYPE_SIMPLE_PRESENCE); + priv->requested_presence = + g_value_get_uint (g_value_array_get_nth (arr, 0)); + + g_free (priv->requested_status); + priv->requested_status = + g_value_dup_string (g_value_array_get_nth (arr, 1)); + + g_free (priv->requested_message); + priv->requested_message = + g_value_dup_string (g_value_array_get_nth (arr, 2)); + } + + if (g_hash_table_lookup (properties, "DisplayName") != NULL) + { + gchar *old = priv->display_name; + + priv->display_name = + g_strdup (tp_asv_get_string (properties, "DisplayName")); + + if (tp_strdiff (old, priv->display_name)) + g_object_notify (G_OBJECT (account), "display-name"); + + g_free (old); + } + + if (g_hash_table_lookup (properties, "Nickname") != NULL) + { + gchar *old = priv->nickname; + + priv->nickname = g_strdup (tp_asv_get_string (properties, "Nickname")); + + if (tp_strdiff (old, priv->nickname)) + g_object_notify (G_OBJECT (account), "nickname"); + + g_free (old); + } + + if (g_hash_table_lookup (properties, "Icon") != NULL) + { + const gchar *icon_name; + gchar *old = priv->icon_name; + + icon_name = tp_asv_get_string (properties, "Icon"); + + if (icon_name == NULL || icon_name[0] == '\0') + priv->icon_name = g_strdup_printf ("im-%s", priv->proto_name); + else + priv->icon_name = g_strdup (icon_name); + + if (tp_strdiff (old, priv->icon_name)) + g_object_notify (G_OBJECT (account), "icon-name"); + + g_free (old); + } + + if (g_hash_table_lookup (properties, "Enabled") != NULL) + { + gboolean enabled = tp_asv_get_boolean (properties, "Enabled", NULL); + if (priv->enabled != enabled) + { + priv->enabled = enabled; + g_object_notify (G_OBJECT (account), "enabled"); + } + } + + if (g_hash_table_lookup (properties, "Valid") != NULL) + { + gboolean old = priv->valid; + + priv->valid = tp_asv_get_boolean (properties, "Valid", NULL); + + if (old != priv->valid) + g_object_notify (G_OBJECT (account), "valid"); + } + + if (g_hash_table_lookup (properties, "Parameters") != NULL) + { + GHashTable *parameters; + + parameters = tp_asv_get_boxed (properties, "Parameters", + TP_HASH_TYPE_STRING_VARIANT_MAP); + + if (priv->parameters != NULL) + g_hash_table_unref (priv->parameters); + + priv->parameters = g_boxed_copy (TP_HASH_TYPE_STRING_VARIANT_MAP, + parameters); + } + + if (priv->connection_status != old_s) + { + g_signal_emit (account, signals[STATUS_CHANGED], 0, + old_s, priv->connection_status, priv->reason, NULL, NULL); + + g_object_notify (G_OBJECT (account), "connection-status"); + g_object_notify (G_OBJECT (account), "connection-status-reason"); + } + + if (presence_changed) + { + g_signal_emit (account, signals[PRESENCE_CHANGED], 0, + priv->presence, priv->status, priv->message); + g_object_notify (G_OBJECT (account), "current-presence-type"); + g_object_notify (G_OBJECT (account), "current-status"); + g_object_notify (G_OBJECT (account), "current-status-message"); + } + + if (g_hash_table_lookup (properties, "Connection") != NULL) + { + g_free (priv->connection_object_path); + + priv->connection_object_path = + g_strdup (tp_asv_get_object_path (properties, "Connection")); + + if (priv->connection != NULL) + { + if (tp_strdiff (priv->connection_object_path, + tp_proxy_get_object_path (priv->connection))) + _tp_account_free_connection (account); + } + } + + if (g_hash_table_lookup (properties, "ConnectAutomatically") != NULL) + { + gboolean old = priv->connect_automatically; + + priv->connect_automatically = + tp_asv_get_boolean (properties, "ConnectAutomatically", NULL); + + if (old != priv->connect_automatically) + g_object_notify (G_OBJECT (account), "connect-automatically"); + } + + if (g_hash_table_lookup (properties, "HasBeenOnline") != NULL) + { + gboolean old = priv->has_been_online; + + priv->has_been_online = + tp_asv_get_boolean (properties, "HasBeenOnline", NULL); + + if (old != priv->has_been_online) + g_object_notify (G_OBJECT (account), "has-been-online"); + } + + _tp_account_become_ready (account, TP_ACCOUNT_FEATURE_CORE); +} + +static void +_tp_account_properties_changed (TpAccount *proxy, + GHashTable *properties, + gpointer user_data, + GObject *weak_object) +{ + TpAccount *self = TP_ACCOUNT (weak_object); + + if (!tp_account_is_prepared (self, TP_ACCOUNT_FEATURE_CORE)) + return; + + _tp_account_update (self, properties); +} + +static void +_tp_account_got_all_cb (TpProxy *proxy, + GHashTable *properties, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpAccount *self = TP_ACCOUNT (weak_object); + + DEBUG ("Got whole set of properties for %s", + tp_proxy_get_object_path (self)); + + if (error != NULL) + { + DEBUG ("Failed to get the initial set of account properties: %s", + error->message); + return; + } + + _tp_account_update (self, properties); +} + +static void +_tp_account_constructed (GObject *object) { TpAccount *self = TP_ACCOUNT (object); + TpAccountPrivate *priv = self->priv; void (*chain_up) (GObject *) = ((GObjectClass *) tp_account_parent_class)->constructed; GError *error = NULL; TpProxySignalConnection *sc; + guint i; + const GQuark *known_features; if (chain_up != NULL) chain_up (object); g_return_if_fail (tp_proxy_get_dbus_daemon (self) != NULL); - sc = tp_cli_account_connect_to_removed (self, tp_account_removed_cb, + priv->features = NULL; + priv->callbacks = NULL; + priv->requested_features = g_array_new (TRUE, FALSE, sizeof (GQuark)); + priv->actual_features = g_array_new (TRUE, FALSE, sizeof (GQuark)); + priv->missing_features = g_array_new (TRUE, FALSE, sizeof (GQuark)); + + known_features = _tp_account_get_known_features (); + + /* Fill features list */ + for (i = 0; i < G_N_ELEMENTS (known_features); i++) + { + TpAccountFeature *feature; + feature = g_slice_new0 (TpAccountFeature); + feature->name = known_features[i]; + feature->ready = FALSE; + priv->features = g_list_prepend (priv->features, feature); + } + + sc = tp_cli_account_connect_to_removed (self, _tp_account_removed_cb, NULL, NULL, NULL, &error); if (sc == NULL) { g_critical ("Couldn't connect to Removed: %s", error->message); g_error_free (error); - g_assert_not_reached (); - return; + } + + tp_account_parse_object_path (tp_proxy_get_object_path (self), + &(priv->cm_name), &(priv->proto_name), NULL, NULL); + + priv->icon_name = g_strdup_printf ("im-%s", priv->proto_name); + + tp_cli_account_connect_to_account_property_changed (self, + _tp_account_properties_changed, NULL, NULL, object, NULL); + + tp_cli_dbus_properties_call_get_all (self, -1, TP_IFACE_ACCOUNT, + _tp_account_got_all_cb, NULL, NULL, G_OBJECT (self)); +} + +static void +_tp_account_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TpAccount *self = TP_ACCOUNT (object); + + switch (prop_id) + { + case PROP_ENABLED: + g_value_set_boolean (value, self->priv->enabled); + break; + case PROP_CURRENT_PRESENCE_TYPE: + g_value_set_uint (value, self->priv->presence); + break; + case PROP_CURRENT_STATUS: + g_value_set_string (value, self->priv->status); + break; + case PROP_CURRENT_STATUS_MESSAGE: + g_value_set_string (value, self->priv->message); + break; + case PROP_CONNECTION_STATUS: + g_value_set_uint (value, self->priv->connection_status); + break; + case PROP_CONNECTION_STATUS_REASON: + g_value_set_uint (value, self->priv->reason); + break; + case PROP_CONNECTION: + g_value_set_object (value, + tp_account_get_connection (self)); + break; + case PROP_DISPLAY_NAME: + g_value_set_string (value, + tp_account_get_display_name (self)); + break; + case PROP_CONNECTION_MANAGER: + g_value_set_string (value, self->priv->cm_name); + break; + case PROP_PROTOCOL: + g_value_set_string (value, self->priv->proto_name); + break; + case PROP_ICON_NAME: + g_value_set_string (value, self->priv->icon_name); + break; + case PROP_CONNECT_AUTOMATICALLY: + g_value_set_boolean (value, self->priv->connect_automatically); + break; + case PROP_HAS_BEEN_ONLINE: + g_value_set_boolean (value, self->priv->has_been_online); + break; + case PROP_VALID: + g_value_set_boolean (value, self->priv->valid); + break; + case PROP_REQUESTED_PRESENCE_TYPE: + g_value_set_uint (value, self->priv->requested_presence); + break; + case PROP_REQUESTED_STATUS: + g_value_set_string (value, self->priv->requested_status); + break; + case PROP_REQUESTED_STATUS_MESSAGE: + g_value_set_string (value, self->priv->requested_message); + break; + case PROP_NICKNAME: + g_value_set_string (value, self->priv->nickname); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } } static void +_tp_account_dispose (GObject *object) +{ + TpAccount *self = TP_ACCOUNT (object); + TpAccountPrivate *priv = self->priv; + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + _tp_account_free_connection (self); + + /* release any references held by the object here */ + if (G_OBJECT_CLASS (tp_account_parent_class)->dispose != NULL) + G_OBJECT_CLASS (tp_account_parent_class)->dispose (object); +} + +static void +_tp_account_feature_free (gpointer data, + gpointer user_data) +{ + g_slice_free (TpAccountFeature, data); +} + +static void +_tp_account_finalize (GObject *object) +{ + TpAccount *self = TP_ACCOUNT (object); + TpAccountPrivate *priv = self->priv; + + g_free (priv->connection_object_path); + g_free (priv->status); + g_free (priv->message); + g_free (priv->requested_status); + g_free (priv->requested_message); + + g_free (priv->nickname); + + g_free (priv->cm_name); + g_free (priv->proto_name); + g_free (priv->icon_name); + g_free (priv->display_name); + + g_list_foreach (priv->features, _tp_account_feature_free, NULL); + g_list_free (priv->features); + priv->features = NULL; + + /* GSimpleAsyncResult keeps a ref to the source GObject, so this list + * should be empty. */ + g_assert_cmpuint (g_list_length (priv->callbacks), ==, 0); + + g_list_free (priv->callbacks); + priv->callbacks = NULL; + + g_array_free (priv->requested_features, TRUE); + g_array_free (priv->actual_features, TRUE); + g_array_free (priv->missing_features, TRUE); + + /* free any data held directly by the object here */ + if (G_OBJECT_CLASS (tp_account_parent_class)->finalize != NULL) + G_OBJECT_CLASS (tp_account_parent_class)->finalize (object); +} + +static void tp_account_class_init (TpAccountClass *klass) { TpProxyClass *proxy_class = (TpProxyClass *) klass; @@ -137,7 +805,429 @@ tp_account_class_init (TpAccountClass *klass) g_type_class_add_private (klass, sizeof (TpAccountPrivate)); - object_class->constructed = tp_account_constructed; + object_class->constructed = _tp_account_constructed; + object_class->get_property = _tp_account_get_property; + object_class->dispose = _tp_account_dispose; + object_class->finalize = _tp_account_finalize; + + /** + * TpAccount:enabled: + * + * Whether this account is enabled or not. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is FALSE. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_ENABLED, + g_param_spec_boolean ("enabled", + "Enabled", + "Whether this account is enabled or not", + FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:current-presence-type: + * + * The account connection's current presence type. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %TP_CONNECTION_PRESENCE_TYPE_UNSET. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_CURRENT_PRESENCE_TYPE, + g_param_spec_uint ("current-presence-type", + "Presence", + "The account connection's current presence type", + 0, + NUM_TP_CONNECTION_PRESENCE_TYPES, + TP_CONNECTION_PRESENCE_TYPE_UNSET, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:current-status: + * + * The current Status string of the account. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %NULL. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_CURRENT_STATUS, + g_param_spec_string ("current-status", + "Current Status", + "The Status string of the account", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount: current-status-message: + * + * The current status message message of the account. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %NULL. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_CURRENT_STATUS_MESSAGE, + g_param_spec_string ("current-status-message", + "current-status-message", + "The Status message string of the account", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:connection-status: + * + * The account's connection status type. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %TP_CONNECTION_STATUS_DISCONNECTED. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_CONNECTION_STATUS, + g_param_spec_uint ("connection-status", + "ConnectionStatus", + "The account's connection status type", + 0, + NUM_TP_CONNECTION_STATUSES, + TP_CONNECTION_STATUS_DISCONNECTED, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:connection-status-reason: + * + * The account's connection status reason. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_CONNECTION_STATUS_REASON, + g_param_spec_uint ("connection-status-reason", + "ConnectionStatusReason", + "The account's connection status reason", + 0, + NUM_TP_CONNECTION_STATUS_REASONS, + TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:connection: + * + * The connection of the account, or NULL if account is offline. + * It is not guaranteed that the returned #TpConnection object is ready. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %NULL. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_CONNECTION, + g_param_spec_object ("connection", + "Connection", + "The account's connection", + TP_TYPE_CONNECTION, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:display-name: + * + * The account's display name, from the DisplayName property. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %NULL. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_DISPLAY_NAME, + g_param_spec_string ("display-name", + "DisplayName", + "The account's display name", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:connection-manager: + * + * The account's connection manager name. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_CONNECTION_MANAGER, + g_param_spec_string ("connection-manager", + "Connection manager", + "The account's connection manager name", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:protocol: + * + * The account's protocol name. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_PROTOCOL, + g_param_spec_string ("protocol", + "Protocol", + "The account's protocol name", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:icon-name: + * + * The account's icon name. To change this propery, use + * tp_account_set_icon_name_async(). + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %NULL. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_ICON_NAME, + g_param_spec_string ("icon-name", + "Icon", + "The account's icon name", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:connect-automatically: + * + * Whether the account should connect automatically or not. To change this + * property, use tp_account_set_connect_automatically_async(). + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %FALSE. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_CONNECT_AUTOMATICALLY, + g_param_spec_boolean ("connect-automatically", + "ConnectAutomatically", + "Whether this account should connect automatically or not", + FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:has-been-online: + * + * Whether this account has been online or not. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %FALSE. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_HAS_BEEN_ONLINE, + g_param_spec_boolean ("has-been-online", + "HasBeenOnline", + "Whether this account has been online or not", + FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:valid: + * + * Whether this account is valid. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %FALSE. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_VALID, + g_param_spec_boolean ("valid", + "Valid", + "Whether this account is valid", + FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:requested-presence-type: + * + * The account's requested presence type. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_REQUESTED_PRESENCE_TYPE, + g_param_spec_uint ("requested-presence-type", + "RequestedPresence", + "The account's requested presence type", + 0, + NUM_TP_CONNECTION_PRESENCE_TYPES, + TP_CONNECTION_PRESENCE_TYPE_UNSET, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:requested-status: + * + * The requested Status string of the account. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_REQUESTED_STATUS, + g_param_spec_string ("requested-status", + "RequestedStatus", + "The account's requested status string", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:requested-status-message: + * + * The requested status message message of the account. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_REQUESTED_STATUS_MESSAGE, + g_param_spec_string ("requested-status-message", + "RequestedStatusMessage", + "The requested Status message string of the account", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount:nickname + * + * The nickname that should be set for the user on this account. + * + * One can receive change notifications on this property by connecting + * to the #GObject::notify signal and using this property as the signal + * detail. + * + * This is not guaranteed to have been retrieved until + * tp_account_prepare_async() has finished; until then, the value is + * %NULL. + * + * Since: 0.9.0 + */ + g_object_class_install_property (object_class, PROP_NICKNAME, + g_param_spec_string ("nickname", + "Nickname", + "The account's nickname", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)); + + /** + * TpAccount::status-changed: + * @account: the #TpAccount + * @old_status: old connection status + * @new_status: new connection status + * @reason: the reason for the status change + * @dbus_error_name: currently unused, but for exposing the dbus error name + * on a connection error in the future + * @details: currently unused, but for exposing the error details + * on a connection error in the future + * + * Emitted when the connection status on the account changes. + * + * Since: 0.9.0 + */ + signals[STATUS_CHANGED] = g_signal_new ("status-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + _tp_marshal_VOID__UINT_UINT_UINT_STRING_POINTER, + G_TYPE_NONE, 5, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING, + G_TYPE_HASH_TABLE); + + /** + * TpAccount::presence-changed: + * @account: the #TpAccount + * @presence: the new presence + * @status: the new presence status + * @status_message: the new presence status message + * + * Emitted when the presence of the account changes. + * + * Since: 0.9.0 + */ + signals[PRESENCE_CHANGED] = g_signal_new ("presence-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + _tp_marshal_VOID__UINT_STRING_STRING, + G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING); proxy_class->interface = TP_IFACE_QUARK_ACCOUNT; tp_account_init_known_interfaces (); @@ -180,7 +1270,8 @@ tp_account_init_known_interfaces (void) * @object_path: The non-NULL object path of this account * @error: Used to raise an error if @object_path is not valid * - * Convenience function to create a new account proxy. + * Convenience function to create a new account proxy. The returned #TpAccount + * is not guaranteed to be ready at the point of return. * * Returns: a new reference to an account proxy, or %NULL if @object_path is * not valid @@ -192,27 +1283,1498 @@ tp_account_new (TpDBusDaemon *bus_daemon, { TpAccount *self; - g_return_val_if_fail (bus_daemon != NULL, NULL); + g_return_val_if_fail (TP_IS_DBUS_DAEMON (bus_daemon), NULL); g_return_val_if_fail (object_path != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - if (!tp_dbus_check_valid_object_path (object_path, error)) + if (!tp_account_parse_object_path (object_path, NULL, NULL, NULL, error)) + return NULL; + + self = TP_ACCOUNT (g_object_new (TP_TYPE_ACCOUNT, + "dbus-daemon", bus_daemon, + "dbus-connection", ((TpProxy *) bus_daemon)->dbus_connection, + "bus-name", TP_ACCOUNT_MANAGER_BUS_NAME, + "object-path", object_path, + NULL)); + + return self; +} + +static gchar * +unescape_protocol (gchar *protocol) +{ + if (strstr (protocol, "_2d") != NULL) + { + /* Work around MC5 bug where it escapes with tp_escape_as_identifier + * rather than doing it properly. MC5 saves the object path in your + * config, so if you've ever used a buggy MC5, the path will be wrong + * forever. + */ + gchar **chunks = g_strsplit (protocol, "_2d", 0); + gchar *new = g_strjoinv ("-", chunks); + + g_strfreev (chunks); + g_free (protocol); + protocol = new; + } + + g_strdelimit (protocol, "_", '-'); + + return protocol; +} + +/** + * tp_account_get_connection: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the same as the #TpAccount:connection property + * + * Since: 0.9.0 + **/ +TpConnection * +tp_account_get_connection (TpAccount *account) +{ + TpAccountPrivate *priv; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + priv = account->priv; + + if (priv->connection == NULL && priv->connection_object_path != NULL) + _tp_account_set_connection (account, priv->connection_object_path); + + return priv->connection; +} + +/** + * tp_account_ensure_connection: + * @account: a #TpAccount + * @path: the path to connection object for #TpAccount + * + * Set the connection of the account by specifying the connection object path. + * This function does not return a new ref and it is not guaranteed that the + * returned #TpConnection object is ready. + * + * The use-case for this function is in a HandleChannels callback and you + * already know the object path for the connection, so you can let @account + * create its #TpConnection and return it for use. + * + * Returns: the connection of the account, or %NULL if either the object path + * @path is invalid or it is the null-value "/" + * + * Since: 0.9.0 + **/ +TpConnection * +tp_account_ensure_connection (TpAccount *account, + const gchar *path) +{ + TpAccountPrivate *priv; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + priv = account->priv; + + /* double-check that the object path is valid */ + if (!tp_dbus_check_valid_object_path (path, NULL)) + return NULL; + + /* Should be a full object path, not the special "/" value */ + if (strlen (path) == 1) + return NULL; + + _tp_account_set_connection (account, path); + + return priv->connection; +} + +/** + * tp_account_get_display_name: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the same as the #TpAccount:display-name property + * + * Since: 0.9.0 + **/ +const gchar * +tp_account_get_display_name (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return account->priv->display_name; +} + +/** + * tp_account_is_valid: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the same as the #TpAccount:valid property + * + * Since: 0.9.0 + */ +gboolean +tp_account_is_valid (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + + return account->priv->valid; +} + +/** + * tp_account_get_connection_manager: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the same as the #TpAccount:connection-manager property + * + * Since: 0.9.0 + */ +const gchar * +tp_account_get_connection_manager (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return account->priv->cm_name; +} + +/** + * tp_account_get_protocol: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the same as the #TpAccount:protocol property + * + * Since: 0.9.0 + */ +const gchar * +tp_account_get_protocol (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return account->priv->proto_name; +} + +/** + * tp_account_get_icon_name: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the same as the #TpAccount:icon-name property + * + * Since: 0.9.0 + */ +const gchar * +tp_account_get_icon_name (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return account->priv->icon_name; +} + +/** + * tp_account_get_parameters: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the hash table of parameters on @account + * + * Since: 0.9.0 + */ +const GHashTable * +tp_account_get_parameters (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return account->priv->parameters; +} + +/** + * tp_account_is_enabled: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the same as the #TpAccount:enabled property + * + * Since: 0.9.0 + */ +gboolean +tp_account_is_enabled (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + + return account->priv->enabled; +} + +static void +_tp_account_property_set_cb (TpProxy *proxy, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + GSimpleAsyncResult *result = user_data; + + if (error != NULL) + { + DEBUG ("Failed to set property: %s", error->message); + g_simple_async_result_set_from_error (result, (GError *) error); + } + + g_simple_async_result_complete (result); + g_object_unref (result); +} + +/** + * tp_account_set_enabled_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async set of the Enabled property. + * + * Returns: %TRUE if the set was successful, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_set_enabled_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_set_enabled_finish), FALSE); + + return TRUE; +} + +/** + * tp_account_set_enabled_async: + * @account: a #TpAccount + * @enabled: the new enabled value of @account + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous set of the Enabled property of @account. When the + * operation is finished, @callback will be called. You can then call + * tp_account_set_enabled_finish() to get the result of the operation. + * + * Since: 0.9.0 + */ +void +tp_account_set_enabled_async (TpAccount *account, + gboolean enabled, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TpAccountPrivate *priv; + GValue value = {0, }; + GSimpleAsyncResult *result; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + priv = account->priv; + + result = g_simple_async_result_new (G_OBJECT (account), + callback, user_data, tp_account_set_enabled_finish); + + if (priv->enabled == enabled) + { + g_simple_async_result_complete_in_idle (result); + return; + } + + g_value_init (&value, G_TYPE_BOOLEAN); + g_value_set_boolean (&value, enabled); + + tp_cli_dbus_properties_call_set (TP_PROXY (account), + -1, TP_IFACE_ACCOUNT, "Enabled", &value, + _tp_account_property_set_cb, result, NULL, G_OBJECT (account)); + + g_value_reset (&value); +} + +static void +_tp_account_reconnected_cb (TpAccount *proxy, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + GSimpleAsyncResult *result = user_data; + + if (error != NULL) + g_simple_async_result_set_from_error (result, (GError *) error); + + g_simple_async_result_complete (result); + g_object_unref (result); +} + +/** + * tp_account_reconnect_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @error: a #GError to be filled + * + * Finishes an async reconnect of @account. + * + * Returns: %TRUE if the reconnect call was successful, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_reconnect_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_reconnect_finish), FALSE); + + return TRUE; +} + +/** + * tp_account_reconnect_async: + * @account: a #TpAccount + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous reconnect of @account. When the operation is + * finished, @callback will be called. You can then call + * tp_account_reconnect_finish() to get the result of the operation. + * + * Since: 0.9.0 + */ +void +tp_account_reconnect_async (TpAccount *account, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + result = g_simple_async_result_new (G_OBJECT (account), + callback, user_data, tp_account_reconnect_finish); + + tp_cli_account_call_reconnect (account, -1, _tp_account_reconnected_cb, + result, NULL, G_OBJECT (account)); +} + +/** + * tp_account_request_presence_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async presence change request on @account. + * + * Returns: %TRUE if the operation was successful, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_request_presence_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_request_presence_finish), FALSE); + + return TRUE; +} + +/** + * tp_account_request_presence_async: + * @account: a #TpAccount + * @type: the requested presence + * @status: a status message to set, or %NULL + * @message: a message for the change, or %NULL + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous change of presence on @account. When the + * operation is finished, @callback will be called. You can then call + * tp_account_request_presence_finish() to get the result of the operation. + * + * Since: 0.9.0 + */ +void +tp_account_request_presence_async (TpAccount *account, + TpConnectionPresenceType type, + const gchar *status, + const gchar *message, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GValue value = {0, }; + GValueArray *arr; + GSimpleAsyncResult *result; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + result = g_simple_async_result_new (G_OBJECT (account), + callback, user_data, tp_account_request_presence_finish); + + g_value_init (&value, TP_STRUCT_TYPE_SIMPLE_PRESENCE); + g_value_take_boxed (&value, dbus_g_type_specialized_construct ( + TP_STRUCT_TYPE_SIMPLE_PRESENCE)); + arr = (GValueArray *) g_value_get_boxed (&value); + + g_value_set_uint (arr->values, type); + g_value_set_static_string (arr->values + 1, status); + g_value_set_static_string (arr->values + 2, message); + + tp_cli_dbus_properties_call_set (TP_PROXY (account), -1, + TP_IFACE_ACCOUNT, "RequestedPresence", &value, + _tp_account_property_set_cb, result, NULL, G_OBJECT (account)); + + g_value_unset (&value); +} + +static void +_tp_account_updated_cb (TpAccount *proxy, + const gchar **reconnect_required, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); + + if (error != NULL) + g_simple_async_result_set_from_error (result, (GError *) error); + else + g_simple_async_result_set_op_res_gpointer (result, reconnect_required, NULL); + + g_simple_async_result_complete (result); + g_object_unref (G_OBJECT (result)); +} + +/** + * tp_account_update_parameters_async: + * @account: a #TpAccount + * @parameters: new parameters to set on @account + * @unset_parameters: list of parameters to unset on @account + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous update of parameters of @account. When the + * operation is finished, @callback will be called. You can then call + * tp_account_update_parameters_finish() to get the result of the operation. + * + * Since: 0.9.0 + */ +void +tp_account_update_parameters_async (TpAccount *account, + GHashTable *parameters, + const gchar **unset_parameters, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + result = g_simple_async_result_new (G_OBJECT (account), + callback, user_data, tp_account_update_parameters_finish); + + tp_cli_account_call_update_parameters (account, -1, parameters, + unset_parameters, _tp_account_updated_cb, result, + NULL, G_OBJECT (account)); +} + +/** + * tp_account_update_parameters_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @reconnect_required: a #GStrv to fill with properties that need a reconnect + * to take effect + * @error: a #GError to fill + * + * Finishes an async update of the parameters on @account. + * + * Returns: %TRUE if the request succeeded, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_update_parameters_finish (TpAccount *account, + GAsyncResult *result, + gchar ***reconnect_required, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_update_parameters_finish), FALSE); + + if (reconnect_required != NULL) + *reconnect_required = + g_strdupv (g_simple_async_result_get_op_res_gpointer (simple)); + + return TRUE; +} + +/** + * tp_account_set_display_name_async: + * @account: a #TpAccount + * @display_name: a new display name, or %NULL to unset the display name + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous set of the DisplayName property of @account. When + * the operation is finished, @callback will be called. You can then call + * tp_account_set_display_name_finish() to get the result of the operation. + * + * Since: 0.9.0 + */ +void +tp_account_set_display_name_async (TpAccount *account, + const char *display_name, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + GValue value = {0, }; + const gchar *display_name_set; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + if (display_name == NULL) + display_name_set = ""; + else + display_name_set = display_name; + + result = g_simple_async_result_new (G_OBJECT (account), callback, + user_data, tp_account_set_display_name_finish); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, display_name_set); + + tp_cli_dbus_properties_call_set (account, -1, TP_IFACE_ACCOUNT, + "DisplayName", &value, _tp_account_property_set_cb, result, NULL, + G_OBJECT (account)); + + g_value_unset (&value); +} + +/** + * tp_account_set_display_name_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async set of the DisplayName property. + * + * Returns: %TRUE if the call was successful, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_set_display_name_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_set_display_name_finish), FALSE); + + return TRUE; +} + +/** + * tp_account_set_icon_name_async: + * @account: a #TpAccount + * @icon_name: a new icon name, or %NULL to unset the icon name + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous set of the Icon property of @account. When + * the operation is finished, @callback will be called. You can then call + * tp_account_set_icon_name_finish() to get the result of the operation. + * + * Since: 0.9.0 + */ +void +tp_account_set_icon_name_async (TpAccount *account, + const char *icon_name, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + GValue value = {0, }; + const char *icon_name_set; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + if (icon_name == NULL) + /* settings an empty icon name is allowed */ + icon_name_set = ""; + else + icon_name_set = icon_name; + + result = g_simple_async_result_new (G_OBJECT (account), callback, + user_data, tp_account_set_icon_name_finish); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, icon_name_set); + + tp_cli_dbus_properties_call_set (account, -1, TP_IFACE_ACCOUNT, + "Icon", &value, _tp_account_property_set_cb, result, NULL, + G_OBJECT (account)); + + g_value_unset (&value); +} + +/** + * tp_account_set_icon_name_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async set of the Icon parameter. + * + * Returns: %TRUE if the operation was successful, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_set_icon_name_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_set_icon_name_finish), FALSE); + + return TRUE; +} + +static void +_tp_account_remove_cb (TpAccount *proxy, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); + + if (error != NULL) + g_simple_async_result_set_from_error (result, (GError *) error); + + g_simple_async_result_complete (result); + g_object_unref (G_OBJECT (result)); +} + +/** + * tp_account_remove_async: + * @account: a #TpAccount + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous removal of @account. When the operation is + * finished, @callback will be called. You can then call + * tp_account_remove_finish() to get the result of the operation. + * + * Since: 0.9.0 + */ +void +tp_account_remove_async (TpAccount *account, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + result = g_simple_async_result_new (G_OBJECT (account), + callback, user_data, tp_account_remove_finish); + + tp_cli_account_call_remove (account, -1, _tp_account_remove_cb, result, NULL, + G_OBJECT (account)); +} + +/** + * tp_account_remove_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async removal of @account. + * + * Returns: %TRUE if the operation was successful, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_remove_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_remove_finish), FALSE); + + return TRUE; +} + +/** + * tp_account_get_connect_automatically: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the same as the #TpAccount:connect-automatically property + * + * Since: 0.9.0 + */ +gboolean +tp_account_get_connect_automatically (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + + return account->priv->connect_automatically; +} + +/** + * tp_account_set_connect_automatically_async: + * @account: a #TpAccount + * @connect_automatically: new value for the parameter + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous set of the ConnectAutomatically property of + * @account. When the operation is finished, @callback will be called. You can + * then call tp_account_set_display_name_finish() to get the result of the + * operation. + * + * Since: 0.9.0 + */ +void +tp_account_set_connect_automatically_async (TpAccount *account, + gboolean connect_automatically, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + GValue value = {0, }; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + result = g_simple_async_result_new (G_OBJECT (account), callback, + user_data, tp_account_set_connect_automatically_finish); + + g_value_init (&value, G_TYPE_BOOLEAN); + g_value_set_boolean (&value, connect_automatically); + + tp_cli_dbus_properties_call_set (account, -1, TP_IFACE_ACCOUNT, + "ConnectAutomatically", &value, _tp_account_property_set_cb, result, + NULL, G_OBJECT (account)); + + g_value_unset (&value); +} + +/** + * tp_account_set_connect_automatically_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async set of the ConnectAutomatically property. + * + * Returns: %TRUE if the call was successful, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_set_connect_automatically_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_set_connect_automatically_finish), FALSE); + + return TRUE; +} + +/** + * tp_account_get_has_been_online: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the same as the #TpAccount:has-been-online property + * + * Since: 0.9.0 + */ +gboolean +tp_account_get_has_been_online (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + + return account->priv->has_been_online; +} + +/** + * tp_account_get_connection_status: + * @account: a #TpAccount + * @reason: a #TpConnectionStatusReason to fill, or %NULL + * + * Gets the connection status and reason from @account. The two values + * are the same as the #TpAccount:connection-status and + * #TpAccount:connection-status-reason properties. + * + * Returns: the same as the #TpAccount:connection-status property + * + * Since: 0.9.0 + */ +TpConnectionStatus +tp_account_get_connection_status (TpAccount *account, + TpConnectionStatusReason *reason) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), + TP_CONNECTION_STATUS_DISCONNECTED); /* there's no _UNSET */ + + if (reason != NULL) + *reason = account->priv->reason; + + return account->priv->connection_status; +} + +/** + * tp_account_get_current_presence: + * @account: a #TpAccount + * @status: return location for the current status + * @status_message: return location for the current status message + * + * Gets the current presence, status and status message of @account. These + * values are the same as the #TpAccount:current-presence-type, + * #TpAccount:current-status and #TpAccount:current-status-message properties. + * + * Returns: the same as the #TpAccount:current-presence-type property + * + * Since: 0.9.0 + */ +TpConnectionPresenceType +tp_account_get_current_presence (TpAccount *account, + gchar **status, + gchar **status_message) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), + TP_CONNECTION_PRESENCE_TYPE_UNSET); + + if (status != NULL) + *status = g_strdup (account->priv->status); + + if (status_message != NULL) + *status_message = g_strdup (account->priv->message); + + return account->priv->presence; +} + +/** + * tp_account_get_requested_presence: + * @account: a #TpAccount + * @status: return location for the requested status + * @status_message: return location for the requested status message + * + * Gets the requested presence, status and status message of @account. These + * values are the same as the #TpAccount:requested-presence-type, + * #TpAccount:requested-status and #TpAccount:requested-status-message + * properties. + * + * Returns: the same as the #TpAccount:requested-presence-type property + * + * Since: 0.9.0 + */ +TpConnectionPresenceType +tp_account_get_requested_presence (TpAccount *account, + gchar **status, + gchar **status_message) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), + TP_CONNECTION_PRESENCE_TYPE_UNSET); + + if (status != NULL) + *status = g_strdup (account->priv->requested_status); + + if (status_message != NULL) + *status_message = g_strdup (account->priv->requested_message); + + return account->priv->requested_presence; +} + +/** + * tp_account_get_nickname: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: the same as the #TpAccount:nickname property + * + * Since: 0.9.0 + */ +const gchar * +tp_account_get_nickname (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return account->priv->nickname; +} + +/** + * tp_account_set_nickname_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async nickname change request on @account. + * + * Returns: %TRUE if the operation was successful, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_set_nickname_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_set_nickname_finish), FALSE); + + return TRUE; +} + +/** + * tp_account_set_nickname_async: + * @account: a #TpAccount + * @nickname: a new nickname to set + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous change of the Nickname parameter on @account. When + * the operation is finished, @callback will be called. You can then call + * tp_account_set_nickname_finish() to get the result of the operation. + * + * Since: 0.9.0 + */ +void +tp_account_set_nickname_async (TpAccount *account, + const gchar *nickname, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GValue value = {0, }; + GSimpleAsyncResult *result; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + g_return_if_fail (nickname != NULL); + + result = g_simple_async_result_new (G_OBJECT (account), + callback, user_data, tp_account_request_presence_finish); + + if (nickname == NULL) + { + g_simple_async_report_error_in_idle (G_OBJECT (account), + callback, user_data, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + "Can't set an empty nickname"); + return; + } + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, nickname); + + tp_cli_dbus_properties_call_set (TP_PROXY (account), -1, + TP_IFACE_ACCOUNT, "Nickname", &value, + _tp_account_property_set_cb, result, NULL, G_OBJECT (account)); + + g_value_unset (&value); +} + +static void +_tp_account_got_avatar_cb (TpProxy *proxy, + const GValue *out_Value, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); + GValueArray *avatar; + GArray *res; + + if (error != NULL) + { + DEBUG ("Failed to get avatar: %s", error->message); + g_simple_async_result_set_from_error (result, (GError *) error); + } + else + { + avatar = g_value_get_boxed (out_Value); + res = g_value_get_boxed (g_value_array_get_nth (avatar, 0)); + g_simple_async_result_set_op_res_gpointer (result, res, NULL); + } + + g_simple_async_result_complete (result); + g_object_unref (result); +} + +/** + * tp_account_get_avatar_async: + * @account: a #TpAccount + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous get of @account's avatar. When + * the operation is finished, @callback will be called. You can then call + * tp_account_get_avatar_finish() to get the result of the operation. + * + * Since: 0.9.0 + */ +void +tp_account_get_avatar_async (TpAccount *account, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + result = g_simple_async_result_new (G_OBJECT (account), + callback, user_data, tp_account_get_avatar_finish); + + tp_cli_dbus_properties_call_get (account, -1, + TP_IFACE_ACCOUNT_INTERFACE_AVATAR, "Avatar", _tp_account_got_avatar_cb, + result, NULL, G_OBJECT (account)); +} + +/** + * tp_account_get_avatar_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async get operation of @account's avatar. + * + * Returns: a #GArray of the account's avatar, or %NULL on failure + * + * Since: 0.9.0 + */ +const GArray * +tp_account_get_avatar_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) return NULL; + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_get_avatar_finish), NULL); + + return g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (result)); +} + +/** + * tp_account_is_prepared: + * @account: a #TpAccount + * @feature: a feature which is required + * + * <!-- --> + * + * Returns: %TRUE whether @feature is ready on @account, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_is_prepared (TpAccount *account, + GQuark feature) +{ + TpAccountFeature *f; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + + if (tp_proxy_get_invalidated (account) != NULL) + return FALSE; + + f = _tp_account_get_feature (account, feature); + + if (f == NULL) + return FALSE; + + return f->ready; +} + +/** + * tp_account_prepare_async: + * @account: a #TpAccount + * @features: a 0-terminated list of features, or %NULL + * @callback: a callback to call when the request is satisfied + * @user_data: data to pass to @callback + * + * Requests an asynchronous preparation of @account with the features specified + * by @features. When the operation is finished, @callback will be called. You + * can then call tp_account_prepare_finish() to get the result of the + * operation. + * + * If @features is %NULL, then @callback will be called when the implied + * %TP_ACCOUNT_FEATURE_CORE feature is ready. + * + * If %NULL is given to @callback, then no callback will be called when the + * operation is finished. Instead, it will simply set @features on @manager. + * Note that if @callback is %NULL, then @user_data must also be %NULL. + * + * Since: 0.9.0 + */ +void +tp_account_prepare_async (TpAccount *account, + const GQuark *features, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TpAccountPrivate *priv; + GSimpleAsyncResult *result; + const GQuark *f; + + g_return_if_fail (TP_IS_ACCOUNT (account)); + + priv = account->priv; + + /* In this object, there are no features which are activatable (core is + * forced on you). They'd be activated here though. */ + + for (f = features; f != NULL && *f != 0; f++) + { + /* Only add features to requested which exist on this object and are not + * already in the list. */ + if (_tp_account_get_feature (account, *f) != NULL + && _tp_account_feature_in_array (*f, priv->requested_features)) + g_array_append_val (priv->requested_features, *f); + } + + if (callback == NULL) + return; + + result = g_simple_async_result_new (G_OBJECT (account), + callback, user_data, tp_account_prepare_finish); + + if (_tp_account_check_features (account, features)) + { + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + } + else + { + TpAccountFeatureCallback *cb; + + cb = g_slice_new0 (TpAccountFeatureCallback); + cb->result = result; + cb->features = features; + priv->callbacks = g_list_prepend (priv->callbacks, cb); + } +} + +/** + * tp_account_prepare_finish: + * @account: a #TpAccount + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Finishes an async preparation of the account @account. + * + * Returns: %TRUE if the preparation was successful, otherwise %FALSE + * + * Since: 0.9.0 + */ +gboolean +tp_account_prepare_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (account), tp_account_prepare_finish), FALSE); + + return TRUE; +} + +/** + * _tp_account_get_requested_features: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: a 0-terminated list of features requested on @account + * + * Since: 0.9.0 + */ +const GQuark * +_tp_account_get_requested_features (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return (const GQuark *) account->priv->requested_features->data; +} + +/** + * _tp_account_get_actual_features: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: a 0-terminated list of actual features on @account + * + * Since: 0.9.0 + */ +const GQuark * +_tp_account_get_actual_features (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return (const GQuark *) account->priv->actual_features->data; +} + +/** + * _tp_account_get_missing_features: + * @account: a #TpAccount + * + * <!-- --> + * + * Returns: a 0-terminated list of missing features from @account + * that have been requested + * + * Since: 0.9.0 + */ +const GQuark * +_tp_account_get_missing_features (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return (const GQuark *) account->priv->missing_features->data; +} + +static void +set_or_free (gchar **target, + gchar *source) +{ + if (target != NULL) + *target = source; + else + g_free (source); +} + +/** + * tp_account_parse_object_path: + * @object_path: a Telepathy Account's object path + * @cm: location at which to store the account's connection manager's name + * @protocol: location at which to store the account's protocol + * @account_id: location at which to store the account's unique identifier + * @error: location at which to return an error + * + * Validates and parses a Telepathy Account's object path, extracting the + * connection manager's name, the protocol, and the account's unique identifier + * from the path. This includes replacing underscores with hyphens in the + * protocol name, as defined in the Account specification. + * + * Any of the out parameters may be %NULL if not needed. + * + * Returns: %TRUE if @object_path was successfully parsed; %FALSE and sets + * @error otherwise. + * + * Since: 0.9.0 + */ +gboolean +tp_account_parse_object_path (const gchar *object_path, + gchar **cm, + gchar **protocol, + gchar **account_id, + GError **error) +{ + const gchar *suffix; + gchar **segments; + + if (!tp_dbus_check_valid_object_path (object_path, error)) + return FALSE; + if (!g_str_has_prefix (object_path, TP_ACCOUNT_OBJECT_PATH_BASE)) { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, "Account path does not start with the right prefix: %s", object_path); - return NULL; + return FALSE; } - self = TP_ACCOUNT (g_object_new (TP_TYPE_ACCOUNT, - "dbus-daemon", bus_daemon, - "dbus-connection", ((TpProxy *) bus_daemon)->dbus_connection, - "bus-name", TP_ACCOUNT_MANAGER_BUS_NAME, - "object-path", object_path, - NULL)); + suffix = object_path + strlen (TP_ACCOUNT_OBJECT_PATH_BASE); - return self; + segments = g_strsplit (suffix, "/", 0); + + if (g_strv_length (segments) != 3) + { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "Account path '%s' is malformed: should have 3 trailing components, " + "not %u", object_path, g_strv_length (segments)); + goto free_segments_and_fail; + } + + if (!g_ascii_isalpha (segments[0][0])) + { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "Account path '%s' is malformed: CM name should start with a letter", + object_path); + goto free_segments_and_fail; + } + + if (!g_ascii_isalpha (segments[1][0])) + { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "Account path '%s' is malformed: " + "protocol name should start with a letter", + object_path); + goto free_segments_and_fail; + } + + if (!g_ascii_isalpha (segments[2][0]) && segments[2][0] != '_') + { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "Account path '%s' is malformed: " + "account ID should start with a letter or underscore", + object_path); + goto free_segments_and_fail; + } + + set_or_free (cm, segments[0]); + set_or_free (protocol, unescape_protocol (segments[1])); + set_or_free (account_id, segments[2]); + + /* Not g_strfreev because we stole or freed the individual strings */ + g_free (segments); + return TRUE; + +free_segments_and_fail: + g_strfreev (segments); + return FALSE; +} + +/** + * _tp_account_refresh_properties: + * @account: a #TpAccount + * + * Refreshes @account's hashtable of properties with what actually exists on + * the account manager. + * + * Since: 0.9.0 + */ +void +_tp_account_refresh_properties (TpAccount *account) +{ + g_return_if_fail (TP_IS_ACCOUNT (account)); + + tp_cli_dbus_properties_call_get_all (account, -1, TP_IFACE_ACCOUNT, + _tp_account_got_all_cb, NULL, NULL, G_OBJECT (account)); } diff --git a/telepathy-glib/account.h b/telepathy-glib/account.h index 4072d7509..b9d9feb25 100644 --- a/telepathy-glib/account.h +++ b/telepathy-glib/account.h @@ -22,6 +22,9 @@ #ifndef TP_ACCOUNT_H #define TP_ACCOUNT_H +#include <gio/gio.h> + +#include <telepathy-glib/connection.h> #include <telepathy-glib/proxy.h> #include <telepathy-glib/dbus.h> @@ -63,11 +66,126 @@ GType tp_account_get_type (void); (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TYPE_ACCOUNT, \ TpAccountClass)) +#define TP_ACCOUNT_FEATURE_CORE \ + tp_account_get_feature_quark_core () + +GQuark tp_account_get_feature_quark_core (void) G_GNUC_CONST; + TpAccount *tp_account_new (TpDBusDaemon *bus_daemon, const gchar *object_path, GError **error); +gboolean tp_account_parse_object_path (const gchar *object_path, + gchar **cm, gchar **protocol, gchar **account_id, GError **error); + void tp_account_init_known_interfaces (void); +TpConnection *tp_account_get_connection (TpAccount *account); + +TpConnection *tp_account_ensure_connection (TpAccount *account, + const gchar *path); + +const gchar *tp_account_get_display_name (TpAccount *account); + +const gchar *tp_account_get_connection_manager (TpAccount *account); + +const gchar *tp_account_get_protocol (TpAccount *account); + +const gchar *tp_account_get_icon_name (TpAccount *account); + +void tp_account_set_enabled_async (TpAccount *account, + gboolean enabled, GAsyncReadyCallback callback, gpointer user_data); + +gboolean tp_account_set_enabled_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void tp_account_reconnect_async (TpAccount *account, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean tp_account_reconnect_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +gboolean tp_account_is_enabled (TpAccount *account); + +gboolean tp_account_is_valid (TpAccount *account); + +void tp_account_update_parameters_async (TpAccount *account, + GHashTable *parameters, const gchar **unset_parameters, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean tp_account_update_parameters_finish (TpAccount *account, + GAsyncResult *result, gchar ***reconnect_required, GError **error); + +void tp_account_remove_async (TpAccount *account, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean tp_account_remove_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void tp_account_set_display_name_async (TpAccount *account, + const gchar *display_name, GAsyncReadyCallback callback, + gpointer user_data); + +gboolean tp_account_set_display_name_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void tp_account_set_icon_name_async (TpAccount *account, + const gchar *icon_name, GAsyncReadyCallback callback, + gpointer user_data); + +gboolean tp_account_set_icon_name_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void tp_account_request_presence_async (TpAccount *account, + TpConnectionPresenceType type, const gchar *status, const gchar *message, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean tp_account_request_presence_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +gboolean tp_account_get_connect_automatically (TpAccount *account); + +void tp_account_set_connect_automatically_async (TpAccount *account, + gboolean connect_automatically, GAsyncReadyCallback callback, + gpointer user_data); + +gboolean tp_account_set_connect_automatically_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +gboolean tp_account_get_has_been_online (TpAccount *account); + +TpConnectionStatus tp_account_get_connection_status (TpAccount *account, + TpConnectionStatusReason *reason); + +TpConnectionPresenceType tp_account_get_current_presence (TpAccount *account, + gchar **status, gchar **status_message); + +TpConnectionPresenceType tp_account_get_requested_presence ( + TpAccount *account, gchar **status, gchar **status_message); + +const GHashTable *tp_account_get_parameters (TpAccount *account); + +const gchar *tp_account_get_nickname (TpAccount *account); + +void tp_account_set_nickname_async (TpAccount *account, + const gchar *nickname, GAsyncReadyCallback callback, gpointer user_data); + +gboolean tp_account_set_nickname_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void tp_account_get_avatar_async (TpAccount *account, + GAsyncReadyCallback callback, gpointer user_data); + +const GArray *tp_account_get_avatar_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +gboolean tp_account_is_prepared (TpAccount *account, GQuark feature); + +void tp_account_prepare_async (TpAccount *account, const GQuark *features, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean tp_account_prepare_finish (TpAccount *account, GAsyncResult *result, + GError **error); + G_END_DECLS #include <telepathy-glib/_gen/tp-cli-account.h> diff --git a/telepathy-glib/signals-marshal.list b/telepathy-glib/signals-marshal.list index 0d7a7eede..b157c9ae7 100644 --- a/telepathy-glib/signals-marshal.list +++ b/telepathy-glib/signals-marshal.list @@ -1,3 +1,4 @@ +VOID:OBJECT,INT,UINT,UINT VOID:OBJECT,POINTER VOID:OBJECT,POINTER,POINTER VOID:POINTER,OBJECT @@ -5,3 +6,6 @@ VOID:POINTER,UINT,INT,STRING VOID:STRING,UINT,UINT,BOXED VOID:UINT,INT,STRING VOID:UINT,OBJECT +VOID:UINT,STRING,STRING +VOID:UINT,UINT,UINT,STRING,POINTER +VOID:OBJECT,BOOLEAN diff --git a/telepathy-glib/telepathy-glib-uninstalled.pc.in b/telepathy-glib/telepathy-glib-uninstalled.pc.in index 7adc8c4c4..6d62d0ee7 100644 --- a/telepathy-glib/telepathy-glib-uninstalled.pc.in +++ b/telepathy-glib/telepathy-glib-uninstalled.pc.in @@ -7,6 +7,6 @@ Name: Telepathy-GLib (uninstalled copy) Description: GLib utility library for the Telepathy framework Version: @VERSION@ Requires: pkg-config >= 0.21 -Requires.private: dbus-glib-1 >= 0.73, glib-2.0 >= 2.16, gobject-2.0 >= 2.16 +Requires.private: dbus-glib-1 >= 0.73, glib-2.0 >= 2.20, gobject-2.0 >= 2.20, gio-2.0 >= 2.20 Libs: ${abs_top_builddir}/telepathy-glib/libtelepathy-glib.la Cflags: -I${abs_top_srcdir} -I${abs_top_builddir} diff --git a/telepathy-glib/telepathy-glib.pc.in b/telepathy-glib/telepathy-glib.pc.in index 8560f0366..b52f0c48f 100644 --- a/telepathy-glib/telepathy-glib.pc.in +++ b/telepathy-glib/telepathy-glib.pc.in @@ -7,6 +7,6 @@ Name: Telepathy-GLib Description: GLib utility library for the Telepathy framework Version: @VERSION@ Requires: pkg-config >= 0.21 -Requires.private: dbus-glib-1 >= 0.73, glib-2.0 >= 2.16, gobject-2.0 >= 2.16 +Requires.private: dbus-glib-1 >= 0.73, glib-2.0 >= 2.20, gobject-2.0 >= 2.20, gio-2.0 >= 2.20 Libs: -L${libdir} -ltelepathy-glib Cflags: -I${includedir}/telepathy-1.0 diff --git a/telepathy-glib/versions/0.9.0.abi b/telepathy-glib/versions/0.9.0.abi new file mode 100644 index 000000000..2be1ed781 --- /dev/null +++ b/telepathy-glib/versions/0.9.0.abi @@ -0,0 +1,56 @@ +Version: TELEPATHY_GLIB_0.9.0 +Extends: TELEPATHY_GLIB_0.7.37 +Release: 0.9.0 + +tp_account_ensure_connection +tp_account_get_avatar_async +tp_account_get_avatar_finish +tp_account_get_connect_automatically +tp_account_get_connection +tp_account_get_connection_manager +tp_account_get_connection_status +tp_account_get_current_presence +tp_account_get_display_name +tp_account_get_feature_quark_core +tp_account_get_has_been_online +tp_account_get_icon_name +tp_account_get_nickname +tp_account_get_parameters +tp_account_get_protocol +tp_account_get_requested_presence +tp_account_is_enabled +tp_account_is_prepared +tp_account_is_valid +tp_account_manager_create_account_async +tp_account_manager_create_account_finish +tp_account_manager_dup +tp_account_manager_enable_restart +tp_account_manager_ensure_account +tp_account_manager_get_feature_quark_core +tp_account_manager_get_most_available_presence +tp_account_manager_get_valid_accounts +tp_account_manager_is_prepared +tp_account_manager_prepare_async +tp_account_manager_prepare_finish +tp_account_manager_set_all_requested_presences +tp_account_parse_object_path +tp_account_prepare_async +tp_account_prepare_finish +tp_account_reconnect_async +tp_account_reconnect_finish +tp_account_remove_async +tp_account_remove_finish +tp_account_request_presence_async +tp_account_request_presence_finish +tp_account_set_connect_automatically_async +tp_account_set_connect_automatically_finish +tp_account_set_display_name_async +tp_account_set_display_name_finish +tp_account_set_enabled_async +tp_account_set_enabled_finish +tp_account_set_icon_name_async +tp_account_set_icon_name_finish +tp_account_set_nickname_async +tp_account_set_nickname_finish +tp_account_update_parameters_async +tp_account_update_parameters_finish diff --git a/tests/Makefile.am b/tests/Makefile.am index cb672df16..b9159abea 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -73,7 +73,8 @@ include $(top_srcdir)/tools/check-coding-style.mk check-local: check-coding-style LDADD = \ - $(top_builddir)/telepathy-glib/libtelepathy-glib.la + $(top_builddir)/telepathy-glib/libtelepathy-glib.la \ + $(GLIB_LIBS) AM_CFLAGS = \ $(ERROR_CFLAGS) \ diff --git a/tests/dbus/Makefile.am b/tests/dbus/Makefile.am index 9be880e8e..142c14927 100644 --- a/tests/dbus/Makefile.am +++ b/tests/dbus/Makefile.am @@ -1,5 +1,3 @@ -include $(top_srcdir)/tools/shave.mk - noinst_PROGRAMS = \ test-account \ test-account-manager \ @@ -45,7 +43,8 @@ TESTS = $(noinst_PROGRAMS) LDADD = \ $(top_builddir)/telepathy-glib/libtelepathy-glib.la \ - $(top_builddir)/tests/lib/libtp-glib-tests.la + $(top_builddir)/tests/lib/libtp-glib-tests.la \ + $(GLIB_LIBS) test_account_SOURCES = account.c @@ -207,7 +206,7 @@ _gen/svc.c _gen/svc.h: with-properties.xml \ $(top_srcdir)/tools/glib-ginterface-gen.py \ Makefile.am $(mkdir_p) _gen - $(QUIET_GEN)$(PYTHON) $(top_srcdir)/tools/glib-ginterface-gen.py \ + $(AM_V_GEN)$(PYTHON) $(top_srcdir)/tools/glib-ginterface-gen.py \ --filename=_gen/svc \ --signal-marshal-prefix=NOT_NEEDED \ $< Test_Svc_ @@ -215,4 +214,4 @@ _gen/svc.c _gen/svc.h: with-properties.xml \ _gen/errors-check.h: $(top_srcdir)/spec/errors.xml \ $(top_srcdir)/tools/glib-errors-check-gen.py $(mkdir_p) _gen - $(QUIET_GEN)$(PYTHON) $(top_srcdir)/tools/glib-errors-check-gen.py $< > $@ + $(AM_V_GEN)$(PYTHON) $(top_srcdir)/tools/glib-errors-check-gen.py $< > $@ diff --git a/tests/dbus/account-manager.c b/tests/dbus/account-manager.c index 7aa373adf..1f152b7d4 100644 --- a/tests/dbus/account-manager.c +++ b/tests/dbus/account-manager.c @@ -56,6 +56,28 @@ test_new (Test *test, test->am = tp_account_manager_new (test->dbus); } +static void +test_dup (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + TpAccountManager *one, *two; + TpDBusDaemon *dbus_one, *dbus_two; + + one = tp_account_manager_dup (); + two = tp_account_manager_dup (); + + g_assert (one == two); + + dbus_one = tp_dbus_daemon_dup (NULL); + dbus_two = tp_proxy_get_dbus_daemon (one); + + g_assert (dbus_one == dbus_two); + + g_object_unref (dbus_one); + g_object_unref (two); + g_object_unref (one); +} + int main (int argc, char **argv) @@ -64,6 +86,7 @@ main (int argc, g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); g_test_add ("/am/new", Test, NULL, setup, test_new, teardown); + g_test_add ("/am/dup", Test, NULL, setup, test_dup, teardown); return g_test_run (); } diff --git a/tests/dbus/account.c b/tests/dbus/account.c index aeb876215..70c0b6d72 100644 --- a/tests/dbus/account.c +++ b/tests/dbus/account.c @@ -10,6 +10,62 @@ #include <telepathy-glib/account.h> #include <telepathy-glib/debug.h> +#include <telepathy-glib/defs.h> + +static void +test_parse_failure (gconstpointer test_data) +{ + GError *error = NULL; + + g_assert (!tp_account_parse_object_path (test_data, NULL, NULL, NULL, + &error)); + g_assert (error != NULL); + g_error_free (error); +} + +typedef struct { + const gchar *path; + const gchar *cm; + const gchar *protocol; + const gchar *account_id; +} TestParseData; + +static TestParseData * +test_parse_data_new (const gchar *path, + const gchar *cm, + const gchar *protocol, + const gchar *account_id) +{ + TestParseData *t = g_slice_new (TestParseData); + + t->path = path; + t->cm = cm; + t->protocol = protocol; + t->account_id = account_id; + + return t; +} + +static void +test_parse_success (gconstpointer test_data) +{ + TestParseData *t = (TestParseData *) test_data; + gchar *cm, *protocol, *account_id; + GError *error = NULL; + + g_assert (tp_account_parse_object_path (t->path, &cm, &protocol, &account_id, + &error)); + g_assert_no_error (error); + g_assert_cmpstr (cm, ==, t->cm); + g_assert_cmpstr (protocol, ==, t->protocol); + g_assert_cmpstr (account_id, ==, t->account_id); + + g_free (cm); + g_free (protocol); + g_free (account_id); + + g_slice_free (TestParseData, t); +} typedef struct { GMainLoop *mainloop; @@ -23,9 +79,6 @@ static void setup (Test *test, gconstpointer data) { - g_type_init (); - tp_debug_set_flags ("all"); - test->mainloop = g_main_loop_new (NULL, FALSE); test->dbus = tp_dbus_daemon_dup (NULL); g_assert (test->dbus != NULL); @@ -62,7 +115,7 @@ test_new (Test *test, g_assert (test->account == NULL); test->account = tp_account_new (test->dbus, - "/org/freedesktop/Telepathy/Account/whatever", NULL); + "/org/freedesktop/Telepathy/Account/what/ev/er", NULL); g_assert (test->account != NULL); } @@ -70,9 +123,49 @@ int main (int argc, char **argv) { + g_type_init (); + tp_debug_set_flags ("all"); + g_test_init (&argc, &argv, NULL); g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); + g_test_add_data_func ("/account/parse/spaces", + "this is not an object path", test_parse_failure); + g_test_add_data_func ("/account/parse/no-prefix", + "/this/is/not/an/account/path", test_parse_failure); + g_test_add_data_func ("/account/parse/too-few-components", + "/org/freedesktop/Telepathy/Account/wrong", test_parse_failure); + g_test_add_data_func ("/account/parse/too-many-components", + "/org/freedesktop/Telepathy/Account/a/b/c/d", test_parse_failure); + g_test_add_data_func ("/account/parse/illegal-components", + "/org/freedesktop/Telepathy/Account/1/2/3", test_parse_failure); + + g_test_add_data_func ("/account/parse/legal", + test_parse_data_new ( + TP_ACCOUNT_OBJECT_PATH_BASE "gabble/jabber/badgers", + "gabble", "jabber", "badgers"), + test_parse_success); + g_test_add_data_func ("/account/parse/hyphenated-protocol", + test_parse_data_new ( + TP_ACCOUNT_OBJECT_PATH_BASE "salut/local_xmpp/badgers", + "salut", "local-xmpp", "badgers"), + test_parse_success); + g_test_add_data_func ("/account/parse/wrongly-escaped-protocol", + test_parse_data_new ( + TP_ACCOUNT_OBJECT_PATH_BASE "salut/local_2dxmpp/badgers", + "salut", "local-xmpp", "badgers"), + test_parse_success); + g_test_add_data_func ("/account/parse/wrongly-escaped-corner-case", + test_parse_data_new ( + TP_ACCOUNT_OBJECT_PATH_BASE "salut/local_2d/badgers", + "salut", "local-", "badgers"), + test_parse_success); + g_test_add_data_func ("/account/parse/underscored-account", + test_parse_data_new ( + TP_ACCOUNT_OBJECT_PATH_BASE "haze/msn/_thisseemsunlikely", + "haze", "msn", "_thisseemsunlikely"), + test_parse_success); + g_test_add ("/account/new", Test, NULL, setup, test_new, teardown); return g_test_run (); diff --git a/tools/Makefile.am b/tools/Makefile.am index a817df4e0..63260d584 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,5 +1,3 @@ -include $(top_srcdir)/tools/shave.mk - abs_top_builddir = @abs_top_builddir@ noinst_SCRIPTS = telepathy-glib-env @@ -42,23 +40,23 @@ CLEANFILES = libtpcodegen.pyc libtpcodegen.pyo libglibcodegen.pyc libglibcodegen all: $(EXTRA_DIST) libglibcodegen.py: libtpcodegen.py - $(QUIET_GEN)touch $@ + $(AM_V_GEN)touch $@ c-constants-gen.py: libglibcodegen.py - $(QUIET_GEN)touch $@ + $(AM_V_GEN)touch $@ glib-client-marshaller-gen.py: libglibcodegen.py - $(QUIET_GEN)touch $@ + $(AM_V_GEN)touch $@ glib-errors-enum-body-gen.py: libglibcodegen.py - $(QUIET_GEN)touch $@ + $(AM_V_GEN)touch $@ glib-errors-enum-header-gen.py: libglibcodegen.py - $(QUIET_GEN)touch $@ + $(AM_V_GEN)touch $@ glib-ginterface-gen.py: libglibcodegen.py - $(QUIET_GEN)touch $@ + $(AM_V_GEN)touch $@ glib-gtypes-generator.py: libglibcodegen.py - $(QUIET_GEN)touch $@ + $(AM_V_GEN)touch $@ glib-interfaces-gen.py: libglibcodegen.py - $(QUIET_GEN)touch $@ + $(AM_V_GEN)touch $@ glib-signals-marshal-gen.py: libglibcodegen.py - $(QUIET_GEN)touch $@ + $(AM_V_GEN)touch $@ TELEPATHY_SPEC_SRCDIR = $(top_srcdir)/../telepathy-spec maintainer-update-from-telepathy-spec: |