summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2013-09-25 14:40:35 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2013-09-25 14:40:35 +0100
commit5c1edec846876d56b3376683a1f2c50a09db2514 (patch)
tree1ebe7b3bb3d75eac5a835eb97e834b14f4e5ad1f
parent7405b4da3e8c50b23d686cb5f0970e0fba1cf375 (diff)
parentf9963519c30820e1369705ddd36f9e5a004c215e (diff)
Merge remote-tracking branch 'smcv/next' into next
Reviewed-by: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> Bug: https://bugs.freedesktop.org/show_bug.cgi?id=69767
-rw-r--r--.gitignore6
-rw-r--r--NEWS71
-rw-r--r--configure.ac25
-rw-r--r--extensions/Connection_Interface_Mail_Notification.xml653
-rw-r--r--extensions/Makefile.am4
-rw-r--r--extensions/all.xml7
-rw-r--r--extensions/extensions.c1
-rw-r--r--src/Makefile.am15
-rw-r--r--src/connection-aliasing.c138
-rw-r--r--src/connection-aliasing.h5
-rw-r--r--src/connection-avatars.c83
-rw-r--r--src/connection-avatars.h14
-rw-r--r--src/connection-capabilities.c331
-rw-r--r--src/connection-capabilities.h2
-rw-r--r--src/connection-mail.c59
-rw-r--r--src/connection-manager.c8
-rw-r--r--src/connection-manager.h2
-rw-r--r--src/connection-presence.c65
-rw-r--r--src/connection.c201
-rw-r--r--src/connection.h20
-rw-r--r--src/contact-list.c30
-rw-r--r--src/contact-list.h4
-rw-r--r--src/debug.c4
-rw-r--r--src/im-channel-factory.c28
-rw-r--r--src/im-channel.c423
-rw-r--r--src/im-channel.h4
-rw-r--r--src/main.c22
-rw-r--r--src/media-backend.c615
-rw-r--r--src/media-backend.h68
-rw-r--r--src/media-channel.c1784
-rw-r--r--src/media-channel.h80
-rw-r--r--src/media-manager.c579
-rw-r--r--src/media-manager.h65
-rw-r--r--src/media-stream.c1399
-rw-r--r--src/media-stream.h126
-rw-r--r--src/notify.c2
-rw-r--r--src/protocol.c98
-rw-r--r--src/request.c120
-rw-r--r--src/request.h3
-rw-r--r--src/util.c1
-rw-r--r--tests/Makefile.am29
-rw-r--r--tests/haze.service.in3
-rw-r--r--tests/twisted/Makefile.am66
-rw-r--r--tests/twisted/avatar-requirements.py2
-rw-r--r--tests/twisted/cm/protocols.py41
-rw-r--r--tests/twisted/connect/fail.py2
-rw-r--r--tests/twisted/connect/success.py15
-rw-r--r--tests/twisted/connect/twice-to-same-account.py13
-rw-r--r--tests/twisted/constants.py191
-rw-r--r--tests/twisted/gabbletest.py856
-rw-r--r--tests/twisted/hazetest.py777
-rw-r--r--tests/twisted/presence/presence.py18
-rw-r--r--tests/twisted/roster/groups.py202
-rw-r--r--tests/twisted/roster/initial-roster.py148
-rw-r--r--tests/twisted/roster/publish.py169
-rw-r--r--tests/twisted/roster/removed-from-rp-subscribe.py62
-rw-r--r--tests/twisted/roster/subscribe.py87
-rw-r--r--tests/twisted/run-test.sh.in69
-rw-r--r--tests/twisted/sasl/close.py43
-rw-r--r--tests/twisted/sasl/saslutil.py130
-rw-r--r--tests/twisted/sasl/telepathy-password.py53
-rw-r--r--tests/twisted/servicetest.py129
-rw-r--r--tests/twisted/simple-caps.py50
-rw-r--r--tests/twisted/text/destroy.py101
-rw-r--r--tests/twisted/text/ensure.py90
-rw-r--r--tests/twisted/text/initiate-requestotron.py78
-rw-r--r--tests/twisted/text/respawn.py134
-rw-r--r--tests/twisted/text/test-text-delayed.py12
-rw-r--r--tests/twisted/text/test-text-no-body.py13
-rw-r--r--tests/twisted/text/test-text.py82
-rw-r--r--tests/twisted/tools/Makefile.am31
-rwxr-xr-xtests/twisted/tools/exec-with-log.sh (renamed from tests/exec-with-log.sh)13
-rw-r--r--tests/twisted/tools/haze.service.in3
-rw-r--r--tests/twisted/tools/tmp-session-bus.conf.in (renamed from tests/tmp-session-bus.conf.in)2
-rw-r--r--tests/twisted/tools/with-session-bus.sh (renamed from tools/with-session-bus.sh)18
-rw-r--r--tools/Makefile.am1
-rw-r--r--tools/glib-ginterface-gen.py2
-rw-r--r--tools/run_and_bt.gdb7
-rw-r--r--tools/telepathy.am23
79 files changed, 2908 insertions, 8022 deletions
diff --git a/.gitignore b/.gitignore
index 5580cdd..674f8d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,9 +32,11 @@ src/telepathy-haze
src/telepathy-haze.8
tags
/tests/haze-testing.log
-/tests/im.telepathy1.ConnectionManager.haze.service
-/tests/tmp-session-bus.conf
+/tests/twisted/tools/im.telepathy1.ConnectionManager.haze.service
+/tests/twisted/tools/tmp-session-bus.conf
/tests/twisted/config.py
+/tests/twisted/haze-twisted-tests.list
+/tests/twisted/run-test.sh
/telepathy-haze*.tar.gz*
/telepathy-haze*/
diff --git a/NEWS b/NEWS
index d3324c9..1aeff83 100644
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,74 @@
-telepathy-haze 0.7.0 (UNRELEASED)
+telepathy-haze 0.9.0 (UNRELEASED)
=================================
-...
+Dependencies:
+
+• telepathy-glib ≥ 0.21.0 is required
+
+Features removed:
+
+• StreamedMedia channels are no longer supported. We'd potentially
+ accept patches to bring back audio/video support, but only via the
+ newer Call1 channels. (fd.o #69318, Simon)
+
+Internal changes:
+
+• fix various deprecation warnings (fd.o #69272; Guillaume, Simon)
+
+• remove deprecated Capabilities and Presence interfaces (fd.o #69318;
+ Guillaume, Simon)
+
+Fixes:
+
+• Report contacts with unknown presence as 'unknown' rather than raising
+ an error, and don't crash if libpurple reports a "primitive status"
+ that we don't understand (fd.o #69474, Simon)
+
+• Fix a memory leak when IM channels are closed (fd.o #31723, Simon)
+
+• Fix ContactGroups.SetContactGroups() implementation so it removes
+ the contact from groups if desired (fd.o #49389, Simon)
+
+telepathy-haze 0.7.1 (2013-09-17)
+=================================
+
+The “buzzes like a fridge” release.
+
+This is a release candidate for telepathy-haze 0.8, recommended for
+use with GNOME 3.10.
+
+Deprecations:
+
+• This will be the last branch with StreamedMedia support. Anyone
+ relying on Haze for audio/video calling is invited to port it to Call1.
+
+Enhancements:
+
+• Support interactive password prompting for protocols like SIPE that
+ have an optional password (fd.o #63326; Stefan Becker, Will Thompson)
+
+Fixes:
+
+• Adapt to Sametime accounts getting a "usersplit" in libpurple 2.10.1
+ (fd.o #44631, Simon McVittie)
+
+• Regression test improvements (fd.o #69269, #65290, #65296, #63119;
+ Will Thompson, Guillaume Desmottes, Simon McVittie)
+
+telepathy-haze 0.7.0 (2012-11-21)
+=================================
+
+This is the start of a new unstable branch. Don't swing on it too hard.
+
+Enhancements:
+
+• Jonny implemented ContactCapabilities;
+• Simon got rid of some deprecated API;
+• So did Xavier;
+• Gabriele Giacone kindly mapped two of the extant Skype prpls to
+ "skype-x11" and "skype-dbus". Maybe you'd like to give them a whirl?
+ (fd.o#57201)
+
telepathy-haze 0.6.0 (2012-04-04)
=================================
diff --git a/configure.ac b/configure.ac
index 077a1e2..a8c8876 100644
--- a/configure.ac
+++ b/configure.ac
@@ -7,8 +7,8 @@ AC_PREREQ([2.59])
# set nano_version to 1
m4_define([haze_major_version], [0])
-m4_define([haze_minor_version], [6])
-m4_define([haze_micro_version], [999])
+m4_define([haze_minor_version], [8])
+m4_define([haze_micro_version], [99])
m4_define([haze_nano_version], [1])
m4_define([haze_base_version],
@@ -32,6 +32,7 @@ AC_CONFIG_FILES([Makefile
m4/Makefile
tests/Makefile
tests/twisted/Makefile
+ tests/twisted/tools/Makefile
tools/Makefile
extensions/Makefile
])
@@ -72,10 +73,18 @@ AC_ARG_ENABLE(leaky-request-stubs,
AC_SUBST(ENABLE_LEAKY_REQUEST_STUBS)
PKG_CHECK_MODULES(PURPLE,[purple >= 2.7])
-PKG_CHECK_MODULES(TP_GLIB,[telepathy-glib-1 >= 0.99])
PKG_CHECK_MODULES(GLIB,[glib-2.0 >= 2.22, gobject-2.0, gio-2.0])
PKG_CHECK_MODULES(DBUS_GLIB,[dbus-glib-1 >= 0.73])
+AC_DEFINE([TP_SEAL_ENABLE], [], [Prevent to use sealed variables])
+AC_DEFINE([TP_VERSION_MIN_REQUIRED], [TP_VERSION_1_0], [Ignore post 1.0 deprecations])
+AC_DEFINE([TP_VERSION_MAX_ALLOWED], [TP_VERSION_1_0], [Prevent post 1.0 APIs])
+PKG_CHECK_MODULES([TP_GLIB], [telepathy-glib-1 >= 0.99.1, telepathy-glib-1-dbus >= 0.99.1])
+
+dnl MIN_REQUIRED must stay to 2.30 because of GValueArray
+AC_DEFINE([GLIB_VERSION_MIN_REQUIRED], [GLIB_VERSION_2_30], [Ignore post 2.30 deprecations])
+AC_DEFINE([GLIB_VERSION_MAX_ALLOWED], [GLIB_VERSION_2_30], [Prevent post 2.30 APIs])
+
GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`
AC_SUBST(GLIB_GENMARSHAL)
@@ -104,16 +113,6 @@ AC_MSG_RESULT([$TEST_PYTHON])
AC_SUBST(TEST_PYTHON)
AM_CONDITIONAL([WANT_TWISTED_TESTS], test false != "$TEST_PYTHON")
-AC_ARG_ENABLE(media,
- AC_HELP_STRING([--disable-media],[disable audio/video calls]),
- [
- AM_CONDITIONAL([MEDIA_ENABLED], false)
- ],[
- AC_DEFINE(ENABLE_MEDIA, [], [Enable audio/video calls])
- AM_CONDITIONAL([MEDIA_ENABLED], true)
- ])
-AC_SUBST(ENABLE_MEDIA)
-
#AS_AC_EXPAND(DATADIR, $datadir)
#DBUS_SERVICES_DIR="$DATADIR/dbus-1/services"
#AC_SUBST(DBUS_SERVICES_DIR)
diff --git a/extensions/Connection_Interface_Mail_Notification.xml b/extensions/Connection_Interface_Mail_Notification.xml
deleted file mode 100644
index 35678c2..0000000
--- a/extensions/Connection_Interface_Mail_Notification.xml
+++ /dev/null
@@ -1,653 +0,0 @@
-<?xml version="1.0" ?>
-<node name="/Connection_Interface_Mail_Notification"
- xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
- >
- <tp:copyright> Copyright (C) 2007 Collabora Limited </tp:copyright>
- <tp:license xmlns="http://www.w3.org/1999/xhtml">
- <p>This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
-License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version.</p>
-
-<p>This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Library General Public License for more details.</p>
-
-<p>You should have received a copy of the GNU Lesser General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</p>
- </tp:license>
- <interface
- name="org.freedesktop.Telepathy.Connection.Interface.MailNotification.DRAFT"
- tp:causes-havoc="experimental">
- <tp:requires interface="org.freedesktop.Telepathy.Connection"/>
- <tp:added version="0.19.1">(as draft 1)</tp:added>
-
- <tp:flags name="Mail_Notification_Flags" value-prefix="Mail_Notification_Flag" type="u" >
- <tp:flag suffix="Supports_Unread_Mail_Count" value="1">
- <tp:docstring>
- This Connection provides the number of unread e-mails (or e-mail
- threads) in the main folder of your e-mail account, as the
- <tp:member-ref>UnreadMailCount</tp:member-ref> property. The
- connection manager will update this value by emitting the
- <tp:member-ref>UnreadMailsChanged</tp:member-ref> signal.
- </tp:docstring>
- </tp:flag>
- <tp:flag suffix="Supports_Unread_Mails" value="2">
- <tp:docstring>
- This Connection provides a detailed list of unread e-mails, as the
- <tp:member-ref>UnreadMails</tp:member-ref> property. If this flag
- is set, <tt>Supports_Unread_Mail_Count</tt> MUST be set, and
- <tt>Emits_Mails_Received</tt> MUST NOT be set.
- The Connection will update the list by emitting the
- <tp:member-ref>UnreadMailsChanged</tp:member-ref> signals.
- </tp:docstring>
- </tp:flag>
- <tp:flag suffix="Emits_Mails_Received" value="4">
- <tp:docstring>
- This Connection emits the <tp:member-ref>MailsReceived</tp:member-ref>
- signal, which provides details about newly arrived e-mails but does
- not maintain their read/unread status afterwards. This flag MUST NOT
- be combined with <tt>Supports_Unread_Mails</tt>.
- </tp:docstring>
- </tp:flag>
- <tp:flag suffix="Supports_Request_Inbox_URL" value="8">
- <tp:docstring>
- This Connection can provide a URL (with optional POST data) to
- open the the inbox of the e-mail account in a web-based client, via
- the <tp:member-ref>RequestInboxURL</tp:member-ref> method.
- </tp:docstring>
- </tp:flag>
- <tp:flag suffix="Supports_Request_Mail_URL" value="16">
- <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- <p>This Connection can provide a URL (with optional POST data) to open
- a specific mail in a web-based client, via the
- <tp:member-ref>RequestMailURL</tp:member-ref> method. This feature
- is not useful unless either Emits_Mails_Received or
- Supports_Unread_Mails is set.</p>
-
- <p>If this flag is not set, clients SHOULD fall back to using
- <tp:member-ref>RequestInboxURL</tp:member-ref> if available.</p>
- </tp:docstring>
- </tp:flag>
- <tp:flag suffix="Thread_Based" value="32">
- <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- <p>Each <tp:type>Mail</tp:type> represents a thread of e-mails, which
- MAY have more than one sender.</p>
-
- <tp:rationale>
- <p>Google Talk notifies users about new mail in terms of unread
- threads, rather than unread e-mails.</p>
- </tp:rationale>
- </tp:docstring>
- </tp:flag>
-
- <tp:docstring>
- <p>Flags representing capabilities provided by a connection manager.
- Those values can be used as bitfield. Some flags depend on, or
- conflict with, each other.</p>
-
- <p>Connections SHOULD implement as many of these features as the
- underlying protocol allows, preferring to implement
- Supports_Unread_Mails instead of Emits_Mails_Received if both are
- possible.</p>
- </tp:docstring>
- </tp:flags>
-
- <tp:enum name="HTTP_Method" type="u">
- <tp:enumvalue suffix="Get" value="0">
- <tp:docstring>
- Use the GET method when opening the URL.
- </tp:docstring>
- </tp:enumvalue>
- <tp:enumvalue suffix="Post" value="1">
- <tp:docstring>
- Use the POST method when opening the URL. Refer to
- <tp:type>HTTP_Post_Data</tp:type> for more details.
- </tp:docstring>
- </tp:enumvalue>
- <tp:docstring>
- The HTTP Method with which to request a URL.
- </tp:docstring>
- </tp:enum>
-
- <tp:struct name="HTTP_Post_Data" array-name="HTTP_Post_Data_List">
- <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- <p>A pair (key, value) representing POST data compatible with the
- application/x-www-form-urlencoded MIME type. The strings MUST be
- valid UTF-8 strings, and the characters used in the key MUST obey
- the requirements of the
- <a href="http://www.w3.org/TR/html401/types.html#type-cdata">
- HTML CDATA type</a>. The value MUST NOT be
- encoded with HTML entities.</p>
-
- <p>For example, if the POST data should contain a key "less-than" with value
- "&lt;", and a key "percent" with value "%", this should be represented as
- two HTTP_Post_Data structures, ("less-than", "&lt;") and ("percent", "%"),
- resulting in a POST request whose request body is "less-than=&amp;lt;&amp;percent=%25".
- If a client passes this to a browser by writing it into an HTML form, it
- could do so by representing it as:</p>
-
- <pre>
- &lt;input type="hidden" name="less-than"&gt;&amp;lt;&lt;/input&gt;
- &lt;input type="hidden" name="percent"&gt;%&lt;/input&gt;
- </pre>
-
- <tp:rationale>
- <p>This data can be used to generate a HTML file that will
- automatically load the URL with appropriate POST data, in which case
- the client MUST convert any characters that are special within HTML
- into HTML entities. Alternatively, it can be used in an API that will
- instruct the browser how to load the URL (like the Netscape Plug-in
- API), in which case the client MUST escape
- <a href="http://www.ietf.org/rfc/rfc1738.txt">characters that are
- reserved in URLs</a>, if appropriate for that API.</p>
-
- <p>An array of pairs is used instead of a map from keys to values,
- because it's valid to repeat keys in both HTML and
- x-www-form-urlencoded data.</p>
- </tp:rationale>
- </tp:docstring>
- <tp:member type="s" name="Key">
- <tp:docstring>The key, corresponding to a HTML control
- name</tp:docstring>
- </tp:member>
- <tp:member type="s" name="Value">
- <tp:docstring>The value</tp:docstring>
- </tp:member>
- </tp:struct>
-
- <tp:struct name="Mail_Address" array-name="Mail_Address_List">
- <tp:docstring>
- A pair (name, address) representing an e-mail address,
- such as ("Nicolas Dufresne", "nicolas.dufresne@collabora.co.uk").
- </tp:docstring>
- <tp:member type="s" name="Name">
- <tp:docstring>The displayed name corresponding to the e-mail
- address</tp:docstring>
- </tp:member>
- <tp:member type="s" name="Address">
- <tp:docstring>The actual e-mail address</tp:docstring>
- </tp:member>
- </tp:struct>
-
- <tp:struct name="Mail_URL">
- <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- <p>A structure containing the required information to open a web-based
- e-mail UI, without needing re-authentication (if possible).</p>
-
- <p>Because the URL and POST data frequently contain short-lived
- credential tokens, a new URL should be requested (by calling one of
- the methods that returns a Mail_URL) for each visit to the web-based
- UI, and the URL should be visited soon after it is returned.</p>
- </tp:docstring>
- <tp:member type="s" name="URL">
- <tp:docstring>
- The URL to which to send a request.
- </tp:docstring>
- </tp:member>
- <tp:member type="u" name="Method" tp:type="HTTP_Method">
- <tp:docstring>
- The HTTP method of the request.
- </tp:docstring>
- </tp:member>
- <tp:member type="a(ss)" name="Post_Data" tp:type="HTTP_Post_Data[]">
- <tp:docstring>
- An array of name-value pairs containing the POST data to use when
- opening the URL. This MUST be an empty array if the Method is not
- POST.
- </tp:docstring>
- </tp:member>
- </tp:struct>
-
- <tp:mapping name="Mail" array-name="Mail_List">
- <tp:docstring>
- An extensible map representing a mail, or (on protocols where
- <tt>Thread_Based</tt> appears in
- <tp:member-ref>MailNotificationFlags</tp:member-ref>) a thread of
- mails. All keys are optional where not otherwise stated; however, at
- least one of "senders" and "subject" must be included.
- </tp:docstring>
-
- <tp:member type="s" name="Key">
- <tp:docstring>
- <p>A key providing information about the mail or thread. Well-known
- keys are as follows:</p>
-
- <dl>
- <dt>id &#8212; s</dt>
- <dd>
- <p>A unique ID for this e-mail. CMs with
- <tt>Supports_Unread_Mails</tt> set in
- <tp:member-ref>MailNotificationFlags</tp:member-ref> MUST provide
- this key in each <tp:type>Mail</tp:type>.</p>
-
- <p>If provided, the ID SHOULD be unique to a Mail at least until
- that mail is removed with the
- <tp:member-ref>UnreadMailsChanged</tp:member-ref> signal
- (in protocols with <tt>Supports_Unread_Emails</tt>), or
- unique for the duration of a session (otherwise).</p>
-
- <tp:rationale>
- <p>In protocols with Supports_Unread_Mails, this key is used to
- indicate which mail was removed. In protocols without that
- feature, it's impossible to tell when a mail has been removed
- (and hence how long the identifier will remain valid for use
- with <tp:member-ref>RequestMailURL</tp:member-ref>).</p>
- </tp:rationale>
- </dd>
-
- <dt>url-data &#8212; any type</dt>
- <dd>An opaque identifier (typically a string or list of strings)
- provided to the Connection when calling
- <tp:member-ref>RequestMailURL</tp:member-ref>,
- containing information used by the Connection to build the URL.
- </dd>
-
- <dt>senders &#8212; a(ss) (<tp:type>Mail_Address</tp:type>)</dt>
- <dd>
- An array of sender display name and e-mail address pairs. Note that
- only e-mails represented as a thread can have multiple senders.
- </dd>
-
- <dt>to-addresses &#8212; a(ss) (<tp:type>Mail_Address</tp:type>)</dt>
- <dd>
- An array of display name and e-mail address pairs representing
- the recipients.
- </dd>
-
- <dt>cc-addresses &#8212; a(ss) (<tp:type>Mail_Address</tp:type>)</dt>
- <dd>
- An array of display name and e-mail address pairs representing
- the carbon-copy recipients.
- </dd>
-
- <dt>sent-timestamp &#8212; x (<tp:type>Unix_Timestamp64</tp:type>)</dt>
- <dd>A UNIX timestamp indicating when the message was sent, or for
- a thread, when the most recent message was sent.
- </dd>
-
- <dt>received-timestamp &#8212; x (<tp:type>Unix_Timestamp64</tp:type>)</dt>
- <dd>A UNIX timestamp indicating when the message was received, or for
- a thread, when the most recent message was received.
- </dd>
-
- <dt>has-attachments &#8212; b</dt>
- <dd>If true, this mail has attachments.</dd>
-
- <dt>subject &#8212; s</dt>
- <dd>
- The subject of the message. This MUST be encoded in UTF-8.
- </dd>
-
- <dt>content-type &#8212; s</dt>
- <dd>
- <p>The MIME type of the message content. Two types are currently
- supported: "text/plain" for plain text, and "text/html" for a
- HTML document. If omitted, "text/plain" MUST be assumed.
- Regardless of MIME type, the content MUST be valid UTF-8 (which
- may require that the Connection transcodes it from a legacy
- encoding).</p>
-
- <tp:rationale>
- <p>All strings on D-Bus must be UTF-8.</p>
- </tp:rationale>
- </dd>
-
- <dt>truncated &#8212; b</dt>
- <dd>
- If true, the content is only a partial message; if false or
- omitted, the content is the entire message.
- </dd>
-
- <dt>content &#8212; s</dt>
- <dd>
- The body of the message, possibly truncated, encoded as appropriate
- for "content-type".
- </dd>
-
- <dt>folder &#8212; s</dt>
- <dd>
- The name of the folder containing this e-mails.
- If omitted, the inbox SHOULD be assumed.
- </dd>
- </dl>
- </tp:docstring>
- </tp:member>
-
- <tp:member name="Value" type="v">
- <tp:docstring>The value, of whatever type is appropriate for the
- key.</tp:docstring>
- </tp:member>
- </tp:mapping>
-
- <property name="MailNotificationFlags" type="u" access="read"
- tp:type="Mail_Notification_Flags"
- tp:name-for-bindings="Mail_Notification_Flags">
- <tp:docstring>
- Integer representing the bitwise-OR of supported features for e-mails
- notification on this server. This property MUST NOT change after the
- Connection becomes CONNECTED.
-
- <tp:rationale>
- This property indicates the behavior and availability
- of the other properties and signals within this interface. A
- connection manager that cannot at least set one of the flags
- in the <tp:type>Mail_Notification_Flags</tp:type>
- SHOULD NOT provide this interface.
- </tp:rationale>
- </tp:docstring>
- </property>
-
- <property name="UnreadMailCount" type="u" access="read"
- tp:name-for-bindings="Unread_Mail_Count">
- <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- <p>The number of unread messages in the Inbox. Change notification is
- via <tp:member-ref>UnreadMailsChanged</tp:member-ref>.</p>
-
- <p>This property is only useful if <tt>Supports_Unread_Mail_Count</tt>
- is set in the <tp:member-ref>MailNotificationFlags</tp:member-ref>;
- otherwise, it MUST be zero.</p>
-
- <p>If <tt>Thread_Based</tt> appears in the
- <tp:member-ref>MailNotificationFlags</tp:member-ref>, this property
- counts the number of threads, not the number of mails.</p>
- </tp:docstring>
- </property>
-
- <property name="UnreadMails" type="aa{sv}" tp:type="Mail[]"
- tp:name-for-bindings="Unread_Mails" access="read">
- <tp:docstring>
- A array of unread <tp:type>Mail</tp:type>s. Change notification is via
- <tp:member-ref>UnreadMailsChanged</tp:member-ref>. This property is
- only useful if <tt>Supports_Unread_Mails</tt> is set in
- <tp:member-ref>MailNotificationFlags</tp:member-ref>; otherwise, it MUST be
- an empty list.
- </tp:docstring>
- </property>
-
- <property name="MailAddress" type="s"
- tp:name-for-bindings="Mail_Address" access="read">
- <tp:docstring>
- A string representing the e-mail address of the account. The CMs MUST
- provide this information.
- <tp:rationale>
- In close integration of MailNotification with other e-mail services,
- the e-mail address can be used has a unique identifier for the
- account. Possible integration could be between Telepathy and
- Evolution where the e-mail address is the common information in
- both interfaces.
- </tp:rationale>
- </tp:docstring>
- </property>
-
- <signal name="MailsReceived" tp:name-for-bindings="Mails_Received">
- <arg name="Mails" type="aa{sv}" tp:type="Mail[]">
- <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- <p>An array of <tp:type>Mail</tp:type>s. Those e-mail MUST NOT have
- the "id" key.</p>
-
- <tp:rationale>
- <p>On connections that emit this signal, it's impossible to tell
- when a mail has been removed, and hence when "id" has become
- invalid.</p>
- </tp:rationale>
- </tp:docstring>
- </arg>
-
- <tp:docstring>
- Emitted when new e-mails messages arrive to the inbox associated with
- this connection. This signal is used for protocols that are not able
- to maintain the <tp:member-ref>UnreadMails</tp:member-ref> list, but
- do provide real-time notification about newly arrived e-mails. It MUST
- NOT be emitted unless <tt>Emits_Mails_Received</tt> is set in
- <tp:member-ref>MailNotificationFlags</tp:member-ref>.
- </tp:docstring>
- </signal>
-
- <signal name="UnreadMailsChanged"
- tp:name-for-bindings="Unread_Mails_Changed">
- <arg name="Count" type="u">
- <tp:docstring>
- Number of unread messages in the inbox (the new value of
- <tp:member-ref>UnreadMailCount</tp:member-ref>).
- </tp:docstring>
- </arg>
- <arg name="Mails_Added" type="aa{sv}" tp:type="Mail[]">
- <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- <p>A list of <tp:type>Mail</tp:type> that are being added or updated
- in <tp:member-ref>UnreadMails</tp:member-ref>.</p>
-
- <tp:rationale>
- <p>Mails may be updated when the URL information (URL and POST data)
- have changed, or senders were added or removed from an e-mail
- thread.</p>
- </tp:rationale>
-
- <p>If the <tt>Supports_Unread_Mails</tt> flag is not set, this list
- MUST be empty, even if Count has increased.</p>
- </tp:docstring>
- </arg>
- <arg name="Mails_Removed" type="as">
- <tp:docstring>
- A list of e-mail IDs that are being removed from
- <tp:member-ref>UnreadMails</tp:member-ref>.
- If the <tt>Supports_Unread_Mails</tt> flag is not set, this list
- MUST be empty, even if Count has decreased.
- </tp:docstring>
- </arg>
- <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- <p>Emitted when <tp:member-ref>UnreadMails</tp:member-ref> or
- <tp:member-ref>UnreadMailCount</tp:member-ref> have changed. It MUST
- NOT be emited if <tt>Supports_Unread_Mail_Count</tt> flag is not set
- in <tp:member-ref>MailNotificationFlags</tp:member-ref>.</p>
-
- <p><tt>Mails_Added</tt> and
- <tt>Mails_Removed</tt> MUST be empty if the
- <tt>Supports_Unread_Mails</tt> flag is not set.</p>
- </tp:docstring>
- </signal>
-
- <method name="Subscribe"
- tp:name-for-bindings="Subscribe">
- <tp:docstring>
- <p>This method subscribes a client to the notification interface. This
- MUST be called by clients before using this interface.</p>
-
- <p>The Connection tracks a subscription count (like a refcount) for
- each unique bus name that has called Subscribe(). When a client calls
- Unsubscribe(), it releases one "reference". If a client exits
- (or crashes), the Connection releases all "references" held on its
- behalf.</p>
-
- <tp:rationale>
- <p>The reference count imposed on the subscription simplifies
- implementation of client running in the same process
- (e.g. plug-ins): two plug-ins interested in mail notification can
- call Subscribe and Unsubscribe independently without interfering
- with each other.</p>
-
- <p>This method exists to reduce memory and network overhead when
- there is no active subscription. An example of a protocol that
- benefits from this method is the Google XMPP Mail Notification
- extension: in this protocol, the CM receives a notification
- that something has changed, but to get more information, the CM
- must request this information. Knowing that nobody is currently
- interested in this information, the CM can avoid generating
- useless network traffic. Similarly, the CM may free
- the list of unread messages to reduce memory overhead.</p>
- </tp:rationale>
-
- </tp:docstring>
- <tp:possible-errors>
- <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/>
- <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented"/>
- </tp:possible-errors>
- </method>
-
- <method name="Unsubscribe"
- tp:name-for-bindings="Unsubscribe">
- <tp:docstring>
- This method unsubscribes a client from the notification interface.
- This SHOULD be called by each client that has successfully called
- Subscribe when it no longer needs the mail notification interface.
-
- <tp:rationale>
- See <tp:member-ref>Subscribe</tp:member-ref> for rationale.
- </tp:rationale>
- </tp:docstring>
- <tp:possible-errors>
- <tp:error name="org.freedesktop.Telepathy.Error.NotAvailable">
- <tp:docstring>
- Raised if the client calling this method has no references to
- release.
- </tp:docstring>
- </tp:error>
- <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented"/>
- </tp:possible-errors>
- </method>
-
- <method name="RequestInboxURL"
- tp:name-for-bindings="Request_Inbox_URL">
- <arg direction="out" name="URL" type="(sua(ss))" tp:type="Mail_URL" >
- <tp:docstring>
- A struture containing a URL and optional additional data to open a
- webmail client, without re-authentication if possible.
- </tp:docstring>
- </arg>
- <tp:docstring>
- This method creates and returns a URL and an optional POST data that
- allow opening the Inbox folder of a webmail account. This URL MAY
- contain tokens with a short lifetime, so clients SHOULD request a new
- URL for each visit to the webmail interface. This method is implemented
- only if the <tt>Supports_Request_Inbox_URL</tt> flag is set in
- <tp:member-ref>MailNotificationFlags</tp:member-ref>.
-
- <tp:rationale>
- We are not using properties here because the tokens are unsuitable
- for sharing between clients, and network round-trips may be required
- to obtain the information that leads to authentication free webmail
- access.
- </tp:rationale>
- </tp:docstring>
- <tp:possible-errors>
- <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/>
- <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/>
- <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented"/>
- </tp:possible-errors>
- </method>
-
- <method name="RequestMailURL"
- tp:name-for-bindings="Request_Mail_URL">
- <arg direction="in" name="ID" type="s">
- <tp:docstring>
- The mail's <tt>id</tt> as found in the <tp:type>Mail</tp:type>
- structure, or the empty string if no <tt>id</tt> key was provided.
- </tp:docstring>
- </arg>
- <arg direction="in" name="URL_Data" type="v">
- <tp:docstring>
- Whatever <tt>url-data</tt> was found in the <tp:type>Mail</tp:type>
- structure, or the boolean value False (D-Bus type 'b') if no
- <tt>url-data</tt> was provided in the Mail.
- </tp:docstring>
- </arg>
- <arg direction="out" name="URL" type="(sua(ss))" tp:type="Mail_URL" >
- <tp:docstring>
- A struture that contains a URL and optional additional data to open a
- webmail client, without re-authentication if possible.
- </tp:docstring>
- </arg>
- <tp:docstring>
- This method creates and returns a URL and optional POST data that
- allow opening a specific mail in a webmail interface. This
- method is implemented only if <tt>Supports_Request_Mail_URL</tt> flag
- is set in <tp:member-ref>MailNotificationFlags</tp:member-ref>.
- <tp:rationale>
- See <tp:member-ref>RequestInboxURL</tp:member-ref> for design
- rationale.
- </tp:rationale>
- </tp:docstring>
- <tp:possible-errors>
- <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/>
- <tp:error name="org.freedesktop.Telepathy.Error.NetworkError"/>
- <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented"/>
- <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"/>
- </tp:possible-errors>
- </method>
-
- <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
- <p>An interface to support receiving notifications about a e-mail
- account associated with this connection.</p>
-
- <p>In protocols where this is possible, this interface also allows the
- connection manager to provide the necessary information for clients
- to open a web-based mail client without having to re-authenticate.</p>
-
- <p>To use this interface, a client MUST first subscribe using the
- <tp:member-ref>Subscribe</tp:member-ref> method. The subscription
- mechanic aims at reducing network traffic and memory footprint in the
- situation where nobody is currently interesting in provided
- information. When done with this interface, clients SHOULD call
- <tp:member-ref>Unsubscribe</tp:member-ref> to release resources in
- the CM.</p>
-
- <p>Protocols have various different levels of Mail Notification support.
- To describe the level of support, the interface provides a property
- called <tp:member-ref>MailNotificationFlags</tp:member-ref>.
- Not all combinations are valid; protocols can be divided into four
- categories as follows.</p>
-
- <p>Connections to the most capable protocols, such as Google's XMPP Mail
- Notification extension, have the Supports_Unread_Mails flag (this
- implies that they must also have Supports_Unread_Mail_Count, but not
- Emits_Mails_Received). On these connections, clients
- requiring change notification MUST monitor the
- <tp:member-ref>UnreadMailsChanged</tp:member-ref> signal, and
- either recover the initial state from the
- <tp:member-ref>UnreadMails</tp:member-ref> property (if they require
- details other than the number of mails) or the
- <tp:member-ref>UnreadMailCount</tp:member-ref> property (if they
- are only interested in the number of unread mails). The
- <tp:member-ref>MailsReceived</tp:member-ref> signal is never emitted
- on these connections, so clients that will display a short-term
- notification for each new mail MUST do so in response to emission of
- the <tp:member-ref>UnreadMailsChanged</tp:member-ref> signal.</p>
-
- <p>The most common situation, seen in protocols like MSN and Yahoo, is
- that the number of unread mails is provided and kept up-to-date,
- and a separate notification is emitted with some details of each new
- mail. This is a combination of the following two features, and clients
- SHOULD implement one or both as appropriate for their requirements.</p>
-
- <p>On protocols that have the Emits_Mails_Received flag (which implies
- that they do not have Supports_Unread_Mails), the CM does not keep
- track of any mails; it simply emits a notification whenever new mail
- arrives. Those events may be used for short term display (like a
- notification popup) to inform the user. No protocol is known to support
- only this feature, but it is useful for integration with libraries that
- that do not implement tracking of the number of mails. Clients
- requiring these notifications MUST monitor the
- <tp:member-ref>MailsReceived</tp:member-ref> signal on any connections
- with this flag.</p>
-
- <p>On protocols that have the Supports_Unread_Mail_Count flag but not
- the Supports_Unread_Mails flag, clients cannot display complete
- details of unread email, but can display an up-to-date count of the
- <em>number</em> of unread mails. To do this, they must monitor the
- <tp:member-ref>UnreadMailsChanged</tp:member-ref> signal, and
- retrieve the initial state from the
- <tp:member-ref>UnreadMailCount</tp:member-ref> property.</p>
-
- <p>
- Orthogonal features described by the
- <tp:member-ref>MailNotificationFlags</tp:member-ref> property include the
- RequestSomethingURL methods, which are used to obtain URLs allowing
- clients to open a webmail client. Connections SHOULD support as many
- of these methods as possible.</p>
- </tp:docstring>
- </interface>
-</node>
-<!-- vim:set sw=2 sts=2 et ft=xml: -->
-
diff --git a/extensions/Makefile.am b/extensions/Makefile.am
index c10f042..239c599 100644
--- a/extensions/Makefile.am
+++ b/extensions/Makefile.am
@@ -2,7 +2,7 @@ tools_dir = $(top_srcdir)/tools
EXTRA_DIST = \
all.xml \
- Connection_Interface_Mail_Notification.xml
+ $(NULL)
noinst_LTLIBRARIES = libhaze-extensions.la
@@ -52,7 +52,7 @@ _gen/svc.c _gen/svc.h: _gen/all.xml $(tools_dir)/glib-ginterface-gen.py \
Makefile.am
$(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \
--filename=_gen/svc --signal-marshal-prefix=_haze_ext \
- --include='<telepathy-glib/dbus.h>' \
+ --include='<telepathy-glib/telepathy-glib.h>' \
--include='"_gen/signals-marshal.h"' \
--allow-unstable \
--not-implemented-func='tp_dbus_g_method_return_not_implemented' \
diff --git a/extensions/all.xml b/extensions/all.xml
index 708cc7d..66b4a89 100644
--- a/extensions/all.xml
+++ b/extensions/all.xml
@@ -22,11 +22,6 @@ License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p>
</tp:license>
-<tp:generic-types>
- <tp:external-type name="Unix_Timestamp64" type="t"
- from="Telepathy specification"/>
-</tp:generic-types>
-
-<xi:include href="Connection_Interface_Mail_Notification.xml"/>
+<!-- currently empty -->
</tp:spec>
diff --git a/extensions/extensions.c b/extensions/extensions.c
index c81d126..77cc3f2 100644
--- a/extensions/extensions.c
+++ b/extensions/extensions.c
@@ -1,3 +1,4 @@
+#include <config.h>
#include "extensions.h"
/* auto-generated stubs */
diff --git a/src/Makefile.am b/src/Makefile.am
index bc9fd28..59bae17 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,19 +8,6 @@ man_MANS = telepathy-haze.8
EXTRA_DIST = telepathy-haze.8.in
CLEANFILES = $(man_MANS)
-if MEDIA_ENABLED
-haze_media_sources = media-backend.c \
- media-backend.h \
- media-channel.c \
- media-channel.h \
- media-manager.c \
- media-manager.h \
- media-stream.c \
- media-stream.h
-else
-haze_media_sources =
-endif
-
telepathy_haze_SOURCES = main.c \
defines.h \
debug.c \
@@ -53,7 +40,7 @@ telepathy_haze_SOURCES = main.c \
request.h \
util.c \
util.h \
- $(haze_media_sources)
+ $(NULL)
telepathy_haze_LDADD = $(top_builddir)/extensions/libhaze-extensions.la
diff --git a/src/connection-aliasing.c b/src/connection-aliasing.c
index 6d1017c..7b78a60 100644
--- a/src/connection-aliasing.c
+++ b/src/connection-aliasing.c
@@ -18,6 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
+
+#include <config.h>
#include "connection-aliasing.h"
#include <telepathy-glib/telepathy-glib.h>
@@ -38,24 +40,57 @@ can_alias (HazeConnection *conn)
return (prpl->alias_buddy != NULL);
}
-static void
-haze_connection_get_alias_flags (TpSvcConnectionInterfaceAliasing *self,
- DBusGMethodInvocation *context)
-{
- TpBaseConnection *base = TP_BASE_CONNECTION (self);
- HazeConnection *conn = HAZE_CONNECTION (base);
- guint flags = 0;
+typedef enum {
+ DP_FLAGS
+} AliasingDBusProperty;
- TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+static TpDBusPropertiesMixinPropImpl props[] = {
+ { "AliasFlags", GINT_TO_POINTER (DP_FLAGS), NULL },
+ { NULL }
+};
+TpDBusPropertiesMixinPropImpl *haze_connection_aliasing_properties = props;
- if (can_alias (conn))
+void
+haze_connection_aliasing_properties_getter (GObject *object,
+ GQuark interface,
+ GQuark name,
+ GValue *value,
+ gpointer getter_data)
+{
+ AliasingDBusProperty which = GPOINTER_TO_INT (getter_data);
+ HazeConnection *conn = HAZE_CONNECTION (object);
+ TpBaseConnection *base = (TpBaseConnection *) conn;
+
+ if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED)
{
- flags = TP_CONNECTION_ALIAS_FLAG_USER_SET;
+ /* not CONNECTED yet, so our connection doesn't have the prpl info
+ * yet - return dummy values */
+ if (G_VALUE_HOLDS_UINT (value))
+ g_value_set_uint (value, 0);
+ else
+ g_assert_not_reached ();
+
+ return;
}
- DEBUG ("alias flags: %u", flags);
- tp_svc_connection_interface_aliasing_return_from_get_alias_flags (
- context, flags);
+ switch (which)
+ {
+ case DP_FLAGS:
+ {
+ guint flags = 0;
+
+ if (can_alias (conn))
+ {
+ flags = TP_CONNECTION_ALIAS_FLAG_USER_SET;
+ }
+
+ g_value_set_uint (value, flags);
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
}
static const gchar *
@@ -68,7 +103,7 @@ get_alias (HazeConnection *self,
const gchar *bname = tp_handle_inspect (contact_handles, handle);
const gchar *alias;
- if (handle == base->self_handle)
+ if (handle == tp_base_connection_get_self_handle (base))
{
alias = purple_connection_get_display_name (self->account->gc);
@@ -97,41 +132,6 @@ get_alias (HazeConnection *self,
}
static void
-haze_connection_get_aliases (TpSvcConnectionInterfaceAliasing *self,
- const GArray *contacts,
- DBusGMethodInvocation *context)
-{
- HazeConnection *conn = HAZE_CONNECTION (self);
- TpBaseConnection *base = TP_BASE_CONNECTION (conn);
- TpHandleRepoIface *contact_handles =
- tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT);
- guint i;
- GError *error = NULL;
- GHashTable *aliases;
-
- if (!tp_handles_are_valid (contact_handles, contacts, FALSE, &error))
- {
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
-
- aliases = g_hash_table_new (NULL, NULL);
-
- for (i = 0; i < contacts->len; i++)
- {
- TpHandle handle = g_array_index (contacts, TpHandle, i);
-
- g_hash_table_insert (aliases, GUINT_TO_POINTER (handle),
- (gchar *) get_alias (conn, handle));
- }
-
- tp_svc_connection_interface_aliasing_return_from_get_aliases (
- context, aliases);
- g_hash_table_destroy (aliases);
-}
-
-static void
haze_connection_request_aliases (TpSvcConnectionInterfaceAliasing *self,
const GArray *contacts,
DBusGMethodInvocation *context)
@@ -177,31 +177,22 @@ set_alias_success_cb (PurpleAccount *account,
const char *new_alias)
{
TpBaseConnection *base_conn;
- GPtrArray *aliases;
- GValue entry = {0, };
+ GHashTable *aliases;
DEBUG ("purple_account_set_public_alias succeeded, new alias %s",
new_alias);
base_conn = ACCOUNT_GET_TP_BASE_CONNECTION (account);
- g_value_init (&entry, TP_STRUCT_TYPE_ALIAS_PAIR);
- g_value_take_boxed (&entry,
- dbus_g_type_specialized_construct (TP_STRUCT_TYPE_ALIAS_PAIR));
-
- dbus_g_type_struct_set (&entry,
- 0, base_conn->self_handle,
- 1, new_alias,
- G_MAXUINT);
-
- aliases = g_ptr_array_sized_new (1);
- g_ptr_array_add (aliases, g_value_get_boxed (&entry));
+ aliases = g_hash_table_new (NULL, NULL);
+ g_hash_table_insert (aliases,
+ GUINT_TO_POINTER (tp_base_connection_get_self_handle (base_conn)),
+ (gchar *) new_alias);
tp_svc_connection_interface_aliasing_emit_aliases_changed (base_conn,
aliases);
- g_value_unset (&entry);
- g_ptr_array_free (aliases, TRUE);
+ g_hash_table_unref (aliases);
}
static void
@@ -228,7 +219,8 @@ set_aliases_foreach (gpointer key,
{
/* stop already */
}
- else if (handle == TP_BASE_CONNECTION (data->conn)->self_handle)
+ else if (handle == tp_base_connection_get_self_handle (
+ TP_BASE_CONNECTION (data->conn)))
{
DEBUG ("setting alias for myself to \"%s\"", new_alias);
purple_account_set_public_alias (data->conn->account,
@@ -316,8 +308,6 @@ haze_connection_aliasing_iface_init (gpointer g_iface,
#define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x (\
klass, haze_connection_##x)
- IMPLEMENT(get_alias_flags);
- IMPLEMENT(get_aliases);
IMPLEMENT(request_aliases);
IMPLEMENT(set_aliases);
#undef IMPLEMENT
@@ -330,7 +320,7 @@ blist_node_aliased_cb (PurpleBlistNode *node,
{
PurpleBuddy *buddy;
TpBaseConnection *base_conn;
- GPtrArray *aliases;
+ GHashTable *aliases;
TpHandle handle;
TpHandleRepoIface *contact_handles;
@@ -343,17 +333,15 @@ blist_node_aliased_cb (PurpleBlistNode *node,
tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT);
handle = tp_handle_ensure (contact_handles, buddy->name, NULL, NULL);
- aliases = g_ptr_array_sized_new (1);
- g_ptr_array_add (aliases, tp_value_array_build (2,
- G_TYPE_UINT, handle,
- G_TYPE_STRING, purple_buddy_get_alias (buddy),
- G_TYPE_INVALID));
+ aliases = g_hash_table_new (NULL, NULL);
+ g_hash_table_insert (aliases,
+ GUINT_TO_POINTER (handle),
+ (gchar *) purple_buddy_get_alias (buddy));
tp_svc_connection_interface_aliasing_emit_aliases_changed (base_conn,
aliases);
- g_ptr_array_free (aliases, TRUE);
- tp_handle_unref (contact_handles, handle);
+ g_hash_table_unref (aliases);
}
void
diff --git a/src/connection-aliasing.h b/src/connection-aliasing.h
index b1002ec..6bc565a 100644
--- a/src/connection-aliasing.h
+++ b/src/connection-aliasing.h
@@ -23,10 +23,15 @@
#include <glib-object.h>
+#include <telepathy-glib/telepathy-glib.h>
void haze_connection_aliasing_iface_init (gpointer g_iface,
gpointer iface_data);
void haze_connection_aliasing_class_init (GObjectClass *object_class);
void haze_connection_aliasing_init (GObject *object);
+extern TpDBusPropertiesMixinPropImpl *haze_connection_aliasing_properties;
+void haze_connection_aliasing_properties_getter (GObject *object,
+ GQuark interface, GQuark name, GValue *value, gpointer getter_data);
+
#endif
diff --git a/src/connection-avatars.c b/src/connection-avatars.c
index 169950a..375bd2e 100644
--- a/src/connection-avatars.c
+++ b/src/connection-avatars.c
@@ -19,13 +19,13 @@
*
*/
+#include <config.h>
#include "connection-avatars.h"
#include <string.h>
-#include <telepathy-glib/contacts-mixin.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/svc-connection.h>
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
#include <libpurple/cipher.h>
@@ -33,6 +33,25 @@
#include "debug.h"
static gchar **
+dup_mime_types (PurpleBuddyIconSpec *icon_spec)
+{
+ gchar **mime_types, **i;
+ gchar *format;
+
+ mime_types = g_strsplit (icon_spec->format, ",", 0);
+
+ for (i = mime_types; *i != NULL; i++)
+ {
+ format = *i;
+ /* FIXME: image/ico is not the correct mime type. */
+ *i = g_strconcat ("image/", format, NULL);
+ g_free (format);
+ }
+
+ return mime_types;
+}
+
+static gchar **
_get_acceptable_mime_types (HazeConnection *self)
{
PurplePluginProtocolInfo *prpl_info = HAZE_CONNECTION_GET_PRPL_INFO (self);
@@ -41,20 +60,8 @@ _get_acceptable_mime_types (HazeConnection *self)
if (self->acceptable_avatar_mime_types == NULL)
{
- gchar **mime_types, **i;
- gchar *format;
-
- mime_types = g_strsplit (prpl_info->icon_spec.format, ",", 0);
-
- for (i = mime_types; *i != NULL; i++)
- {
- format = *i;
- /* FIXME: image/ico is not the correct mime type. */
- *i = g_strconcat ("image/", format, NULL);
- g_free (format);
- }
-
- self->acceptable_avatar_mime_types = mime_types;
+ self->acceptable_avatar_mime_types = dup_mime_types (
+ &prpl_info->icon_spec);
}
return self->acceptable_avatar_mime_types;
@@ -97,7 +104,7 @@ haze_connection_avatars_properties_getter (GObject *object,
PurplePluginProtocolInfo *prpl_info;
PurpleBuddyIconSpec *icon_spec;
- if (base->status != TP_CONNECTION_STATUS_CONNECTED)
+ if (tp_base_connection_get_status (base) != TP_CONNECTION_STATUS_CONNECTED)
{
/* not CONNECTED yet, so our connection doesn't have the prpl info
* yet - return dummy values */
@@ -153,6 +160,36 @@ haze_connection_avatars_properties_getter (GObject *object,
}
}
+void
+haze_connection_get_icon_spec_requirements (PurpleBuddyIconSpec *icon_spec,
+ GStrv *mime_types,
+ guint *min_height,
+ guint *min_width,
+ guint *rec_height,
+ guint *rec_width,
+ guint *max_height,
+ guint *max_width,
+ guint *max_bytes)
+{
+ if (mime_types != NULL)
+ *mime_types = dup_mime_types (icon_spec);
+ if (min_height != NULL)
+ *min_height = icon_spec->min_height;
+ if (min_width != NULL)
+ *min_width = icon_spec->min_width;
+ /* libpurple has no recommendation */
+ if (rec_height != NULL)
+ *rec_height = 0;
+ if (rec_width != NULL)
+ *rec_width = 0;
+ if (max_height != NULL)
+ *max_height = icon_spec->max_height;
+ if (max_width != NULL)
+ *max_width = icon_spec->max_width;
+ if (max_bytes != NULL)
+ *max_bytes = icon_spec->max_filesize;
+}
+
static GArray *
get_avatar (HazeConnection *conn,
TpHandle handle)
@@ -163,7 +200,8 @@ get_avatar (HazeConnection *conn,
tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT);
gconstpointer icon_data = NULL;
size_t icon_size = 0;
- if (handle == base->self_handle)
+
+ if (handle == tp_base_connection_get_self_handle (base))
{
PurpleStoredImage *image =
purple_buddy_icons_find_account_icon (conn->account);
@@ -283,7 +321,7 @@ haze_connection_get_known_avatar_tokens (TpSvcConnectionInterfaceAvatars *self,
* avatar you last used. So we special-case self_handle here.
*/
- if (handle == base_conn->self_handle)
+ if (handle == tp_base_connection_get_self_handle (base_conn))
{
GArray *avatar = get_avatar (conn, handle);
if (avatar != NULL)
@@ -347,7 +385,7 @@ haze_connection_clear_avatar (TpSvcConnectionInterfaceAvatars *self,
tp_svc_connection_interface_avatars_return_from_clear_avatar (context);
tp_svc_connection_interface_avatars_emit_avatar_updated (conn,
- base_conn->self_handle, "");
+ tp_base_connection_get_self_handle (base_conn), "");
}
static void
@@ -425,7 +463,7 @@ haze_connection_set_avatar (TpSvcConnectionInterfaceAvatars *self,
tp_svc_connection_interface_avatars_return_from_set_avatar (context, token);
tp_svc_connection_interface_avatars_emit_avatar_updated (conn,
- base_conn->self_handle, token);
+ tp_base_connection_get_self_handle (base_conn), token);
g_free (token);
}
@@ -463,7 +501,6 @@ buddy_icon_changed_cb (PurpleBuddy *buddy,
tp_svc_connection_interface_avatars_emit_avatar_updated (conn, contact,
token);
- tp_handle_unref (contact_repo, contact);
g_free (token);
}
diff --git a/src/connection-avatars.h b/src/connection-avatars.h
index 8517cc3..8ac37ab 100644
--- a/src/connection-avatars.h
+++ b/src/connection-avatars.h
@@ -23,7 +23,9 @@
#include <glib-object.h>
-#include <telepathy-glib/dbus-properties-mixin.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+#include <libpurple/purple.h>
void haze_connection_avatars_iface_init (gpointer g_iface, gpointer iface_data);
void haze_connection_avatars_class_init (GObjectClass *object_class);
@@ -33,4 +35,14 @@ extern TpDBusPropertiesMixinPropImpl *haze_connection_avatars_properties;
void haze_connection_avatars_properties_getter (GObject *object,
GQuark interface, GQuark name, GValue *value, gpointer getter_data);
+void haze_connection_get_icon_spec_requirements (PurpleBuddyIconSpec *icon_spec,
+ GStrv *mime_types,
+ guint *min_height,
+ guint *min_width,
+ guint *rec_height,
+ guint *rec_width,
+ guint *max_height,
+ guint *max_width,
+ guint *max_bytes);
+
#endif
diff --git a/src/connection-capabilities.c b/src/connection-capabilities.c
index 0d5195f..5a05e29 100644
--- a/src/connection-capabilities.c
+++ b/src/connection-capabilities.c
@@ -29,119 +29,6 @@
#include "connection.h"
#include "debug.h"
-#ifdef ENABLE_MEDIA
-#include "mediamanager.h"
-#endif
-
-static void
-free_rcc_list (GPtrArray *rccs)
-{
- g_boxed_free (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, rccs);
-}
-
-#ifdef ENABLE_MEDIA
-static PurpleMediaCaps
-tp_flags_to_purple_caps (guint flags)
-{
- PurpleMediaCaps caps = PURPLE_MEDIA_CAPS_NONE;
- if (flags & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO)
- caps |= PURPLE_MEDIA_CAPS_AUDIO;
- if (flags & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO)
- caps |= PURPLE_MEDIA_CAPS_VIDEO;
- return caps;
-}
-
-static guint
-purple_caps_to_tp_flags (PurpleMediaCaps caps)
-{
- guint flags = 0;
- if (caps & PURPLE_MEDIA_CAPS_AUDIO)
- flags |= TP_CHANNEL_MEDIA_CAPABILITY_AUDIO;
- if (caps & PURPLE_MEDIA_CAPS_VIDEO)
- flags |= TP_CHANNEL_MEDIA_CAPABILITY_VIDEO;
- return flags;
-}
-
-static GPtrArray * haze_connection_get_handle_contact_capabilities (
- HazeConnection *self, TpHandle handle);
-
-static void
-_emit_capabilities_changed (HazeConnection *conn,
- TpHandle handle,
- const guint old_specific,
- const guint new_specific)
-{
- GPtrArray *caps_arr;
- guint i;
-
- /* o.f.T.C.Capabilities */
-
- caps_arr = g_ptr_array_new ();
-
- if (old_specific != 0 || new_specific != 0)
- {
- GValue caps_monster_struct = {0, };
- guint old_generic = old_specific ?
- TP_CONNECTION_CAPABILITY_FLAG_CREATE |
- TP_CONNECTION_CAPABILITY_FLAG_INVITE : 0;
- guint new_generic = new_specific ?
- TP_CONNECTION_CAPABILITY_FLAG_CREATE |
- TP_CONNECTION_CAPABILITY_FLAG_INVITE : 0;
-
- if (0 != (old_specific ^ new_specific))
- {
- g_value_init (&caps_monster_struct,
- TP_STRUCT_TYPE_CAPABILITY_CHANGE);
- g_value_take_boxed (&caps_monster_struct,
- dbus_g_type_specialized_construct
- (TP_STRUCT_TYPE_CAPABILITY_CHANGE));
-
- dbus_g_type_struct_set (&caps_monster_struct,
- 0, handle,
- 1, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
- 2, old_generic,
- 3, new_generic,
- 4, old_specific,
- 5, new_specific,
- G_MAXUINT);
-
- g_ptr_array_add (caps_arr, g_value_get_boxed (&caps_monster_struct));
- }
- }
-
- if (caps_arr->len)
- tp_svc_connection_interface_capabilities_emit_capabilities_changed (
- conn, caps_arr);
-
- if (caps_arr->len > 0)
- {
- GHashTable *ret = g_hash_table_new_full (g_direct_hash, g_direct_equal,
- NULL, (GDestroyNotify) free_rcc_list);
- GPtrArray *arr;
-
- arr = haze_connection_get_handle_contact_capabilities (conn, handle);
- g_hash_table_insert (ret, GUINT_TO_POINTER (handle), arr);
-
- tp_svc_connection_interface_contact_capabilities_emit_contact_capabilities_changed (
- conn, ret);
-
- g_hash_table_unref (ret);
- }
-
- for (i = 0; i < caps_arr->len; i++)
- {
- g_boxed_free (TP_STRUCT_TYPE_CAPABILITY_CHANGE,
- g_ptr_array_index (caps_arr, i));
- }
-
- g_ptr_array_free (caps_arr, TRUE);
-}
-#endif
-
-typedef enum {
- CAPS_FLAGS_AUDIO = 1 << 0,
- CAPS_FLAGS_VIDEO = 1 << 1,
-} CapsFlags;
static void
haze_connection_update_capabilities (TpSvcConnectionInterfaceContactCapabilities *iface,
@@ -150,76 +37,9 @@ haze_connection_update_capabilities (TpSvcConnectionInterfaceContactCapabilities
{
HazeConnection *self = HAZE_CONNECTION (iface);
TpBaseConnection *base = (TpBaseConnection *) self;
-#ifdef ENABLE_MEDIA
- guint i;
- PurpleMediaCaps old_caps, caps;
- GHashTableIter iter;
- gpointer value;
-#endif
TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-#ifdef ENABLE_MEDIA
- caps = PURPLE_MEDIA_CAPS_NONE;
- old_caps = purple_media_manager_get_ui_caps (
- purple_media_manager_get ());
-
- DEBUG ("enter");
-
- /* go through all the clients and if they can do audio or video save
- * it in the client_caps hash table */
- for (i = 0; i < clients->len; i++)
- {
- GValueArray *va = g_ptr_array_index (clients, i);
- const gchar *client_name = g_value_get_string (va->values + 0);
- const GPtrArray *rccs = g_value_get_boxed (va->values + 1);
- guint j;
- CapsFlags flags = 0;
-
- g_hash_table_remove (self->client_caps, client_name);
-
- for (j = 0; j < rccs->len; j++)
- {
- GHashTable *class = g_ptr_array_index (rccs, i);
-
- if (tp_strdiff (tp_asv_get_string (class, TP_PROP_CHANNEL_CHANNEL_TYPE),
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
- continue;
-
- if (tp_asv_get_boolean (class,
- TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO, NULL))
- flags |= CAPS_FLAGS_AUDIO;
-
- if (tp_asv_get_boolean (class,
- TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO, NULL))
- flags |= CAPS_FLAGS_VIDEO;
- }
-
- if (flags != 0)
- {
- g_hash_table_insert (self->client_caps, g_strdup (client_name),
- GUINT_TO_POINTER (flags));
- }
- }
-
- /* now we have an updated client_caps hash table, go through it and
- * let libpurple know */
- g_hash_table_iter_init (&iter, self->client_caps);
- while (g_hash_table_iter_next (&iter, NULL, &value))
- {
- CapsFlags flags = GPOINTER_TO_UINT (value);
-
- if (flags & CAPS_FLAGS_AUDIO)
- caps |= PURPLE_MEDIA_CAPS_AUDIO;
- if (flags & CAPS_FLAGS_VIDEO)
- caps |= PURPLE_MEDIA_CAPS_VIDEO;
- }
-
- purple_media_manager_set_ui_caps (purple_media_manager_get(), caps);
-
- _emit_capabilities_changed (self, base->self_handle, old_caps, caps);
-#endif
-
tp_svc_connection_interface_contact_capabilities_return_from_update_capabilities (
context);
}
@@ -228,22 +48,6 @@ static GPtrArray *
haze_connection_get_handle_contact_capabilities (HazeConnection *self,
TpHandle handle)
{
-#ifdef ENABLE_MEDIA
- PurpleAccount *account = self->account;
- TpBaseConnection *conn = TP_BASE_CONNECTION (self);
- TpHandleRepoIface *contact_handles =
- tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT);
- const gchar *bname;
- PurpleMediaCaps caps;
- GValue media_monster = {0, };
- guint typeflags = 0;
- const gchar * const sm_allowed_audio[] = {
- TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO, NULL };
- const gchar * const sm_allowed_video[] = {
- TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO,
- TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO,
- NULL };
-#endif
GPtrArray *arr = g_ptr_array_new ();
GValue monster = {0, };
GHashTable *fixed_properties;
@@ -260,57 +64,6 @@ haze_connection_get_handle_contact_capabilities (HazeConnection *self,
/* TODO: Check for presence */
-#ifdef ENABLE_MEDIA
- if (handle == conn->self_handle)
- caps = purple_media_manager_get_ui_caps (purple_media_manager_get ());
- else
- {
- bname = tp_handle_inspect (contact_handles, handle);
- caps = purple_prpl_get_media_caps (account, bname);
- }
-
- typeflags = purple_caps_to_tp_flags(caps);
-
- if (typeflags != 0)
- {
- const gchar * const *allowed;
-
- g_value_init (&media_monster,
- TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS);
- g_value_take_boxed (&media_monster,
- dbus_g_type_specialized_construct (
- TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS));
-
- fixed_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
- (GDestroyNotify) tp_g_value_slice_free);
-
- channel_type_value = tp_g_value_slice_new (G_TYPE_STRING);
- g_value_set_static_string (channel_type_value,
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
- g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_CHANNEL_TYPE,
- channel_type_value);
-
- target_handle_type_value = tp_g_value_slice_new (G_TYPE_UINT);
- g_value_set_uint (target_handle_type_value, TP_HANDLE_TYPE_CONTACT);
- g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
- target_handle_type_value);
-
- if (typeflags & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO)
- allowed = sm_allowed_video;
- else
- allowed = sm_allowed_audio;
-
- dbus_g_type_struct_set (&media_monster,
- 0, fixed_properties,
- 1, allowed,
- G_MAXUINT);
-
- g_hash_table_unref (fixed_properties);
-
- g_ptr_array_add (arr, g_value_get_boxed (&media_monster));
- }
-#endif
-
g_value_init (&monster, TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS);
g_value_take_boxed (&monster,
dbus_g_type_specialized_construct (
@@ -372,47 +125,6 @@ conn_capabilities_fill_contact_attributes_contact_caps (
}
}
-static void
-haze_connection_get_contact_capabilities (
- TpSvcConnectionInterfaceContactCapabilities *svc,
- const GArray *handles,
- DBusGMethodInvocation *context)
-{
- HazeConnection *self = HAZE_CONNECTION (svc);
- TpBaseConnection *base = (TpBaseConnection *) self;
- TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
- TP_HANDLE_TYPE_CONTACT);
- guint i;
- GHashTable *ret;
- GError *error = NULL;
-
- TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-
- if (!tp_handles_are_valid (contact_handles, handles, FALSE, &error))
- {
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
-
- ret = g_hash_table_new_full (NULL, NULL, NULL,
- (GDestroyNotify) free_rcc_list);
-
- for (i = 0; i < handles->len; i++)
- {
- TpHandle handle = g_array_index (handles, TpHandle, i);
- GPtrArray *arr;
-
- arr = haze_connection_get_handle_contact_capabilities (self, handle);
- g_hash_table_insert (ret, GUINT_TO_POINTER (handle), arr);
- }
-
- tp_svc_connection_interface_contact_capabilities_return_from_get_contact_capabilities
- (context, ret);
-
- g_hash_table_unref (ret);
-}
-
void
haze_connection_contact_capabilities_iface_init (gpointer g_iface,
gpointer iface_data)
@@ -423,56 +135,13 @@ haze_connection_contact_capabilities_iface_init (gpointer g_iface,
tp_svc_connection_interface_contact_capabilities_implement_##x (\
klass, haze_connection_##x)
IMPLEMENT(update_capabilities);
- IMPLEMENT(get_contact_capabilities);
#undef IMPLEMENT
}
-#ifdef ENABLE_MEDIA
-static void
-caps_changed_cb (PurpleBuddy *buddy,
- PurpleMediaCaps caps,
- PurpleMediaCaps oldcaps)
-{
- PurpleAccount *account = purple_buddy_get_account (buddy);
- HazeConnection *conn = ACCOUNT_GET_HAZE_CONNECTION (account);
- TpBaseConnection *base_conn = TP_BASE_CONNECTION (conn);
- TpHandleRepoIface *contact_repo =
- tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT);
- const gchar *bname = purple_buddy_get_name(buddy);
- TpHandle contact = tp_handle_ensure (contact_repo, bname, NULL, NULL);
-
- _emit_capabilities_changed (conn, contact,
- purple_caps_to_tp_flags(oldcaps),
- purple_caps_to_tp_flags(caps));
-}
-#endif
-
-void
-haze_connection_capabilities_class_init (GObjectClass *object_class)
-{
-#ifdef ENABLE_MEDIA
- purple_signal_connect (purple_blist_get_handle (), "buddy-caps-changed",
- object_class, PURPLE_CALLBACK (caps_changed_cb), NULL);
-#endif
-}
-
void
haze_connection_capabilities_init (GObject *object)
{
- HazeConnection *self = HAZE_CONNECTION (object);
-
tp_contacts_mixin_add_contact_attributes_iface (object,
TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES,
conn_capabilities_fill_contact_attributes_contact_caps);
-
- self->client_caps = g_hash_table_new_full (g_str_hash, g_str_equal,
- (GDestroyNotify) g_free, NULL);
-}
-
-void
-haze_connection_capabilities_finalize (GObject *object)
-{
- HazeConnection *self = HAZE_CONNECTION (object);
-
- tp_clear_pointer (&self->client_caps, g_hash_table_unref);
}
diff --git a/src/connection-capabilities.h b/src/connection-capabilities.h
index 791581b..e98bf98 100644
--- a/src/connection-capabilities.h
+++ b/src/connection-capabilities.h
@@ -25,8 +25,6 @@
void haze_connection_contact_capabilities_iface_init (gpointer g_iface,
gpointer iface_data);
-void haze_connection_capabilities_class_init (GObjectClass *object_class);
void haze_connection_capabilities_init (GObject *object);
-void haze_connection_capabilities_finalize (GObject *object);
#endif
diff --git a/src/connection-mail.c b/src/connection-mail.c
index f286c27..767ee2d 100644
--- a/src/connection-mail.c
+++ b/src/connection-mail.c
@@ -17,12 +17,12 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "config.h"
#include "extensions/extensions.h"
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/svc-connection.h>
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
#include "connection.h"
#include "connection-mail.h"
@@ -41,32 +41,9 @@ enum
static GPtrArray empty_array = { 0 };
-
-static void
-haze_connection_mail_subscribe (
- HazeSvcConnectionInterfaceMailNotification *iface,
- DBusGMethodInvocation *context)
-{
- /* Nothing do do, no resources attached to mail notification */
- haze_svc_connection_interface_mail_notification_return_from_subscribe (
- context);
-}
-
-
-static void
-haze_connection_mail_unsubscribe (
- HazeSvcConnectionInterfaceMailNotification *iface,
- DBusGMethodInvocation *context)
-{
- /* Nothing do do, no resources attached to mail notification */
- haze_svc_connection_interface_mail_notification_return_from_unsubscribe (
- context);
-}
-
-
static void
haze_connection_mail_request_inbox_url (
- HazeSvcConnectionInterfaceMailNotification *iface,
+ TpSvcConnectionInterfaceMailNotification *iface,
DBusGMethodInvocation *context)
{
GError e = {TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
@@ -77,7 +54,7 @@ haze_connection_mail_request_inbox_url (
static void
haze_connection_mail_request_mail_url (
- HazeSvcConnectionInterfaceMailNotification *iface,
+ TpSvcConnectionInterfaceMailNotification *iface,
const gchar *in_id,
const GValue *in_url_data,
DBusGMethodInvocation *context)
@@ -94,11 +71,11 @@ haze_connection_mail_request_mail_url (
result = tp_value_array_build (3,
G_TYPE_STRING, g_value_get_string (in_url_data),
- G_TYPE_UINT, HAZE_HTTP_METHOD_GET,
- HAZE_ARRAY_TYPE_HTTP_POST_DATA_LIST, &empty_array,
+ G_TYPE_UINT, TP_HTTP_METHOD_GET,
+ TP_ARRAY_TYPE_HTTP_POST_DATA_LIST, &empty_array,
G_TYPE_INVALID);
- haze_svc_connection_interface_mail_notification_return_from_request_inbox_url (
+ tp_svc_connection_interface_mail_notification_return_from_request_inbox_url (
context, result);
g_value_array_free (result);
@@ -116,12 +93,10 @@ void
haze_connection_mail_iface_init (gpointer g_iface,
gpointer iface_data)
{
- HazeSvcConnectionInterfaceMailNotificationClass *klass = g_iface;
+ TpSvcConnectionInterfaceMailNotificationClass *klass = g_iface;
-#define IMPLEMENT(x) haze_svc_connection_interface_mail_notification_implement_##x (\
+#define IMPLEMENT(x) tp_svc_connection_interface_mail_notification_implement_##x (\
klass, haze_connection_mail_##x)
- IMPLEMENT(subscribe);
- IMPLEMENT(unsubscribe);
IMPLEMENT(request_inbox_url);
IMPLEMENT(request_mail_url);
#undef IMPLEMENT
@@ -181,8 +156,8 @@ haze_connection_mail_properties_getter (GObject *object,
if (name == prop_quarks[PROP_MAIL_NOTIFICATION_FLAGS])
g_value_set_uint (value,
- HAZE_MAIL_NOTIFICATION_FLAG_EMITS_MAILS_RECEIVED
- | HAZE_MAIL_NOTIFICATION_FLAG_SUPPORTS_REQUEST_MAIL_URL);
+ TP_MAIL_NOTIFICATION_FLAG_EMITS_MAILS_RECEIVED
+ | TP_MAIL_NOTIFICATION_FLAG_SUPPORTS_REQUEST_MAIL_URL);
else if (name == prop_quarks[PROP_UNREAD_MAIL_COUNT])
g_value_set_uint (value, 0);
else if (name == prop_quarks[PROP_UNREAD_MAILS])
@@ -217,7 +192,7 @@ static GPtrArray *
wrap_mail_address (const char *name_str, const char *addr_str)
{
GPtrArray *addr_array;
- GType addr_type = HAZE_STRUCT_TYPE_MAIL_ADDRESS;
+ GType addr_type = TP_STRUCT_TYPE_MAIL_ADDRESS;
GValue addr = {0};
addr_array = g_ptr_array_new ();
@@ -247,8 +222,8 @@ haze_connection_mail_notify_emails (PurpleConnection *pc,
const char **urls)
{
GPtrArray *mails;
- HazeSvcConnectionInterfaceMailNotification *conn =
- HAZE_SVC_CONNECTION_INTERFACE_MAIL_NOTIFICATION (
+ TpSvcConnectionInterfaceMailNotification *conn =
+ TP_SVC_CONNECTION_INTERFACE_MAIL_NOTIFICATION (
ACCOUNT_GET_TP_BASE_CONNECTION (
purple_connection_get_account (pc)));
@@ -281,7 +256,7 @@ haze_connection_mail_notify_emails (PurpleConnection *pc,
/* Filter out any aberations */
if (from && to && subject && url)
{
- GType addr_list_type = HAZE_ARRAY_TYPE_MAIL_ADDRESS_LIST;
+ GType addr_list_type = TP_ARRAY_TYPE_MAIL_ADDRESS_LIST;
GPtrArray *senders, *recipients;
GHashTable *mail;
@@ -317,7 +292,7 @@ haze_connection_mail_notify_emails (PurpleConnection *pc,
}
}
- haze_svc_connection_interface_mail_notification_emit_mails_received (
+ tp_svc_connection_interface_mail_notification_emit_mails_received (
conn, mails);
g_ptr_array_unref (mails);
diff --git a/src/connection-manager.c b/src/connection-manager.c
index 6377b9e..58c802d 100644
--- a/src/connection-manager.c
+++ b/src/connection-manager.c
@@ -19,6 +19,9 @@
*
*/
+#include <config.h>
+#include "connection-manager.h"
+
#include <string.h>
#include <glib.h>
@@ -27,9 +30,8 @@
#include <libpurple/prpl.h>
#include <libpurple/accountopt.h>
-#include <telepathy-glib/debug-sender.h>
+#include <telepathy-glib/telepathy-glib.h>
-#include "connection-manager.h"
#include "debug.h"
G_DEFINE_TYPE(HazeConnectionManager,
@@ -95,9 +97,7 @@ haze_connection_manager_class_init (HazeConnectionManagerClass *klass)
object_class->constructed = _haze_cm_constructed;
object_class->finalize = _haze_cm_finalize;
- base_class->new_connection = NULL;
base_class->cm_dbus_name = "haze";
- base_class->protocol_params = NULL;
g_type_class_add_private (klass, sizeof (HazeConnectionManagerPrivate));
}
diff --git a/src/connection-manager.h b/src/connection-manager.h
index b90f37f..4d24ff8 100644
--- a/src/connection-manager.h
+++ b/src/connection-manager.h
@@ -22,7 +22,7 @@
*/
#include <glib-object.h>
-#include <telepathy-glib/base-connection-manager.h>
+#include <telepathy-glib/telepathy-glib.h>
#include "protocol.h"
#include "connection.h"
diff --git a/src/connection-presence.c b/src/connection-presence.c
index 812e5f9..ddc5809 100644
--- a/src/connection-presence.c
+++ b/src/connection-presence.c
@@ -19,10 +19,12 @@
*
*/
+#include <config.h>
#include "connection-presence.h"
+
#include "debug.h"
-#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/telepathy-glib.h>
static const TpPresenceStatusOptionalArgumentSpec arg_specs[] = {
{ "message", "s" },
@@ -36,10 +38,12 @@ typedef enum {
HAZE_STATUS_EXT_AWAY,
HAZE_STATUS_INVISIBLE,
HAZE_STATUS_OFFLINE,
+ HAZE_STATUS_UNKNOWN,
HAZE_NUM_STATUSES
} HazeStatusIndex;
+/* Indexed by HazeStatusIndex */
static const TpPresenceStatusSpec statuses[] = {
{ "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE,
arg_specs, NULL, NULL },
@@ -51,6 +55,7 @@ static const TpPresenceStatusSpec statuses[] = {
arg_specs, NULL, NULL },
{ "hidden", TP_CONNECTION_PRESENCE_TYPE_HIDDEN, TRUE, NULL, NULL, NULL },
{ "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE, NULL, NULL, NULL },
+ { "unknown", TP_CONNECTION_PRESENCE_TYPE_UNKNOWN, FALSE, NULL, NULL, NULL },
{ NULL, TP_CONNECTION_PRESENCE_TYPE_UNSET, FALSE, NULL, NULL, NULL }
};
@@ -61,7 +66,7 @@ static const PurpleStatusPrimitive primitives[] = {
PURPLE_STATUS_AWAY,
PURPLE_STATUS_EXTENDED_AWAY,
PURPLE_STATUS_INVISIBLE,
- PURPLE_STATUS_OFFLINE
+ PURPLE_STATUS_UNSET,
};
/* Indexed by PurpleStatusPrimitive */
@@ -87,22 +92,36 @@ _get_tp_status (PurpleStatus *p_status)
gchar *message;
TpPresenceStatus *tp_status;
- g_assert (p_status != NULL);
+ if (p_status == NULL)
+ {
+ status_ix = HAZE_STATUS_UNKNOWN;
+ }
+ else
+ {
+ type = purple_status_get_type (p_status);
+ prim = purple_status_type_get_primitive (type);
- type = purple_status_get_type (p_status);
- prim = purple_status_type_get_primitive (type);
- status_ix = status_indices[prim];
+ if (prim <= 0 || prim >= G_N_ELEMENTS (status_indices))
+ {
+ /* guess wildly rather than crashing */
+ status_ix = HAZE_STATUS_AVAILABLE;
+ }
+ else
+ {
+ status_ix = status_indices[prim];
+ }
- xhtml_message = purple_status_get_attr_string (p_status, "message");
- if (xhtml_message)
- {
- GValue *message_v = g_slice_new0 (GValue);
+ xhtml_message = purple_status_get_attr_string (p_status, "message");
+ if (xhtml_message)
+ {
+ GValue *message_v = g_slice_new0 (GValue);
- message = purple_markup_strip_html (xhtml_message);
- g_value_init (message_v, G_TYPE_STRING);
- g_value_set_string (message_v, message);
- g_hash_table_insert (arguments, "message", message_v);
- g_free (message);
+ message = purple_markup_strip_html (xhtml_message);
+ g_value_init (message_v, G_TYPE_STRING);
+ g_value_set_string (message_v, message);
+ g_hash_table_insert (arguments, "message", message_v);
+ g_free (message);
+ }
}
tp_status = tp_presence_status_new (status_ix, arguments);
@@ -145,8 +164,7 @@ _status_available (GObject *obj,
static GHashTable *
_get_contact_statuses (GObject *obj,
- const GArray *contacts,
- GError **error)
+ const GArray *contacts)
{
GHashTable *status_table = g_hash_table_new_full (g_direct_hash,
g_direct_equal, NULL, NULL);
@@ -166,7 +184,7 @@ _get_contact_statuses (GObject *obj,
g_assert (tp_handle_is_valid (handle_repo, handle, NULL));
- if (handle == base_conn->self_handle)
+ if (handle == tp_base_connection_get_self_handle (base_conn))
{
p_status = purple_account_get_active_status (conn->account);
}
@@ -178,22 +196,18 @@ _get_contact_statuses (GObject *obj,
if (buddy)
{
PurplePresence *presence = purple_buddy_get_presence (buddy);
+
p_status = purple_presence_get_active_status (presence);
}
else
{
DEBUG ("[%s] %s isn't on the blist, ergo no status!",
conn->account->username, bname);
- g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
- "Presence for %u unknown; subscribe to them first", handle);
- g_hash_table_destroy (status_table);
- status_table = NULL;
- break;
+ p_status = NULL;
}
}
tp_status = _get_tp_status (p_status);
-
g_hash_table_insert (status_table, GINT_TO_POINTER (handle), tp_status);
}
@@ -217,7 +231,7 @@ haze_connection_presence_account_status_changed (PurpleAccount *account,
tp_status = _get_tp_status (status);
tp_presence_mixin_emit_one_presence_update (G_OBJECT (base_conn),
- base_conn->self_handle, tp_status);
+ tp_base_connection_get_self_handle (base_conn), tp_status);
}
}
@@ -242,7 +256,6 @@ update_status (PurpleBuddy *buddy,
tp_presence_mixin_emit_one_presence_update (G_OBJECT (conn), handle,
tp_status);
- tp_handle_unref (handle_repo, handle);
}
static void
diff --git a/src/connection.c b/src/connection.c
index e1b7761..8489f87 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -38,6 +38,7 @@
#include "connection-avatars.h"
#include "connection-mail.h"
#include "extensions/extensions.h"
+#include "request.h"
#include "connection-capabilities.h"
@@ -79,7 +80,7 @@ G_DEFINE_TYPE_WITH_CODE(HazeConnection,
tp_base_contact_list_mixin_groups_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_BLOCKING,
tp_base_contact_list_mixin_blocking_iface_init);
- G_IMPLEMENT_INTERFACE (HAZE_TYPE_SVC_CONNECTION_INTERFACE_MAIL_NOTIFICATION,
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_MAIL_NOTIFICATION,
haze_connection_mail_iface_init);
);
@@ -87,7 +88,7 @@ static const gchar * implemented_interfaces[] = {
/* Conditionally present */
TP_IFACE_CONNECTION_INTERFACE_AVATARS,
- HAZE_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION,
+ TP_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION,
TP_IFACE_CONNECTION_INTERFACE_CONTACT_BLOCKING,
# define HAZE_NUM_CONDITIONAL_INTERFACES 3
@@ -107,16 +108,44 @@ static const gchar * implemented_interfaces[] = {
NULL
};
-const gchar **
-haze_connection_get_implemented_interfaces (void)
+static void
+add_always_present_connection_interfaces (GPtrArray *interfaces)
+{
+ const gchar **iter;
+
+ for (iter = implemented_interfaces + HAZE_NUM_CONDITIONAL_INTERFACES;
+ *iter != NULL; iter++)
+ g_ptr_array_add (interfaces, (gchar *) *iter);
+}
+
+static GPtrArray *
+haze_connection_get_interfaces_always_present (TpBaseConnection *base)
{
- return implemented_interfaces;
+ GPtrArray *interfaces;
+
+ interfaces = TP_BASE_CONNECTION_CLASS (
+ haze_connection_parent_class)->get_interfaces_always_present (base);
+
+ add_always_present_connection_interfaces (interfaces);
+
+ return interfaces;
}
-const gchar **
-haze_connection_get_guaranteed_interfaces (void)
+static void add_optional_connection_interfaces (GPtrArray *ifaces,
+ PurplePluginProtocolInfo *prpl_info);
+
+/* Returns a (transfer container) not NULL terminated of (const gchar *)
+ * interface names. */
+GPtrArray *
+haze_connection_dup_implemented_interfaces (PurplePluginProtocolInfo *prpl_info)
{
- return implemented_interfaces + HAZE_NUM_CONDITIONAL_INTERFACES;
+ GPtrArray *ifaces;
+
+ ifaces = g_ptr_array_new ();
+ add_always_present_connection_interfaces (ifaces);
+ add_optional_connection_interfaces (ifaces, prpl_info);
+
+ return ifaces;
}
struct _HazeConnectionPrivate
@@ -128,6 +157,9 @@ struct _HazeConnectionPrivate
gchar *prpl_id;
PurplePluginProtocolInfo *prpl_info;
+ /* Set if purple_account_request_password() was called */
+ gpointer password_request;
+
/* Set if purple_account_disconnect has been called or is scheduled to be
* called, so should not be called again.
*/
@@ -142,28 +174,56 @@ struct _HazeConnectionPrivate
#define PC_GET_BASE_CONN(pc) \
(ACCOUNT_GET_TP_BASE_CONNECTION (purple_connection_get_account (pc)))
+static gboolean
+protocol_info_supports_avatar (PurplePluginProtocolInfo *prpl_info)
+{
+ return (prpl_info->icon_spec.format != NULL);
+}
+
+static gboolean
+protocol_info_supports_blocking (PurplePluginProtocolInfo *prpl_info)
+{
+ return (prpl_info->add_deny != NULL);
+}
+
+static gboolean
+protocol_info_supports_mail_notification (PurplePluginProtocolInfo *prpl_info)
+{
+ return ((prpl_info->options & OPT_PROTO_MAIL_CHECK) != 0);
+}
+
+static void
+add_optional_connection_interfaces (GPtrArray *ifaces,
+ PurplePluginProtocolInfo *prpl_info)
+{
+ if (protocol_info_supports_avatar (prpl_info))
+ g_ptr_array_add (ifaces,
+ TP_IFACE_CONNECTION_INTERFACE_AVATARS);
+
+ if (protocol_info_supports_blocking (prpl_info))
+ g_ptr_array_add (ifaces,
+ TP_IFACE_CONNECTION_INTERFACE_CONTACT_BLOCKING);
+
+ if (protocol_info_supports_mail_notification (prpl_info))
+ g_ptr_array_add (ifaces,
+ TP_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION);
+}
+
static void
connected_cb (PurpleConnection *pc)
{
TpBaseConnection *base_conn = PC_GET_BASE_CONN (pc);
HazeConnection *conn = HAZE_CONNECTION (base_conn);
PurplePluginProtocolInfo *prpl_info = HAZE_CONNECTION_GET_PRPL_INFO (conn);
+ GPtrArray *ifaces;
- if (prpl_info->icon_spec.format != NULL)
- {
- static const gchar *avatar_ifaces[] = {
- TP_IFACE_CONNECTION_INTERFACE_AVATARS,
- NULL };
- tp_base_connection_add_interfaces (base_conn, avatar_ifaces);
- }
+ ifaces = g_ptr_array_new ();
+ add_optional_connection_interfaces (ifaces, prpl_info);
+ g_ptr_array_add (ifaces, NULL);
- if (prpl_info->add_deny != NULL)
- {
- static const gchar *blocking_ifaces[] = {
- TP_IFACE_CONNECTION_INTERFACE_CONTACT_BLOCKING,
- NULL };
- tp_base_connection_add_interfaces (base_conn, blocking_ifaces);
- }
+ tp_base_connection_add_interfaces (base_conn,
+ (const gchar **) ifaces->pdata);
+ g_ptr_array_unref (ifaces);
tp_base_contact_list_set_list_received (
(TpBaseContactList *) conn->contact_list);
@@ -289,7 +349,8 @@ haze_report_disconnect_reason (PurpleConnection *gc,
priv->disconnecting = TRUE;
map_purple_error_to_tp (reason,
- (base_conn->status == TP_CONNECTION_STATUS_CONNECTING),
+ (tp_base_connection_get_status (base_conn) ==
+ TP_CONNECTION_STATUS_CONNECTING),
&tp_reason, &tp_error_name);
details = tp_asv_new ("debug-message", G_TYPE_STRING, text, NULL);
tp_base_connection_disconnect_with_dbus_error (base_conn, tp_error_name,
@@ -314,7 +375,8 @@ disconnected_cb (PurpleConnection *pc)
priv->disconnecting = TRUE;
- if(base_conn->status != TP_CONNECTION_STATUS_DISCONNECTED)
+ if (tp_base_connection_get_status (base_conn) !=
+ TP_CONNECTION_STATUS_DISCONNECTED)
{
/* Because we have report_disconnect_reason, if status is not already
* DISCONNECTED, we know that it was requested. */
@@ -436,16 +498,25 @@ _haze_connection_password_manager_prompt_cb (GObject *source,
{
DEBUG ("Simple password manager failed: %s", error->message);
- if (base_conn->status != TP_CONNECTION_STATUS_DISCONNECTED)
+ if (priv->password_request)
+ {
+ haze_request_password_cb (priv->password_request, NULL);
+ /* no need to call purple_account_disconnect(): the prpl will take
+ * the account offline. If we're lucky it'll use an
+ * AUTHENTICATION_FAILED-type message.
+ */
+ }
+ else if (tp_base_connection_get_status (base_conn) !=
+ TP_CONNECTION_STATUS_DISCONNECTED)
{
tp_base_connection_disconnect_with_dbus_error (base_conn,
tp_error_get_dbus_name (error->code), NULL,
TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
+ /* no need to call purple_account_disconnect because _connect
+ * was never called ...
+ */
}
- /* no need to call purple_account_disconnect because _connect
- * was never called */
-
g_error_free (error);
return;
}
@@ -453,11 +524,18 @@ _haze_connection_password_manager_prompt_cb (GObject *source,
g_free (priv->password);
priv->password = g_strdup (password->str);
- purple_account_set_password (self->account, priv->password);
+ if (priv->password_request)
+ {
+ haze_request_password_cb (priv->password_request, priv->password);
+ }
+ else
+ {
+ purple_account_set_password (self->account, priv->password);
- purple_account_set_enabled(self->account, UI_ID, TRUE);
- purple_account_connect (self->account);
- priv->connect_called = TRUE;
+ purple_account_set_enabled(self->account, UI_ID, TRUE);
+ purple_account_connect (self->account);
+ priv->connect_called = TRUE;
+ }
}
static gboolean
@@ -469,13 +547,16 @@ _haze_connection_start_connecting (TpBaseConnection *base,
TpHandleRepoIface *contact_handles =
tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT);
const gchar *password;
+ TpHandle self_handle;
g_return_val_if_fail (self->account != NULL, FALSE);
- base->self_handle = tp_handle_ensure (contact_handles,
+ self_handle = tp_handle_ensure (contact_handles,
purple_account_get_username (self->account), NULL, error);
- if (!base->self_handle)
- return FALSE;
+ if (self_handle == 0)
+ return FALSE;
+
+ tp_base_connection_set_self_handle (base, self_handle);
tp_base_connection_change_status(base, TP_CONNECTION_STATUS_CONNECTING,
TP_CONNECTION_STATUS_REASON_REQUESTED);
@@ -506,6 +587,30 @@ _haze_connection_start_connecting (TpBaseConnection *base,
return TRUE;
}
+void
+haze_connection_request_password (PurpleAccount *account,
+ void *user_data)
+{
+ HazeConnection *self = ACCOUNT_GET_HAZE_CONNECTION (account);
+ HazeConnectionPrivate *priv = self->priv;
+
+ priv->password_request = user_data;
+
+ /* pop up auth channel */
+ tp_simple_password_manager_prompt_async (self->password_manager,
+ _haze_connection_password_manager_prompt_cb,
+ self);
+}
+
+void
+haze_connection_cancel_password_request (PurpleAccount *account)
+{
+ HazeConnection *self = ACCOUNT_GET_HAZE_CONNECTION (account);
+ HazeConnectionPrivate *priv = self->priv;
+
+ priv->password_request = NULL;
+}
+
static void
_haze_connection_shut_down (TpBaseConnection *base)
{
@@ -557,16 +662,6 @@ _haze_connection_create_channel_managers (TpBaseConnection *base)
g_object_new (HAZE_TYPE_IM_CHANNEL_FACTORY, "connection", self, NULL));
g_ptr_array_add (channel_managers, self->im_factory);
-#ifdef ENABLE_MEDIA
- /* Instantiate the media manager only if the protocol support calls */
- if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC (self->priv->prpl_info, initiate_media))
- {
- self->media_manager = HAZE_MEDIA_MANAGER (
- g_object_new (HAZE_TYPE_MEDIA_MANAGER, "connection", self, NULL));
- g_ptr_array_add (channel_managers, self->media_manager);
- }
-#endif
-
self->password_manager = tp_simple_password_manager_new (
TP_BASE_CONNECTION (self));
g_ptr_array_add (channel_managers, self->password_manager);
@@ -711,8 +806,6 @@ haze_connection_finalize (GObject *object)
tp_contacts_mixin_finalize (object);
tp_presence_mixin_finalize (object);
- haze_connection_capabilities_finalize (object);
-
g_strfreev (self->acceptable_avatar_mime_types);
g_free (priv->username);
g_free (priv->password);
@@ -740,11 +833,15 @@ haze_connection_class_init (HazeConnectionClass *klass)
{ NULL }
};
static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+ { TP_IFACE_CONNECTION_INTERFACE_ALIASING,
+ haze_connection_aliasing_properties_getter,
+ NULL,
+ NULL }, /* initialized a bit later */
{ TP_IFACE_CONNECTION_INTERFACE_AVATARS,
haze_connection_avatars_properties_getter,
NULL,
NULL }, /* initialized a bit later */
- { HAZE_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION,
+ { TP_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION,
haze_connection_mail_properties_getter,
NULL,
mail_props,
@@ -768,8 +865,8 @@ haze_connection_class_init (HazeConnectionClass *klass)
haze_connection_get_unique_connection_name;
base_class->start_connecting = _haze_connection_start_connecting;
base_class->shut_down = _haze_connection_shut_down;
- base_class->interfaces_always_present =
- haze_connection_get_guaranteed_interfaces();
+ base_class->get_interfaces_always_present =
+ haze_connection_get_interfaces_always_present;
param_spec = g_param_spec_boxed ("parameters", "gchar * => GValue",
"Connection parameters (password, etc.)",
@@ -797,7 +894,8 @@ haze_connection_class_init (HazeConnectionClass *klass)
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_PRPL_INFO, param_spec);
- prop_interfaces[0].props = haze_connection_avatars_properties;
+ prop_interfaces[0].props = haze_connection_aliasing_properties;
+ prop_interfaces[1].props = haze_connection_avatars_properties;
klass->properties_class.interfaces = prop_interfaces;
tp_dbus_properties_mixin_class_init (object_class,
G_STRUCT_OFFSET (HazeConnectionClass, properties_class));
@@ -809,7 +907,6 @@ haze_connection_class_init (HazeConnectionClass *klass)
haze_connection_presence_class_init (object_class);
haze_connection_aliasing_class_init (object_class);
haze_connection_avatars_class_init (object_class);
- haze_connection_capabilities_class_init (object_class);
}
static void
diff --git a/src/connection.h b/src/connection.h
index c69a7c1..0eef922 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -22,17 +22,13 @@
*/
#include <glib-object.h>
-#include <telepathy-glib/base-connection.h>
-#include <telepathy-glib/contacts-mixin.h>
-#include <telepathy-glib/presence-mixin.h>
-#include <telepathy-glib/simple-password-manager.h>
+#include <telepathy-glib/telepathy-glib.h>
#include <libpurple/account.h>
#include <libpurple/prpl.h>
#include "contact-list.h"
#include "im-channel-factory.h"
-#include "media-manager.h"
G_BEGIN_DECLS
@@ -54,7 +50,6 @@ struct _HazeConnection {
HazeContactList *contact_list;
HazeImChannelFactory *im_factory;
- HazeMediaManager *media_manager;
TpSimplePasswordManager *password_manager;
TpContactsMixin contacts;
@@ -62,11 +57,6 @@ struct _HazeConnection {
gchar **acceptable_avatar_mime_types;
- GHashTable *client_caps;
-
- /* Part of the hack for Jabber media caps */
- gulong status_changed_id;
-
HazeConnectionPrivate *priv;
};
@@ -108,9 +98,15 @@ GType haze_connection_get_type (void);
const gchar *haze_get_fallback_group (void);
-const gchar **haze_connection_get_implemented_interfaces (void);
+GPtrArray * haze_connection_dup_implemented_interfaces (
+ PurplePluginProtocolInfo *prpl_info);
+
const gchar **haze_connection_get_guaranteed_interfaces (void);
+void haze_connection_request_password (PurpleAccount *account,
+ gpointer user_data);
+void haze_connection_cancel_password_request (PurpleAccount *account);
+
G_END_DECLS
#endif /* #ifndef __HAZE_CONNECTION_H__*/
diff --git a/src/contact-list.c b/src/contact-list.c
index fbbc1f9..7397c1d 100644
--- a/src/contact-list.c
+++ b/src/contact-list.c
@@ -19,15 +19,14 @@
*
*/
+#include <config.h>
+#include "contact-list.h"
+
#include <string.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/gtypes.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/intset.h>
+#include <telepathy-glib/telepathy-glib.h>
#include "connection.h"
-#include "contact-list.h"
#include "debug.h"
typedef struct _PublishRequestData PublishRequestData;
@@ -193,10 +192,7 @@ haze_contact_list_dup_contacts (TpBaseContactList *cl)
purple_buddy_get_name (sl_iter->data), NULL, NULL);
if (G_LIKELY (handle != 0))
- {
- tp_handle_set_add (handles, handle);
- tp_handle_unref (contact_repo, handle);
- }
+ tp_handle_set_add (handles, handle);
}
g_slist_free (buddies);
@@ -311,8 +307,6 @@ buddy_added_cb (PurpleBuddy *buddy, gpointer unused)
group_name = purple_group_get_name (purple_buddy_get_group (buddy));
tp_base_contact_list_one_contact_groups_changed (
(TpBaseContactList *) contact_list, handle, &group_name, 1, NULL, 0);
-
- tp_handle_unref (contact_repo, handle);
}
static void
@@ -330,7 +324,8 @@ buddy_removed_cb (PurpleBuddy *buddy, gpointer unused)
/* Every buddy gets removed after disconnection, because the PurpleAccount
* gets deleted. So let's ignore removals when we're offline.
*/
- if (base_conn->status == TP_CONNECTION_STATUS_DISCONNECTED)
+ if (tp_base_connection_get_status (base_conn) ==
+ TP_CONNECTION_STATUS_DISCONNECTED)
return;
contact_list = conn->contact_list;
@@ -362,8 +357,6 @@ buddy_removed_cb (PurpleBuddy *buddy, gpointer unused)
tp_base_contact_list_one_contact_removed (
(TpBaseContactList *) contact_list, handle);
}
-
- tp_handle_unref (contact_repo, handle);
}
@@ -385,18 +378,11 @@ static void
remove_pending_publish_request (HazeContactList *self,
TpHandle handle)
{
- HazeConnection *conn = self->priv->conn;
- TpBaseConnection *base_conn = TP_BASE_CONNECTION (conn);
- TpHandleRepoIface *handle_repo =
- tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT);
-
gpointer h = GUINT_TO_POINTER (handle);
gboolean removed;
removed = g_hash_table_remove (self->priv->pending_publish_requests, h);
g_assert (removed);
-
- tp_handle_unref (handle_repo, handle);
}
void
@@ -1037,7 +1023,7 @@ haze_contact_list_set_contact_groups_async (TpBaseContactList *cl,
for (i = 0; i < n_names; i++)
{
- if (tp_strdiff (group_name, names[i]))
+ if (!tp_strdiff (group_name, names[i]))
{
desired = TRUE;
break;
diff --git a/src/contact-list.h b/src/contact-list.h
index 59b75c2..647a40d 100644
--- a/src/contact-list.h
+++ b/src/contact-list.h
@@ -22,7 +22,9 @@
#include <glib-object.h>
-#include <telepathy-glib/base-contact-list.h>
+#include <libpurple/account.h>
+
+#include <telepathy-glib/telepathy-glib.h>
G_BEGIN_DECLS
diff --git a/src/debug.c b/src/debug.c
index 976332b..6269edf 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -18,14 +18,14 @@
*
*/
+#include <config.h>
#include "debug.h"
#include <string.h>
#include <stdarg.h>
#include <libpurple/debug.h>
-#include <telepathy-glib/debug.h>
-#include <telepathy-glib/debug-sender.h>
+#include <telepathy-glib/telepathy-glib.h>
typedef enum
diff --git a/src/im-channel-factory.c b/src/im-channel-factory.c
index b4d0901..3147d98 100644
--- a/src/im-channel-factory.c
+++ b/src/im-channel-factory.c
@@ -265,28 +265,24 @@ new_im_channel (HazeImChannelFactory *self,
TpHandle initiator,
gpointer request_token)
{
- TpBaseConnection *conn;
HazeIMChannel *chan;
- char *object_path;
GSList *requests = NULL;
g_assert (HAZE_IS_IM_CHANNEL_FACTORY (self));
- conn = (TpBaseConnection *) self->priv->conn;
-
g_assert (!g_hash_table_lookup (self->priv->channels,
GINT_TO_POINTER (handle)));
- object_path = g_strdup_printf ("%s/ImChannel%u", conn->object_path, handle);
-
chan = g_object_new (HAZE_TYPE_IM_CHANNEL,
"connection", self->priv->conn,
- "object-path", object_path,
"handle", handle,
"initiator-handle", initiator,
+ "requested", (handle != initiator),
NULL);
+ tp_base_channel_register (TP_BASE_CHANNEL (chan));
- DEBUG ("Created IM channel with object path %s", object_path);
+ DEBUG ("Created IM channel with object path %s",
+ tp_base_channel_get_object_path (TP_BASE_CHANNEL (chan)));
g_signal_connect (chan, "closed", G_CALLBACK (im_channel_closed_cb), self);
@@ -301,8 +297,6 @@ new_im_channel (HazeImChannelFactory *self,
TP_EXPORTABLE_CHANNEL (chan), requests);
g_slist_free (requests);
- g_free (object_path);
-
return chan;
}
@@ -453,14 +447,6 @@ haze_create_conversation (PurpleConversation *conv)
static void
haze_destroy_conversation (PurpleConversation *conv)
{
- PurpleAccount *account = purple_conversation_get_account (conv);
-
- HazeImChannelFactory *im_factory =
- ACCOUNT_GET_HAZE_CONNECTION (account)->im_factory;
- TpBaseConnection *base_conn = TP_BASE_CONNECTION (im_factory->priv->conn);
- TpHandleRepoIface *contact_repo =
- tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT);
-
HazeConversationUiData *ui_data;
DEBUG ("(PurpleConversation *)%p destroyed", conv);
@@ -472,7 +458,6 @@ haze_destroy_conversation (PurpleConversation *conv)
ui_data = PURPLE_CONV_GET_HAZE_UI_DATA (conv);
- tp_handle_unref (contact_repo, ui_data->contact_handle);
if (ui_data->resend_typing_timeout_id)
g_source_remove (ui_data->resend_typing_timeout_id);
@@ -583,8 +568,9 @@ haze_im_channel_factory_request (HazeImChannelFactory *self,
goto error;
}
- chan = get_im_channel (self, handle, base_conn->self_handle,
- request_token, &created);
+ chan = get_im_channel (self, handle,
+ tp_base_connection_get_self_handle (base_conn), request_token,
+ &created);
g_assert (chan != NULL);
if (!created)
diff --git a/src/im-channel.c b/src/im-channel.c
index 11a174d..f4fa174 100644
--- a/src/im-channel.c
+++ b/src/im-channel.c
@@ -19,139 +19,92 @@
*
*/
+#include <config.h>
+#include "im-channel.h"
+
#include <telepathy-glib/telepathy-glib.h>
#include <telepathy-glib/telepathy-glib-dbus.h>
-#include "im-channel.h"
#include "connection.h"
#include "debug.h"
-/* properties */
-enum
-{
- PROP_CONNECTION = 1,
- PROP_OBJECT_PATH,
- PROP_CHANNEL_TYPE,
- PROP_HANDLE_TYPE,
- PROP_HANDLE,
- PROP_TARGET_ID,
- PROP_INTERFACES,
- PROP_INITIATOR_HANDLE,
- PROP_INITIATOR_ID,
- PROP_REQUESTED,
- PROP_CHANNEL_PROPERTIES,
- PROP_CHANNEL_DESTROYED,
-
- LAST_PROPERTY
-};
-
struct _HazeIMChannelPrivate
{
- HazeConnection *conn;
- char *object_path;
- TpHandle handle;
- TpHandle initiator;
-
PurpleConversation *conv;
-
- gboolean closed;
gboolean dispose_has_run;
};
-static void channel_iface_init (gpointer, gpointer);
static void destroyable_iface_init (gpointer g_iface, gpointer iface_data);
static void chat_state_iface_init (gpointer g_iface, gpointer iface_data);
-G_DEFINE_TYPE_WITH_CODE(HazeIMChannel, haze_im_channel, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
+G_DEFINE_TYPE_WITH_CODE(HazeIMChannel, haze_im_channel, TP_TYPE_BASE_CHANNEL,
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT,
tp_message_mixin_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_DESTROYABLE,
destroyable_iface_init);
+
+ /* For some reason we reimplement ChatState rather than having the
+ * TpMessageMixin do it :-( */
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CHAT_STATE,
- chat_state_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
- tp_dbus_properties_mixin_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL))
+ chat_state_iface_init))
static void
-haze_im_channel_close (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
+haze_im_channel_close (TpBaseChannel *base)
{
- HazeIMChannel *self = HAZE_IM_CHANNEL (iface);
- HazeIMChannelPrivate *priv = self->priv;
+ HazeIMChannel *self = HAZE_IM_CHANNEL (base);
- if (priv->closed)
- {
- DEBUG ("Already closed");
- goto out;
- }
-
- /* requires support from TpChannelManager */
+ /* The IM factory will resurrect the channel if we have pending
+ * messages. When we're resurrected, we want the initiator
+ * to be the contact who sent us those messages, if it isn't already */
if (tp_message_mixin_has_pending_messages ((GObject *) self, NULL))
{
- if (priv->initiator != priv->handle)
- {
- TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
- (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT);
-
- g_assert (priv->initiator != 0);
- g_assert (priv->handle != 0);
-
- tp_handle_unref (contact_repo, priv->initiator);
- priv->initiator = priv->handle;
- tp_handle_ref (contact_repo, priv->initiator);
- }
-
+ DEBUG ("Not really closing, I still have pending messages");
tp_message_mixin_set_rescued ((GObject *) self);
+ tp_base_channel_reopened (base,
+ tp_base_channel_get_target_handle (base));
}
else
{
- purple_conversation_destroy (priv->conv);
- priv->conv = NULL;
- priv->closed = TRUE;
+ tp_clear_pointer (&self->priv->conv, purple_conversation_destroy);
+ tp_base_channel_destroyed (base);
}
+}
- tp_svc_channel_emit_closed (iface);
+static PurpleAccount *
+haze_im_channel_get_account (HazeIMChannel *self)
+{
+ TpBaseChannel *base = TP_BASE_CHANNEL (self);
+ TpBaseConnection *base_conn = tp_base_channel_get_connection (base);
+ HazeConnection *conn = HAZE_CONNECTION (base_conn);
-out:
- tp_svc_channel_return_from_close(context);
+ return conn->account;
}
static gboolean
_chat_state_available (HazeIMChannel *chan)
{
PurplePluginProtocolInfo *prpl_info =
- PURPLE_PLUGIN_PROTOCOL_INFO (chan->priv->conn->account->gc->prpl);
+ PURPLE_PLUGIN_PROTOCOL_INFO (
+ haze_im_channel_get_account (chan)->gc->prpl);
return (prpl_info->send_typing != NULL);
}
-static const char * const*
-_haze_im_channel_interfaces (HazeIMChannel *chan)
+static GPtrArray *
+haze_im_channel_get_interfaces (TpBaseChannel *base)
{
- static const char * const interfaces[] = {
- TP_IFACE_CHANNEL_INTERFACE_CHAT_STATE,
- TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE,
- NULL
- };
-
- if (_chat_state_available (chan))
- return interfaces;
- else
- return interfaces + 1;
-}
+ HazeIMChannel *self = HAZE_IM_CHANNEL (base);
+ GPtrArray *interfaces;
-static void
-channel_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface;
+ interfaces = TP_BASE_CHANNEL_CLASS (
+ haze_im_channel_parent_class)->get_interfaces (base);
-#define IMPLEMENT(x) tp_svc_channel_implement_##x (\
- klass, haze_im_channel_##x)
- IMPLEMENT(close);
-#undef IMPLEMENT
+ if (_chat_state_available (self))
+ g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_CHAT_STATE);
+
+ g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_DESTROYABLE);
+
+ return interfaces;
}
/**
@@ -173,9 +126,8 @@ haze_im_channel_destroy (TpSvcChannelInterfaceDestroyable *iface,
/* Clear out any pending messages */
tp_message_mixin_clear ((GObject *) self);
- /* Close() and Destroy() have the same signature, so we can safely
- * chain to the other function now */
- haze_im_channel_close ((TpSvcChannel *) self, context);
+ haze_im_channel_close (TP_BASE_CHANNEL (self));
+ tp_svc_channel_interface_destroyable_return_from_destroy (context);
}
static void
@@ -397,123 +349,21 @@ err:
}
static void
-haze_im_channel_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- HazeIMChannel *chan = HAZE_IM_CHANNEL (object);
- HazeIMChannelPrivate *priv = chan->priv;
- TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn;
-
- switch (property_id) {
- case PROP_OBJECT_PATH:
- g_value_set_string (value, priv->object_path);
- break;
- case PROP_CHANNEL_TYPE:
- g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TEXT);
- break;
- case PROP_HANDLE_TYPE:
- g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
- break;
- case PROP_HANDLE:
- g_value_set_uint (value, priv->handle);
- break;
- case PROP_TARGET_ID:
- {
- TpHandleRepoIface *repo = tp_base_connection_get_handles (base_conn,
- TP_HANDLE_TYPE_CONTACT);
-
- g_value_set_string (value, tp_handle_inspect (repo, priv->handle));
- break;
- }
- case PROP_INITIATOR_HANDLE:
- g_value_set_uint (value, priv->initiator);
- break;
- case PROP_INITIATOR_ID:
- {
- TpHandleRepoIface *repo = tp_base_connection_get_handles (base_conn,
- TP_HANDLE_TYPE_CONTACT);
-
- g_value_set_string (value, tp_handle_inspect (repo, priv->initiator));
- break;
- }
- case PROP_REQUESTED:
- g_value_set_boolean (value,
- (priv->initiator == base_conn->self_handle));
- break;
- case PROP_CONNECTION:
- g_value_set_object (value, priv->conn);
- break;
- case PROP_INTERFACES:
- g_value_set_boxed (value, _haze_im_channel_interfaces (chan));
- break;
- case PROP_CHANNEL_DESTROYED:
- g_value_set_boolean (value, priv->closed);
- break;
- case PROP_CHANNEL_PROPERTIES:
- g_value_take_boxed (value,
- tp_dbus_properties_mixin_make_properties_hash (object,
- TP_IFACE_CHANNEL, "TargetHandle",
- TP_IFACE_CHANNEL, "TargetHandleType",
- TP_IFACE_CHANNEL, "ChannelType",
- TP_IFACE_CHANNEL, "TargetID",
- TP_IFACE_CHANNEL, "InitiatorHandle",
- TP_IFACE_CHANNEL, "InitiatorID",
- TP_IFACE_CHANNEL, "Requested",
- TP_IFACE_CHANNEL, "Interfaces",
- TP_IFACE_CHANNEL_TYPE_TEXT,
- "MessagePartSupportFlags",
- TP_IFACE_CHANNEL_TYPE_TEXT,
- "DeliveryReportingSupport",
- TP_IFACE_CHANNEL_TYPE_TEXT,
- "SupportedContentTypes",
- TP_IFACE_CHANNEL_TYPE_TEXT, "MessageTypes",
- NULL));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-haze_im_channel_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
+haze_im_channel_fill_immutable_properties (TpBaseChannel *chan,
+ GHashTable *properties)
{
- HazeIMChannel *chan = HAZE_IM_CHANNEL (object);
- HazeIMChannelPrivate *priv = chan->priv;
-
- switch (property_id) {
- case PROP_OBJECT_PATH:
- g_free (priv->object_path);
- priv->object_path = g_value_dup_string (value);
- break;
- case PROP_HANDLE:
- /* we don't ref it here because we don't have access to the
- * contact repo yet - instead we ref it in the constructor.
- */
- priv->handle = g_value_get_uint (value);
- break;
- case PROP_INITIATOR_HANDLE:
- /* similarly we can't ref this yet */
- priv->initiator = g_value_get_uint (value);
- break;
- case PROP_CHANNEL_TYPE:
- case PROP_HANDLE_TYPE:
- /* this property is writable in the interface, but not actually
- * meaningfully changable on this channel, so we do nothing.
- */
- break;
- case PROP_CONNECTION:
- priv->conn = g_value_get_object (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
+ TpBaseChannelClass *cls = TP_BASE_CHANNEL_CLASS (
+ haze_im_channel_parent_class);
+
+ cls->fill_immutable_properties (chan, properties);
+
+ tp_dbus_properties_mixin_fill_properties_hash (
+ G_OBJECT (chan), properties,
+ TP_IFACE_CHANNEL_TYPE_TEXT, "MessagePartSupportFlags",
+ TP_IFACE_CHANNEL_TYPE_TEXT, "DeliveryReportingSupport",
+ TP_IFACE_CHANNEL_TYPE_TEXT, "SupportedContentTypes",
+ TP_IFACE_CHANNEL_TYPE_TEXT, "MessageTypes",
+ NULL);
}
static const TpChannelTextMessageType supported_message_types[] = {
@@ -534,31 +384,21 @@ haze_im_channel_constructor (GType type, guint n_props,
GObject *obj;
HazeIMChannel *chan;
HazeIMChannelPrivate *priv;
- TpHandleRepoIface *contact_handles;
+ TpBaseChannel *base;
TpBaseConnection *conn;
- TpDBusDaemon *bus;
obj = G_OBJECT_CLASS (haze_im_channel_parent_class)->
constructor (type, n_props, props);
chan = HAZE_IM_CHANNEL (obj);
+ base = TP_BASE_CHANNEL (obj);
priv = chan->priv;
- conn = (TpBaseConnection *) (priv->conn);
-
- contact_handles = tp_base_connection_get_handles (conn,
- TP_HANDLE_TYPE_CONTACT);
- tp_handle_ref (contact_handles, priv->handle);
- g_assert (priv->initiator != 0);
- tp_handle_ref (contact_handles, priv->initiator);
+ conn = tp_base_channel_get_connection (base);
tp_message_mixin_init (obj, G_STRUCT_OFFSET (HazeIMChannel, messages),
conn);
tp_message_mixin_implement_sending (obj, haze_im_channel_send, 3,
supported_message_types, 0, 0, supported_content_types);
- bus = tp_base_connection_get_dbus_daemon (conn);
- tp_dbus_daemon_register_object (bus, priv->object_path, obj);
-
- priv->closed = FALSE;
priv->dispose_has_run = FALSE;
return obj;
@@ -569,128 +409,46 @@ haze_im_channel_dispose (GObject *obj)
{
HazeIMChannel *chan = HAZE_IM_CHANNEL (obj);
HazeIMChannelPrivate *priv = chan->priv;
- TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
- TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (conn,
- TP_HANDLE_TYPE_CONTACT);
if (priv->dispose_has_run)
return;
priv->dispose_has_run = TRUE;
- if (priv->handle != 0)
- tp_handle_unref (contact_handles, priv->handle);
+ tp_clear_pointer (&priv->conv, purple_conversation_destroy);
- if (priv->initiator != 0)
- tp_handle_unref (contact_handles, priv->initiator);
+ tp_message_mixin_finalize (obj);
- if (!priv->closed)
- {
- purple_conversation_destroy (priv->conv);
- priv->conv = NULL;
- tp_svc_channel_emit_closed (obj);
- priv->closed = TRUE;
- }
+ G_OBJECT_CLASS (haze_im_channel_parent_class)->dispose (obj);
+}
- g_free (priv->object_path);
- tp_message_mixin_finalize (obj);
+static gchar *
+haze_im_channel_get_object_path_suffix (TpBaseChannel *chan)
+{
+ return g_strdup_printf ("IMChannel%u",
+ tp_base_channel_get_target_handle (chan));
}
static void
haze_im_channel_class_init (HazeIMChannelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GParamSpec *param_spec;
-
- static gboolean properties_mixin_initialized = FALSE;
- static TpDBusPropertiesMixinPropImpl channel_props[] = {
- { "TargetHandleType", "handle-type", NULL },
- { "TargetHandle", "handle", NULL },
- { "TargetID", "target-id", NULL },
- { "ChannelType", "channel-type", NULL },
- { "Interfaces", "interfaces", NULL },
- { "Requested", "requested", NULL },
- { "InitiatorHandle", "initiator-handle", NULL },
- { "InitiatorID", "initiator-id", NULL },
- { NULL }
- };
- static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
- { TP_IFACE_CHANNEL,
- tp_dbus_properties_mixin_getter_gobject_properties,
- NULL,
- channel_props,
- },
- { NULL }
- };
-
+ TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (klass);
g_type_class_add_private (klass, sizeof (HazeIMChannelPrivate));
- object_class->get_property = haze_im_channel_get_property;
- object_class->set_property = haze_im_channel_set_property;
object_class->constructor = haze_im_channel_constructor;
object_class->dispose = haze_im_channel_dispose;
- g_object_class_override_property (object_class, PROP_OBJECT_PATH,
- "object-path");
- g_object_class_override_property (object_class, PROP_CHANNEL_TYPE,
- "channel-type");
- g_object_class_override_property (object_class, PROP_HANDLE_TYPE,
- "handle-type");
- g_object_class_override_property (object_class, PROP_HANDLE,
- "handle");
- g_object_class_override_property (object_class, PROP_CHANNEL_DESTROYED,
- "channel-destroyed");
- g_object_class_override_property (object_class, PROP_CHANNEL_PROPERTIES,
- "channel-properties");
-
- param_spec = g_param_spec_object ("connection", "HazeConnection object",
- "Haze connection object that owns this IM channel object.",
- HAZE_TYPE_CONNECTION,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
-
- param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
- "Additional Channel.Interface.* interfaces",
- G_TYPE_STRV,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
-
- param_spec = g_param_spec_string ("target-id", "Other person's username",
- "The username of the other person in the conversation",
- NULL,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_TARGET_ID, param_spec);
-
- param_spec = g_param_spec_boolean ("requested", "Requested?",
- "True if this channel was requested by the local user",
- FALSE,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_REQUESTED, param_spec);
-
- param_spec = g_param_spec_uint ("initiator-handle", "Initiator's handle",
- "The contact who initiated the channel",
- 0, G_MAXUINT32, 0,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INITIATOR_HANDLE,
- param_spec);
-
- param_spec = g_param_spec_string ("initiator-id", "Initiator's ID",
- "The string obtained by inspecting the initiator-handle",
- NULL,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INITIATOR_ID,
- param_spec);
-
-
- if (!properties_mixin_initialized)
- {
- properties_mixin_initialized = TRUE;
- klass->properties_class.interfaces = prop_interfaces;
- tp_dbus_properties_mixin_class_init (object_class,
- G_STRUCT_OFFSET (HazeIMChannelClass, properties_class));
+ base_class->channel_type = TP_IFACE_CHANNEL_TYPE_TEXT;
+ base_class->get_interfaces = haze_im_channel_get_interfaces;
+ base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT;
+ base_class->close = haze_im_channel_close;
+ base_class->fill_immutable_properties =
+ haze_im_channel_fill_immutable_properties;
+ base_class->get_object_path_suffix =
+ haze_im_channel_get_object_path_suffix;
- tp_message_mixin_init_dbus_properties (object_class);
- }
+ tp_message_mixin_init_dbus_properties (object_class);
}
static void
@@ -706,14 +464,16 @@ haze_im_channel_start (HazeIMChannel *self)
const char *recipient;
HazeIMChannelPrivate *priv = self->priv;
TpHandleRepoIface *contact_handles;
- TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn;
+ TpBaseChannel *base = TP_BASE_CHANNEL (self);
+ TpBaseConnection *base_conn = tp_base_channel_get_connection (base);
+ HazeConnection *conn = HAZE_CONNECTION (base_conn);
contact_handles = tp_base_connection_get_handles (base_conn,
TP_HANDLE_TYPE_CONTACT);
- recipient = tp_handle_inspect(contact_handles, priv->handle);
+ recipient = tp_handle_inspect (contact_handles,
+ tp_base_channel_get_target_handle (base));
priv->conv = purple_conversation_new (PURPLE_CONV_TYPE_IM,
- priv->conn->account,
- recipient);
+ conn->account, recipient);
}
static TpMessage *
@@ -722,7 +482,8 @@ _make_message (HazeIMChannel *self,
PurpleMessageFlags flags,
time_t mtime)
{
- TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->conn;
+ TpBaseChannel *base = TP_BASE_CHANNEL (self);
+ TpBaseConnection *base_conn = tp_base_channel_get_connection (base);
TpMessage *message = tp_cm_message_new (base_conn, 2);
TpChannelTextMessageType type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
time_t now = time (NULL);
@@ -732,7 +493,8 @@ _make_message (HazeIMChannel *self,
else if (purple_message_meify (text_plain, -1))
type = TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
- tp_cm_message_set_sender (message, self->priv->handle);
+ tp_cm_message_set_sender (message,
+ tp_base_channel_get_target_handle (base));
tp_message_set_uint32 (message, 0, "message-type", type);
/* FIXME: the second half of this test shouldn't be necessary but prpl-jabber
@@ -754,11 +516,13 @@ static TpMessage *
_make_delivery_report (HazeIMChannel *self,
char *text_plain)
{
- TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->conn;
+ TpBaseChannel *base = TP_BASE_CHANNEL (self);
+ TpBaseConnection *base_conn = tp_base_channel_get_connection (base);
TpMessage *report = tp_cm_message_new (base_conn, 2);
/* "MUST be the intended recipient of the original message" */
- tp_cm_message_set_sender (report, self->priv->handle);
+ tp_cm_message_set_sender (report,
+ tp_base_channel_get_target_handle (base));
tp_message_set_uint32 (report, 0, "message-type",
TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT);
/* FIXME: we don't know that the failure is temporary */
@@ -781,6 +545,7 @@ haze_im_channel_receive (HazeIMChannel *self,
PurpleMessageFlags flags,
time_t mtime)
{
+ TpBaseChannel *base = TP_BASE_CHANNEL (self);
gchar *line_broken, *text_plain;
/* Replaces newline characters with <br>, which then get turned back into
@@ -802,7 +567,7 @@ haze_im_channel_receive (HazeIMChannel *self,
_make_delivery_report (self, text_plain));
else
DEBUG ("channel %u: ignoring message %s with flags %u",
- self->priv->handle, text_plain, flags);
+ tp_base_channel_get_target_handle (base), text_plain, flags);
g_free (text_plain);
}
diff --git a/src/im-channel.h b/src/im-channel.h
index 6427828..3486d05 100644
--- a/src/im-channel.h
+++ b/src/im-channel.h
@@ -35,13 +35,13 @@ typedef struct _HazeIMChannelClass HazeIMChannelClass;
struct _HazeIMChannelClass {
- GObjectClass parent_class;
+ TpBaseChannelClass parent_class;
TpDBusPropertiesMixinClass properties_class;
};
struct _HazeIMChannel {
- GObject parent;
+ TpBaseChannel parent;
TpMessageMixin messages;
diff --git a/src/main.c b/src/main.c
index afa0a02..bf4b601 100644
--- a/src/main.c
+++ b/src/main.c
@@ -28,6 +28,7 @@
#include <errno.h>
#include <signal.h>
+#include <dbus/dbus.h>
#include <glib.h>
#include <libpurple/account.h>
@@ -38,15 +39,11 @@
#include <libpurple/prefs.h>
#include <libpurple/util.h>
-#ifdef ENABLE_MEDIA
-#include <libpurple/mediamanager.h>
-#endif
-
#ifdef HAVE_PURPLE_DBUS_UNINIT
#include <libpurple/dbus-server.h>
#endif
-#include <telepathy-glib/run.h>
+#include <telepathy-glib/telepathy-glib.h>
#include "defines.h"
#include "debug.h"
@@ -55,10 +52,6 @@
#include "request.h"
#include "util.h"
-#ifdef ENABLE_MEDIA
-#include "media-backend.h"
-#endif
-
/* Copied verbatim from nullclient, modulo changing whitespace. */
#define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
#define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
@@ -141,9 +134,7 @@ haze_ui_init (void)
purple_accounts_set_ui_ops (haze_get_account_ui_ops ());
purple_conversations_set_ui_ops (haze_get_conv_ui_ops ());
purple_connections_set_ui_ops (haze_get_connection_ui_ops ());
-#ifdef ENABLE_LEAKY_REQUEST_STUBS
purple_request_set_ui_ops (haze_request_get_ui_ops ());
-#endif
purple_notify_set_ui_ops (haze_notify_get_ui_ops ());
purple_privacy_set_ui_ops (haze_get_privacy_ui_ops ());
}
@@ -213,11 +204,6 @@ init_libpurple (void)
PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION);
set_libpurple_preferences ();
-
-#ifdef ENABLE_MEDIA
- purple_media_manager_set_backend_type (purple_media_manager_get (),
- HAZE_TYPE_MEDIA_BACKEND);
-#endif
}
static TpBaseConnectionManager *
@@ -254,6 +240,10 @@ main(int argc,
{
int ret = 0;
+ if (!dbus_threads_init_default ())
+ g_error ("Unable to initialize libdbus for thread-safety "
+ "(out of memory?)");
+
g_set_prgname(UI_ID);
haze_debug_set_flags_from_env ();
diff --git a/src/media-backend.c b/src/media-backend.c
deleted file mode 100644
index ec40af9..0000000
--- a/src/media-backend.c
+++ /dev/null
@@ -1,615 +0,0 @@
-/*
- * media-backend.c - Source for HazeMediaBackend
- * Copyright © 2006-2009 Collabora Ltd.
- * Copyright © 2006-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
- */
-
-#include "config.h"
-#include "media-backend.h"
-
-#include <libpurple/media/backend-iface.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/errors.h>
-#include <telepathy-glib/svc-media-interfaces.h>
-
-#include <string.h>
-
-#include "debug.h"
-
-static void media_backend_iface_init(PurpleMediaBackendIface *iface);
-static void session_handler_iface_init (gpointer g_iface,
- gpointer iface_data);
-static void haze_backend_state_changed_cb (PurpleMedia *media,
- PurpleMediaState state,
- const gchar *sid,
- const gchar *name,
- HazeMediaBackend *backend);
-
-G_DEFINE_TYPE_WITH_CODE (HazeMediaBackend,
- haze_media_backend,
- G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (PURPLE_TYPE_MEDIA_BACKEND,
- media_backend_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_MEDIA_SESSION_HANDLER,
- session_handler_iface_init);
- )
-
-/* properties */
-enum
-{
- PROP_CONFERENCE_TYPE = 1,
- PROP_MEDIA,
- PROP_OBJECT_PATH,
- PROP_STREAMS,
- LAST_PROPERTY
-};
-
-/* private structure */
-struct _HazeMediaBackendPrivate
-{
- gchar *conference_type;
- gchar *object_path;
- gpointer media;
- GPtrArray *streams;
-
- guint next_stream_id;
- gboolean ready;
-};
-
-static void
-haze_media_backend_init (HazeMediaBackend *self)
-{
- HazeMediaBackendPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- HAZE_TYPE_MEDIA_BACKEND, HazeMediaBackendPrivate);
-
- self->priv = priv;
-
- priv->next_stream_id = 1;
- priv->streams = g_ptr_array_sized_new (1);
-}
-
-static void
-haze_media_backend_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- HazeMediaBackend *backend = HAZE_MEDIA_BACKEND (object);
- HazeMediaBackendPrivate *priv = backend->priv;
-
- switch (property_id)
- {
- case PROP_CONFERENCE_TYPE:
- g_value_set_string (value, priv->conference_type);
- break;
- case PROP_MEDIA:
- g_value_set_object (value, priv->media);
- break;
- case PROP_OBJECT_PATH:
- g_value_set_string (value, priv->object_path);
- break;
- case PROP_STREAMS:
- g_value_set_boxed (value, priv->streams);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-haze_media_backend_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- HazeMediaBackend *backend = HAZE_MEDIA_BACKEND (object);
- HazeMediaBackendPrivate *priv = backend->priv;
-
- switch (property_id)
- {
- case PROP_CONFERENCE_TYPE:
- g_free (priv->conference_type);
- priv->conference_type = g_value_dup_string (value);
- break;
- case PROP_MEDIA:
- g_assert (priv->media == NULL);
- priv->media = g_value_get_object (value);
-
- g_object_add_weak_pointer(G_OBJECT(priv->media), &priv->media);
- g_signal_connect (priv->media, "state-changed",
- G_CALLBACK (haze_backend_state_changed_cb), backend);
- break;
- case PROP_OBJECT_PATH:
- g_assert (priv->object_path == NULL);
- priv->object_path = g_value_dup_string (value);
-
- if (priv->object_path != NULL)
- {
- TpDBusDaemon *dbus_daemon = tp_dbus_daemon_dup (NULL);
-
- g_return_if_fail (dbus_daemon != NULL);
- tp_dbus_daemon_register_object (dbus_daemon,
- priv->object_path, G_OBJECT (backend));
- g_object_unref (dbus_daemon);
- }
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void haze_media_backend_dispose (GObject *object);
-static void haze_media_backend_finalize (GObject *object);
-
-static void
-haze_media_backend_class_init (HazeMediaBackendClass *haze_media_backend_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (haze_media_backend_class);
- GParamSpec *param_spec;
-
- g_type_class_add_private (haze_media_backend_class,
- sizeof (HazeMediaBackendPrivate));
-
- object_class->get_property = haze_media_backend_get_property;
- object_class->set_property = haze_media_backend_set_property;
-
- object_class->dispose = haze_media_backend_dispose;
- object_class->finalize = haze_media_backend_finalize;
-
- g_object_class_override_property(object_class, PROP_CONFERENCE_TYPE,
- "conference-type");
- g_object_class_override_property(object_class, PROP_MEDIA, "media");
-
- param_spec = g_param_spec_string ("object-path", "D-Bus object path",
- "The D-Bus object path used for this "
- "object on the bus.",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec);
-
- param_spec = g_param_spec_boxed ("streams", "Streams",
- "List of streams handled by this backend.",
- G_TYPE_PTR_ARRAY,
- G_PARAM_READABLE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_STREAMS, param_spec);
-}
-
-void
-haze_media_backend_dispose (GObject *object)
-{
- DEBUG ("called");
-
- if (G_OBJECT_CLASS (haze_media_backend_parent_class)->dispose)
- G_OBJECT_CLASS (haze_media_backend_parent_class)->dispose (object);
-}
-
-void
-haze_media_backend_finalize (GObject *object)
-{
- HazeMediaBackend *self = HAZE_MEDIA_BACKEND (object);
- HazeMediaBackendPrivate *priv = self->priv;
-
- g_free (priv->conference_type);
- g_free (priv->object_path);
-
- if (priv->streams != NULL)
- g_ptr_array_free (priv->streams, TRUE);
-
- G_OBJECT_CLASS (haze_media_backend_parent_class)->finalize (object);
-}
-
-static HazeMediaStream *
-get_stream_by_name (HazeMediaBackend *self,
- const gchar *sid)
-{
- HazeMediaBackendPrivate *priv = self->priv;
- guint i;
-
- for (i = 0; i < priv->streams->len; ++i)
- {
- HazeMediaStream *stream = g_ptr_array_index (priv->streams, i);
-
- if (!strcmp (sid, stream->name))
- return stream;
- }
-
- return NULL;
-}
-
-HazeMediaStream *
-haze_media_backend_get_stream_by_name (HazeMediaBackend *self,
- const gchar *sid)
-{
- return get_stream_by_name (self, sid);
-}
-
-static void
-haze_backend_state_changed_cb (PurpleMedia *media,
- PurpleMediaState state,
- const gchar *sid,
- const gchar *name,
- HazeMediaBackend *backend)
-{
- HazeMediaBackendPrivate *priv = backend->priv;
-
- if (state == PURPLE_MEDIA_STATE_END && sid != NULL && name == NULL)
- {
- HazeMediaStream *stream = get_stream_by_name (backend, sid);
-
- if (stream != NULL)
- {
- g_ptr_array_remove_fast (priv->streams, stream);
- g_object_unref (stream);
- }
- }
-}
-
-static void
-_emit_new_stream (HazeMediaBackend *self,
- HazeMediaStream *stream)
-{
- gchar *object_path;
- guint id, media_type;
-
- g_object_get (stream,
- "object-path", &object_path,
- "id", &id,
- "media-type", &media_type,
- NULL);
-
- /* all of the streams are bidirectional from farsight's point of view, it's
- * just in the signalling they change */
- DEBUG ("emitting MediaSessionHandler:NewStreamHandler signal for %s stream %d",
- media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video", id);
- tp_svc_media_session_handler_emit_new_stream_handler (self,
- object_path, id, media_type, TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL);
-
- g_free (object_path);
-}
-
-static gboolean
-haze_media_backend_add_stream (PurpleMediaBackend *self,
- const gchar *sid, const gchar *who,
- PurpleMediaSessionType type, gboolean initiator,
- const gchar *transmitter,
- guint num_params, GParameter *params)
-{
- HazeMediaBackendPrivate *priv = HAZE_MEDIA_BACKEND (self)->priv;
- HazeMediaStream *stream;
- gchar *object_path;
- guint media_type, id, stun_port = 3478; /* default stun port */
- const gchar *nat_traversal = NULL, *stun_server = NULL;
- TpDBusDaemon *dbus_daemon = tp_dbus_daemon_dup (NULL);
-
- DEBUG ("called");
-
- g_return_val_if_fail (dbus_daemon != NULL, FALSE);
-
- id = priv->next_stream_id++;
-
- object_path = g_strdup_printf ("%s/MediaStream%u",
- priv->object_path, id);
-
- if (type & PURPLE_MEDIA_AUDIO)
- media_type = TP_MEDIA_STREAM_TYPE_AUDIO;
- else
- media_type = TP_MEDIA_STREAM_TYPE_VIDEO;
-
- if (!strcmp (transmitter, "nice"))
- {
- guint i;
-
- for (i = 0; i < num_params; ++i)
- {
- if (!strcmp (params[i].name, "compatibility-mode") &&
- G_VALUE_HOLDS (&params[i].value, G_TYPE_UINT))
- {
- guint mode = g_value_get_uint (&params[i].value);
-
- switch (mode)
- {
- case 0: /* NICE_COMPATIBILITY_DRAFT19 */
- nat_traversal = "ice-udp";
- break;
- case 1: /* NICE_COMPATIBILITY_GOOGLE */
- nat_traversal = "gtalk-p2p";
- break;
- case 2: /* NICE_COMPATIBILITY_MSN */
- nat_traversal = "wlm-8.5";
- break;
- case 3: /* NICE_COMPATIBILITY_WLM2009 */
- nat_traversal = "wlm-2009";
- break;
- default:
- g_assert_not_reached ();
- }
- }
- else if (!strcmp (params[i].name, "stun-ip") &&
- G_VALUE_HOLDS (&params[i].value, G_TYPE_STRING))
- {
- stun_server = g_value_get_string (&params[i].value);
- }
- else if (!strcmp (params[i].name, "stun-port") &&
- G_VALUE_HOLDS (&params[i].value, G_TYPE_UINT))
- {
- stun_port = g_value_get_uint (&params[i].value);
- }
- }
-
- if (nat_traversal == NULL)
- nat_traversal = "ice-udp";
- }
- else if (!strcmp (transmitter, "rawudp"))
- {
- nat_traversal = "none";
- }
- else
- {
- g_assert_not_reached ();
- }
-
- stream = haze_media_stream_new (object_path, dbus_daemon, priv->media,
- sid, who, media_type, id, initiator, nat_traversal, NULL, FALSE);
-
- if (stun_server != NULL)
- haze_media_stream_add_stun_server (stream, stun_server, stun_port);
-
- g_free (object_path);
-
- DEBUG ("%p: created new MediaStream %p for sid '%s'",
- self, stream, sid);
-
- g_ptr_array_add (priv->streams, stream);
-
- if (priv->ready)
- _emit_new_stream (HAZE_MEDIA_BACKEND (self), stream);
-
- g_object_unref (dbus_daemon);
-
- return TRUE;
-}
-
-static void
-haze_media_backend_add_remote_candidates (PurpleMediaBackend *self,
- const gchar *sid,
- const gchar *who,
- GList *remote_candidates)
-{
- HazeMediaStream *stream;
-
- DEBUG ("called");
-
- stream = get_stream_by_name (HAZE_MEDIA_BACKEND (self), sid);
-
- if (stream != NULL)
- haze_media_stream_add_remote_candidates (stream, remote_candidates);
- else
- DEBUG ("Couldn't find stream");
-}
-
-static gboolean
-haze_media_backend_codecs_ready (PurpleMediaBackend *self,
- const gchar *sid)
-{
- HazeMediaStream *stream;
- gboolean ready = FALSE;
-
- DEBUG ("called");
-
- if (sid != NULL)
- {
- stream = get_stream_by_name (HAZE_MEDIA_BACKEND (self), sid);
-
- if (stream != NULL)
- g_object_get (stream, "codecs-ready", &ready, NULL);
-
- return ready;
- }
- else
- {
- HazeMediaBackendPrivate *priv = HAZE_MEDIA_BACKEND (self)->priv;
- guint i;
-
- for (i = 0; i < priv->streams->len; ++i)
- {
- stream = g_ptr_array_index (priv->streams, i);
-
- if (stream != NULL)
- g_object_get (stream, "codecs-ready", &ready, NULL);
-
- if (!ready)
- return FALSE;
- }
-
- return TRUE;
- }
-}
-
-static GList *
-haze_media_backend_get_codecs (PurpleMediaBackend *self,
- const gchar *sid)
-{
- HazeMediaStream *stream;
- GList *ret = NULL;
-
- DEBUG ("called");
-
- stream = get_stream_by_name (HAZE_MEDIA_BACKEND (self), sid);
-
- if (stream != NULL)
- ret = haze_media_stream_get_codecs (stream);
-
- return ret;
-}
-
-static GList *
-haze_media_backend_get_local_candidates (PurpleMediaBackend *self,
- const gchar *sid,
- const gchar *who)
-{
- HazeMediaStream *stream;
- GList *ret = NULL;
-
- DEBUG ("called");
-
- stream = get_stream_by_name (HAZE_MEDIA_BACKEND (self), sid);
-
- if (stream != NULL)
- ret = haze_media_stream_get_local_candidates (stream);
-
- return ret;
-}
-
-static gboolean
-haze_media_backend_set_remote_codecs (PurpleMediaBackend *self,
- const gchar *sid,
- const gchar *who,
- GList *codecs)
-{
- HazeMediaStream *stream;
-
- DEBUG ("called");
-
- stream = get_stream_by_name (HAZE_MEDIA_BACKEND (self), sid);
-
- if (stream != NULL)
- haze_media_stream_set_remote_codecs (stream, codecs);
- else
- DEBUG ("Couldn't find stream");
-
- return TRUE;
-}
-
-static gboolean
-haze_media_backend_set_send_codec (PurpleMediaBackend *self,
- const gchar *sid,
- PurpleMediaCodec *codec)
-{
- return FALSE;
-}
-
-static void
-haze_media_backend_ready (TpSvcMediaSessionHandler *iface,
- DBusGMethodInvocation *context)
-{
- HazeMediaBackend *self = HAZE_MEDIA_BACKEND (iface);
- HazeMediaBackendPrivate *priv = self->priv;
-
- if (!priv->ready)
- {
- guint i;
-
- DEBUG ("emitting NewStreamHandler for each stream");
-
- priv->ready = TRUE;
-
- for (i = 0; i < priv->streams->len; i++)
- _emit_new_stream (self, g_ptr_array_index (priv->streams, i));
- }
-
- tp_svc_media_session_handler_return_from_ready (context);
-}
-
-static void
-haze_media_backend_error (TpSvcMediaSessionHandler *iface,
- guint errno,
- const gchar *message,
- DBusGMethodInvocation *context)
-{
- HazeMediaBackend *self = HAZE_MEDIA_BACKEND (iface);
- HazeMediaBackendPrivate *priv;
- GPtrArray *tmp;
- guint i;
-
- g_assert (HAZE_IS_MEDIA_BACKEND (self));
-
- priv = self->priv;
-
- if (priv->media == NULL)
- {
- /* This could also be because someone called Error() before the
- * SessionHandler was announced. But the fact that the SessionHandler is
- * actually also the Channel, and thus this method is available before
- * NewSessionHandler is emitted, is an implementation detail. So the
- * error message describes the only legitimate situation in which this
- * could arise.
- */
- GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "call has already ended" };
-
- DEBUG ("no session, returning an error.");
- dbus_g_method_return_error (context, &e);
- return;
- }
-
- DEBUG ("Media.SessionHandler::Error called, error %u (%s) -- "
- "emitting error on each stream", errno, message);
-
- purple_media_end (priv->media, NULL, NULL);
-
- /* Calling haze_media_stream_error () on all the streams will ultimately
- * cause them all to emit 'closed'. In response to 'closed', stream_close_cb
- * unrefs them, and removes them from priv->streams. So, we copy the stream
- * list to avoid it being modified from underneath us.
- */
- tmp = g_ptr_array_sized_new (priv->streams->len);
-
- for (i = 0; i < priv->streams->len; i++)
- g_ptr_array_add (tmp, g_ptr_array_index (priv->streams, i));
-
- for (i = 0; i < tmp->len; i++)
- {
- HazeMediaStream *stream = g_ptr_array_index (tmp, i);
-
- haze_media_stream_error (stream, errno, message, NULL);
- }
-
- g_ptr_array_free (tmp, TRUE);
-
- tp_svc_media_session_handler_return_from_error (context);
-}
-
-static void
-media_backend_iface_init (PurpleMediaBackendIface *iface)
-{
-#define IMPLEMENT(x) iface->x = haze_media_backend_##x
- IMPLEMENT(add_stream);
- IMPLEMENT(add_remote_candidates);
- IMPLEMENT(codecs_ready);
- IMPLEMENT(get_codecs);
- IMPLEMENT(get_local_candidates);
- IMPLEMENT(set_remote_codecs);
- IMPLEMENT(set_send_codec);
-#undef IMPLEMENT
-}
-
-static void
-session_handler_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcMediaSessionHandlerClass *klass =
- (TpSvcMediaSessionHandlerClass *) g_iface;
-
-#define IMPLEMENT(x) tp_svc_media_session_handler_implement_##x (\
- klass, haze_media_backend_##x)
- IMPLEMENT(error);
- IMPLEMENT(ready);
-#undef IMPLEMENT
-}
diff --git a/src/media-backend.h b/src/media-backend.h
deleted file mode 100644
index d673c55..0000000
--- a/src/media-backend.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * media-backend.h - Header for HazeMediaBackend
- * Copyright (C) 2006, 2009 Collabora Ltd.
- * Copyright (C) 2006 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 __HAZE_MEDIA_BACKEND__
-#define __HAZE_MEDIA_BACKEND__
-
-#include <glib-object.h>
-
-#include "media-stream.h"
-
-G_BEGIN_DECLS
-
-typedef struct _HazeMediaBackend HazeMediaBackend;
-typedef struct _HazeMediaBackendClass HazeMediaBackendClass;
-typedef struct _HazeMediaBackendPrivate HazeMediaBackendPrivate;
-
-struct _HazeMediaBackendClass {
- GObjectClass parent_class;
-};
-
-struct _HazeMediaBackend {
- GObject parent;
-
- HazeMediaBackendPrivate *priv;
-};
-
-GType haze_media_backend_get_type (void);
-HazeMediaStream *haze_media_backend_get_stream_by_name (
- HazeMediaBackend *self,
- const gchar *sid);
-
-/* TYPE MACROS */
-#define HAZE_TYPE_MEDIA_BACKEND \
- (haze_media_backend_get_type ())
-#define HAZE_MEDIA_BACKEND(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj), HAZE_TYPE_MEDIA_BACKEND, \
- HazeMediaBackend))
-#define HAZE_MEDIA_BACKEND_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass), HAZE_TYPE_MEDIA_BACKEND, \
- HazeMediaBackendClass))
-#define HAZE_IS_MEDIA_BACKEND(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj), HAZE_TYPE_MEDIA_BACKEND))
-#define HAZE_IS_MEDIA_BACKEND_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass), HAZE_TYPE_MEDIA_BACKEND))
-#define HAZE_MEDIA_BACKEND_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS ((obj), HAZE_TYPE_MEDIA_BACKEND, \
- HazeMediaBackendClass))
-
-G_END_DECLS
-
-#endif /* #ifndef __HAZE_MEDIA_BACKEND__ */
diff --git a/src/media-channel.c b/src/media-channel.c
deleted file mode 100644
index c81974e..0000000
--- a/src/media-channel.c
+++ /dev/null
@@ -1,1784 +0,0 @@
-/*
- * media-channel.c - Source for HazeMediaChannel
- * Copyright (C) 2006, 2009 Collabora Ltd.
- * Copyright (C) 2006 Nokia Corporation
- *
- * Copied heavily from telepathy-gabble
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "config.h"
-#include "media-channel.h"
-
-#include <libpurple/media/backend-iface.h>
-#include <libpurple/mediamanager.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/channel-iface.h>
-#include <telepathy-glib/gtypes.h>
-#include <telepathy-glib/svc-channel.h>
-#include <telepathy-glib/svc-properties-interface.h>
-#include <telepathy-glib/svc-media-interfaces.h>
-
-#include "connection.h"
-#include "debug.h"
-#include "media-backend.h"
-#include "media-stream.h"
-
-static void channel_iface_init (gpointer, gpointer);
-static void media_signalling_iface_init (gpointer, gpointer);
-static void streamed_media_iface_init (gpointer, gpointer);
-static gboolean haze_media_channel_add_member (GObject *obj,
- TpHandle handle,
- const gchar *message,
- GError **error);
-static gboolean haze_media_channel_remove_member (GObject *obj,
- TpHandle handle, const gchar *message, guint reason, GError **error);
-
-G_DEFINE_TYPE_WITH_CODE (HazeMediaChannel, haze_media_channel,
- G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
- tp_group_mixin_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_STREAMED_MEDIA,
- streamed_media_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MEDIA_SIGNALLING,
- media_signalling_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
- tp_dbus_properties_mixin_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_EXPORTABLE_CHANNEL, NULL);
- G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL));
-
-static const gchar *haze_media_channel_interfaces[] = {
- TP_IFACE_CHANNEL_INTERFACE_GROUP,
- TP_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING,
- NULL
-};
-
-/* properties */
-enum
-{
- PROP_OBJECT_PATH = 1,
- PROP_CHANNEL_TYPE,
- PROP_HANDLE_TYPE,
- PROP_HANDLE,
- PROP_TARGET_ID,
- PROP_INITIAL_PEER,
- PROP_PEER,
- PROP_REQUESTED,
- PROP_CONNECTION,
- PROP_CREATOR,
- PROP_CREATOR_ID,
- PROP_INTERFACES,
- PROP_CHANNEL_DESTROYED,
- PROP_CHANNEL_PROPERTIES,
- PROP_INITIAL_AUDIO,
- PROP_INITIAL_VIDEO,
- PROP_MEDIA,
- LAST_PROPERTY
-};
-
-struct _HazeMediaChannelPrivate
-{
- HazeConnection *conn;
- gchar *object_path;
- TpHandle creator;
- TpHandle initial_peer;
-
- PurpleMedia *media;
-
- guint next_stream_id;
-
- /* list of PendingStreamRequest* in no particular order */
- GList *pending_stream_requests;
-
- TpLocalHoldState hold_state;
- TpLocalHoldStateReason hold_state_reason;
-
- TpChannelCallStateFlags call_state;
-
- gboolean initial_audio;
- gboolean initial_video;
-
- gboolean ready;
- gboolean media_ended;
- gboolean closed;
- gboolean dispose_has_run;
-};
-
-static void
-haze_media_channel_init (HazeMediaChannel *self)
-{
- HazeMediaChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- HAZE_TYPE_MEDIA_CHANNEL, HazeMediaChannelPrivate);
-
- self->priv = priv;
-
- priv->next_stream_id = 1;
-}
-
-/**
- * make_stream_list:
- *
- * Creates an array of MediaStreamInfo structs.
- */
-static GPtrArray *
-make_stream_list (HazeMediaChannel *self,
- guint len,
- HazeMediaStream **streams)
-{
- HazeMediaChannelPrivate *priv = self->priv;
- GPtrArray *ret;
- guint i;
- GType info_type = TP_STRUCT_TYPE_MEDIA_STREAM_INFO;
-
- ret = g_ptr_array_sized_new (len);
-
- for (i = 0; i < len; i++)
- {
- GValue entry = { 0, };
- guint id;
- TpHandle peer;
- TpMediaStreamType type;
- TpMediaStreamState connection_state;
- CombinedStreamDirection combined_direction;
-
- g_object_get (streams[i],
- "id", &id,
- "media-type", &type,
- "connection-state", &connection_state,
- "combined-direction", &combined_direction,
- NULL);
-
- peer = priv->initial_peer;
-
- g_value_init (&entry, info_type);
- g_value_take_boxed (&entry,
- dbus_g_type_specialized_construct (info_type));
-
- dbus_g_type_struct_set (&entry,
- 0, id,
- 1, peer,
- 2, type,
- 3, connection_state,
- 4, COMBINED_DIRECTION_GET_DIRECTION (combined_direction),
- 5, COMBINED_DIRECTION_GET_PENDING_SEND (combined_direction),
- G_MAXUINT);
-
- g_ptr_array_add (ret, g_value_get_boxed (&entry));
- }
-
- return ret;
-}
-
-typedef struct {
- /* number of streams requested == number of content objects */
- guint len;
- /* array of @len borrowed pointers */
- guint *types;
- /* accumulates borrowed pointers to streams. Initially @len NULL pointers;
- * when the stream for contents[i] is created, it is stored at streams[i].
- */
- HazeMediaStream **streams;
- /* number of non-NULL elements in streams (0 <= satisfied <= contents) */
- guint satisfied;
- /* succeeded_cb(context, GPtrArray<TP_STRUCT_TYPE_MEDIA_STREAM_INFO>)
- * will be called if the stream request succeeds.
- */
- GFunc succeeded_cb;
- /* failed_cb(context, GError *) will be called if the stream request fails.
- */
- GFunc failed_cb;
- gpointer context;
-} PendingStreamRequest;
-
-static PendingStreamRequest *
-pending_stream_request_new (const GArray *types,
- GFunc succeeded_cb,
- GFunc failed_cb,
- gpointer context)
-{
- PendingStreamRequest *p = g_slice_new0 (PendingStreamRequest);
-
- g_assert (succeeded_cb);
- g_assert (failed_cb);
-
- p->len = types->len;
- p->types = g_memdup (types->data, types->len * sizeof (gpointer));
- p->streams = g_new0 (HazeMediaStream *, types->len);
- p->satisfied = 0;
- p->succeeded_cb = succeeded_cb;
- p->failed_cb = failed_cb;
- p->context = context;
-
- return p;
-}
-
-static gboolean
-pending_stream_request_maybe_satisfy (PendingStreamRequest *p,
- HazeMediaChannel *channel,
- guint type,
- HazeMediaStream *stream)
-{
- guint i;
-
- for (i = 0; i < p->len; i++)
- {
- if (p->types[i] == type)
- {
- g_assert (p->streams[i] == NULL);
- p->streams[i] = stream;
-
- if (++p->satisfied == p->len && p->context != NULL)
- {
- GPtrArray *ret = make_stream_list (channel, p->len, p->streams);
-
- p->succeeded_cb (p->context, ret);
- g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL);
- g_ptr_array_free (ret, TRUE);
- p->context = NULL;
- return TRUE;
- }
- }
- }
-
- return FALSE;
-}
-
-static void
-pending_stream_request_free (gpointer data)
-{
- PendingStreamRequest *p = data;
-
- if (p->context != NULL)
- {
- GError e = { TP_ERROR, TP_ERROR_CANCELLED,
- "The session terminated before the requested streams could be added"
- };
-
- p->failed_cb (p->context, &e);
- }
-
- g_free (p->types);
- g_free (p->streams);
-
- g_slice_free (PendingStreamRequest, p);
-}
-
-static void
-stream_direction_changed_cb (HazeMediaStream *stream,
- GParamSpec *pspec,
- HazeMediaChannel *chan)
-{
- guint id;
- CombinedStreamDirection combined;
- TpMediaStreamDirection direction;
- TpMediaStreamPendingSend pending_send;
-
- g_object_get (stream,
- "id", &id,
- "combined-direction", &combined,
- NULL);
-
- direction = COMBINED_DIRECTION_GET_DIRECTION (combined);
- pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (combined);
-
- DEBUG ("direction: %u, pending_send: %u", direction, pending_send);
-
- tp_svc_channel_type_streamed_media_emit_stream_direction_changed (
- chan, id, direction, pending_send);
-}
-
-static void
-media_error_cb (PurpleMedia *media,
- const gchar *error,
- HazeMediaChannel *chan)
-{
- g_assert (HAZE_MEDIA_CHANNEL(chan)->priv != NULL);
- DEBUG ("Media error on %s: %s", chan->priv->object_path, error);
-}
-
-static void
-media_state_changed_cb (PurpleMedia *media,
- PurpleMediaState state,
- gchar *sid, gchar *name,
- HazeMediaChannel *chan)
-{
- HazeMediaChannelPrivate *priv = chan->priv;
-
- DEBUG ("%s %s %s",
- state == PURPLE_MEDIA_STATE_NEW ? "NEW" :
- state == PURPLE_MEDIA_STATE_CONNECTED ? "CONNECTED" :
- state == PURPLE_MEDIA_STATE_END ? "END" :
- "UNKNOWN", sid, name);
-
- if (state == PURPLE_MEDIA_STATE_NEW)
- {
- if (sid != NULL && name != NULL)
- {
- HazeMediaBackend *backend;
- HazeMediaStream *stream;
- TpMediaStreamType type;
- guint id;
-
- g_object_get (priv->media, "backend", &backend, NULL);
- stream = haze_media_backend_get_stream_by_name (backend, sid);
- g_object_unref (backend);
-
- g_object_get (G_OBJECT (stream), "id", &id, NULL);
- type = haze_media_stream_get_media_type (stream);
-
- /* if any RequestStreams call was waiting for a stream to be created for
- * that content, return from it successfully */
- {
- GList *iter = priv->pending_stream_requests;
-
- while (iter != NULL)
- {
- if (pending_stream_request_maybe_satisfy (iter->data,
- chan, type, stream))
- {
- GList *dead = iter;
-
- pending_stream_request_free (dead->data);
-
- iter = dead->next;
- priv->pending_stream_requests = g_list_delete_link (
- priv->pending_stream_requests, dead);
- }
- else
- {
- iter = iter->next;
- }
- }
- }
-
- g_signal_connect (stream, "notify::combined-direction",
- (GCallback) stream_direction_changed_cb, chan);
-
- tp_svc_channel_type_streamed_media_emit_stream_added (
- chan, id, priv->initial_peer, type);
-
- stream_direction_changed_cb (stream, NULL, chan);
- }
- }
-
- if (sid != NULL && name == NULL)
- {
- TpMediaStreamState tp_state;
- HazeMediaBackend *backend;
- HazeMediaStream *stream;
-
- if (state == PURPLE_MEDIA_STATE_NEW)
- tp_state = TP_MEDIA_STREAM_STATE_CONNECTING;
- else if (state == PURPLE_MEDIA_STATE_CONNECTED)
- tp_state = TP_MEDIA_STREAM_STATE_CONNECTED;
- else if (state == PURPLE_MEDIA_STATE_END)
- tp_state = TP_MEDIA_STREAM_STATE_DISCONNECTED;
- else
- {
- DEBUG ("Invalid state %d", state);
- return;
- }
-
- g_object_get (G_OBJECT (priv->media), "backend", &backend, NULL);
- stream = haze_media_backend_get_stream_by_name (backend, sid);
- g_object_unref (backend);
-
- if (stream != NULL)
- {
- guint id;
- g_object_get (stream, "id", &id, NULL);
- tp_svc_channel_type_streamed_media_emit_stream_state_changed (chan,
- id, tp_state);
- }
- }
-
- if (state == PURPLE_MEDIA_STATE_END)
- {
- if (sid != NULL && name == NULL)
- {
- HazeMediaBackend *backend;
- HazeMediaStream *stream;
-
- g_object_get (G_OBJECT (priv->media), "backend", &backend, NULL);
- stream = haze_media_backend_get_stream_by_name (backend, sid);
- g_object_unref (backend);
-
- if (stream != NULL)
- {
- guint id;
- g_object_get (stream, "id", &id, NULL);
- tp_svc_channel_type_streamed_media_emit_stream_removed (
- chan, id);
- }
- }
- else if (sid == NULL && name == NULL)
- {
- TpGroupMixin *mixin = TP_GROUP_MIXIN (chan);
- guint terminator;
- TpHandle peer;
- TpIntset *set;
-
- priv->media_ended = TRUE;
-
- peer = priv->initial_peer;
-
- /*
- * Primarily, sessions will be ended with hangup or reject. Any that
- * aren't are because of local errors so set the terminator to self.
- */
- terminator = mixin->self_handle;
-
- set = tp_intset_new ();
-
- /* remove us and the peer from the member list */
- tp_intset_add (set, mixin->self_handle);
- tp_intset_add (set, peer);
-
- tp_group_mixin_change_members ((GObject *) chan,
- "Media session ended", NULL, set, NULL, NULL,
- terminator, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
-
- tp_intset_destroy (set);
-
- /* any contents that we were waiting for have now lost */
- g_list_foreach (priv->pending_stream_requests,
- (GFunc) pending_stream_request_free, NULL);
- g_list_free (priv->pending_stream_requests);
- priv->pending_stream_requests = NULL;
-
- if (!priv->closed)
- {
- DEBUG ("calling media channel close from state changed cb");
- haze_media_channel_close (chan);
- }
- }
- }
-}
-
-static void
-media_stream_info_cb(PurpleMedia *media,
- PurpleMediaInfoType type,
- gchar *sid,
- gchar *name,
- gboolean local,
- HazeMediaChannel *chan)
-{
- HazeMediaChannelPrivate *priv = chan->priv;
- TpBaseConnection *conn = (TpBaseConnection *)priv->conn;
-
- if (type == PURPLE_MEDIA_INFO_ACCEPT)
- {
- TpIntset *set;
- TpHandle actor;
-
- if (local == FALSE)
- actor = priv->initial_peer;
- else
- actor = conn->self_handle;
-
- set = tp_intset_new_containing (actor);
-
- /* add the peer to the member list */
- tp_group_mixin_change_members (G_OBJECT (chan), "", set, NULL, NULL,
- NULL, actor, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
-
- if (sid != NULL && name == NULL && purple_media_is_initiator (
- media, sid, name) == FALSE)
- {
- HazeMediaBackend *backend;
- HazeMediaStream *stream;
-
- g_object_get (G_OBJECT (priv->media), "backend", &backend, NULL);
- stream = haze_media_backend_get_stream_by_name (backend, sid);
- g_object_unref (backend);
-
- g_object_set (stream, "combined-direction",
- MAKE_COMBINED_DIRECTION (
- TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0), NULL);
- }
- }
- else if (type == PURPLE_MEDIA_INFO_REJECT ||
- type == PURPLE_MEDIA_INFO_HANGUP)
- {
- TpGroupMixin *mixin = TP_GROUP_MIXIN (chan);
- guint terminator;
- TpIntset *set;
-
- if (sid != NULL)
- return;
-
- if (local == TRUE)
- terminator = conn->self_handle;
- else
- /* This will need to get the handle from name for multi-user calls */
- terminator = priv->initial_peer;
-
- set = tp_intset_new ();
-
- if (name != NULL)
- /* Remove participant */
- tp_intset_add (set, priv->initial_peer);
- else
- /* Remove us */
- tp_intset_add (set, mixin->self_handle);
-
- tp_group_mixin_change_members ((GObject *) chan,
- NULL, NULL, set, NULL, NULL, terminator,
- TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
-
- tp_intset_destroy (set);
- }
-}
-
-static void
-_latch_to_session (HazeMediaChannel *chan)
-{
- HazeMediaChannelPrivate *priv = chan->priv;
- HazeMediaBackend *backend;
- gchar *object_path;
-
- g_assert (priv->media != NULL);
-
- DEBUG ("%p: Latching onto session %p", chan, priv->media);
-
- g_signal_connect(G_OBJECT(priv->media), "error",
- G_CALLBACK(media_error_cb), chan);
- g_signal_connect(G_OBJECT(priv->media), "state-changed",
- G_CALLBACK(media_state_changed_cb), chan);
- g_signal_connect(G_OBJECT(priv->media), "stream-info",
- G_CALLBACK(media_stream_info_cb), chan);
-
- object_path = g_strdup_printf ("%s/MediaSession0", priv->object_path);
-
- g_object_get (G_OBJECT (priv->media), "backend", &backend, NULL);
- g_object_set (G_OBJECT (backend), "object-path", object_path, NULL);
- g_object_unref (backend);
-
- tp_svc_channel_interface_media_signalling_emit_new_session_handler (
- G_OBJECT (chan), object_path, "rtp");
-
- g_free (object_path);
-}
-
-static GObject *
-haze_media_channel_constructor (GType type, guint n_props,
- GObjectConstructParam *props)
-{
- GObject *obj;
- HazeMediaChannelPrivate *priv;
- TpBaseConnection *conn;
- TpDBusDaemon *bus;
- TpIntset *set;
- TpHandleRepoIface *contact_handles;
-
- obj = G_OBJECT_CLASS (haze_media_channel_parent_class)->
- constructor (type, n_props, props);
-
- priv = HAZE_MEDIA_CHANNEL (obj)->priv;
- conn = (TpBaseConnection *) priv->conn;
- contact_handles = tp_base_connection_get_handles (conn,
- TP_HANDLE_TYPE_CONTACT);
-
- /* register object on the bus */
- bus = tp_base_connection_get_dbus_daemon (conn);
- tp_dbus_daemon_register_object (bus, priv->object_path, obj);
-
- tp_group_mixin_init (obj, G_STRUCT_OFFSET (HazeMediaChannel, group),
- contact_handles, conn->self_handle);
-
- if (priv->media != NULL)
- priv->creator = priv->initial_peer;
- else
- priv->creator = conn->self_handle;
-
- /* automatically add creator to channel, but also ref them again (because
- * priv->creator is the InitiatorHandle) */
- g_assert (priv->creator != 0);
- tp_handle_ref (contact_handles, priv->creator);
-
- set = tp_intset_new_containing (priv->creator);
- tp_group_mixin_change_members (obj, "", set, NULL, NULL, NULL, 0,
- TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
- tp_intset_destroy (set);
-
- /* We implement the 0.17.6 properties correctly, and can include a message
- * when ending a call.
- */
- tp_group_mixin_change_flags (obj,
- TP_CHANNEL_GROUP_FLAG_PROPERTIES |
- TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE |
- TP_CHANNEL_GROUP_FLAG_MESSAGE_REJECT |
- TP_CHANNEL_GROUP_FLAG_MESSAGE_RESCIND,
- 0);
-
-
- if (priv->media != NULL)
- {
- /* This is an incoming call; make us local pending and don't set any
- * group flags (all we can do is add or remove ourselves, which is always
- * valid per the spec)
- */
- set = tp_intset_new_containing (conn->self_handle);
- tp_group_mixin_change_members (obj, "", NULL, NULL, set, NULL,
- priv->initial_peer, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED);
- tp_intset_destroy (set);
-
- /* Set up signal callbacks */
- _latch_to_session (HAZE_MEDIA_CHANNEL (obj));
- }
-
- return obj;
-}
-
-static void
-haze_media_channel_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- HazeMediaChannel *chan = HAZE_MEDIA_CHANNEL (object);
- HazeMediaChannelPrivate *priv = chan->priv;
- TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn;
-
- switch (property_id) {
- case PROP_OBJECT_PATH:
- g_value_set_string (value, priv->object_path);
- break;
- case PROP_CHANNEL_TYPE:
- g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
- break;
- case PROP_HANDLE_TYPE:
- /* This is used to implement TargetHandleType, which is immutable. If
- * the peer was known at channel-creation time, this will be Contact;
- * otherwise, it must be None even if we subsequently learn who the peer
- * is.
- */
- if (priv->initial_peer != 0)
- g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
- else
- g_value_set_uint (value, TP_HANDLE_TYPE_NONE);
- break;
- case PROP_INITIAL_PEER:
- case PROP_HANDLE:
- /* As above: TargetHandle is immutable, so non-0 only if the peer handle
- * was known at creation time.
- */
- g_value_set_uint (value, priv->initial_peer);
- break;
- case PROP_TARGET_ID:
- /* As above. */
- if (priv->initial_peer != 0)
- {
- TpHandleRepoIface *repo = tp_base_connection_get_handles (
- base_conn, TP_HANDLE_TYPE_CONTACT);
- const gchar *target_id = tp_handle_inspect (repo, priv->initial_peer);
-
- g_value_set_string (value, target_id);
- }
- else
- {
- g_value_set_static_string (value, "");
- }
-
- break;
- case PROP_PEER:
- {
- TpHandle peer = 0;
-
- if (priv->initial_peer != 0)
- peer = priv->initial_peer;
-
- g_value_set_uint (value, peer);
- break;
- }
- case PROP_CONNECTION:
- g_value_set_object (value, priv->conn);
- break;
- case PROP_CREATOR:
- g_value_set_uint (value, priv->creator);
- break;
- case PROP_CREATOR_ID:
- {
- TpHandleRepoIface *repo = tp_base_connection_get_handles (
- base_conn, TP_HANDLE_TYPE_CONTACT);
-
- g_value_set_string (value, tp_handle_inspect (repo, priv->creator));
- }
- break;
- case PROP_REQUESTED:
- g_value_set_boolean (value, (priv->creator == base_conn->self_handle));
- break;
- case PROP_INTERFACES:
- g_value_set_boxed (value, haze_media_channel_interfaces);
- break;
- case PROP_CHANNEL_DESTROYED:
- g_value_set_boolean (value, priv->closed);
- break;
- case PROP_CHANNEL_PROPERTIES:
- g_value_take_boxed (value,
- tp_dbus_properties_mixin_make_properties_hash (object,
- TP_IFACE_CHANNEL, "TargetHandle",
- TP_IFACE_CHANNEL, "TargetHandleType",
- TP_IFACE_CHANNEL, "ChannelType",
- TP_IFACE_CHANNEL, "TargetID",
- TP_IFACE_CHANNEL, "InitiatorHandle",
- TP_IFACE_CHANNEL, "InitiatorID",
- TP_IFACE_CHANNEL, "Requested",
- TP_IFACE_CHANNEL, "Interfaces",
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, "InitialAudio",
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, "InitialVideo",
- NULL));
- break;
- case PROP_MEDIA:
- g_value_set_object (value, priv->media);
- break;
- case PROP_INITIAL_AUDIO:
- g_value_set_boolean (value, priv->initial_audio);
- break;
- case PROP_INITIAL_VIDEO:
- g_value_set_boolean (value, priv->initial_video);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-haze_media_channel_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- HazeMediaChannel *chan = HAZE_MEDIA_CHANNEL (object);
- HazeMediaChannelPrivate *priv = chan->priv;
-
- switch (property_id) {
- case PROP_OBJECT_PATH:
- g_free (priv->object_path);
- priv->object_path = g_value_dup_string (value);
- break;
- case PROP_HANDLE_TYPE:
- case PROP_HANDLE:
- case PROP_CHANNEL_TYPE:
- /* these properties are writable in the interface, but not actually
- * meaningfully changable on this channel, so we do nothing */
- break;
- case PROP_CONNECTION:
- priv->conn = g_value_get_object (value);
- break;
- case PROP_CREATOR:
- priv->creator = g_value_get_uint (value);
- break;
- case PROP_INITIAL_PEER:
- priv->initial_peer = g_value_get_uint (value);
-
- if (priv->initial_peer != 0)
- {
- TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn;
- TpHandleRepoIface *repo = tp_base_connection_get_handles (base_conn,
- TP_HANDLE_TYPE_CONTACT);
- tp_handle_ref (repo, priv->initial_peer);
- }
-
- break;
- case PROP_MEDIA:
- g_assert (priv->media == NULL);
- priv->media = g_value_dup_object (value);
- break;
- case PROP_INITIAL_AUDIO:
- priv->initial_audio = g_value_get_boolean (value);
- break;
- case PROP_INITIAL_VIDEO:
- priv->initial_video = g_value_get_boolean (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void haze_media_channel_dispose (GObject *object);
-static void haze_media_channel_finalize (GObject *object);
-
-static void
-haze_media_channel_class_init (HazeMediaChannelClass *haze_media_channel_class)
-{
- static TpDBusPropertiesMixinPropImpl channel_props[] = {
- { "TargetHandleType", "handle-type", NULL },
- { "TargetHandle", "handle", NULL },
- { "TargetID", "target-id", NULL },
- { "ChannelType", "channel-type", NULL },
- { "Interfaces", "interfaces", NULL },
- { "Requested", "requested", NULL },
- { "InitiatorHandle", "creator", NULL },
- { "InitiatorID", "creator-id", NULL },
- { NULL }
- };
- static TpDBusPropertiesMixinPropImpl streamed_media_props[] = {
- { "InitialAudio", "initial-audio", NULL },
- { "InitialVideo", "initial-video", NULL },
- { NULL }
- };
- static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
- { TP_IFACE_CHANNEL,
- tp_dbus_properties_mixin_getter_gobject_properties,
- NULL,
- channel_props,
- },
- { TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
- tp_dbus_properties_mixin_getter_gobject_properties,
- NULL,
- streamed_media_props,
- },
- { NULL }
- };
- GObjectClass *object_class = G_OBJECT_CLASS (haze_media_channel_class);
- GParamSpec *param_spec;
-
- g_type_class_add_private (haze_media_channel_class,
- sizeof (HazeMediaChannelPrivate));
-
- object_class->constructor = haze_media_channel_constructor;
-
- object_class->get_property = haze_media_channel_get_property;
- object_class->set_property = haze_media_channel_set_property;
-
- object_class->dispose = haze_media_channel_dispose;
- object_class->finalize = haze_media_channel_finalize;
-
- g_object_class_override_property (object_class, PROP_OBJECT_PATH,
- "object-path");
- g_object_class_override_property (object_class, PROP_CHANNEL_TYPE,
- "channel-type");
- g_object_class_override_property (object_class, PROP_HANDLE_TYPE,
- "handle-type");
- g_object_class_override_property (object_class, PROP_HANDLE, "handle");
-
- g_object_class_override_property (object_class, PROP_CHANNEL_DESTROYED,
- "channel-destroyed");
- g_object_class_override_property (object_class, PROP_CHANNEL_PROPERTIES,
- "channel-properties");
-
- param_spec = g_param_spec_string ("target-id", "Target ID",
- "The string that would result from inspecting TargetHandle",
- NULL,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_TARGET_ID, param_spec);
-
- param_spec = g_param_spec_uint ("initial-peer", "Other participant",
- "The TpHandle representing the other participant in the channel if known "
- "at construct-time; 0 if the other participant was unknown at the time "
- "of channel creation",
- 0, G_MAXUINT32, 0,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INITIAL_PEER, param_spec);
-
- param_spec = g_param_spec_uint ("peer", "Other participant",
- "The TpHandle representing the other participant in the channel if "
- "currently known; 0 if this is an anonymous channel on which "
- "RequestStreams has not yet been called.",
- 0, G_MAXUINT32, 0,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_PEER, param_spec);
-
- param_spec = g_param_spec_object ("connection", "HazeConnection object",
- "Haze connection object that owns this media channel object.",
- HAZE_TYPE_CONNECTION,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
-
- param_spec = g_param_spec_uint ("creator", "Channel creator",
- "The TpHandle representing the contact who created the channel.",
- 0, G_MAXUINT32, 0,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CREATOR, param_spec);
-
- param_spec = g_param_spec_string ("creator-id", "Creator ID",
- "The ID obtained by inspecting the creator handle.",
- NULL,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CREATOR_ID, param_spec);
-
- param_spec = g_param_spec_boolean ("requested", "Requested?",
- "True if this channel was requested by the local user",
- FALSE,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_REQUESTED, param_spec);
-
- param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces",
- "Additional Channel.Interface.* interfaces",
- G_TYPE_STRV,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
-
- param_spec = g_param_spec_object ("media", "PurpleMedia object",
- "Purple media associated with this media channel object.",
- PURPLE_TYPE_MEDIA,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_MEDIA, param_spec);
-
- param_spec = g_param_spec_boolean ("initial-audio", "InitialAudio",
- "Whether the channel initially contained an audio stream",
- FALSE,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INITIAL_AUDIO,
- param_spec);
-
- param_spec = g_param_spec_boolean ("initial-video", "InitialVideo",
- "Whether the channel initially contained an video stream",
- FALSE,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_INITIAL_VIDEO,
- param_spec);
-
- haze_media_channel_class->dbus_props_class.interfaces = prop_interfaces;
- tp_dbus_properties_mixin_class_init (object_class,
- G_STRUCT_OFFSET (HazeMediaChannelClass, dbus_props_class));
-
- tp_group_mixin_class_init (object_class,
- G_STRUCT_OFFSET (HazeMediaChannelClass, group_class),
- haze_media_channel_add_member, NULL);
- tp_group_mixin_class_set_remove_with_reason_func (object_class,
- haze_media_channel_remove_member);
- tp_group_mixin_class_allow_self_removal (object_class);
-
- tp_group_mixin_init_dbus_properties (object_class);
-}
-
-void
-haze_media_channel_dispose (GObject *object)
-{
- HazeMediaChannel *self = HAZE_MEDIA_CHANNEL (object);
- HazeMediaChannelPrivate *priv = self->priv;
- TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
- TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (
- conn, TP_HANDLE_TYPE_CONTACT);
-
- if (priv->dispose_has_run)
- return;
-
- DEBUG ("called");
-
- priv->dispose_has_run = TRUE;
-
- if (!priv->closed)
- haze_media_channel_close (self);
-
- g_assert (priv->closed);
-
- tp_handle_unref (contact_handles, priv->creator);
- priv->creator = 0;
-
- if (priv->initial_peer != 0)
- {
- tp_handle_unref (contact_handles, priv->initial_peer);
- priv->initial_peer = 0;
- }
-
- if (priv->media != NULL)
- g_object_unref (priv->media);
- priv->media = NULL;
-
- if (G_OBJECT_CLASS (haze_media_channel_parent_class)->dispose)
- G_OBJECT_CLASS (haze_media_channel_parent_class)->dispose (object);
-}
-
-void
-haze_media_channel_finalize (GObject *object)
-{
- HazeMediaChannel *self = HAZE_MEDIA_CHANNEL (object);
- HazeMediaChannelPrivate *priv = self->priv;
-
- g_free (priv->object_path);
-
- tp_group_mixin_finalize (object);
-
- G_OBJECT_CLASS (haze_media_channel_parent_class)->finalize (object);
-}
-
-
-/**
- * haze_media_channel_close_async:
- *
- * Implements D-Bus method Close
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-haze_media_channel_close_async (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- HazeMediaChannel *self = HAZE_MEDIA_CHANNEL (iface);
-
- DEBUG ("called");
- haze_media_channel_close (self);
- tp_svc_channel_return_from_close (context);
-}
-
-void
-haze_media_channel_close (HazeMediaChannel *self)
-{
- HazeMediaChannelPrivate *priv = self->priv;
-
- DEBUG ("called on %p", self);
-
- if (!priv->closed)
- {
- priv->closed = TRUE;
-
- if (priv->media && !priv->media_ended)
- {
- priv->media_ended = TRUE;
- purple_media_stream_info (priv->media,
- PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, FALSE);
- }
-
- tp_svc_channel_emit_closed (self);
- }
-}
-
-
-/**
- * haze_media_channel_get_channel_type
- *
- * Implements D-Bus method GetChannelType
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-haze_media_channel_get_channel_type (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- tp_svc_channel_return_from_get_channel_type (context,
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA);
-}
-
-
-/**
- * haze_media_channel_get_handle
- *
- * Implements D-Bus method GetHandle
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-haze_media_channel_get_handle (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- HazeMediaChannel *self = HAZE_MEDIA_CHANNEL (iface);
-
- if (self->priv->initial_peer == 0)
- tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_NONE, 0);
- else
- tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT,
- self->priv->initial_peer);
-}
-
-
-/**
- * haze_media_channel_get_interfaces
- *
- * Implements D-Bus method GetInterfaces
- * on interface org.freedesktop.Telepathy.Channel
- */
-static void
-haze_media_channel_get_interfaces (TpSvcChannel *iface,
- DBusGMethodInvocation *context)
-{
- tp_svc_channel_return_from_get_interfaces (context,
- haze_media_channel_interfaces);
-}
-
-/**
- * haze_media_channel_list_streams
- *
- * Implements D-Bus method ListStreams
- * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
- */
-static void
-haze_media_channel_list_streams (TpSvcChannelTypeStreamedMedia *iface,
- DBusGMethodInvocation *context)
-{
- HazeMediaChannel *self = HAZE_MEDIA_CHANNEL (iface);
- HazeMediaChannelPrivate *priv;
- GPtrArray *ret;
-
- g_assert (HAZE_IS_MEDIA_CHANNEL (self));
-
- priv = self->priv;
-
- /* If the session has not yet started, return an empty array. */
- if (priv->media == NULL)
- {
- ret = g_ptr_array_new ();
- }
- else
- {
- HazeMediaBackend *backend;
- GPtrArray *streams;
-
- g_object_get (G_OBJECT (priv->media), "backend", &backend, NULL);
- g_object_get (G_OBJECT (backend), "streams", &streams, NULL);
-
- ret = make_stream_list (self, streams->len,
- (HazeMediaStream **) streams->pdata);
-
- g_ptr_array_unref (streams);
- g_object_unref (backend);
- }
-
- tp_svc_channel_type_streamed_media_return_from_list_streams (context, ret);
- g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL);
- g_ptr_array_free (ret, TRUE);
-}
-
-/**
- * haze_media_channel_remove_streams
- *
- * Implements DBus method RemoveStreams
- * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
- */
-static void
-haze_media_channel_remove_streams (TpSvcChannelTypeStreamedMedia *iface,
- const GArray * streams,
- DBusGMethodInvocation *context)
-{
- HazeMediaChannel *obj = HAZE_MEDIA_CHANNEL (iface);
- HazeMediaChannelPrivate *priv;
- HazeMediaBackend *backend;
- GPtrArray *backend_streams;
- guint i, j;
- GPtrArray *media_ids;
- const gchar *target_id;
-
- g_assert (HAZE_IS_MEDIA_CHANNEL (obj));
-
- priv = obj->priv;
-
- g_object_get (obj, "target-id", &target_id, NULL);
-
- if ((purple_prpl_get_media_caps (priv->conn->account, target_id) &
- PURPLE_MEDIA_CAPS_MODIFY_SESSION) == 0)
- {
- TpBaseConnection *base_conn = TP_BASE_CONNECTION (priv->conn);
- gchar *name;
- GError *e;
-
- g_object_get (base_conn, "protocol", &name, NULL);
- g_set_error (&e, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
- "Streams can't be removed in Haze's \"%s\" protocol's calls", name);
- g_free (name);
-
- DEBUG ("%s", e->message);
- dbus_g_method_return_error (context, e);
-
- g_error_free(e);
- return;
- }
-
- media_ids = g_ptr_array_new ();
-
- g_object_get (G_OBJECT (priv->media), "backend", &backend, NULL);
- g_object_get (G_OBJECT (backend), "streams", &backend_streams, NULL);
- g_object_unref (backend);
-
- for (i = 0; i < streams->len; ++i)
- {
- guint id = g_array_index (streams, guint, i);
-
- for (j = 0; j < backend_streams->len; j++)
- {
- HazeMediaStream *stream = g_ptr_array_index (backend_streams, j);
- guint stream_id;
-
- g_object_get (G_OBJECT (stream), "id", &stream_id, NULL);
-
- if (id == stream_id)
- {
- g_ptr_array_add (media_ids, stream->name);
- break;
- }
- }
-
- if (j >= backend_streams->len)
- {
- GError e = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
- "Requested stream wasn't found" };
- DEBUG ("%s", e.message);
- dbus_g_method_return_error (context, &e);
- g_ptr_array_free (media_ids, TRUE);
- return;
- }
- }
-
- for (i = 0; i < media_ids->len; ++i)
- {
- gchar *id = g_ptr_array_index (media_ids, i);
- for (j = i + 1; j < media_ids->len; ++j)
- {
- if (id == g_ptr_array_index (media_ids, j))
- {
- g_ptr_array_remove_index (media_ids, j);
- --j;
- }
- }
- }
-
- for (i = 0; i < media_ids->len; ++i)
- {
- purple_media_end (priv->media, g_ptr_array_index (media_ids, i), NULL);
- }
-
- g_ptr_array_unref (backend_streams);
- g_ptr_array_free (media_ids, TRUE);
- tp_svc_channel_type_streamed_media_return_from_remove_streams (context);
-}
-
-/**
- * haze_media_channel_request_stream_direction
- *
- * Implements D-Bus method RequestStreamDirection
- * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
- */
-static void
-haze_media_channel_request_stream_direction (TpSvcChannelTypeStreamedMedia *iface,
- guint stream_id,
- guint stream_direction,
- DBusGMethodInvocation *context)
-{
- /* Libpurple doesn't have API for this yet */
- GError e = { TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
- "Stream direction can't be set Haze calls" };
- DEBUG ("%s", e.message);
- dbus_g_method_return_error (context, &e);
-}
-
-
-static gboolean
-init_media_cb (PurpleMediaManager *manager,
- PurpleMedia *media,
- PurpleAccount *account,
- const gchar *username,
- HazeMediaChannel *self)
-{
- HazeMediaChannelPrivate *priv = self->priv;
- TpBaseConnection *base_conn = TP_BASE_CONNECTION (priv->conn);
- TpHandleRepoIface *contact_repo =
- tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT);
- TpHandle contact = tp_handle_ensure (contact_repo, username, NULL, NULL);
-
- if (priv->conn->account != account || priv->initial_peer != contact)
- return TRUE;
-
- g_assert (priv->media == NULL);
- priv->media = g_object_ref (media);
- if (priv->media != NULL)
- {
- _latch_to_session (self);
- }
-
- g_signal_handlers_disconnect_by_func (manager, init_media_cb, self);
-
- return TRUE;
-}
-
-static gboolean
-_haze_media_channel_request_contents (HazeMediaChannel *chan,
- TpHandle peer,
- const GArray *media_types,
- GError **error)
-{
- HazeMediaChannelPrivate *priv = chan->priv;
- gboolean want_audio, want_video;
- guint idx;
- TpHandleRepoIface *contact_handles;
- const gchar *contact_id;
- guint audio_count = 0, video_count = 0;
-
- DEBUG ("called");
-
- want_audio = want_video = FALSE;
-
- for (idx = 0; idx < media_types->len; idx++)
- {
- guint media_type = g_array_index (media_types, guint, idx);
-
- if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
- {
- want_audio = TRUE;
- }
- else if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
- {
- want_video = TRUE;
- }
- else
- {
- g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
- "given media type %u is invalid", media_type);
- return FALSE;
- }
- }
-
- /* existing call; the recipient and the mode has already been decided */
- if (priv->media != NULL)
- {
- PurpleMediaCaps caps;
- const gchar *target_id;
- g_object_get (chan, "target-id", &target_id, NULL);
- caps = purple_prpl_get_media_caps (priv->conn->account, target_id);
-
- /* Check if the contact supports modifying the session */
- if ((caps & PURPLE_MEDIA_CAPS_MODIFY_SESSION) == 0)
- {
- TpBaseConnection *base_conn = TP_BASE_CONNECTION (priv->conn);
- gchar *name;
-
- g_object_get (base_conn, "protocol", &name, NULL);
- g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
- "Streams can't be added in Haze's \"%s\" protocol's calls",
- name);
-
- g_free (name);
- return FALSE;
- }
-
- /* Check if contact supports the desired media type */
- if ((want_audio == FALSE || caps & PURPLE_MEDIA_CAPS_AUDIO) &&
- (want_video == FALSE || caps & PURPLE_MEDIA_CAPS_VIDEO))
- {
- g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
- "Member does not have the desired audio/video capabilities");
- return FALSE;
- }
- }
-
- /* if we've got here, we're good to make the streams */
-
- contact_handles = tp_base_connection_get_handles (
- TP_BASE_CONNECTION (priv->conn), TP_HANDLE_TYPE_CONTACT);
- contact_id = tp_handle_inspect (contact_handles, peer);
-
- /* Be ready to retrieve the newly created media object */
- if (priv->media == NULL)
- g_signal_connect (purple_media_manager_get (), "init-media",
- G_CALLBACK (init_media_cb), chan);
-
- for (idx = 0; idx < media_types->len; idx++)
- {
- guint media_type = g_array_index (media_types, guint, idx);
-
- if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
- ++audio_count;
- else if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
- ++video_count;
- }
-
- while (audio_count > 0 || video_count > 0)
- {
- PurpleMediaSessionType type = PURPLE_MEDIA_NONE;
-
- if (audio_count > 0)
- {
- type |= PURPLE_MEDIA_AUDIO;
- --audio_count;
- }
-
- if (video_count > 0)
- {
- type |= PURPLE_MEDIA_VIDEO;
- --video_count;
- }
-
- if (purple_prpl_initiate_media (priv->conn->account,
- contact_id, type) == FALSE)
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void
-media_channel_request_streams (HazeMediaChannel *self,
- TpHandle contact_handle,
- const GArray *types,
- GFunc succeeded_cb,
- GFunc failed_cb,
- gpointer context)
-{
- HazeMediaChannelPrivate *priv = self->priv;
- PendingStreamRequest *psr = NULL;
- GError *error = NULL;
-
- if (types->len == 0)
- {
- GPtrArray *empty = g_ptr_array_sized_new (0);
-
- DEBUG ("no streams to request");
- succeeded_cb (context, empty);
- g_ptr_array_free (empty, TRUE);
-
- return;
- }
-
- if (priv->media != NULL)
- {
- TpHandle peer;
-
- peer = priv->initial_peer;
-
- if (peer != contact_handle)
- {
- g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
- "cannot add streams for %u: this channel's peer is %u",
- contact_handle, peer);
- goto error;
- }
- }
-
- /*
- * Pending stream requests can be completed before request_contents returns.
- * Add the pending stream request up here so it isn't missed.
- */
- psr = pending_stream_request_new (types, succeeded_cb, failed_cb,
- context);
- priv->pending_stream_requests = g_list_prepend (priv->pending_stream_requests,
- psr);
-
- if (!_haze_media_channel_request_contents (self, contact_handle,
- types, &error))
- goto error;
-
- return;
-
-error:
- if (psr != NULL)
- {
- priv->pending_stream_requests = g_list_remove (
- priv->pending_stream_requests, psr);
- pending_stream_request_free (psr);
- }
-
- DEBUG ("returning error %u: %s", error->code, error->message);
- failed_cb (context, error);
- g_error_free (error);
-}
-
-/**
- * haze_media_channel_request_streams
- *
- * Implements D-Bus method RequestStreams
- * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia
- */
-static void
-haze_media_channel_request_streams (TpSvcChannelTypeStreamedMedia *iface,
- guint contact_handle,
- const GArray *types,
- DBusGMethodInvocation *context)
-{
- HazeMediaChannel *self = HAZE_MEDIA_CHANNEL (iface);
- TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->conn;
- TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (
- base_conn, TP_HANDLE_TYPE_CONTACT);
- GError *error = NULL;
-
- if (!tp_handle_is_valid (contact_handles, contact_handle, &error))
- {
- DEBUG ("that's not a handle, sonny! (%u)", contact_handle);
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
- else
- {
- /* FIXME: disallow this if we've put the peer on hold? */
-
- media_channel_request_streams (self, contact_handle, types,
- (GFunc) tp_svc_channel_type_streamed_media_return_from_request_streams,
- (GFunc) dbus_g_method_return_error,
- context);
- }
-}
-
-/**
- * haze_media_channel_request_initial_streams:
- * @chan: an outgoing call, which must have just been constructed.
- * @succeeded_cb: called with arguments @user_data and a GPtrArray of
- * TP_STRUCT_TYPE_MEDIA_STREAM_INFO if the request succeeds.
- * @failed_cb: called with arguments @user_data and a GError * if the request
- * fails.
- * @user_data: context for the callbacks.
- *
- * Request streams corresponding to the values of InitialAudio and InitialVideo
- * in the channel request.
- */
-void
-haze_media_channel_request_initial_streams (HazeMediaChannel *chan,
- GFunc succeeded_cb,
- GFunc failed_cb,
- gpointer user_data)
-{
- HazeMediaChannelPrivate *priv = chan->priv;
- GArray *types = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2);
- guint media_type;
-
- /* This has to be an outgoing call... */
- g_assert (priv->creator == priv->conn->parent.self_handle);
-
- if (priv->initial_audio)
- {
- media_type = TP_MEDIA_STREAM_TYPE_AUDIO;
- g_array_append_val (types, media_type);
- }
-
- if (priv->initial_video)
- {
- media_type = TP_MEDIA_STREAM_TYPE_VIDEO;
- g_array_append_val (types, media_type);
- }
-
- media_channel_request_streams (chan, priv->initial_peer, types,
- succeeded_cb, failed_cb, user_data);
-
- g_array_free (types, TRUE);
-}
-
-static gboolean
-haze_media_channel_add_member (GObject *obj,
- TpHandle handle,
- const gchar *message,
- GError **error)
-{
- HazeMediaChannel *chan = HAZE_MEDIA_CHANNEL (obj);
- HazeMediaChannelPrivate *priv = chan->priv;
- TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
- TpIntset *set;
-
- /* did we create this channel? */
- if (priv->creator == mixin->self_handle)
- {
- /* yes: check we don't have a peer already, and if not add this one to
- * remote pending (but don't send an invitation yet).
- */
- if (priv->media != NULL)
- {
- TpHandle peer;
-
- peer = priv->initial_peer;
-
- if (peer != handle)
- {
- g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
- "handle %u cannot be added: this channel's peer is %u",
- handle, peer);
- return FALSE;
- }
- }
-
- /* make the peer remote pending */
- set = tp_intset_new_containing (handle);
- tp_group_mixin_change_members (obj, "", NULL, NULL, NULL, set,
- mixin->self_handle, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED);
- tp_intset_destroy (set);
-
- /* and remove CanAdd, since it was only here to allow this deprecated
- * API. */
- tp_group_mixin_change_flags (obj, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD);
-
- return TRUE;
- }
- else
- {
- /* no: has a session been created, is the handle being added ours,
- * and are we in local pending? (call answer) */
- if (priv->media &&
- handle == mixin->self_handle &&
- tp_handle_set_is_member (mixin->local_pending, handle))
- {
- /* is the call on hold? */
- if (priv->hold_state != TP_LOCAL_HOLD_STATE_UNHELD)
- {
- g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
- "Can't answer a call while it's on hold");
- return FALSE;
- }
-
- /* make us a member */
- set = tp_intset_new_containing (handle);
- tp_group_mixin_change_members (obj, "", set, NULL, NULL, NULL,
- handle, TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
- tp_intset_destroy (set);
-
- /* signal acceptance */
- purple_media_stream_info(priv->media,
- PURPLE_MEDIA_INFO_ACCEPT, NULL, NULL, TRUE);
-
- return TRUE;
- }
- }
-
- g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
- "handle %u cannot be added in the current state", handle);
- return FALSE;
-}
-
-static gboolean
-haze_media_channel_remove_member (GObject *obj,
- TpHandle handle,
- const gchar *message,
- guint reason,
- GError **error)
-{
- HazeMediaChannel *chan = HAZE_MEDIA_CHANNEL (obj);
- HazeMediaChannelPrivate *priv = chan->priv;
- TpGroupMixin *mixin = TP_GROUP_MIXIN (obj);
-
- /* We don't set CanRemove, and did allow self removal. So tp-glib should
- * ensure this.
- */
- g_assert (handle == mixin->self_handle);
-
- /* Closing up might make HazeMediaManager release its ref. */
- g_object_ref (chan);
-
- if (priv->media == NULL)
- {
- haze_media_channel_close (chan);
- }
- else
- {
- switch (reason)
- {
- /* Should one of these trigger reject? */
- case TP_CHANNEL_GROUP_CHANGE_REASON_NONE:
- case TP_CHANNEL_GROUP_CHANGE_REASON_OFFLINE:
- case TP_CHANNEL_GROUP_CHANGE_REASON_BUSY:
- case TP_CHANNEL_GROUP_CHANGE_REASON_ERROR:
- case TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER:
- purple_media_stream_info(priv->media,
- PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, TRUE);
- break;
- default:
- /* The remaining options don't make sense */
- g_object_unref (chan);
- return FALSE;
- }
- }
-
- /* Remove CanAdd if it was there for the deprecated anonymous channel
- * semantics, since the channel will go away RSN. */
- tp_group_mixin_change_flags (obj, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD);
-
- g_object_unref (chan);
-
- return TRUE;
-}
-
-/**
- * haze_media_channel_get_session_handlers
- *
- * Implements D-Bus method GetSessionHandlers
- * on interface org.freedesktop.Telepathy.Channel.Interface.MediaSignalling
- */
-static void
-haze_media_channel_get_session_handlers (
- TpSvcChannelInterfaceMediaSignalling *iface,
- DBusGMethodInvocation *context)
-{
- HazeMediaChannel *self = HAZE_MEDIA_CHANNEL (iface);
- HazeMediaChannelPrivate *priv;
- GPtrArray *ret;
- GType info_type = TP_STRUCT_TYPE_MEDIA_SESSION_HANDLER_INFO;
-
- g_assert (HAZE_IS_MEDIA_CHANNEL (self));
-
- priv = self->priv;
-
- if (priv->media)
- {
- GValue handler = { 0, };
- HazeMediaBackend *backend;
- gchar *object_path;
-
- g_object_get (G_OBJECT (priv->media), "backend", &backend, NULL);
- g_object_get (G_OBJECT (backend), "object-path", &object_path, NULL);
- g_object_unref (backend);
-
- g_value_init (&handler, info_type);
- g_value_take_boxed (&handler,
- dbus_g_type_specialized_construct (info_type));
-
- dbus_g_type_struct_set (&handler,
- 0, object_path,
- 1, "rtp",
- G_MAXUINT);
-
- g_free (object_path);
-
- ret = g_ptr_array_sized_new (1);
- g_ptr_array_add (ret, g_value_get_boxed (&handler));
- }
- else
- {
- ret = g_ptr_array_sized_new (0);
- }
-
- tp_svc_channel_interface_media_signalling_return_from_get_session_handlers (
- context, ret);
- g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL);
- g_ptr_array_free (ret, TRUE);
-}
-
-static void
-channel_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcChannelClass *klass = (TpSvcChannelClass *) g_iface;
-
-#define IMPLEMENT(x, suffix) tp_svc_channel_implement_##x (\
- klass, haze_media_channel_##x##suffix)
- IMPLEMENT(close,_async);
- IMPLEMENT(get_channel_type,);
- IMPLEMENT(get_handle,);
- IMPLEMENT(get_interfaces,);
-#undef IMPLEMENT
-}
-
-static void
-streamed_media_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcChannelTypeStreamedMediaClass *klass =
- (TpSvcChannelTypeStreamedMediaClass *) g_iface;
-
-#define IMPLEMENT(x) tp_svc_channel_type_streamed_media_implement_##x (\
- klass, haze_media_channel_##x)
- IMPLEMENT(list_streams);
- IMPLEMENT(remove_streams);
- IMPLEMENT(request_stream_direction);
- IMPLEMENT(request_streams);
-#undef IMPLEMENT
-}
-
-static void
-media_signalling_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcChannelInterfaceMediaSignallingClass *klass =
- (TpSvcChannelInterfaceMediaSignallingClass *) g_iface;
-
-#define IMPLEMENT(x) tp_svc_channel_interface_media_signalling_implement_##x (\
- klass, haze_media_channel_##x)
- IMPLEMENT(get_session_handlers);
-#undef IMPLEMENT
-}
diff --git a/src/media-channel.h b/src/media-channel.h
deleted file mode 100644
index 60081d1..0000000
--- a/src/media-channel.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * media-channel.h - Header for HazeMediaChannel
- * Copyright (C) 2006, 2009 Collabora Ltd.
- * Copyright (C) 2006 Nokia Corporation
- *
- * Copied heavily from telepathy-gabble
- *
- * 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 __HAZE_MEDIA_CHANNEL_H__
-#define __HAZE_MEDIA_CHANNEL_H__
-
-#include <glib-object.h>
-
-#include <telepathy-glib/dbus-properties-mixin.h>
-#include <telepathy-glib/group-mixin.h>
-
-G_BEGIN_DECLS
-
-typedef struct _HazeMediaChannel HazeMediaChannel;
-typedef struct _HazeMediaChannelPrivate HazeMediaChannelPrivate;
-typedef struct _HazeMediaChannelClass HazeMediaChannelClass;
-
-struct _HazeMediaChannelClass {
- GObjectClass parent_class;
-
- TpGroupMixinClass group_class;
- TpDBusPropertiesMixinClass dbus_props_class;
-};
-
-struct _HazeMediaChannel {
- GObject parent;
-
- TpGroupMixin group;
-
- HazeMediaChannelPrivate *priv;
-};
-
-GType haze_media_channel_get_type (void);
-
-/* TYPE MACROS */
-#define HAZE_TYPE_MEDIA_CHANNEL \
- (haze_media_channel_get_type ())
-#define HAZE_MEDIA_CHANNEL(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj), HAZE_TYPE_MEDIA_CHANNEL,\
- HazeMediaChannel))
-#define HAZE_MEDIA_CHANNEL_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass), HAZE_TYPE_MEDIA_CHANNEL,\
- HazeMediaChannelClass))
-#define HAZE_IS_MEDIA_CHANNEL(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj), HAZE_TYPE_MEDIA_CHANNEL))
-#define HAZE_IS_MEDIA_CHANNEL_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass), HAZE_TYPE_MEDIA_CHANNEL))
-#define HAZE_MEDIA_CHANNEL_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS ((obj), HAZE_TYPE_MEDIA_CHANNEL, \
- HazeMediaChannelClass))
-
-void haze_media_channel_request_initial_streams (HazeMediaChannel *chan,
- GFunc succeeded_cb,
- GFunc failed_cb,
- gpointer user_data);
-
-void haze_media_channel_close (HazeMediaChannel *self);
-
-G_END_DECLS
-
-#endif /* #ifndef __HAZE_MEDIA_CHANNEL_H__*/
diff --git a/src/media-manager.c b/src/media-manager.c
deleted file mode 100644
index 3df771c..0000000
--- a/src/media-manager.c
+++ /dev/null
@@ -1,579 +0,0 @@
-/*
- * media-manager.c - Source for HazeMediaManager
- * Copyright (C) 2006, 2009 Collabora Ltd.
- *
- * Copied heavily from telepathy-gabble
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "config.h"
-#include "media-manager.h"
-
-#include <libpurple/mediamanager.h>
-#include <telepathy-glib/channel-manager.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/gtypes.h>
-#include <telepathy-glib/interfaces.h>
-
-#include "connection.h"
-#include "debug.h"
-#include "media-channel.h"
-
-static void channel_manager_iface_init (gpointer, gpointer);
-static void haze_media_manager_close_all (HazeMediaManager *self);
-static void haze_media_manager_constructed (GObject *object);
-
-G_DEFINE_TYPE_WITH_CODE (HazeMediaManager, haze_media_manager,
- G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER,
- channel_manager_iface_init));
-
-/* properties */
-enum
-{
- PROP_CONNECTION = 1,
- LAST_PROPERTY
-};
-
-struct _HazeMediaManagerPrivate
-{
- HazeConnection *conn;
- gulong status_changed_id;
-
- GPtrArray *channels;
- guint channel_index;
-
- gboolean dispose_has_run;
-};
-
-static void
-haze_media_manager_init (HazeMediaManager *self)
-{
- HazeMediaManagerPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- HAZE_TYPE_MEDIA_MANAGER, HazeMediaManagerPrivate);
-
- self->priv = priv;
-
- priv->channels = g_ptr_array_sized_new (1);
- priv->channel_index = 0;
-
- priv->conn = NULL;
- priv->dispose_has_run = FALSE;
-}
-
-static void
-haze_media_manager_dispose (GObject *object)
-{
- HazeMediaManager *self = HAZE_MEDIA_MANAGER (object);
- HazeMediaManagerPrivate *priv = self->priv;
-
- if (priv->dispose_has_run)
- return;
-
- DEBUG ("dispose called");
- priv->dispose_has_run = TRUE;
-
- haze_media_manager_close_all (self);
- g_assert (priv->channels->len == 0);
- g_ptr_array_free (priv->channels, TRUE);
-
- if (G_OBJECT_CLASS (haze_media_manager_parent_class)->dispose)
- G_OBJECT_CLASS (haze_media_manager_parent_class)->dispose (object);
-}
-
-static void
-haze_media_manager_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- HazeMediaManager *self = HAZE_MEDIA_MANAGER (object);
- HazeMediaManagerPrivate *priv = self->priv;
-
- switch (property_id) {
- case PROP_CONNECTION:
- g_value_set_object (value, priv->conn);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-haze_media_manager_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- HazeMediaManager *self = HAZE_MEDIA_MANAGER (object);
- HazeMediaManagerPrivate *priv = self->priv;
-
- switch (property_id) {
- case PROP_CONNECTION:
- priv->conn = g_value_get_object (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-haze_media_manager_class_init (HazeMediaManagerClass *haze_media_manager_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (haze_media_manager_class);
- GParamSpec *param_spec;
-
- g_type_class_add_private (haze_media_manager_class,
- sizeof (HazeMediaManagerPrivate));
-
- object_class->constructed = haze_media_manager_constructed;
- object_class->dispose = haze_media_manager_dispose;
-
- object_class->get_property = haze_media_manager_get_property;
- object_class->set_property = haze_media_manager_set_property;
-
- param_spec = g_param_spec_object ("connection", "HazeConnection object",
- "Haze connection object that owns this media channel manager object.",
- HAZE_TYPE_CONNECTION,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
-
-}
-
-/**
- * media_channel_closed_cb:
- *
- * Signal callback for when a media channel is closed. Removes the references
- * that #HazeMediaManager holds to them.
- */
-static void
-media_channel_closed_cb (HazeMediaChannel *chan, gpointer user_data)
-{
- HazeMediaManager *self = HAZE_MEDIA_MANAGER (user_data);
- HazeMediaManagerPrivate *priv = self->priv;
-
- tp_channel_manager_emit_channel_closed_for_object (self,
- TP_EXPORTABLE_CHANNEL (chan));
-
- DEBUG ("removing media channel %p with ref count %d",
- chan, G_OBJECT (chan)->ref_count);
-
- g_ptr_array_remove (priv->channels, chan);
- g_object_unref (chan);
-}
-
-/**
- * new_media_channel
- *
- * Creates a new empty HazeMediaChannel.
- */
-static HazeMediaChannel *
-new_media_channel (HazeMediaManager *self,
- PurpleMedia *media,
- TpHandle peer,
- gboolean initial_audio,
- gboolean initial_video)
-{
- HazeMediaManagerPrivate *priv;
- TpBaseConnection *conn;
-
- HazeMediaChannel *chan;
- gchar *object_path;
-
- g_assert (HAZE_IS_MEDIA_MANAGER (self));
-
- priv = self->priv;
- conn = (TpBaseConnection *) priv->conn;
-
- object_path = g_strdup_printf ("%s/MediaChannel%u",
- conn->object_path, priv->channel_index);
- priv->channel_index += 1;
-
- chan = g_object_new (HAZE_TYPE_MEDIA_CHANNEL,
- "connection", priv->conn,
- "object-path", object_path,
- "media", media,
- "initial-peer", peer,
- "initial-audio", initial_audio,
- "initial-video", initial_video,
- NULL);
-
- DEBUG ("object path %s", object_path);
-
- g_signal_connect (chan, "closed", (GCallback) media_channel_closed_cb, self);
-
- g_ptr_array_add (priv->channels, chan);
-
- g_free (object_path);
-
- return chan;
-}
-
-static void
-haze_media_manager_close_all (HazeMediaManager *self)
-{
- HazeMediaManagerPrivate *priv = self->priv;
- GPtrArray *tmp = g_ptr_array_sized_new (priv->channels->len);
- guint i;
-
- for (i = 0; i < priv->channels->len; i++)
- g_ptr_array_add (tmp, g_ptr_array_index (priv->channels, i));
-
- DEBUG ("closing channels");
-
- for (i = 0; i < tmp->len; i++)
- {
- HazeMediaChannel *chan = g_ptr_array_index (tmp, i);
-
- DEBUG ("closing %p", chan);
- haze_media_channel_close (chan);
- }
-
- if (priv->status_changed_id != 0)
- {
- g_signal_handler_disconnect (priv->conn,
- priv->status_changed_id);
- priv->status_changed_id = 0;
- }
-}
-
-static gboolean
-init_media_cb (PurpleMediaManager *manager,
- PurpleMedia *media,
- PurpleAccount *account,
- const gchar *username,
- HazeMediaManager *self)
-{
- HazeMediaManagerPrivate *priv = self->priv;
- TpBaseConnection *base_conn = TP_BASE_CONNECTION (priv->conn);
- TpHandleRepoIface *contact_repo =
- tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT);
- TpHandle contact = tp_handle_ensure (contact_repo, username, NULL, NULL);
- HazeMediaChannel *chan;
-
- if (purple_media_is_initiator (media, NULL, NULL) == TRUE)
- return TRUE;
-
- chan = new_media_channel (self, media, contact, FALSE, FALSE);
- tp_channel_manager_emit_new_channel (self,
- TP_EXPORTABLE_CHANNEL (chan), NULL);
- DEBUG ("called");
- return TRUE;
-}
-
-static void
-connection_status_changed_cb (HazeConnection *conn,
- guint status,
- guint reason,
- HazeMediaManager *self)
-{
- switch (status)
- {
- case TP_CONNECTION_STATUS_CONNECTING:
- g_signal_connect (purple_media_manager_get (), "init-media",
- G_CALLBACK (init_media_cb), self);
- break;
-
- case TP_CONNECTION_STATUS_DISCONNECTED:
- g_signal_handlers_disconnect_by_func (purple_media_manager_get (),
- G_CALLBACK (init_media_cb), self);
- haze_media_manager_close_all (self);
- break;
- }
-}
-
-static void
-haze_media_manager_constructed (GObject *object)
-{
- void (*chain_up) (GObject *) =
- G_OBJECT_CLASS (haze_media_manager_parent_class)->constructed;
- HazeMediaManager *self = HAZE_MEDIA_MANAGER (object);
- HazeMediaManagerPrivate *priv = self->priv;
-
- if (chain_up != NULL)
- chain_up (object);
-
- priv->status_changed_id = g_signal_connect (priv->conn,
- "status-changed", (GCallback) connection_status_changed_cb, object);
-}
-
-static void
-haze_media_manager_foreach_channel (TpChannelManager *manager,
- TpExportableChannelFunc foreach,
- gpointer user_data)
-{
- HazeMediaManager *self = HAZE_MEDIA_MANAGER (manager);
- HazeMediaManagerPrivate *priv = self->priv;
- guint i;
-
- for (i = 0; i < priv->channels->len; i++)
- {
- TpExportableChannel *channel = TP_EXPORTABLE_CHANNEL (
- g_ptr_array_index (priv->channels, i));
-
- foreach (channel, user_data);
- }
-}
-
-static const gchar * const media_channel_fixed_properties[] = {
- TP_IFACE_CHANNEL ".ChannelType",
- TP_IFACE_CHANNEL ".TargetHandleType",
- NULL
-};
-
-static const gchar * const named_channel_allowed_properties[] = {
- TP_IFACE_CHANNEL ".TargetHandle",
- TP_IFACE_CHANNEL ".TargetID",
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio",
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo",
- NULL
-};
-
-/* not advertised in foreach_channel_class - can only be requested with
- * RequestChannel, not with CreateChannel/EnsureChannel */
-static const gchar * const anon_channel_allowed_properties[] = {
- NULL
-};
-
-static GHashTable *
-haze_media_manager_channel_class (void)
-{
- return tp_asv_new (
- TP_IFACE_CHANNEL ".ChannelType", G_TYPE_STRING,
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
- TP_IFACE_CHANNEL ".TargetHandleType", G_TYPE_UINT,
- TP_HANDLE_TYPE_CONTACT,
- NULL);
-}
-
-static void
-haze_media_manager_foreach_channel_class (TpChannelManager *manager,
- TpChannelManagerChannelClassFunc func,
- gpointer user_data)
-{
- GHashTable *table = haze_media_manager_channel_class ();
-
- func (manager, table, named_channel_allowed_properties, user_data);
-
- g_hash_table_destroy (table);
-}
-
-typedef enum
-{
- METHOD_REQUEST,
- METHOD_CREATE,
- METHOD_ENSURE,
-} RequestMethod;
-
-typedef struct
-{
- HazeMediaManager *self;
- HazeMediaChannel *channel;
- gpointer request_token;
-} MediaChannelRequest;
-
-static MediaChannelRequest *
-media_channel_request_new (HazeMediaManager *self,
- HazeMediaChannel *channel,
- gpointer request_token)
-{
- MediaChannelRequest *mcr = g_slice_new0 (MediaChannelRequest);
-
- mcr->self = self;
- mcr->channel = channel;
- mcr->request_token = request_token;
-
- return mcr;
-}
-
-static void
-media_channel_request_free (MediaChannelRequest *mcr)
-{
- g_slice_free (MediaChannelRequest, mcr);
-}
-
-static void
-media_channel_request_succeeded_cb (MediaChannelRequest *mcr,
- GPtrArray *streams)
-{
- GSList *request_tokens;
-
- request_tokens = g_slist_prepend (NULL, mcr->request_token);
- tp_channel_manager_emit_new_channel (mcr->self,
- TP_EXPORTABLE_CHANNEL (mcr->channel), request_tokens);
- g_slist_free (request_tokens);
-
- media_channel_request_free (mcr);
-}
-
-static void
-media_channel_request_failed_cb (MediaChannelRequest *mcr,
- GError *error)
-{
- tp_channel_manager_emit_request_failed (mcr->self, mcr->request_token,
- error->domain, error->code, error->message);
-
- media_channel_request_free (mcr);
-}
-
-static gboolean
-haze_media_manager_requestotron (TpChannelManager *manager,
- gpointer request_token,
- GHashTable *request_properties,
- RequestMethod method)
-{
- HazeMediaManager *self = HAZE_MEDIA_MANAGER (manager);
- HazeMediaManagerPrivate *priv = self->priv;
- TpHandleType handle_type;
- TpHandle handle;
- HazeMediaChannel *channel = NULL;
- GError *error = NULL;
- gboolean initial_audio, initial_video;
-
- /* Supported modes of operation:
- * - CreateChannel({THT: Contact, TH: n}):
- * channel has TargetHandle=n
- * n is not in the group interface at all
- * call is started when caller calls RequestStreams.
- * - EnsureChannel({THT: Contact, TH: n}):
- * look for a channel whose peer is n, and return that if found with
- * whatever properties and group membership it has;
- * otherwise the same as into CreateChannel
- */
-
- if (tp_strdiff (tp_asv_get_string (request_properties,
- TP_IFACE_CHANNEL ".ChannelType"),
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
- return FALSE;
-
- handle_type = tp_asv_get_uint32 (request_properties,
- TP_IFACE_CHANNEL ".TargetHandleType", NULL);
-
- handle = tp_asv_get_uint32 (request_properties,
- TP_IFACE_CHANNEL ".TargetHandle", NULL);
-
- initial_audio = tp_asv_get_boolean (request_properties,
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio", NULL);
- initial_video = tp_asv_get_boolean (request_properties,
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo", NULL);
-
- switch (handle_type)
- {
- case TP_HANDLE_TYPE_NONE:
- /* already checked by TpBaseConnection */
- g_assert (handle == 0);
-
- g_set_error (&error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
- "A valid Contact handle must be provided when requesting a media "
- "channel");
-
- goto error;
- case TP_HANDLE_TYPE_CONTACT:
- /* validity already checked by TpBaseConnection */
- g_assert (handle != 0);
-
- if (tp_channel_manager_asv_has_unknown_properties (request_properties,
- media_channel_fixed_properties, named_channel_allowed_properties,
- &error))
- goto error;
-
- if (method == METHOD_ENSURE)
- {
- guint i;
- TpHandle peer = 0;
-
- for (i = 0; i < priv->channels->len; i++)
- {
- channel = g_ptr_array_index (priv->channels, i);
- g_object_get (channel, "peer", &peer, NULL);
-
- if (peer == handle)
- {
- /* Per the spec, we ignore InitialAudio and InitialVideo when
- * looking for an existing channel.
- */
- tp_channel_manager_emit_request_already_satisfied (self,
- request_token, TP_EXPORTABLE_CHANNEL (channel));
- return TRUE;
- }
- }
- }
-
- channel = new_media_channel (self, NULL, handle,
- initial_audio, initial_video);
- break;
- default:
- return FALSE;
- }
-
- g_assert (channel != NULL);
-
- haze_media_channel_request_initial_streams (channel,
- (GFunc) media_channel_request_succeeded_cb,
- (GFunc) media_channel_request_failed_cb,
- media_channel_request_new (self, channel, request_token));
-
- return TRUE;
-
-error:
- tp_channel_manager_emit_request_failed (self, request_token,
- error->domain, error->code, error->message);
- g_error_free (error);
- return TRUE;
-}
-
-static gboolean
-haze_media_manager_request_channel (TpChannelManager *manager,
- gpointer request_token,
- GHashTable *request_properties)
-{
- return haze_media_manager_requestotron (manager, request_token,
- request_properties, METHOD_REQUEST);
-}
-
-
-static gboolean
-haze_media_manager_create_channel (TpChannelManager *manager,
- gpointer request_token,
- GHashTable *request_properties)
-{
- return haze_media_manager_requestotron (manager, request_token,
- request_properties, METHOD_CREATE);
-}
-
-static gboolean
-haze_media_manager_ensure_channel (TpChannelManager *manager,
- gpointer request_token,
- GHashTable *request_properties)
-{
- return haze_media_manager_requestotron (manager, request_token,
- request_properties, METHOD_ENSURE);
-}
-
-static void
-channel_manager_iface_init (gpointer g_iface,
- gpointer iface_data)
-{
- TpChannelManagerIface *iface = g_iface;
-
- iface->foreach_channel = haze_media_manager_foreach_channel;
- iface->foreach_channel_class = haze_media_manager_foreach_channel_class;
- iface->request_channel = haze_media_manager_request_channel;
- iface->create_channel = haze_media_manager_create_channel;
- iface->ensure_channel = haze_media_manager_ensure_channel;
-}
diff --git a/src/media-manager.h b/src/media-manager.h
deleted file mode 100644
index 72a0409..0000000
--- a/src/media-manager.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * media-manager.h - Header for HazeMediaManager
- * Copyright (C) 2006, 2009 Collabora Ltd.
- *
- * Copied heavily from telepathy-gabble
- *
- * 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 __MEDIA_MANAGER_H__
-#define __MEDIA_MANAGER_H__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef struct _HazeMediaManager HazeMediaManager;
-typedef struct _HazeMediaManagerClass HazeMediaManagerClass;
-typedef struct _HazeMediaManagerPrivate HazeMediaManagerPrivate;
-
-struct _HazeMediaManagerClass {
- GObjectClass parent_class;
-};
-
-struct _HazeMediaManager {
- GObject parent;
-
- HazeMediaManagerPrivate *priv;
-};
-
-GType haze_media_manager_get_type (void);
-
-/* TYPE MACROS */
-#define HAZE_TYPE_MEDIA_MANAGER \
- (haze_media_manager_get_type ())
-#define HAZE_MEDIA_MANAGER(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj), HAZE_TYPE_MEDIA_MANAGER,\
- HazeMediaManager))
-#define HAZE_MEDIA_MANAGER_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass), HAZE_TYPE_MEDIA_MANAGER,\
- HazeMediaManagerClass))
-#define HAZE_IS_MEDIA_MANAGER(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj), HAZE_TYPE_MEDIA_MANAGER))
-#define HAZE_IS_MEDIA_MANAGER_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass), HAZE_TYPE_MEDIA_MANAGER))
-#define HAZE_MEDIA_MANAGER_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS ((obj), HAZE_TYPE_MEDIA_MANAGER,\
- HazeMediaManagerClass))
-
-G_END_DECLS
-
-#endif /* #ifndef __MEDIA_MANAGER_H__ */
-
diff --git a/src/media-stream.c b/src/media-stream.c
deleted file mode 100644
index 7b4f29f..0000000
--- a/src/media-stream.c
+++ /dev/null
@@ -1,1399 +0,0 @@
-/*
- * media-stream.c - Source for HazeMediaStream
- * Copyright © 2006-2009 Collabora Ltd.
- * Copyright © 2006-2009 Nokia Corporation
- *
- * Copied heavily from telepathy-gabble.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "config.h"
-#include "media-stream.h"
-
-#include <libpurple/media/backend-iface.h>
-#include <string.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/errors.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/properties-mixin.h>
-#include <telepathy-glib/svc-media-interfaces.h>
-
-#define DEBUG_FLAG HAZE_DEBUG_MEDIA
-
-#include "debug.h"
-
-static void stream_handler_iface_init (gpointer, gpointer);
-
-G_DEFINE_TYPE_WITH_CODE (HazeMediaStream,
- haze_media_stream,
- G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
- tp_dbus_properties_mixin_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_MEDIA_STREAM_HANDLER,
- stream_handler_iface_init)
- )
-
-/* properties */
-enum
-{
- PROP_OBJECT_PATH = 1,
- PROP_DBUS_DAEMON,
- PROP_NAME,
- PROP_PEER,
- PROP_ID,
- PROP_MEDIA_TYPE,
- PROP_CONNECTION_STATE,
- PROP_READY,
- PROP_PLAYING,
- PROP_COMBINED_DIRECTION,
- PROP_LOCAL_HOLD,
- PROP_MEDIA,
- PROP_CODECS_READY,
- PROP_STUN_SERVERS,
- PROP_RELAY_INFO,
- PROP_NAT_TRAVERSAL,
- PROP_CREATED_LOCALLY,
- LAST_PROPERTY
-};
-
-/* private structure */
-
-struct _HazeMediaStreamPrivate
-{
- PurpleMedia *media;
-
- gchar *object_path;
- TpDBusDaemon *dbus_daemon;
- guint id;
- guint media_type;
-
- GList *codecs;
- GList *remote_codecs;
- GList *local_candidates;
- GList *remote_candidates;
-
- /* Whether we're waiting for a codec intersection from the streaming
- * implementation. If FALSE, SupportedCodecs is a no-op.
- */
- gboolean awaiting_intersection;
-
- guint remote_candidate_count;
-
- gchar *nat_traversal;
- /* GPtrArray(GValueArray(STRING, UINT)) */
- GPtrArray *stun_servers;
- /* GPtrArray(GHashTable(string => GValue)) */
- GPtrArray *relay_info;
-
- gboolean on_hold;
-
- gboolean closed;
- gboolean dispose_has_run;
- gboolean local_hold;
- gboolean ready;
- gboolean sending;
- gboolean created_locally;
-};
-
-HazeMediaStream *
-haze_media_stream_new (const gchar *object_path,
- TpDBusDaemon *dbus_daemon,
- PurpleMedia *media,
- const gchar *name,
- const gchar *peer,
- guint media_type,
- guint id,
- gboolean created_locally,
- const gchar *nat_traversal,
- const GPtrArray *relay_info,
- gboolean local_hold)
-{
- GPtrArray *empty = NULL;
- HazeMediaStream *result;
-
- g_return_val_if_fail (PURPLE_IS_MEDIA (media), NULL);
-
- if (relay_info == NULL)
- {
- empty = g_ptr_array_sized_new (0);
- relay_info = empty;
- }
-
- result = g_object_new (HAZE_TYPE_MEDIA_STREAM,
- "object-path", object_path,
- "dbus-daemon", dbus_daemon,
- "media", media,
- "name", name,
- "peer", peer,
- "media-type", media_type,
- "id", id,
- "created-locally", created_locally,
- "nat-traversal", nat_traversal,
- "relay-info", relay_info,
- "local-hold", local_hold,
- NULL);
-
- if (empty != NULL)
- g_ptr_array_free (empty, TRUE);
-
- return result;
-}
-
-TpMediaStreamType
-haze_media_stream_get_media_type (HazeMediaStream *self)
-{
- return self->priv->media_type;
-}
-
-static void
-haze_media_stream_init (HazeMediaStream *self)
-{
- HazeMediaStreamPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- HAZE_TYPE_MEDIA_STREAM, HazeMediaStreamPrivate);
-
- self->priv = priv;
-
- priv->stun_servers = g_ptr_array_sized_new (1);
-}
-
-static GObject *
-haze_media_stream_constructor (GType type, guint n_props,
- GObjectConstructParam *props)
-{
- GObject *obj;
- HazeMediaStream *stream;
- HazeMediaStreamPrivate *priv;
-
- /* call base class constructor */
- obj = G_OBJECT_CLASS (haze_media_stream_parent_class)->
- constructor (type, n_props, props);
- stream = HAZE_MEDIA_STREAM (obj);
- priv = stream->priv;
-
- g_assert (priv->media != NULL);
-
- /* go for the bus */
- tp_dbus_daemon_register_object (priv->dbus_daemon, priv->object_path, obj);
-
- if (priv->created_locally)
- {
- g_object_set (stream, "combined-direction",
- MAKE_COMBINED_DIRECTION (TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL,
- 0), NULL);
- }
- else
- {
- priv->awaiting_intersection = TRUE;
- g_object_set (stream, "combined-direction",
- MAKE_COMBINED_DIRECTION (TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL,
- TP_MEDIA_STREAM_PENDING_LOCAL_SEND), NULL);
- }
-
- return obj;
-}
-
-static void
-haze_media_stream_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- HazeMediaStream *stream = HAZE_MEDIA_STREAM (object);
- HazeMediaStreamPrivate *priv = stream->priv;
-
- switch (property_id) {
- case PROP_OBJECT_PATH:
- g_value_set_string (value, priv->object_path);
- break;
- case PROP_DBUS_DAEMON:
- g_value_set_object (value, priv->dbus_daemon);
- break;
- case PROP_NAME:
- g_value_set_string (value, stream->name);
- break;
- case PROP_PEER:
- g_value_set_string (value, stream->peer);
- break;
- case PROP_ID:
- g_value_set_uint (value, priv->id);
- break;
- case PROP_MEDIA_TYPE:
- g_value_set_uint (value, priv->media_type);
- break;
- case PROP_CONNECTION_STATE:
- g_value_set_uint (value, stream->connection_state);
- break;
- case PROP_READY:
- g_value_set_boolean (value, priv->ready);
- break;
- case PROP_PLAYING:
- g_value_set_boolean (value, stream->playing);
- break;
- case PROP_COMBINED_DIRECTION:
- g_value_set_uint (value, stream->combined_direction);
- break;
- case PROP_LOCAL_HOLD:
- g_value_set_boolean (value, priv->local_hold);
- break;
- case PROP_MEDIA:
- g_value_set_object (value, priv->media);
- break;
- case PROP_CODECS_READY:
- g_value_set_boolean (value, priv->codecs != NULL);
- break;
- case PROP_STUN_SERVERS:
- g_value_set_boxed (value, priv->stun_servers);
- break;
- case PROP_NAT_TRAVERSAL:
- g_value_set_string (value, priv->nat_traversal);
- break;
- case PROP_CREATED_LOCALLY:
- g_value_set_boolean (value, priv->created_locally);
- break;
- case PROP_RELAY_INFO:
- g_value_set_boxed (value, priv->relay_info);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-haze_media_stream_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- HazeMediaStream *stream = HAZE_MEDIA_STREAM (object);
- HazeMediaStreamPrivate *priv = stream->priv;
-
- switch (property_id) {
- case PROP_OBJECT_PATH:
- g_free (priv->object_path);
- priv->object_path = g_value_dup_string (value);
- break;
- case PROP_DBUS_DAEMON:
- g_assert (priv->dbus_daemon == NULL); /* construct-only */
- priv->dbus_daemon = g_value_dup_object (value);
- break;
- case PROP_NAME:
- g_free (stream->name);
- stream->name = g_value_dup_string (value);
- break;
- case PROP_PEER:
- g_free (stream->peer);
- stream->peer = g_value_dup_string (value);
- break;
- case PROP_ID:
- priv->id = g_value_get_uint (value);
- break;
- case PROP_MEDIA_TYPE:
- priv->media_type = g_value_get_uint (value);
- break;
- case PROP_CONNECTION_STATE:
- DEBUG ("stream %s connection state %d",
- stream->name, stream->connection_state);
- stream->connection_state = g_value_get_uint (value);
- break;
- case PROP_READY:
- priv->ready = g_value_get_boolean (value);
- break;
- case PROP_PLAYING:
- break;
- case PROP_COMBINED_DIRECTION:
- DEBUG ("changing combined direction from %u to %u",
- stream->combined_direction, g_value_get_uint (value));
- stream->combined_direction = g_value_get_uint (value);
- break;
- case PROP_MEDIA:
- g_assert (priv->media == NULL);
- priv->media = g_value_dup_object (value);
- break;
- case PROP_NAT_TRAVERSAL:
- g_assert (priv->nat_traversal == NULL);
- priv->nat_traversal = g_value_dup_string (value);
- break;
- case PROP_CREATED_LOCALLY:
- priv->created_locally = g_value_get_boolean (value);
- break;
- case PROP_RELAY_INFO:
- g_assert (priv->relay_info == NULL);
- priv->relay_info = g_value_dup_boxed (value);
- break;
- case PROP_LOCAL_HOLD:
- priv->local_hold = g_value_get_boolean (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void haze_media_stream_dispose (GObject *object);
-static void haze_media_stream_finalize (GObject *object);
-
-static void
-haze_media_stream_class_init (HazeMediaStreamClass *haze_media_stream_class)
-{
- static TpDBusPropertiesMixinPropImpl stream_handler_props[] = {
- { "RelayInfo", "relay-info", NULL },
- { "STUNServers", "stun-servers", NULL },
- { "NATTraversal", "nat-traversal", NULL },
- { "CreatedLocally", "created-locally", NULL },
- { NULL }
- };
- static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
- { TP_IFACE_MEDIA_STREAM_HANDLER,
- tp_dbus_properties_mixin_getter_gobject_properties,
- NULL,
- stream_handler_props,
- },
- { NULL }
- };
- GObjectClass *object_class = G_OBJECT_CLASS (haze_media_stream_class);
- GParamSpec *param_spec;
-
- g_type_class_add_private (haze_media_stream_class,
- sizeof (HazeMediaStreamPrivate));
-
- object_class->constructor = haze_media_stream_constructor;
-
- object_class->get_property = haze_media_stream_get_property;
- object_class->set_property = haze_media_stream_set_property;
-
- object_class->dispose = haze_media_stream_dispose;
- object_class->finalize = haze_media_stream_finalize;
-
- param_spec = g_param_spec_string ("object-path", "D-Bus object path",
- "The D-Bus object path used for this "
- "object on the bus.",
- NULL,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec);
-
- param_spec = g_param_spec_object ("dbus-daemon", "D-Bus connection",
- "Connection to D-Bus",
- TP_TYPE_DBUS_DAEMON,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_DBUS_DAEMON, param_spec);
-
- param_spec = g_param_spec_string ("name", "Stream name",
- "An opaque name for the stream used in the signalling.", NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_NAME, param_spec);
-
- param_spec = g_param_spec_string ("peer", "Peer name",
- "The name for the peer used in the signalling.", NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_PEER, param_spec);
-
- param_spec = g_param_spec_uint ("id", "Stream ID",
- "A stream number for the stream used in the "
- "D-Bus API.",
- 0, G_MAXUINT, 0,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_ID, param_spec);
-
- param_spec = g_param_spec_uint ("media-type", "Stream media type",
- "A constant indicating which media type the stream carries.",
- TP_MEDIA_STREAM_TYPE_AUDIO, TP_MEDIA_STREAM_TYPE_VIDEO,
- TP_MEDIA_STREAM_TYPE_AUDIO,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_MEDIA_TYPE, param_spec);
-
- param_spec = g_param_spec_uint ("connection-state", "Stream connection state",
- "An integer indicating the state of the"
- "stream's connection.",
- TP_MEDIA_STREAM_STATE_DISCONNECTED,
- TP_MEDIA_STREAM_STATE_CONNECTED,
- TP_MEDIA_STREAM_STATE_DISCONNECTED,
- G_PARAM_CONSTRUCT |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_CONNECTION_STATE,
- param_spec);
-
- param_spec = g_param_spec_boolean ("ready", "Ready?",
- "A boolean signifying whether the user "
- "is ready to handle signals from this "
- "object.",
- FALSE,
- G_PARAM_CONSTRUCT |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_READY, param_spec);
-
- param_spec = g_param_spec_boolean ("playing", "Set playing",
- "A boolean signifying whether the stream "
- "has been set playing yet.",
- FALSE,
- G_PARAM_CONSTRUCT |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_PLAYING, param_spec);
-
- param_spec = g_param_spec_uint ("combined-direction",
- "Combined direction",
- "An integer indicating the directions the stream currently sends in, "
- "and the peers who have been asked to send.",
- TP_MEDIA_STREAM_DIRECTION_NONE,
- MAKE_COMBINED_DIRECTION (TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL,
- TP_MEDIA_STREAM_PENDING_LOCAL_SEND |
- TP_MEDIA_STREAM_PENDING_REMOTE_SEND),
- TP_MEDIA_STREAM_DIRECTION_NONE,
- G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_COMBINED_DIRECTION,
- param_spec);
-
- param_spec = g_param_spec_boolean ("local-hold", "Local hold?",
- "True if resources used for this stream have been freed.", FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK);
- g_object_class_install_property (object_class, PROP_LOCAL_HOLD, param_spec);
-
- param_spec = g_param_spec_object ("media", "PurpleMedia object",
- "Media object signalling this media stream.",
- PURPLE_TYPE_MEDIA,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NICK |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_MEDIA, param_spec);
-
- param_spec = g_param_spec_boxed ("stun-servers", "STUN servers",
- "Array of (STRING: address literal, UINT: port) pairs",
- /* FIXME: use correct macro when available */
- tp_type_dbus_array_su (),
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_STUN_SERVERS, param_spec);
-
- param_spec = g_param_spec_boxed ("relay-info", "Relay info",
- "Array of mappings containing relay server information",
- TP_ARRAY_TYPE_STRING_VARIANT_MAP_LIST,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_RELAY_INFO, param_spec);
-
- param_spec = g_param_spec_string ("nat-traversal", "NAT traversal",
- "NAT traversal mechanism for this stream", NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_NAT_TRAVERSAL,
- param_spec);
-
- param_spec = g_param_spec_boolean ("created-locally", "Created locally?",
- "True if this stream was created by the local user", FALSE,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CREATED_LOCALLY,
- param_spec);
-
- param_spec = g_param_spec_boolean ("codecs-ready", "Codecs ready",
- "True if the codecs for this stream are ready to be used", FALSE,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_CODECS_READY,
- param_spec);
-
- haze_media_stream_class->dbus_props_class.interfaces = prop_interfaces;
- tp_dbus_properties_mixin_class_init (object_class,
- G_STRUCT_OFFSET (HazeMediaStreamClass, dbus_props_class));
-}
-
-void
-haze_media_stream_dispose (GObject *object)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (object);
- HazeMediaStreamPrivate *priv = self->priv;
-
- DEBUG ("called");
-
- if (priv->dispose_has_run)
- return;
-
- priv->dispose_has_run = TRUE;
-
- tp_clear_object (&priv->dbus_daemon);
-
- if (priv->local_candidates)
- {
- purple_media_candidate_list_free (priv->local_candidates);
- priv->local_candidates = NULL;
- }
-
- if (priv->remote_candidates)
- {
- purple_media_candidate_list_free (priv->remote_candidates);
- priv->remote_candidates = NULL;
- }
-
- if (priv->codecs)
- {
- purple_media_codec_list_free (priv->codecs);
- priv->codecs = NULL;
- }
-
- if (priv->remote_codecs)
- {
- purple_media_codec_list_free (priv->remote_codecs);
- priv->remote_codecs = NULL;
- }
-
- g_object_unref (priv->media);
- priv->media = NULL;
-
- if (G_OBJECT_CLASS (haze_media_stream_parent_class)->dispose)
- G_OBJECT_CLASS (haze_media_stream_parent_class)->dispose (object);
-}
-
-void
-haze_media_stream_finalize (GObject *object)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (object);
- HazeMediaStreamPrivate *priv = self->priv;
-
- g_free (priv->object_path);
- g_free (priv->nat_traversal);
-
- /* FIXME: use correct macro when available */
- if (priv->stun_servers != NULL)
- g_boxed_free (tp_type_dbus_array_su (), priv->stun_servers);
-
- if (priv->relay_info != NULL)
- g_boxed_free (TP_ARRAY_TYPE_STRING_VARIANT_MAP_LIST, priv->relay_info);
-
- G_OBJECT_CLASS (haze_media_stream_parent_class)->finalize (object);
-}
-
-
-GList *
-haze_media_stream_get_local_candidates (HazeMediaStream *self)
-{
- HazeMediaStreamPrivate *priv = self->priv;
- return g_list_copy (priv->local_candidates);
-}
-
-
-GList *
-haze_media_stream_get_codecs (HazeMediaStream *self)
-{
- HazeMediaStreamPrivate *priv = self->priv;
- return purple_media_codec_list_copy (priv->codecs);
-}
-
-
-static void
-pass_remote_candidates (HazeMediaStream *self)
-{
- HazeMediaStreamPrivate *priv = self->priv;
- GType transport_struct_type = TP_STRUCT_TYPE_MEDIA_STREAM_HANDLER_TRANSPORT;
- GType candidate_struct_type = TP_STRUCT_TYPE_MEDIA_STREAM_HANDLER_CANDIDATE;
- GList *iter = priv->remote_candidates;
-
- for (; iter; iter = g_list_next (iter))
- {
- gchar *address, *username, *password, *candidate_id;
- GValue candidate = { 0, };
- GPtrArray *transports;
- GValue transport = { 0, };
- PurpleMediaCandidate *c = iter->data;
- PurpleMediaCandidateType candidate_type;
- guint type = TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL;
-
- g_value_init (&transport, transport_struct_type);
- g_value_take_boxed (&transport,
- dbus_g_type_specialized_construct (transport_struct_type));
-
- address = purple_media_candidate_get_ip (c);
- username = purple_media_candidate_get_username (c);
- password = purple_media_candidate_get_password (c);
- candidate_type = purple_media_candidate_get_candidate_type (c);
-
- if (candidate_type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST)
- type = TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL;
- else if (candidate_type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX)
- type = TP_MEDIA_STREAM_TRANSPORT_TYPE_DERIVED;
- else if (candidate_type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY)
- type = TP_MEDIA_STREAM_TRANSPORT_TYPE_RELAY;
- else
- DEBUG ("Unknown candidate type");
-
- dbus_g_type_struct_set (&transport,
- 0, purple_media_candidate_get_component_id (c),
- 1, address,
- 2, purple_media_candidate_get_port (c),
- 3, purple_media_candidate_get_protocol (c) ==
- PURPLE_MEDIA_NETWORK_PROTOCOL_UDP ?
- TP_MEDIA_STREAM_BASE_PROTO_UDP :
- TP_MEDIA_STREAM_BASE_PROTO_TCP,
- 4, "RTP",
- 5, "AVP",
- 6, (double)purple_media_candidate_get_priority (c),
- 7, type,
- 8, username,
- 9, password,
- G_MAXUINT);
-
- g_free (password);
- g_free (username);
- g_free (address);
-
- transports = g_ptr_array_sized_new (1);
- g_ptr_array_add (transports, g_value_get_boxed (&transport));
-
- g_value_init (&candidate, candidate_struct_type);
- g_value_take_boxed (&candidate,
- dbus_g_type_specialized_construct (candidate_struct_type));
-
- candidate_id = purple_media_candidate_get_foundation (c);
-
- dbus_g_type_struct_set (&candidate,
- 0, candidate_id,
- 1, transports,
- G_MAXUINT);
-
- DEBUG ("passing 1 remote candidate to stream engine: %s", candidate_id);
-
- tp_svc_media_stream_handler_emit_add_remote_candidate (
- self, candidate_id, transports);
-
- g_free (candidate_id);
- }
-}
-
-
-void
-haze_media_stream_add_remote_candidates (HazeMediaStream *self,
- GList *remote_candidates)
-{
- HazeMediaStreamPrivate *priv = self->priv;
-
- priv->remote_candidates = g_list_concat (
- priv->remote_candidates,
- purple_media_candidate_list_copy (remote_candidates));
-
- if (priv->ready == TRUE)
- pass_remote_candidates (self);
-}
-
-
-static void
-pass_remote_codecs (HazeMediaStream *self)
-{
- HazeMediaStreamPrivate *priv = self->priv;
- GType codec_struct_type = TP_STRUCT_TYPE_MEDIA_STREAM_HANDLER_CODEC;
- GPtrArray *codecs = g_ptr_array_new ();
- GList *iter = priv->remote_codecs;
-
- for (; iter; iter = g_list_next (iter))
- {
- GValue codec = { 0, };
- PurpleMediaCodec *c = iter->data;
- gchar *name;
- GList *codec_params;
- GHashTable *params;
-
- g_value_init (&codec, codec_struct_type);
- g_value_take_boxed (&codec,
- dbus_g_type_specialized_construct (codec_struct_type));
-
- name = purple_media_codec_get_encoding_name (c);
- codec_params = purple_media_codec_get_optional_parameters (c);
- params = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, g_free);
-
- for (; codec_params; codec_params = g_list_next (codec_params))
- {
- PurpleKeyValuePair *pair = codec_params->data;
- g_hash_table_insert (params, pair->key, pair->value);
- }
-
- DEBUG ("new remote %s codec: %u '%s' %u %u %u",
- priv->media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video",
- purple_media_codec_get_id (c), name, priv->media_type,
- purple_media_codec_get_clock_rate (c),
- purple_media_codec_get_channels (c));
-
- dbus_g_type_struct_set (&codec,
- 0, purple_media_codec_get_id (c),
- 1, name,
- 2, priv->media_type,
- 3, purple_media_codec_get_clock_rate (c),
- 4, purple_media_codec_get_channels (c),
- 5, params,
- G_MAXUINT);
-
- g_free (name);
- g_hash_table_destroy (params);
-
- g_ptr_array_add (codecs, g_value_get_boxed (&codec));
- }
-
- DEBUG ("passing %d remote codecs to stream-engine", codecs->len);
-
- tp_svc_media_stream_handler_emit_set_remote_codecs (self, codecs);
-}
-
-
-void
-haze_media_stream_set_remote_codecs (HazeMediaStream *self,
- GList *remote_codecs)
-{
- HazeMediaStreamPrivate *priv = self->priv;
- GList *iter = priv->remote_codecs;
-
- for (; iter; iter = g_list_delete_link (iter, iter))
- g_object_unref (iter->data);
-
- priv->remote_codecs = purple_media_codec_list_copy (remote_codecs);
-
- if (priv->ready == TRUE)
- pass_remote_codecs (self);
-}
-
-void
-haze_media_stream_add_stun_server (HazeMediaStream *self,
- const gchar *stun_ip,
- guint stun_port)
-{
- HazeMediaStreamPrivate *priv = self->priv;
- GValueArray *va;
- GValue ip = {0}, port = {0};
-
- if (stun_ip == NULL || stun_ip[0] == 0)
- {
- DEBUG ("Invalid STUN address passed: %s", stun_ip);
- return;
- }
- else if (stun_port > 65535)
- {
- DEBUG ("Invalid STUN port passed: %d", stun_port);
- return;
- }
-
- g_value_init (&ip, G_TYPE_STRING);
- g_value_set_string (&ip, stun_ip);
- g_value_init (&port, G_TYPE_UINT);
- g_value_set_uint (&port, stun_port);
-
- va = g_value_array_new (2);
- g_value_array_append (va, &ip);
- g_value_array_append (va, &port);
-
- g_ptr_array_add (priv->stun_servers, va);
-}
-
-
-/**
- * haze_media_stream_codec_choice
- *
- * Implements D-Bus method CodecChoice
- * on interface org.freedesktop.Telepathy.Media.StreamHandler
- */
-static void
-haze_media_stream_codec_choice (TpSvcMediaStreamHandler *iface,
- guint codec_id,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
-
- g_assert (HAZE_IS_MEDIA_STREAM (self));
-
- tp_svc_media_stream_handler_return_from_codec_choice (context);
-}
-
-
-gboolean
-haze_media_stream_error (HazeMediaStream *self,
- guint err_no,
- const gchar *message,
- GError **error)
-{
- g_assert (HAZE_IS_MEDIA_STREAM (self));
-
- DEBUG ( "Media.StreamHandler::Error called, error %u (%s) -- emitting signal",
- err_no, message);
-
- purple_media_error (self->priv->media, message);
-
- return TRUE;
-}
-
-
-/**
- * haze_media_stream_error
- *
- * Implements D-Bus method Error
- * on interface org.freedesktop.Telepathy.Media.StreamHandler
- */
-static void
-haze_media_stream_error_async (TpSvcMediaStreamHandler *iface,
- guint errno,
- const gchar *message,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
- GError *error = NULL;
-
- if (haze_media_stream_error (self, errno, message, &error))
- {
- tp_svc_media_stream_handler_return_from_error (context);
- }
- else
- {
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- }
-}
-
-
-/**
- * haze_media_stream_hold:
- *
- * Tell streaming clients that the stream is going on hold, so they should
- * stop streaming and free up any resources they are currently holding
- * (e.g. close hardware devices); or that the stream is coming off hold,
- * so they should reacquire those resources.
- */
-void
-haze_media_stream_hold (HazeMediaStream *self,
- gboolean hold)
-{
- tp_svc_media_stream_handler_emit_set_stream_held (self, hold);
-}
-
-
-/**
- * haze_media_stream_hold_state:
- *
- * Called by streaming clients when the stream's hold state has been changed
- * successfully in response to SetStreamHeld.
- */
-static void
-haze_media_stream_hold_state (TpSvcMediaStreamHandler *iface,
- gboolean hold_state,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
- HazeMediaStreamPrivate *priv = self->priv;
-
- DEBUG ("%p: %s", self, hold_state ? "held" : "unheld");
- priv->local_hold = hold_state;
-
- g_object_notify ((GObject *) self, "local-hold");
-
- tp_svc_media_stream_handler_return_from_hold_state (context);
-}
-
-
-/**
- * haze_media_stream_unhold_failure:
- *
- * Called by streaming clients when an attempt to reacquire the necessary
- * hardware or software resources to unhold the stream, in response to
- * SetStreamHeld, has failed.
- */
-static void
-haze_media_stream_unhold_failure (TpSvcMediaStreamHandler *iface,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
- HazeMediaStreamPrivate *priv = self->priv;
-
- DEBUG ("%p", self);
-
- priv->local_hold = TRUE;
-
-// maybe emit unhold failed here?
- g_object_notify ((GObject *) self, "local-hold");
-
- tp_svc_media_stream_handler_return_from_unhold_failure (context);
-}
-
-
-/**
- * haze_media_stream_native_candidates_prepared
- *
- * Implements D-Bus method NativeCandidatesPrepared
- * on interface org.freedesktop.Telepathy.Media.StreamHandler
- */
-static void
-haze_media_stream_native_candidates_prepared (TpSvcMediaStreamHandler *iface,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
- HazeMediaStreamPrivate *priv;
- PurpleMediaBackend *backend;
-
- g_assert (HAZE_IS_MEDIA_STREAM (self));
-
- priv = self->priv;
-
- g_object_get (G_OBJECT (priv->media), "backend", &backend, NULL);
- g_signal_emit_by_name (backend, "candidates-prepared",
- self->name, self->peer);
- g_object_unref (backend);
-
- tp_svc_media_stream_handler_return_from_native_candidates_prepared (context);
-}
-
-
-/**
- * haze_media_stream_new_active_candidate_pair
- *
- * Implements D-Bus method NewActiveCandidatePair
- * on interface org.freedesktop.Telepathy.Media.StreamHandler
- */
-static void
-haze_media_stream_new_active_candidate_pair (TpSvcMediaStreamHandler *iface,
- const gchar *native_candidate_id,
- const gchar *remote_candidate_id,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
- HazeMediaStreamPrivate *priv;
- PurpleMediaBackend *backend;
- GList *l_iter;
-
- /*
- * This appears to be called for each pair of components.
- * I'm not sure how to go about differentiating between the two
- * components as the ids are the same.
- */
-
- DEBUG ("called (%s, %s)", native_candidate_id, remote_candidate_id);
-
- g_assert (HAZE_IS_MEDIA_STREAM (self));
-
- priv = self->priv;
- l_iter = priv->local_candidates;
- g_object_get (priv->media, "backend", &backend, NULL);
-
- for (; l_iter; l_iter = g_list_next (l_iter))
- {
- PurpleMediaCandidate *lc = l_iter->data;
- GList *r_iter = priv->remote_candidates;
-
- for (; r_iter; r_iter = g_list_next (r_iter))
- {
- PurpleMediaCandidate *rc = r_iter->data;
-
- if (purple_media_candidate_get_component_id (lc) ==
- purple_media_candidate_get_component_id (rc))
- {
- gchar *l_name = purple_media_candidate_get_foundation (lc);
- gchar *r_name = purple_media_candidate_get_foundation (rc);
-
- if (!strcmp (l_name, native_candidate_id) &&
- !strcmp (r_name, remote_candidate_id))
- {
- DEBUG ("Emitting new active candidate pair %d: %s - %s",
- purple_media_candidate_get_component_id (lc),
- l_name, r_name);
-
- g_signal_emit_by_name (backend, "active-candidate-pair",
- self->name, self->peer, lc, rc);
- }
-
- g_free (l_name);
- g_free (r_name);
- }
- }
- }
-
- g_object_unref (backend);
- tp_svc_media_stream_handler_return_from_new_active_candidate_pair (context);
-}
-
-
-/**
- * haze_media_stream_new_native_candidate
- *
- * Implements D-Bus method NewNativeCandidate
- * on interface org.freedesktop.Telepathy.Media.StreamHandler
- */
-static void
-haze_media_stream_new_native_candidate (TpSvcMediaStreamHandler *iface,
- const gchar *candidate_id,
- const GPtrArray *transports,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
- HazeMediaStreamPrivate *priv;
- PurpleMediaBackend *backend;
- guint i;
-
- g_assert (HAZE_IS_MEDIA_STREAM (self));
-
- priv = self->priv;
-
- g_object_get (G_OBJECT (priv->media), "backend", &backend, NULL);
-
- for (i = 0; i < transports->len; i++)
- {
- GValueArray *transport;
- guint component, type, proto;
- PurpleMediaCandidate *c;
- PurpleMediaCandidateType candidate_type =
- PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
- PurpleMediaNetworkProtocol protocol = PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
-
- transport = g_ptr_array_index (transports, i);
- component = g_value_get_uint (g_value_array_get_nth (transport, 0));
- type = g_value_get_uint (g_value_array_get_nth (transport, 7));
- proto = g_value_get_uint (g_value_array_get_nth (transport, 3));
-
- if (type == TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL)
- candidate_type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
- else if (type == TP_MEDIA_STREAM_TRANSPORT_TYPE_DERIVED)
- candidate_type = PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
- else if (type == TP_MEDIA_STREAM_TRANSPORT_TYPE_RELAY)
- candidate_type = PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
- else
- DEBUG ("Unknown candidate type");
-
- if (proto == TP_MEDIA_STREAM_BASE_PROTO_UDP)
- protocol = PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
- else if (proto == TP_MEDIA_STREAM_BASE_PROTO_TCP)
- protocol = PURPLE_MEDIA_NETWORK_PROTOCOL_TCP;
- else
- DEBUG ("Unknown network protocol");
-
- c = purple_media_candidate_new (candidate_id, component, candidate_type,
- protocol,
- /* address */
- g_value_get_string (g_value_array_get_nth (transport, 1)),
- /* port */
- g_value_get_uint (g_value_array_get_nth (transport, 2)));
-
- g_object_set (c, "username",
- g_value_get_string (g_value_array_get_nth (transport, 8)), NULL);
- g_object_set (c, "password",
- g_value_get_string (g_value_array_get_nth (transport, 9)), NULL);
- g_object_set (c, "priority",
- (guint)g_value_get_double (
- g_value_array_get_nth (transport, 6)), NULL);
-
- DEBUG ("new-candidate: %s %s %p", self->name, self->peer, c);
-
- priv->local_candidates = g_list_append (priv->local_candidates, c);
-
- g_signal_emit_by_name (backend, "new-candidate", self->name, self->peer, c);
- }
-
- g_object_unref (backend);
-
- tp_svc_media_stream_handler_return_from_new_native_candidate (context);
-}
-
-static void haze_media_stream_set_local_codecs (TpSvcMediaStreamHandler *,
- const GPtrArray *codecs, DBusGMethodInvocation *);
-
-/**
- * haze_media_stream_ready
- *
- * Implements D-Bus method Ready
- * on interface org.freedesktop.Telepathy.Media.StreamHandler
- */
-static void
-haze_media_stream_ready (TpSvcMediaStreamHandler *iface,
- const GPtrArray *codecs,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
- HazeMediaStreamPrivate *priv;
-
- g_assert (HAZE_IS_MEDIA_STREAM (self));
-
- priv = self->priv;
-
- DEBUG ("ready called");
-
- if (priv->ready == FALSE)
- {
- g_object_set (self, "ready", TRUE, NULL);
-
- tp_svc_media_stream_handler_emit_set_stream_playing (self, TRUE);
-
- if (purple_media_get_session_type (priv->media, self->name) &
- (PURPLE_MEDIA_SEND_AUDIO | PURPLE_MEDIA_SEND_VIDEO))
- {
- g_object_set (self, "combined-direction",
- MAKE_COMBINED_DIRECTION (TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL,
- 0), NULL);
- tp_svc_media_stream_handler_emit_set_stream_sending (self, TRUE);
- }
-
- /* If a new stream is added while the call's on hold, it will have
- * local_hold set at construct time. So once tp-fs has called Ready(), we
- * should let it know this stream's on hold.
- */
- if (priv->local_hold)
- haze_media_stream_hold (self, priv->local_hold);
- }
- else
- {
- DEBUG ("Ready called twice, running plain SetLocalCodecs instead");
- }
-
- /* set_local_codecs and ready return the same thing, so we can do... */
- haze_media_stream_set_local_codecs (iface, codecs, context);
- pass_remote_codecs (self);
- pass_remote_candidates (self);
-}
-
-static void
-convert_param (gchar *key, gchar *value, PurpleMediaCodec *codec)
-{
- purple_media_codec_add_optional_parameter (codec, key, value);
-}
-
-static gboolean
-pass_local_codecs (HazeMediaStream *stream,
- const GPtrArray *codecs,
- gboolean ready,
- GError **error)
-{
- HazeMediaStreamPrivate *priv = stream->priv;
- PurpleMediaSessionType type = PURPLE_MEDIA_AUDIO;
- PurpleMediaCodec *c;
- guint i;
-
- DEBUG ("putting list of %d supported codecs from stream-engine into cache",
- codecs->len);
-
- if (priv->media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
- type = PURPLE_MEDIA_AUDIO;
- else if (priv->media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
- type = PURPLE_MEDIA_VIDEO;
- else
- g_assert_not_reached ();
-
- for (i = 0; i < codecs->len; i++)
- {
- GType codec_struct_type = TP_STRUCT_TYPE_MEDIA_STREAM_HANDLER_CODEC;
-
- GValue codec = { 0, };
- guint id, clock_rate, channels;
- gchar *name;
- GHashTable *params;
-
- g_value_init (&codec, codec_struct_type);
- g_value_set_static_boxed (&codec, g_ptr_array_index (codecs, i));
-
- dbus_g_type_struct_get (&codec,
- 0, &id,
- 1, &name,
- 3, &clock_rate,
- 4, &channels,
- 5, &params,
- G_MAXUINT);
-
- c = purple_media_codec_new (id, name, type, clock_rate);
- g_object_set (c, "channels", channels, NULL);
-
- g_hash_table_foreach (params, (GHFunc)convert_param, c);
-
- DEBUG ("adding codec: %s", purple_media_codec_to_string (c));
-
- priv->codecs = g_list_append (priv->codecs, c);
-
- g_signal_emit_by_name (priv->media, "codecs-changed", stream->name);
- }
-
- return TRUE;
-}
-
-/**
- * haze_media_stream_set_local_codecs
- *
- * Implements D-Bus method SetLocalCodecs
- * on interface org.freedesktop.Telepathy.Media.StreamHandler
- */
-static void
-haze_media_stream_set_local_codecs (TpSvcMediaStreamHandler *iface,
- const GPtrArray *codecs,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
- HazeMediaStreamPrivate *priv = self->priv;
- GError *error = NULL;
-
- DEBUG ("called");
-
- if (PURPLE_IS_MEDIA (priv->media) &&
- purple_media_is_initiator (priv->media, self->name, self->peer))
- {
- if (!pass_local_codecs (self, codecs, self->priv->created_locally,
- &error))
- {
- DEBUG ("failed: %s", error->message);
-
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
- }
- else
- {
- DEBUG ("ignoring local codecs, waiting for codec intersection");
- }
-
- tp_svc_media_stream_handler_return_from_set_local_codecs (context);
-}
-
-/**
- * haze_media_stream_stream_state
- *
- * Implements D-Bus method StreamState
- * on interface org.freedesktop.Telepathy.Media.StreamHandler
- */
-static void
-haze_media_stream_stream_state (TpSvcMediaStreamHandler *iface,
- guint connection_state,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
-
- g_object_set (self, "connection-state", connection_state, NULL);
-
- // emit connection state here
-
- tp_svc_media_stream_handler_return_from_stream_state (context);
-}
-
-
-/**
- * haze_media_stream_supported_codecs
- *
- * Implements D-Bus method SupportedCodecs
- * on interface org.freedesktop.Telepathy.Media.StreamHandler
- */
-static void
-haze_media_stream_supported_codecs (TpSvcMediaStreamHandler *iface,
- const GPtrArray *codecs,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
- HazeMediaStreamPrivate *priv = self->priv;
- GError *error = NULL;
-
- DEBUG ("called");
-
- if (priv->awaiting_intersection)
- {
- if (!pass_local_codecs (self, codecs, TRUE, &error))
- {
- DEBUG ("failed: %s", error->message);
-
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
-
- priv->awaiting_intersection = FALSE;
- }
- else
- {
- /* If we created the stream, we don't need to send the intersection. If
- * we didn't create it, but have already sent the intersection once, we
- * don't need to send it again. In either case, extra calls to
- * SupportedCodecs are in response to an incoming description-info, which
- * can only change parameters and which XEP-0167 §10 says is purely
- * advisory.
- */
- DEBUG ("we already sent, or don't need to send, our codecs");
- }
-
- tp_svc_media_stream_handler_return_from_supported_codecs (context);
-}
-
-/**
- * haze_media_stream_codecs_updated
- *
- * Implements D-Bus method CodecsUpdated
- * on interface org.freedesktop.Telepathy.Media.StreamHandler
- */
-static void
-haze_media_stream_codecs_updated (TpSvcMediaStreamHandler *iface,
- const GPtrArray *codecs,
- DBusGMethodInvocation *context)
-{
- HazeMediaStream *self = HAZE_MEDIA_STREAM (iface);
- GError *error = NULL;
-
- if (self->priv->codecs == NULL)
- {
- GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE,
- "CodecsUpdated may only be called once an initial set of codecs "
- "has been set" };
-
- dbus_g_method_return_error (context, &e);
- return;
- }
-
- if (self->priv->awaiting_intersection)
- {
- /* When awaiting an intersection the initial set of codecs should be set
- * by calling SupportedCodecs as that is the canonical set of codecs,
- * updates are only meaningful afterwards */
- tp_svc_media_stream_handler_return_from_codecs_updated (context);
- return;
- }
-
- if (pass_local_codecs (self, codecs, self->priv->created_locally, &error))
- {
- tp_svc_media_stream_handler_return_from_codecs_updated (context);
- }
- else
- {
- DEBUG ("failed: %s", error->message);
-
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- }
-}
-
-static void
-stream_handler_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcMediaStreamHandlerClass *klass =
- (TpSvcMediaStreamHandlerClass *) g_iface;
-
-#define IMPLEMENT(x,suffix) tp_svc_media_stream_handler_implement_##x (\
- klass, haze_media_stream_##x##suffix)
- IMPLEMENT(codec_choice,);
- IMPLEMENT(error,_async);
- IMPLEMENT(hold_state,);
- IMPLEMENT(native_candidates_prepared,);
- IMPLEMENT(new_active_candidate_pair,);
- IMPLEMENT(new_native_candidate,);
- IMPLEMENT(ready,);
- IMPLEMENT(set_local_codecs,);
- IMPLEMENT(stream_state,);
- IMPLEMENT(supported_codecs,);
- IMPLEMENT(unhold_failure,);
- IMPLEMENT(codecs_updated,);
-#undef IMPLEMENT
-}
diff --git a/src/media-stream.h b/src/media-stream.h
deleted file mode 100644
index 0d6f328..0000000
--- a/src/media-stream.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * media-stream.h - Header for HazeMediaStream
- * Copyright (C) 2006, 2009 Collabora Ltd.
- * Copyright (C) 2006 Nokia Corporation
- *
- * Copied heavily from telepathy-gabble.
- *
- * 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 __HAZE_MEDIA_STREAM_H__
-#define __HAZE_MEDIA_STREAM_H__
-
-#include <glib-object.h>
-#include <libpurple/media.h>
-#include <telepathy-glib/telepathy-glib.h>
-
-G_BEGIN_DECLS
-
-typedef enum
-{
- STREAM_SIG_STATE_NEW,
- STREAM_SIG_STATE_SENT,
- STREAM_SIG_STATE_ACKNOWLEDGED,
- STREAM_SIG_STATE_REMOVING
-} StreamSignallingState;
-
-typedef guint32 CombinedStreamDirection;
-
-typedef struct _HazeMediaStream HazeMediaStream;
-typedef struct _HazeMediaStreamClass HazeMediaStreamClass;
-typedef struct _HazeMediaStreamPrivate HazeMediaStreamPrivate;
-
-struct _HazeMediaStreamClass {
- GObjectClass parent_class;
-
- TpDBusPropertiesMixinClass dbus_props_class;
-};
-
-struct _HazeMediaStream {
- GObject parent;
-
- gchar *name;
- gchar *peer;
-
- TpMediaStreamState connection_state;
-
- CombinedStreamDirection combined_direction;
- gboolean playing;
-
- HazeMediaStreamPrivate *priv;
-};
-
-GType haze_media_stream_get_type (void);
-
-/* TYPE MACROS */
-#define HAZE_TYPE_MEDIA_STREAM \
- (haze_media_stream_get_type ())
-#define HAZE_MEDIA_STREAM(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj), HAZE_TYPE_MEDIA_STREAM, \
- HazeMediaStream))
-#define HAZE_MEDIA_STREAM_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass), HAZE_TYPE_MEDIA_STREAM, \
- HazeMediaStreamClass))
-#define HAZE_IS_MEDIA_STREAM(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj), HAZE_TYPE_MEDIA_STREAM))
-#define HAZE_IS_MEDIA_STREAM_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass), HAZE_TYPE_MEDIA_STREAM))
-#define HAZE_MEDIA_STREAM_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS ((obj), HAZE_TYPE_MEDIA_STREAM, \
- HazeMediaStreamClass))
-
-#define COMBINED_DIRECTION_GET_DIRECTION(d) \
- ((TpMediaStreamDirection) ((d) & TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL))
-#define COMBINED_DIRECTION_GET_PENDING_SEND(d) \
- ((TpMediaStreamPendingSend) ((d) >> 2))
-#define MAKE_COMBINED_DIRECTION(d, p) \
- ((CombinedStreamDirection) ((d) | ((p) << 2)))
-
-gboolean haze_media_stream_error (HazeMediaStream *self, guint err_no,
- const gchar *message, GError **error);
-
-void haze_media_stream_close (HazeMediaStream *close);
-void haze_media_stream_hold (HazeMediaStream *stream, gboolean hold);
-gboolean haze_media_stream_change_direction (HazeMediaStream *stream,
- guint requested_dir, GError **error);
-void haze_media_stream_accept_pending_local_send (HazeMediaStream *stream);
-
-HazeMediaStream *haze_media_stream_new (const gchar *object_path,
- TpDBusDaemon *dbus_daemon,
- PurpleMedia *media,
- const gchar *name,
- const gchar *peer,
- guint media_type,
- guint id,
- gboolean created_locally,
- const gchar *nat_traversal,
- const GPtrArray *relay_info,
- gboolean local_hold);
-TpMediaStreamType haze_media_stream_get_media_type (HazeMediaStream *self);
-
-GList *haze_media_stream_get_local_candidates (HazeMediaStream *self);
-GList *haze_media_stream_get_codecs (HazeMediaStream *self);
-void haze_media_stream_add_remote_candidates (HazeMediaStream *self,
- GList *remote_candidates);
-void haze_media_stream_set_remote_codecs (HazeMediaStream *self,
- GList *remote_codecs);
-void haze_media_stream_add_stun_server (HazeMediaStream *self,
- const gchar *stun_ip,
- guint stun_port);
-
-G_END_DECLS
-
-#endif /* #ifndef __HAZE_MEDIA_STREAM_H__*/
diff --git a/src/notify.c b/src/notify.c
index 7eb0185..9485b6b 100644
--- a/src/notify.c
+++ b/src/notify.c
@@ -18,7 +18,9 @@
*
*/
+#include <config.h>
#include "notify.h"
+
#include "connection-mail.h"
#include "debug.h"
diff --git a/src/protocol.c b/src/protocol.c
index cc01911..6e19d5f 100644
--- a/src/protocol.c
+++ b/src/protocol.c
@@ -32,6 +32,7 @@
#include <telepathy-glib/telepathy-glib-dbus.h>
#include "connection.h"
+#include "connection-avatars.h"
#include "debug.h"
G_DEFINE_TYPE (HazeProtocol, haze_protocol, TP_TYPE_BASE_PROTOCOL)
@@ -89,6 +90,11 @@ static const HazeParameterMapping irc_mappings[] = {
{ NULL, NULL }
};
+static const HazeParameterMapping sametime_mappings[] = {
+ { "usersplit1", "server" },
+ { NULL, NULL }
+};
+
static const HazeParameterMapping jabber_mappings[] = {
{ "connect_server", "server" },
/* usersplit1 => domain is deliberately not in this map, because
@@ -201,13 +207,15 @@ static const KnownProtocolInfo known_protocol_info[] = {
{ "local-xmpp", "prpl-bonjour", bonjour_mappings, "" /* ? */ },
{ "msn", "prpl-msn", NULL, "x-msn" },
{ "qq", "prpl-qq", NULL, "x-qq" /* ? */ },
- { "sametime", "prpl-meanwhile", NULL, "x-sametime" /* ? */ },
+ { "sametime", "prpl-meanwhile", sametime_mappings, "x-sametime" /* ? */ },
{ "sipe", "prpl-sipe", sipe_mappings, "" /* ? */ },
{ "yahoo", "prpl-yahoo", yahoo_mappings, "x-yahoo" },
{ "yahoojp", "prpl-yahoojp", yahoo_mappings, "x-yahoo" /* ? */ },
{ "zephyr", "prpl-zephyr", encoding_to_charset, "x-zephyr" /* ? */ },
{ "mxit", "prpl-loubserp-mxit", NULL, "x-mxit" /* ? */ },
{ "sip", "prpl-simple", NULL, "x-sip" },
+ { "skype-dbus", "prpl-bigbrownchunx-skype-dbus", NULL, "x-skype" },
+ { "skype-x11", "prpl-bigbrownchunx-skype", NULL, "x-skype" },
{ NULL, NULL, NULL }
};
@@ -876,10 +884,32 @@ haze_protocol_identify_account (TpBaseProtocol *base,
return ret;
}
-static GStrv
-haze_protocol_get_interfaces (TpBaseProtocol *base)
+static GPtrArray *
+haze_protocol_get_interfaces_array (TpBaseProtocol *base)
{
- return g_new0 (gchar *, 1);
+ HazeProtocol *self = HAZE_PROTOCOL (base);
+ GPtrArray *interfaces;
+ GPtrArray *tmp;
+ guint i;
+
+ interfaces = TP_BASE_PROTOCOL_CLASS (
+ haze_protocol_parent_class)->get_interfaces_array (base);
+
+ /* Claim to implement Avatars only if we support avatars for this
+ * protocol. */
+ tmp = haze_connection_dup_implemented_interfaces (self->priv->prpl_info);
+ for (i = 0; i < tmp->len; i++)
+ {
+ if (!tp_strdiff (g_ptr_array_index (tmp, i),
+ TP_IFACE_CONNECTION_INTERFACE_AVATARS))
+ {
+ g_ptr_array_add (interfaces, TP_IFACE_PROTOCOL_INTERFACE_AVATARS);
+ break;
+ }
+ }
+ g_ptr_array_unref (tmp);
+
+ return interfaces;
}
static void
@@ -894,16 +924,28 @@ haze_protocol_get_connection_details (TpBaseProtocol *base,
if (connection_interfaces != NULL)
{
- *connection_interfaces = g_strdupv (
- (gchar **) haze_connection_get_implemented_interfaces ());
+ GPtrArray *tmp, *ifaces;
+ guint i;
+
+ tmp = haze_connection_dup_implemented_interfaces (
+ self->priv->prpl_info);
+
+ /* @connection_interfaces takes a NULL terminated (transfer full)
+ * gchar ** so we have to dup each string and append NULL. */
+ ifaces = g_ptr_array_new ();
+
+ for (i = 0; i < tmp->len; i++)
+ g_ptr_array_add (ifaces, g_strdup (g_ptr_array_index (tmp, i)));
+
+ g_ptr_array_add (ifaces, NULL);
+
+ *connection_interfaces = (gchar **) g_ptr_array_free (ifaces, FALSE);
+ g_ptr_array_unref (tmp);
}
if (channel_manager_types != NULL)
{
GType types[] = { HAZE_TYPE_IM_CHANNEL_FACTORY,
-#ifdef ENABLE_MEDIA
- HAZE_TYPE_MEDIA_MANAGER,
-#endif
G_TYPE_INVALID };
*channel_manager_types = g_memdup (types, sizeof (types));
@@ -954,6 +996,41 @@ haze_protocol_dup_authentication_types (TpBaseProtocol *base)
}
static void
+haze_protocol_get_avatar_details (TpBaseProtocol *base,
+ GStrv *supported_mime_types,
+ guint *min_height,
+ guint *min_width,
+ guint *rec_height,
+ guint *rec_width,
+ guint *max_height,
+ guint *max_width,
+ guint *max_bytes)
+{
+ HazeProtocol *self = HAZE_PROTOCOL (base);
+ PurpleBuddyIconSpec *icon_spec;
+
+ icon_spec = &(self->priv->prpl_info->icon_spec);
+
+ if (icon_spec->format == NULL)
+ {
+ /* We don't support avatar for this protocol */
+ *supported_mime_types = NULL;
+ *min_height = 0;
+ *min_width = 0;
+ *rec_height = 0;
+ *rec_width = 0;
+ *max_height = 0;
+ *max_width = 0;
+ *max_bytes = 0;
+ return;
+ }
+
+ haze_connection_get_icon_spec_requirements (icon_spec, supported_mime_types,
+ min_height, min_width, rec_height, rec_width, max_height, max_width,
+ max_bytes);
+}
+
+static void
haze_protocol_class_init (HazeProtocolClass *cls)
{
GObjectClass *object_class = (GObjectClass *) cls;
@@ -964,10 +1041,11 @@ haze_protocol_class_init (HazeProtocolClass *cls)
base_class->new_connection = haze_protocol_new_connection;
base_class->normalize_contact = haze_protocol_normalize_contact;
base_class->identify_account = haze_protocol_identify_account;
- base_class->get_interfaces = haze_protocol_get_interfaces;
+ base_class->get_interfaces_array = haze_protocol_get_interfaces_array;
base_class->get_connection_details = haze_protocol_get_connection_details;
base_class->dup_authentication_types =
haze_protocol_dup_authentication_types;
+ base_class->get_avatar_details = haze_protocol_get_avatar_details;
g_type_class_add_private (cls, sizeof (HazeProtocolPrivate));
object_class->get_property = haze_protocol_get_property;
diff --git a/src/request.c b/src/request.c
index 408678b..8aa7bc9 100644
--- a/src/request.c
+++ b/src/request.c
@@ -18,6 +18,8 @@
*
*/
+#include "config.h"
+
#include <glib-object.h>
#include <libpurple/account.h>
@@ -25,7 +27,9 @@
#include "debug.h"
#include "request.h"
+#include "connection.h"
+#ifdef ENABLE_LEAKY_REQUEST_STUBS
static gpointer
haze_request_input (const char *title,
const char *primary,
@@ -95,7 +99,73 @@ haze_request_action (const char *title,
return NULL;
}
+#endif
+
+struct fields_data {
+ PurpleAccount *account;
+ PurpleRequestFields *fields;
+ PurpleRequestField *password;
+ PurpleRequestFieldsCb ok_cb;
+ PurpleRequestFieldsCb cancel_cb;
+ void *user_data;
+};
+
+static void
+haze_close_request (PurpleRequestType type,
+ void *ui_handle)
+{
+ struct fields_data *fd = ui_handle;
+
+ haze_connection_cancel_password_request (fd->account);
+ purple_request_fields_destroy (fd->fields);
+ g_slice_free (struct fields_data, fd);
+}
+
+void
+haze_request_password_cb (gpointer user_data,
+ const gchar *password)
+{
+ struct fields_data *fd = user_data;
+
+ if (password)
+ {
+ purple_request_field_string_set_value (fd->password, password);
+ if (fd->ok_cb)
+ {
+ (fd->ok_cb) (fd->user_data, fd->fields);
+ }
+ }
+ else
+ {
+ if (fd->cancel_cb)
+ {
+ (fd->cancel_cb) (fd->user_data, fd->fields);
+ }
+ }
+ purple_request_close (PURPLE_REQUEST_FIELDS, fd);
+}
+
+static gboolean
+haze_request_fields_destroy (gpointer user_data)
+{
+ struct fields_data *fd = user_data;
+
+ if (fd->cancel_cb)
+ {
+ (fd->cancel_cb) (fd->user_data, fd->fields);
+ }
+
+ purple_request_close (PURPLE_REQUEST_FIELDS, user_data);
+
+ return FALSE;
+}
+
+/*
+ * We must support purple_account_request_password() which boils down
+ * to purple_request_fields() with certain parameters. I'm not sure
+ * if this the best way of doing this, but it works.
+ */
static gpointer
haze_request_fields (const char *title,
const char *primary,
@@ -110,14 +180,41 @@ haze_request_fields (const char *title,
PurpleConversation *conv,
void *user_data)
{
- DEBUG ("ignoring request:");
- DEBUG (" title: %s", (title ? title : "(null)"));
- DEBUG (" primary: %s", (primary ? primary : "(null)"));
- DEBUG (" secondary: %s", (secondary ? secondary : "(null)"));
+ struct fields_data *fd = g_slice_new0 (struct fields_data);
- return NULL;
+ /* it is our responsibility to destroy this data */
+ fd->account = account;
+ fd->fields = fields;
+ fd->cancel_cb = (PurpleRequestFieldsCb) cancel_cb;
+ fd->user_data = user_data;
+
+ if (purple_request_fields_exists (fields, "password") &&
+ purple_request_fields_exists (fields, "remember"))
+ {
+
+ DEBUG ("triggering password request");
+
+ fd->password = purple_request_fields_get_field (fields, "password");
+ fd->ok_cb = (PurpleRequestFieldsCb) ok_cb;
+
+ haze_connection_request_password (account, fd);
+
+ }
+ else
+ {
+ DEBUG ("ignoring request:");
+ DEBUG (" title: %s", (title ? title : "(null)"));
+ DEBUG (" primary: %s", (primary ? primary : "(null)"));
+ DEBUG (" secondary: %s", (secondary ? secondary : "(null)"));
+
+ /* Avoid leaking of "fields" and "user_data" */
+ g_idle_add (haze_request_fields_destroy, fd);
+ }
+
+ return fd;
}
+#ifdef ENABLE_LEAKY_REQUEST_STUBS
static gpointer
haze_request_file (const char *title,
const char *filename,
@@ -152,20 +249,19 @@ haze_request_folder (const char *title,
return NULL;
}
-
-
-/*
- void (*close_request)(PurpleRequestType type, void *ui_handle);
-*/
+#endif
static PurpleRequestUiOps request_uiops =
{
+#ifdef ENABLE_LEAKY_REQUEST_STUBS
.request_input = haze_request_input,
.request_choice = haze_request_choice,
.request_action = haze_request_action,
- .request_fields = haze_request_fields,
.request_file = haze_request_file,
- .request_folder = haze_request_folder
+ .request_folder = haze_request_folder,
+#endif
+ .request_fields = haze_request_fields,
+ .close_request = haze_close_request
};
PurpleRequestUiOps *
diff --git a/src/request.h b/src/request.h
index d896cc6..cc572d6 100644
--- a/src/request.h
+++ b/src/request.h
@@ -20,4 +20,7 @@
#include <libpurple/request.h>
+void haze_request_password_cb (gpointer user_data,
+ const gchar *password);
+
PurpleRequestUiOps *haze_request_get_ui_ops (void);
diff --git a/src/util.c b/src/util.c
index 5cbb4fb..c29fc73 100644
--- a/src/util.c
+++ b/src/util.c
@@ -7,6 +7,7 @@
* notice and this notice are preserved.
*/
+#include <config.h>
#include "util.h"
#include <glib/gstdio.h>
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2841237..af9cb85 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,31 +4,4 @@ if WANT_TWISTED_TESTS
SUBDIRS += twisted
endif
-%.conf: %.conf.in
- $(AM_V_GEN)sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@
-
-# We don't use the full filename for the .in because > 99 character filenames
-# in tarballs are non-portable (and automake 1.8 doesn't let us build
-# non-archaic tarballs)
-im.telepathy1.ConnectionManager.%.service: %.service.in
- $(AM_V_GEN)sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" \
- -e "s|[@]abs_top_srcdir[@]|@abs_top_srcdir@|g" $< > $@
-
-# D-Bus service file for testing
-service_in_files = haze.service.in
-service_files = im.telepathy1.ConnectionManager.haze.service
-
-# D-Bus config file for testing
-conf_in_files = tmp-session-bus.conf.in
-conf_files = $(conf_in_files:.conf.in=.conf)
-
-BUILT_SOURCES = $(service_files) $(conf_files)
-
-EXTRA_DIST = \
- $(service_in_files) \
- $(conf_in_files) \
- exec-with-log.sh
-
-CLEANFILES = \
- $(BUILT_SOURCES) \
- haze-testing.log
+CLEANFILES = haze-testing.log
diff --git a/tests/haze.service.in b/tests/haze.service.in
deleted file mode 100644
index c815d4b..0000000
--- a/tests/haze.service.in
+++ /dev/null
@@ -1,3 +0,0 @@
-[D-BUS Service]
-Name=im.telepathy1.ConnectionManager.haze
-Exec=@abs_top_srcdir@/tests/exec-with-log.sh @abs_top_srcdir@ @abs_top_builddir@
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index 9bfc33f..f932229 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -1,3 +1,5 @@
+SUBDIRS = tools
+
TWISTED_TESTS = \
avatar-requirements.py \
simple-caps.py \
@@ -11,6 +13,8 @@ TWISTED_TESTS = \
roster/publish.py \
roster/removed-from-rp-subscribe.py \
roster/subscribe.py \
+ sasl/close.py \
+ sasl/telepathy-password.py \
text/destroy.py \
text/ensure.py \
text/initiate-requestotron.py \
@@ -20,39 +24,55 @@ TWISTED_TESTS = \
text/test-text.py
-TESTS =
-
-TESTS_ENVIRONMENT = \
- PYTHONPATH=@abs_top_srcdir@/tests/twisted:@abs_top_builddir@/tests/twisted
-
check-local: check-twisted
-check-twisted:
- rm -f ../haze-testing.log
- sh $(top_srcdir)/tools/with-session-bus.sh \
- --config-file=$(top_builddir)/tests/tmp-session-bus.conf \
- -- $(MAKE) check-TESTS \
- TESTS="$(TWISTED_TESTS)" \
- TESTS_ENVIRONMENT="$(TESTS_ENVIRONMENT) $(TEST_PYTHON)"
+CHECK_TWISTED_SLEEP=0
+
+check-twisted: $(BUILT_SOURCES)
+ $(MAKE) -C tools
+ if test "x$(CHECK_TWISTED_SLEEP)" = x0; then \
+ haze_test_sleep= ; \
+ else \
+ haze_test_sleep=--sleep=$(CHECK_TWISTED_SLEEP); \
+ fi; \
+ HAZE_TEST_UNINSTALLED=1 \
+ HAZE_ABS_TOP_SRCDIR=@abs_top_srcdir@ \
+ HAZE_ABS_TOP_BUILDDIR=@abs_top_builddir@ \
+ HAZE_TEST_SLEEP=$$haze_test_sleep \
+ ./run-test.sh "$(TWISTED_TESTS)"
EXTRA_DIST = \
$(TWISTED_TESTS) \
constants.py \
+ gabbletest.py \
hazetest.py \
+ run-test.sh.in \
+ sasl/saslutil.py \
servicetest.py \
ns.py
-if MEDIA_ENABLED
-MEDIA_ENABLED_PYBOOL = True
-else
-MEDIA_ENABLED_PYBOOL = False
-endif
+haze-twisted-tests.list: Makefile
+ $(AM_V_GEN)echo $(TWISTED_TESTS) > $@
-config.py: Makefile
- $(AM_V_GEN) { \
- echo "MEDIA_ENABLED = $(MEDIA_ENABLED_PYBOOL)"; \
- } > $@
+BUILT_SOURCES = \
+ haze-twisted-tests.list \
+ run-test.sh \
+ $(NULL)
-BUILT_SOURCES = config.py
+# We don't really use hazetestsdir yet - we only support uninstalled testing
+# so far - but I'm substituting it to keep the script more similar to Gabble's.
+# ${pkglibexecdir}/tests is what GNOME's InstalledTests goal recommends.
+run-test.sh: run-test.sh.in Makefile
+ $(AM_V_GEN)sed \
+ -e 's![@]hazetestsdir[@]!${pkglibexecdir}/tests!' \
+ -e 's![@]TEST_PYTHON[@]!$(TEST_PYTHON)!' \
+ < $< > $@.tmp && \
+ chmod +x $@.tmp && \
+ mv $@.tmp $@
-CLEANFILES = *.pyc */*.pyc config.py
+CLEANFILES = \
+ $(BUILT_SOURCES) \
+ haze-[1-9]*.log \
+ *.pyc \
+ */*.pyc \
+ $(NULL)
diff --git a/tests/twisted/avatar-requirements.py b/tests/twisted/avatar-requirements.py
index 1515bd6..83d155e 100644
--- a/tests/twisted/avatar-requirements.py
+++ b/tests/twisted/avatar-requirements.py
@@ -50,4 +50,4 @@ def test(q, bus, conn, stream):
assert rech == 0, rech
if __name__ == '__main__':
- exec_test(test)
+ exec_test(test, do_connect=False)
diff --git a/tests/twisted/cm/protocols.py b/tests/twisted/cm/protocols.py
index 9736021..25eca0a 100644
--- a/tests/twisted/cm/protocols.py
+++ b/tests/twisted/cm/protocols.py
@@ -24,6 +24,14 @@ def test(q, bus, conn, stream):
protocol_iface = dbus.Interface(protocol, cs.PROTOCOL)
protocol_props = dbus.Interface(protocol, cs.PROPERTIES_IFACE)
flat_props = protocol_props.GetAll(cs.PROTOCOL)
+ protocol_avatar_props = protocol_props.GetAll(cs.PROTOCOL_IFACE_AVATARS)
+
+ # Protocol is supposed to implement Interface.Avatars iff the
+ # connection implements Avatars as well.
+ if cs.CONN_IFACE_AVATARS in flat_props['ConnectionInterfaces']:
+ assertContains(cs.PROTOCOL_IFACE_AVATARS, props[cs.PROTOCOL + '.Interfaces'])
+ else:
+ assertDoesNotContain(cs.PROTOCOL_IFACE_AVATARS, props[cs.PROTOCOL + '.Interfaces'])
parameters = props[cs.PROTOCOL + '.Parameters']
assertEquals(parameters, flat_props['Parameters'])
@@ -90,6 +98,20 @@ def test(q, bus, conn, stream):
protocol_iface.IdentifyAccount({
'account': 'smcv',
'server': 'irc.debian.org'}))
+
+ assertDoesNotContain(cs.CONN_IFACE_AVATARS, flat_props['ConnectionInterfaces'])
+ assertDoesNotContain(cs.CONN_IFACE_CONTACT_BLOCKING, flat_props['ConnectionInterfaces'])
+ assertDoesNotContain(cs.CONN_IFACE_MAIL_NOTIFICATION, flat_props['ConnectionInterfaces'])
+
+ # Avatar not supported
+ assertEquals(0, protocol_avatar_props['MaximumAvatarBytes'])
+ assertEquals(0, protocol_avatar_props['MaximumAvatarHeight'])
+ assertEquals(0, protocol_avatar_props['MaximumAvatarWidth'])
+ assertEquals(0, protocol_avatar_props['MinimumAvatarHeight'])
+ assertEquals(0, protocol_avatar_props['MinimumAvatarWidth'])
+ assertEquals(0, protocol_avatar_props['RecommendedAvatarHeight'])
+ assertEquals(0, protocol_avatar_props['RecommendedAvatarWidth'])
+ assertEquals([], protocol_avatar_props['SupportedAvatarMIMETypes'])
elif name == 'myspace':
assertEquals('x-myspace', flat_props['VCardField'])
assertEquals('im-myspace', flat_props['Icon'])
@@ -135,6 +157,20 @@ def test(q, bus, conn, stream):
'embrace-and-extend': r'WORKGROUP\Bill',
'password': 'letmein'})
q.expect('dbus-error', name=cs.INVALID_ARGUMENT)
+
+ assertContains(cs.CONN_IFACE_AVATARS, flat_props['ConnectionInterfaces'])
+ assertContains(cs.CONN_IFACE_CONTACT_BLOCKING, flat_props['ConnectionInterfaces'])
+ assertContains(cs.CONN_IFACE_MAIL_NOTIFICATION, flat_props['ConnectionInterfaces'])
+
+ # libpurple currently says there's no max size
+ assertEquals(0, protocol_avatar_props['MaximumAvatarBytes'])
+ assertEquals(96, protocol_avatar_props['MaximumAvatarHeight'])
+ assertEquals(96, protocol_avatar_props['MaximumAvatarWidth'])
+ assertEquals(32, protocol_avatar_props['MinimumAvatarHeight'])
+ assertEquals(32, protocol_avatar_props['MinimumAvatarWidth'])
+ assertEquals(0, protocol_avatar_props['RecommendedAvatarHeight'])
+ assertEquals(0, protocol_avatar_props['RecommendedAvatarWidth'])
+ assertEquals(['image/png'], protocol_avatar_props['SupportedAvatarMIMETypes'])
elif name == 'qq':
assertEquals('x-qq', flat_props['VCardField'])
assertEquals('im-qq', flat_props['Icon'])
@@ -177,11 +213,6 @@ def test(q, bus, conn, stream):
'login': r'WORKGROUP\Bill',
'password': 'letmein'}))
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged', args=[1, 1])
- conn.Disconnect()
- q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
-
if __name__ == '__main__':
exec_test(test)
diff --git a/tests/twisted/connect/fail.py b/tests/twisted/connect/fail.py
index db24cdb..a45b2b2 100644
--- a/tests/twisted/connect/fail.py
+++ b/tests/twisted/connect/fail.py
@@ -24,5 +24,5 @@ def test(q, bus, conn, stream):
args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_NETWORK_ERROR])
if __name__ == '__main__':
- exec_test(test, {'port': dbus.UInt32(14243)})
+ exec_test(test, {'port': dbus.UInt32(14243)}, do_connect=False)
diff --git a/tests/twisted/connect/success.py b/tests/twisted/connect/success.py
index d9cdb4e..c16f049 100644
--- a/tests/twisted/connect/success.py
+++ b/tests/twisted/connect/success.py
@@ -4,23 +4,20 @@ Test connecting to a server.
"""
from hazetest import exec_test
+import constants as cs
def test(q, bus, conn, stream):
conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged', args=[1, 1])
+ q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED])
q.expect('stream-authenticated')
# FIXME: unlike Gabble, Haze does not signal a presence update to
# available during connect
- #q.expect('dbus-signal', signal='PresenceUpdate',
- # args=[{1L: (0L, {u'available': {}})}])
+ #q.expect('dbus-signal', signal='PresencesChanged',
+ # args=[{1L: (cs.PRESENCE_AVAILABLE, 'available', '')}])
- q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
-
- conn.Disconnect()
- q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
- return True
+ q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
if __name__ == '__main__':
- exec_test(test)
+ exec_test(test, do_connect=False)
diff --git a/tests/twisted/connect/twice-to-same-account.py b/tests/twisted/connect/twice-to-same-account.py
index fb2c25e..8fe3db2 100644
--- a/tests/twisted/connect/twice-to-same-account.py
+++ b/tests/twisted/connect/twice-to-same-account.py
@@ -13,19 +13,6 @@ from servicetest import (
import constants as cs
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect_many(
- EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]),
- EventPattern('stream-authenticated'),
- )
-
- # FIXME: unlike Gabble, Haze does not signal a presence update to
- # available during connect
- #q.expect('dbus-signal', signal='PresenceUpdate',
- # args=[{1L: (0L, {u'available': {}})}])
-
- q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
-
haze = bus.get_object(
tp_name_prefix + '.ConnectionManager.haze',
tp_path_prefix + '/ConnectionManager/haze')
diff --git a/tests/twisted/constants.py b/tests/twisted/constants.py
index 3f4de31..44b7b3d 100644
--- a/tests/twisted/constants.py
+++ b/tests/twisted/constants.py
@@ -22,23 +22,26 @@ CHANNEL_IFACE_DESTROYABLE = CHANNEL + ".Interface.Destroyable1"
CHANNEL_IFACE_DTMF = CHANNEL + ".Interface.DTMF1"
CHANNEL_IFACE_GROUP = CHANNEL + ".Interface.Group1"
CHANNEL_IFACE_HOLD = CHANNEL + ".Interface.Hold1"
-CHANNEL_IFACE_MEDIA_SIGNALLING = CHANNEL + ".Interface.MediaSignalling"
CHANNEL_IFACE_PASSWORD = CHANNEL + ".Interface.Password1"
CHANNEL_IFACE_TUBE = CHANNEL + ".Interface.Tube1"
CHANNEL_IFACE_SASL_AUTH = CHANNEL + ".Interface.SASLAuthentication1"
CHANNEL_IFACE_CONFERENCE = CHANNEL + '.Interface.Conference1'
+CHANNEL_IFACE_ROOM = CHANNEL + '.Interface.Room1'
+CHANNEL_IFACE_ROOM_CONFIG = CHANNEL + '.Interface.RoomConfig1'
+CHANNEL_IFACE_SUBJECT = CHANNEL + '.Interface.Subject1'
+CHANNEL_IFACE_FILE_TRANSFER_METADATA = CHANNEL + '.Interface.FileTransfer.Metadata'
-CHANNEL_TYPE_CALL = CHANNEL + ".Type.Call.DRAFT"
+CHANNEL_TYPE_CALL = CHANNEL + ".Type.Call1"
CHANNEL_TYPE_CONTACT_LIST = CHANNEL + ".Type.ContactList1"
CHANNEL_TYPE_CONTACT_SEARCH = CHANNEL + ".Type.ContactSearch1"
CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text"
CHANNEL_TYPE_TUBES = CHANNEL + ".Type.Tubes"
CHANNEL_TYPE_STREAM_TUBE = CHANNEL + ".Type.StreamTube1"
CHANNEL_TYPE_DBUS_TUBE = CHANNEL + ".Type.DBusTube1"
-CHANNEL_TYPE_STREAMED_MEDIA = CHANNEL + ".Type.StreamedMedia"
CHANNEL_TYPE_FILE_TRANSFER = CHANNEL + ".Type.FileTransfer1"
+CHANNEL_TYPE_ROOM_LIST = CHANNEL + ".Type.RoomList"
CHANNEL_TYPE_SERVER_AUTHENTICATION = \
- CHANNEL + ".Type.ServerAuthentication.DRAFT"
+ CHANNEL + ".Type.ServerAuthentication1"
CHANNEL_TYPE_SERVER_TLS_CONNECTION = \
CHANNEL + ".Type.ServerTLSConnection1"
@@ -56,49 +59,96 @@ INITIATOR_HANDLE = CHANNEL + '.InitiatorHandle'
INITIATOR_ID = CHANNEL + '.InitiatorID'
INTERFACES = CHANNEL + '.Interfaces'
-INITIAL_AUDIO = CHANNEL_TYPE_STREAMED_MEDIA + '.InitialAudio'
-INITIAL_VIDEO = CHANNEL_TYPE_STREAMED_MEDIA + '.InitialVideo'
-IMMUTABLE_STREAMS = CHANNEL_TYPE_STREAMED_MEDIA + '.ImmutableStreams'
-
CALL_INITIAL_AUDIO = CHANNEL_TYPE_CALL + '.InitialAudio'
+CALL_INITIAL_AUDIO_NAME = CHANNEL_TYPE_CALL + '.InitialAudioName'
CALL_INITIAL_VIDEO = CHANNEL_TYPE_CALL + '.InitialVideo'
+CALL_INITIAL_VIDEO_NAME = CHANNEL_TYPE_CALL + '.InitialVideoName'
CALL_MUTABLE_CONTENTS = CHANNEL_TYPE_CALL + '.MutableContents'
-CALL_CONTENT = PREFIX + '.Call.Content.DRAFT'
-CALL_CONTENT_IFACE_MEDIA = PREFIX + '.Call.Content.Interface.Media.DRAFT'
+CALL_CONTENT = PREFIX + '.Call1.Content'
+CALL_CONTENT_IFACE_MEDIA = CALL_CONTENT + '.Interface.Media1'
+CALL_CONTENT_IFACE_DTMF = CALL_CONTENT + '.Interface.DTMF1'
-CALL_CONTENT_CODECOFFER = \
- PREFIX + '.Call.Content.CodecOffer.DRAFT'
+CALL_CONTENT_MEDIADESCRIPTION = CALL_CONTENT + '.MediaDescription1'
-CALL_STREAM = PREFIX + '.Call.Stream.DRAFT'
-CALL_STREAM_IFACE_MEDIA = \
- PREFIX + '.Call.Stream.Interface.Media.DRAFT'
+CALL_STREAM = PREFIX + '.Call1.Stream'
+CALL_STREAM_IFACE_MEDIA = CALL_STREAM + '.Interface.Media'
-CALL_STREAM_ENDPOINT = PREFIX + '.Call.Stream.Endpoint.DRAFT'
+CALL_STREAM_ENDPOINT = CALL_STREAM + '.Endpoint'
CALL_MEDIA_TYPE_AUDIO = 0
CALL_MEDIA_TYPE_VIDEO = 1
-CALL_STREAM_TRANSPORT_RAW_UDP = 0
-CALL_STREAM_TRANSPORT_ICE = 1
-CALL_STREAM_TRANSPORT_GOOGLE = 2
+CALL_CONTENT_PACKETIZATION_RTP = 0
+CALL_CONTENT_PACKETIZATION_RAW = 1
+CALL_CONTENT_PACKETIZATION_MSN_WEBCAM = 2
+
+CALL_STREAM_TRANSPORT_UNKNOWN = 0
+CALL_STREAM_TRANSPORT_RAW_UDP = 1
+CALL_STREAM_TRANSPORT_ICE = 2
+CALL_STREAM_TRANSPORT_GTALK_P2P = 3
+CALL_STREAM_TRANSPORT_WLM_2009 = 4
+CALL_STREAM_TRANSPORT_SHM = 5
+CALL_STREAM_TRANSPORT_MULTICAST = 6
CALL_STATE_UNKNOWN = 0
CALL_STATE_PENDING_INITIATOR = 1
-CALL_STATE_PENDING_RECEIVER = 2
-CALL_STATE_ACCEPTED = 3
-CALL_STATE_ENDED = 4
+CALL_STATE_INITIALISING = 2
+CALL_STATE_INITIALISED = 3
+CALL_STATE_ACCEPTED = 4
+CALL_STATE_ACTIVE = 5
+CALL_STATE_ENDED = 6
+
+CALL_FLAG_LOCALLY_HELD = 1
+CALL_FLAG_LOCALLY_RINGING = 2
+CALL_FLAG_LOCALLY_QUEUED = 4
+CALL_FLAG_FORWARDED = 8
+CALL_FLAG_CLEARING = 16
CALL_MEMBER_FLAG_RINGING = 1
CALL_MEMBER_FLAG_HELD = 2
CALL_DISPOSITION_NONE = 0
-CALL_DISPOSITION_EARLY_MEDIA = 1
-CALL_DISPOSITION_INITIAL = 2
+CALL_DISPOSITION_INITIAL = 1
CALL_SENDING_STATE_NONE = 0
CALL_SENDING_STATE_PENDING_SEND = 1
CALL_SENDING_STATE_SENDING = 2
+CALL_SENDING_STATE_PENDING_STOP_SENDING = 3
+
+CALL_STREAM_FLOW_STATE_STOPPED = 0
+CALL_STREAM_FLOW_STATE_PENDING_START = 1
+CALL_STREAM_FLOW_STATE_PENDING_STOP = 2
+CALL_STREAM_FLOW_STATE_STARTED = 3
+
+CALL_STREAM_ENDPOINT_STATE_CONNECTING = 0
+CALL_STREAM_ENDPOINT_STATE_PROVISIONALLY_CONNECTED = 1
+CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED = 2
+CALL_STREAM_ENDPOINT_STATE_EXHAUSTED_CANDIDATES = 3
+CALL_STREAM_ENDPOINT_STATE_FAILED = 4
+
+CALL_STREAM_CANDIDATE_TYPE_HOST = 1
+CALL_STREAM_CANDIDATE_TYPE_SERVER_REFLEXIVE = 2
+CALL_STREAM_CANDIDATE_TYPE_RELAY = 4
+
+CALL_STATE_CHANGE_REASON_UNKNOWN = 0
+CALL_STATE_CHANGE_REASON_PROGRESS_MADE = 1
+CALL_STATE_CHANGE_REASON_USER_REQUESTED = 2
+CALL_STATE_CHANGE_REASON_FORWARDED = 3
+CALL_STATE_CHANGE_REASON_REJECTED = 4
+CALL_STATE_CHANGE_REASON_NO_ANSWER = 5
+CALL_STATE_CHANGE_REASON_INVALID_CONTACT = 6
+CALL_STATE_CHANGE_REASON_PERMISSION_DENIED = 7
+CALL_STATE_CHANGE_REASON_BUSY = 8
+CALL_STATE_CHANGE_REASON_INTERNAL_ERROR = 9
+CALL_STATE_CHANGE_REASON_SERVICE_ERROR = 10
+CALL_STATE_CHANGE_REASON_NETWORK_ERROR = 11
+CALL_STATE_CHANGE_REASON_MEDIA_ERROR = 12
+CALL_STATE_CHANGE_REASON_CONNECTIVITY_ERROR = 13
+
+CALL_STREAM_COMPONENT_UNKNOWN = 0
+CALL_STREAM_COMPONENT_DATA = 1
+CALL_STREAM_COMPONENT_CONTROL = 2
SUBSCRIPTION_STATE_UNKNOWN = 0
SUBSCRIPTION_STATE_NO = 1
@@ -114,12 +164,10 @@ CONTACT_LIST_STATE_SUCCESS = 3
CONN = PREFIX + ".Connection"
CONN_IFACE_AVATARS = CONN + '.Interface.Avatars1'
CONN_IFACE_ALIASING = CONN + '.Interface.Aliasing1'
-CONN_IFACE_CAPS = CONN + '.Interface.Capabilities1'
CONN_IFACE_CONTACTS = CONN + '.Interface.Contacts1'
CONN_IFACE_CONTACT_CAPS = CONN + '.Interface.ContactCapabilities1'
CONN_IFACE_CONTACT_INFO = CONN + ".Interface.ContactInfo1"
CONN_IFACE_PRESENCE = CONN + '.Interface.Presence1'
-CONN_IFACE_SIMPLE_PRESENCE = CONN + '.Interface.SimplePresence1'
CONN_IFACE_REQUESTS = CONN + '.Interface.Requests'
CONN_IFACE_LOCATION = CONN + '.Interface.Location1'
CONN_IFACE_GABBLE_DECLOAK = CONN + '.Interface.Gabble.Decloak'
@@ -128,8 +176,15 @@ CONN_IFACE_CONTACT_LIST = CONN + '.Interface.ContactList1'
CONN_IFACE_CONTACT_GROUPS = CONN + '.Interface.ContactGroups1'
CONN_IFACE_CLIENT_TYPES = CONN + '.Interface.ClientTypes1'
CONN_IFACE_POWER_SAVING = CONN + '.Interface.PowerSaving1'
+CONN_IFACE_CONTACT_BLOCKING = CONN + '.Interface.ContactBlocking1'
+CONN_IFACE_ADDRESSING = CONN + '.Interface.Addressing1'
+ATTR_CONTACT_ID = CONN + '/contact-id'
ATTR_CONTACT_CAPABILITIES = CONN_IFACE_CONTACT_CAPS + '/capabilities'
+ATTR_PRESENCE = CONN_IFACE_PRESENCE + '/presence'
+ATTR_SUBSCRIBE = CONN_IFACE_CONTACT_LIST + '/subscribe'
+ATTR_PUBLISH = CONN_IFACE_CONTACT_LIST + '/publish'
+ATTR_GROUPS = CONN_IFACE_CONTACT_GROUPS + '/groups'
STREAM_HANDLER = PREFIX + '.Media.StreamHandler'
@@ -154,6 +209,9 @@ NOT_YET = ERROR + '.NotYet'
INVALID_HANDLE = ERROR + '.InvalidHandle'
CERT_UNTRUSTED = ERROR + '.Cert.Untrusted'
SERVICE_BUSY = ERROR + '.ServiceBusy'
+SERVICE_CONFUSED = ERROR + '.ServiceConfused'
+
+BANNED = ERROR + '.Channel.Banned'
UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod'
@@ -187,6 +245,13 @@ TUBE_CHANNEL_STATE_NOT_OFFERED = 3
MEDIA_STREAM_TYPE_AUDIO = 0
MEDIA_STREAM_TYPE_VIDEO = 1
+MEDIA_STREAM_BASE_PROTO_UDP = 0
+MEDIA_STREAM_BASE_PROTO_TCP = 1
+
+MEDIA_STREAM_TRANSPORT_TYPE_LOCAL = 0
+MEDIA_STREAM_TRANSPORT_TYPE_DERIVED = 1
+MEDIA_STREAM_TRANSPORT_TYPE_RELAY = 2
+
SOCKET_ADDRESS_TYPE_UNIX = 0
SOCKET_ADDRESS_TYPE_ABSTRACT_UNIX = 1
SOCKET_ADDRESS_TYPE_IPV4 = 2
@@ -256,6 +321,9 @@ FT_AVAILABLE_SOCKET_TYPES = CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'
FT_TRANSFERRED_BYTES = CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'
FT_INITIAL_OFFSET = CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'
FT_FILE_COLLECTION = CHANNEL_TYPE_FILE_TRANSFER + '.FUTURE.FileCollection'
+FT_URI = CHANNEL_TYPE_FILE_TRANSFER + '.URI'
+FT_SERVICE_NAME = CHANNEL_IFACE_FILE_TRANSFER_METADATA + '.ServiceName'
+FT_METADATA = CHANNEL_IFACE_FILE_TRANSFER_METADATA + '.Metadata'
GF_CAN_ADD = 1
GF_CAN_REMOVE = 2
@@ -293,11 +361,6 @@ HSR_NONE = 0
HSR_REQUESTED = 1
HSR_RESOURCE_NOT_AVAILABLE = 2
-CALL_STATE_RINGING = 1
-CALL_STATE_QUEUED = 2
-CALL_STATE_HELD = 4
-CALL_STATE_FORWARDED = 8
-
CONN_STATUS_CONNECTED = 0
CONN_STATUS_CONNECTING = 1
CONN_STATUS_DISCONNECTED = 2
@@ -347,11 +410,8 @@ PRESENCE_ERROR = 8
CONTACT_INFO_FLAG_CAN_SET = 1
CONTACT_INFO_FLAG_PUSH = 2
-CONTACT_INFO_FIELD_FLAG_PARAMETERS_MANDATORY = 1
-
-# Channel_Type_ServerAuthentication
-AUTH_TYPE_SASL = 0
-AUTH_TYPE_CAPTCHA = 1
+CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT = 1
+CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME = 2
# Channel_Interface_SaslAuthentication
SASL_STATUS_NOT_STARTED = 0
@@ -366,12 +426,20 @@ SASL_ABORT_REASON_INVALID_CHALLENGE = 0
SASL_ABORT_REASON_USER_ABORT = 1
AUTH_METHOD = CHANNEL_TYPE_SERVER_AUTHENTICATION + ".AuthenticationMethod"
-AUTH_INFO = CHANNEL_TYPE_SERVER_AUTHENTICATION + ".AuthenticationInformation"
SASL_AVAILABLE_MECHANISMS = CHANNEL_IFACE_SASL_AUTH + ".AvailableMechanisms"
+SASL_STATUS = CHANNEL_IFACE_SASL_AUTH + ".SASLStatus"
+SASL_ERROR = CHANNEL_IFACE_SASL_AUTH + ".SASLError"
+SASL_ERROR_DETAILS = CHANNEL_IFACE_SASL_AUTH + ".SASLErrorDetails"
+SASL_CONTEXT = CHANNEL_IFACE_SASL_AUTH + ".SASLContext"
+SASL_AUTHORIZATION_IDENTITY = CHANNEL_IFACE_SASL_AUTH + ".AuthorizationIdentity"
+SASL_DEFAULT_REALM = CHANNEL_IFACE_SASL_AUTH + ".DefaultRealm"
+SASL_DEFAULT_USERNAME = CHANNEL_IFACE_SASL_AUTH + ".DefaultUsername"
# Channel_Type_ServerTLSConnection
TLS_CERT_PATH = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".ServerCertificate"
TLS_HOSTNAME = CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".Hostname"
+TLS_REFERENCE_IDENTITIES = \
+ CHANNEL_TYPE_SERVER_TLS_CONNECTION + ".ReferenceIdentities"
# Connection.Interface.Location
@@ -385,7 +453,25 @@ MT_NOTICE = 2
MT_AUTO_REPLY = 3
MT_DELIVERY_REPORT = 4
+class MessageFlag(object):
+ TRUNCATED = 1
+ NON_TEXT_CONTENT = 2
+ SCROLLBACK = 4
+ RESCUED = 8
+
+class SendError(object):
+ UNKNOWN = 0
+ OFFLINE = 1
+ INVALID_CONTACT = 2
+ PERMISSION_DENIED = 3
+ TOO_LONG = 4
+ NOT_IMPLEMENTED = 5
+
PROTOCOL = PREFIX + '.Protocol'
+PROTOCOL_IFACE_PRESENCES = PROTOCOL + '.Interface.Presence1'
+PROTOCOL_IFACE_ADDRESSING = PROTOCOL + '.Interface.Addressing1'
+PROTOCOL_IFACE_AVATARS = PROTOCOL + '.Interface.Avatars1'
+
PARAM_REQUIRED = 1
PARAM_REGISTER = 2
PARAM_HAS_DEFAULT = 4
@@ -416,3 +502,34 @@ DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_FAILURES = 1
DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_SUCCESSES = 2
DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_READ = 4
DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_DELETED = 8
+
+DELIVERY_STATUS_UNKNOWN = 0
+DELIVERY_STATUS_DELIVERED = 1
+DELIVERY_STATUS_TEMPORARILY_FAILED = 2
+DELIVERY_STATUS_PERMANENTLY_FAILED = 3
+DELIVERY_STATUS_ACCEPTED = 4
+DELIVERY_STATUS_READ = 5
+DELIVERY_STATUS_DELETED = 6
+
+MEDIA_STREAM_ERROR_UNKNOWN = 0
+MEDIA_STREAM_ERROR_EOS = 1
+MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED = 2
+MEDIA_STREAM_ERROR_CONNECTION_FAILED = 3
+MEDIA_STREAM_ERROR_NETWORK_ERROR = 4
+MEDIA_STREAM_ERROR_NO_CODECS = 5
+MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR = 6
+MEDIA_STREAM_ERROR_MEDIA_ERROR = 7
+
+PASSWORD_FLAG_PROVIDE = 8
+
+# Channel.Interface.Room
+ROOM_NAME = CHANNEL_IFACE_ROOM + '.RoomName'
+ROOM_SERVER = CHANNEL_IFACE_ROOM + '.Server'
+
+# Channel.Interface.Subject
+SUBJECT = CHANNEL_IFACE_ROOM + '.Subject'
+SUBJECT_PRESENT = 1
+SUBJECT_CAN_SET = 2
+
+DEBUG_IFACE = PREFIX + '.Debug'
+DEBUG_PATH = '/' + PREFIX.replace('.', '/') + '/debug'
diff --git a/tests/twisted/gabbletest.py b/tests/twisted/gabbletest.py
new file mode 100644
index 0000000..11cf3d2
--- /dev/null
+++ b/tests/twisted/gabbletest.py
@@ -0,0 +1,856 @@
+
+"""
+Infrastructure code for testing Gabble by pretending to be a Jabber server.
+"""
+
+import base64
+import os
+import hashlib
+import sys
+import random
+import re
+import traceback
+
+import ns
+import constants as cs
+import servicetest
+from servicetest import (
+ assertEquals, assertLength, assertContains, wrap_channel,
+ EventPattern, call_async, unwrap, Event)
+import twisted
+from twisted.words.xish import domish, xpath
+from twisted.words.protocols.jabber.client import IQ
+from twisted.words.protocols.jabber import xmlstream
+from twisted.internet import reactor, ssl
+
+import dbus
+
+def make_result_iq(stream, iq, add_query_node=True):
+ result = IQ(stream, "result")
+ result["id"] = iq["id"]
+ to = iq.getAttribute('to')
+ if to is not None:
+ result["from"] = to
+ query = iq.firstChildElement()
+
+ if query and add_query_node:
+ result.addElement((query.uri, query.name))
+
+ return result
+
+def acknowledge_iq(stream, iq):
+ stream.send(make_result_iq(stream, iq))
+
+def send_error_reply(stream, iq, error_stanza=None):
+ result = IQ(stream, "error")
+ result["id"] = iq["id"]
+ query = iq.firstChildElement()
+ to = iq.getAttribute('to')
+ if to is not None:
+ result["from"] = to
+
+ if query:
+ result.addElement((query.uri, query.name))
+
+ if error_stanza:
+ result.addChild(error_stanza)
+
+ stream.send(result)
+
+def request_muc_handle(q, conn, stream, muc_jid):
+ servicetest.call_async(q, conn, 'RequestHandles', 2, [muc_jid])
+ event = q.expect('dbus-return', method='RequestHandles')
+ return event.value[0][0]
+
+def make_muc_presence(affiliation, role, muc_jid, alias, jid=None, photo=None):
+ presence = domish.Element((None, 'presence'))
+ presence['from'] = '%s/%s' % (muc_jid, alias)
+ x = presence.addElement((ns.MUC_USER, 'x'))
+ item = x.addElement('item')
+ item['affiliation'] = affiliation
+ item['role'] = role
+ if jid is not None:
+ item['jid'] = jid
+
+ if photo is not None:
+ presence.addChild(
+ elem(ns.VCARD_TEMP_UPDATE, 'x')(
+ elem('photo')(unicode(photo))
+ ))
+
+ return presence
+
+def sync_stream(q, stream):
+ """Used to ensure that Gabble has processed all stanzas sent to it."""
+
+ iq = IQ(stream, "get")
+ id = iq['id']
+ iq.addElement(('http://jabber.org/protocol/disco#info', 'query'))
+ stream.send(iq)
+ q.expect('stream-iq', query_ns='http://jabber.org/protocol/disco#info',
+ predicate=(lambda event:
+ event.stanza['id'] == id and event.iq_type == 'result'))
+
+class GabbleAuthenticator(xmlstream.Authenticator):
+ def __init__(self, username, password, resource=None):
+ self.username = username
+ self.password = password
+ self.resource = resource
+ self.bare_jid = None
+ self.full_jid = None
+ self._event_func = lambda e: None
+ xmlstream.Authenticator.__init__(self)
+
+ def set_event_func(self, event_func):
+ self._event_func = event_func
+
+class JabberAuthenticator(GabbleAuthenticator):
+ "Trivial XML stream authenticator that accepts one username/digest pair."
+
+ # Patch in fix from http://twistedmatrix.com/trac/changeset/23418.
+ # This monkeypatch taken from Gadget source code
+ from twisted.words.xish.utility import EventDispatcher
+
+ def _addObserver(self, onetime, event, observerfn, priority, *args,
+ **kwargs):
+ if self._dispatchDepth > 0:
+ self._updateQueue.append(lambda: self._addObserver(onetime, event,
+ observerfn, priority, *args, **kwargs))
+
+ return self._oldAddObserver(onetime, event, observerfn, priority,
+ *args, **kwargs)
+
+ EventDispatcher._oldAddObserver = EventDispatcher._addObserver
+ EventDispatcher._addObserver = _addObserver
+
+ def __init__(self, username, password, resource=None, emit_events=False):
+ GabbleAuthenticator.__init__(self, username, password, resource)
+ self.emit_events = emit_events
+
+ def streamStarted(self, root=None):
+ if root:
+ self.xmlstream.sid = '%x' % random.randint(1, sys.maxint)
+ self.xmlstream.domain = root.getAttribute('to')
+
+ self.xmlstream.sendHeader()
+ self.xmlstream.addOnetimeObserver(
+ "/iq/query[@xmlns='jabber:iq:auth']", self.initialIq)
+
+ def initialIq(self, iq):
+ if self.emit_events:
+ self._event_func(Event('auth-initial-iq', authenticator=self,
+ iq=iq, id=iq["id"]))
+ else:
+ self.respondToInitialIq(iq)
+
+ self.xmlstream.addOnetimeObserver('/iq/query/username', self.secondIq)
+
+ def respondToInitialIq(self, iq):
+ result = IQ(self.xmlstream, "result")
+ result["id"] = iq["id"]
+ query = result.addElement('query')
+ query["xmlns"] = "jabber:iq:auth"
+ query.addElement('username', content='test')
+ query.addElement('password')
+ query.addElement('digest')
+ query.addElement('resource')
+ self.xmlstream.send(result)
+
+ def secondIq(self, iq):
+ if self.emit_events:
+ self._event_func(Event('auth-second-iq', authenticator=self,
+ iq=iq, id=iq["id"]))
+ else:
+ self.respondToSecondIq(iq)
+
+ def respondToSecondIq(self, iq):
+ username = xpath.queryForNodes('/iq/query/username', iq)
+ assert map(str, username) == [self.username]
+
+ digest = xpath.queryForNodes('/iq/query/digest', iq)
+ expect = hashlib.sha1(self.xmlstream.sid + self.password).hexdigest()
+ assert map(str, digest) == [expect]
+
+ resource = xpath.queryForNodes('/iq/query/resource', iq)
+ assertLength(1, resource)
+ if self.resource is not None:
+ assertEquals(self.resource, str(resource[0]))
+
+ self.bare_jid = '%s@%s' % (self.username, self.xmlstream.domain)
+ self.full_jid = '%s/%s' % (self.bare_jid, resource)
+
+ result = IQ(self.xmlstream, "result")
+ result["id"] = iq["id"]
+ self.xmlstream.send(result)
+ self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
+
+class XmppAuthenticator(GabbleAuthenticator):
+ def __init__(self, username, password, resource=None):
+ GabbleAuthenticator.__init__(self, username, password, resource)
+ self.authenticated = False
+
+ self._mechanisms = ['PLAIN']
+
+ def streamInitialize(self, root):
+ if root:
+ self.xmlstream.sid = root.getAttribute('id')
+ self.xmlstream.domain = root.getAttribute('to')
+
+ if self.xmlstream.sid is None:
+ self.xmlstream.sid = '%x' % random.randint(1, sys.maxint)
+
+ self.xmlstream.sendHeader()
+
+ def streamIQ(self):
+ features = elem(xmlstream.NS_STREAMS, 'features')(
+ elem(ns.NS_XMPP_BIND, 'bind'),
+ elem(ns.NS_XMPP_SESSION, 'session'),
+ )
+ self.xmlstream.send(features)
+
+ self.xmlstream.addOnetimeObserver(
+ "/iq/bind[@xmlns='%s']" % ns.NS_XMPP_BIND, self.bindIq)
+ self.xmlstream.addOnetimeObserver(
+ "/iq/session[@xmlns='%s']" % ns.NS_XMPP_SESSION, self.sessionIq)
+
+ def streamSASL(self):
+ features = domish.Element((xmlstream.NS_STREAMS, 'features'))
+ mechanisms = features.addElement((ns.NS_XMPP_SASL, 'mechanisms'))
+ for mechanism in self._mechanisms:
+ mechanisms.addElement('mechanism', content=mechanism)
+ self.xmlstream.send(features)
+
+ self.xmlstream.addOnetimeObserver("/auth", self.auth)
+
+ def streamStarted(self, root=None):
+ self.streamInitialize(root)
+
+ if self.authenticated:
+ # Initiator authenticated itself, and has started a new stream.
+ self.streamIQ()
+ else:
+ self.streamSASL()
+
+ def auth(self, auth):
+ assert (base64.b64decode(str(auth)) ==
+ '\x00%s\x00%s' % (self.username, self.password))
+
+ success = domish.Element((ns.NS_XMPP_SASL, 'success'))
+ self.xmlstream.send(success)
+ self.xmlstream.reset()
+ self.authenticated = True
+
+ def bindIq(self, iq):
+ resource = xpath.queryForString('/iq/bind/resource', iq)
+ if self.resource is not None:
+ assertEquals(self.resource, resource)
+ else:
+ assert resource is not None
+
+ result = IQ(self.xmlstream, "result")
+ result["id"] = iq["id"]
+ bind = result.addElement((ns.NS_XMPP_BIND, 'bind'))
+ self.bare_jid = '%s@%s' % (self.username, self.xmlstream.domain)
+ self.full_jid = '%s/%s' % (self.bare_jid, resource)
+ jid = bind.addElement('jid', content=self.full_jid)
+ self.xmlstream.send(result)
+
+ self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
+
+ def sessionIq(self, iq):
+ self.xmlstream.send(make_result_iq(self.xmlstream, iq))
+
+class StreamEvent(servicetest.Event):
+ def __init__(self, type_, stanza, stream):
+ servicetest.Event.__init__(self, type_, stanza=stanza)
+ self.stream = stream
+ self.to = stanza.getAttribute("to")
+
+class IQEvent(StreamEvent):
+ def __init__(self, stream, iq):
+ StreamEvent.__init__(self, 'stream-iq', iq, stream)
+ self.iq_type = iq.getAttribute("type")
+ self.iq_id = iq.getAttribute("id")
+
+ query = iq.firstChildElement()
+
+ if query:
+ self.query = query
+ self.query_ns = query.uri
+ self.query_name = query.name
+
+ if query.getAttribute("node"):
+ self.query_node = query.getAttribute("node")
+ else:
+ self.query = None
+
+class PresenceEvent(StreamEvent):
+ def __init__(self, stream, stanza):
+ StreamEvent.__init__(self, 'stream-presence', stanza, stream)
+ self.presence_type = stanza.getAttribute('type')
+
+ statuses = xpath.queryForNodes('/presence/status', stanza)
+
+ if statuses:
+ self.presence_status = str(statuses[0])
+
+class MessageEvent(StreamEvent):
+ def __init__(self, stream, stanza):
+ StreamEvent.__init__(self, 'stream-message', stanza, stream)
+ self.message_type = stanza.getAttribute('type')
+
+class StreamFactory(twisted.internet.protocol.Factory):
+ def __init__(self, streams, jids):
+ self.streams = streams
+ self.jids = jids
+ self.presences = {}
+ self.mappings = dict(map (lambda jid, stream: (jid, stream),
+ jids, streams))
+
+ # Make a copy of the streams
+ self.factory_streams = list(streams)
+ self.factory_streams.reverse()
+
+ # Do not add observers for single instances because it's unnecessary and
+ # some unit tests need to respond to the roster request, and we shouldn't
+ # answer it for them otherwise we break compatibility
+ if len(streams) > 1:
+ # We need to have a function here because lambda keeps a reference on
+ # the stream and jid and in the for loop, there is no context
+ def addObservers(stream, jid):
+ stream.addObserver('/iq', lambda x: \
+ self.forward_iq(stream, jid, x))
+ stream.addObserver('/presence', lambda x: \
+ self.got_presence(stream, jid, x))
+
+ for (jid, stream) in self.mappings.items():
+ addObservers(stream, jid)
+
+ def protocol(self, *args):
+ return self.factory_streams.pop()
+
+
+ def got_presence (self, stream, jid, stanza):
+ stanza.attributes['from'] = jid
+ self.presences[jid] = stanza
+
+ for dest_jid in self.presences.keys():
+ # Dispatch the new presence to other clients
+ stanza.attributes['to'] = dest_jid
+ self.mappings[dest_jid].send(stanza)
+
+ # Don't echo the presence twice
+ if dest_jid != jid:
+ # Dispatch other client's presence to this stream
+ presence = self.presences[dest_jid]
+ presence.attributes['to'] = jid
+ stream.send(presence)
+
+ def lost_presence(self, stream, jid):
+ if self.presences.has_key(jid):
+ del self.presences[jid]
+ for dest_jid in self.presences.keys():
+ presence = domish.Element(('jabber:client', 'presence'))
+ presence['from'] = jid
+ presence['to'] = dest_jid
+ presence['type'] = 'unavailable'
+ self.mappings[dest_jid].send(presence)
+
+ def forward_iq(self, stream, jid, stanza):
+ stanza.attributes['from'] = jid
+
+ query = stanza.firstChildElement()
+
+ # Fake other accounts as being part of our roster
+ if query and query.uri == ns.ROSTER:
+ roster = make_result_iq(stream, stanza)
+ query = roster.firstChildElement()
+ for roster_jid in self.mappings.keys():
+ if jid != roster_jid:
+ item = query.addElement('item')
+ item['jid'] = roster_jid
+ item['subscription'] = 'both'
+ stream.send(roster)
+ return
+
+ to = stanza.getAttribute('to')
+ dest = None
+ if to is not None:
+ dest = self.mappings.get(to)
+
+ if dest is not None:
+ dest.send(stanza)
+
+class BaseXmlStream(xmlstream.XmlStream):
+ initiating = False
+ namespace = 'jabber:client'
+ pep_support = True
+ disco_features = []
+ handle_privacy_lists = True
+
+ def __init__(self, event_func, authenticator):
+ xmlstream.XmlStream.__init__(self, authenticator)
+ self.event_func = event_func
+ self.addObserver('//iq', lambda x: event_func(
+ IQEvent(self, x)))
+ self.addObserver('//message', lambda x: event_func(
+ MessageEvent(self, x)))
+ self.addObserver('//presence', lambda x: event_func(
+ PresenceEvent(self, x)))
+ self.addObserver('//event/stream/authd', self._cb_authd)
+ if self.handle_privacy_lists:
+ self.addObserver("/iq/query[@xmlns='%s']" % ns.PRIVACY,
+ self._cb_priv_list)
+
+ def connectionMade(self):
+ xmlstream.XmlStream.connectionMade(self)
+
+ if 'GABBLE_NODELAY' in os.environ:
+ self.transport.setTcpNoDelay(True)
+
+ def _cb_priv_list(self, iq):
+ send_error_reply(self, iq)
+
+ def _cb_authd(self, _):
+ # called when stream is authenticated
+ assert self.authenticator.full_jid is not None
+ assert self.authenticator.bare_jid is not None
+
+ self.addObserver(
+ "/iq[@to='%s']/query[@xmlns='http://jabber.org/protocol/disco#info']" % self.domain,
+ self._cb_disco_iq)
+ self.addObserver(
+ "/iq[@to='%s']/query[@xmlns='http://jabber.org/protocol/disco#info']"
+ % self.authenticator.bare_jid,
+ self._cb_bare_jid_disco_iq)
+ self.event_func(servicetest.Event('stream-authenticated'))
+
+ def _cb_disco_iq(self, iq):
+ nodes = xpath.queryForNodes(
+ "/iq/query[@xmlns='http://jabber.org/protocol/disco#info']", iq)
+ query = nodes[0]
+
+ for feature in self.disco_features:
+ query.addChild(elem('feature', var=feature))
+
+ iq['type'] = 'result'
+ iq['from'] = iq['to']
+ self.send(iq)
+
+ def _cb_bare_jid_disco_iq(self, iq):
+ # advertise PEP support
+ nodes = xpath.queryForNodes(
+ "/iq/query[@xmlns='http://jabber.org/protocol/disco#info']",
+ iq)
+ query = nodes[0]
+ identity = query.addElement('identity')
+ identity['category'] = 'pubsub'
+ identity['type'] = 'pep'
+
+ iq['type'] = 'result'
+ iq['from'] = iq['to']
+ self.send(iq)
+
+ def onDocumentEnd(self):
+ self.event_func(servicetest.Event('stream-closed'))
+ # We don't chain up XmlStream.onDocumentEnd() because it will
+ # disconnect the TCP connection making tests as
+ # connect/disconnect-timeout.py not working
+
+ def connectionLost(self, reason):
+ self.event_func(servicetest.Event('stream-connection-lost'))
+ xmlstream.XmlStream.connectionLost(self, reason)
+
+ def send_stream_error(self, error='system-shutdown'):
+ # Yes, there are meant to be two different STREAMS namespaces.
+ go_away = \
+ elem(xmlstream.NS_STREAMS, 'error')(
+ elem(ns.STREAMS, error)
+ )
+
+ self.send(go_away)
+
+class JabberXmlStream(BaseXmlStream):
+ version = (0, 9)
+
+class XmppXmlStream(BaseXmlStream):
+ version = (1, 0)
+
+class GoogleXmlStream(BaseXmlStream):
+ version = (1, 0)
+
+ pep_support = False
+ disco_features = [ns.GOOGLE_ROSTER,
+ ns.GOOGLE_JINGLE_INFO,
+ ns.GOOGLE_MAIL_NOTIFY,
+ ns.GOOGLE_QUEUE,
+ ]
+
+ def _cb_bare_jid_disco_iq(self, iq):
+ # Google talk doesn't support PEP :(
+ iq['type'] = 'result'
+ iq['from'] = iq['to']
+ self.send(iq)
+
+
+def make_connection(bus, event_func, params=None, suffix=''):
+ # Gabble accepts a resource in 'account', but the value of 'resource'
+ # overrides it if there is one.
+ test_name = re.sub('(.*tests/twisted/|\./)', '', sys.argv[0])
+ account = 'test%s@localhost/%s' % (suffix, test_name)
+
+ default_params = {
+ 'account': account,
+ 'password': 'pass',
+ 'resource': 'Resource',
+ 'server': 'localhost',
+ 'port': dbus.UInt32(4242),
+ 'fallback-socks5-proxies': dbus.Array([], signature='s'),
+ 'require-encryption': False,
+ }
+
+ if params:
+ default_params.update(params)
+
+ # Allow omitting the 'password' param
+ if default_params['password'] is None:
+ del default_params['password']
+
+ # Allow omitting the 'account' param
+ if default_params['account'] is None:
+ del default_params['account']
+
+ jid = default_params.get('account', None)
+ conn = servicetest.make_connection(bus, event_func, 'gabble', 'jabber',
+ default_params)
+ return (conn, jid)
+
+def make_stream(event_func, authenticator=None, protocol=None,
+ resource=None, suffix=''):
+ # set up Jabber server
+ if authenticator is None:
+ authenticator = XmppAuthenticator('test%s' % suffix, 'pass', resource=resource)
+
+ authenticator.set_event_func(event_func)
+
+ if protocol is None:
+ protocol = XmppXmlStream
+
+ stream = protocol(event_func, authenticator)
+ return stream
+
+def disconnect_conn(q, conn, stream, expected_before=[], expected_after=[]):
+ call_async(q, conn, 'Disconnect')
+
+ tmp = expected_before + [
+ EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]),
+ EventPattern('stream-closed')]
+
+ before_events = q.expect_many(*tmp)
+
+ stream.sendFooter()
+
+ tmp = expected_after + [EventPattern('dbus-return', method='Disconnect')]
+ after_events = q.expect_many(*tmp)
+
+ return before_events[:-2], after_events[:-1]
+
+def element_repr(element):
+ """__repr__ cannot safely return non-ASCII: see
+ <http://bugs.python.org/issue5876>. So we print non-ASCII characters as
+ \uXXXX escapes in debug output
+
+ """
+ return element.toXml().encode('unicode-escape')
+
+def expect_connected(queue):
+ queue.expect('dbus-signal', signal='StatusChanged',
+ args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED])
+ queue.expect('stream-authenticated')
+ queue.expect('dbus-signal', signal='PresencesChanged',
+ args=[{1L: (cs.PRESENCE_AVAILABLE, u'available', '')}])
+ queue.expect('dbus-signal', signal='StatusChanged',
+ args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
+
+def exec_test_deferred(fun, params, protocol=None, timeout=None,
+ authenticator=None, num_instances=1,
+ do_connect=True,
+ make_connection_func=make_connection,
+ expect_connected_func=expect_connected):
+ # hack to ease debugging
+ domish.Element.__repr__ = element_repr
+ colourer = None
+
+ if sys.stdout.isatty() or 'CHECK_FORCE_COLOR' in os.environ:
+ colourer = servicetest.install_colourer()
+
+ try:
+ bus = dbus.SessionBus()
+ except dbus.exceptions.DBusException as e:
+ print e
+ os._exit(1)
+
+ queue = servicetest.IteratingEventQueue(timeout)
+ queue.verbose = (
+ os.environ.get('CHECK_TWISTED_VERBOSE', '') != ''
+ or '-v' in sys.argv)
+
+ conns = []
+ jids = []
+ streams = []
+ resource = params.get('resource') if params is not None else None
+ for i in range(0, num_instances):
+ if i == 0:
+ suffix = ''
+ else:
+ suffix = str(i)
+
+ try:
+ (conn, jid) = make_connection_func(bus, queue.append, params, suffix)
+ except Exception, e:
+ # Crap. This is normally because the connection's still kicking
+ # around on the bus. Let's bin any connections we *did* manage to
+ # get going and then bail out unceremoniously.
+ print e
+
+ for conn in conns:
+ conn.Disconnect()
+
+ os._exit(1)
+
+ conns.append(conn)
+ jids.append(jid)
+ streams.append(make_stream(queue.append, protocol=protocol,
+ authenticator=authenticator,
+ resource=resource, suffix=suffix))
+
+ factory = StreamFactory(streams, jids)
+ port = reactor.listenTCP(4242, factory, interface='localhost')
+
+ def signal_receiver(*args, **kw):
+ if kw['path'] == '/org/freedesktop/DBus' and \
+ kw['member'] == 'NameOwnerChanged':
+ bus_name, old_name, new_name = args
+ if new_name == '':
+ for i, conn in enumerate(conns):
+ stream = streams[i]
+ jid = jids[i]
+ if conn._requested_bus_name == bus_name:
+ factory.lost_presence(stream, jid)
+ break
+ queue.append(Event('dbus-signal',
+ path=unwrap(kw['path']),
+ signal=kw['member'], args=map(unwrap, args),
+ interface=kw['interface']))
+
+ match_all_signals = bus.add_signal_receiver(
+ signal_receiver,
+ None, # signal name
+ None, # interface
+ None,
+ path_keyword='path',
+ member_keyword='member',
+ interface_keyword='interface',
+ byte_arrays=True
+ )
+
+ error = None
+
+ try:
+ if do_connect:
+ for conn in conns:
+ conn.Connect()
+ expect_connected_func(queue)
+
+ if len(conns) == 1:
+ fun(queue, bus, conns[0], streams[0])
+ else:
+ fun(queue, bus, conns, streams)
+ except Exception, e:
+ traceback.print_exc()
+ error = e
+ queue.verbose = False
+
+ if colourer:
+ sys.stdout = colourer.fh
+
+ d = port.stopListening()
+
+ # Does the Connection object still exist?
+ for i, conn in enumerate(conns):
+ if not bus.name_has_owner(conn.object.bus_name):
+ # Connection has already been disconnected and destroyed
+ continue
+ try:
+ if conn.Properties.Get(cs.CONN, 'Status') == cs.CONN_STATUS_CONNECTED:
+ # Connection is connected, properly disconnect it
+ disconnect_conn(queue, conn, streams[i])
+ else:
+ # Connection is not connected, call Disconnect() to destroy it
+ conn.Disconnect()
+ except dbus.DBusException, e:
+ pass
+ except Exception, e:
+ traceback.print_exc()
+ error = e
+
+ try:
+ conn.Disconnect()
+ raise AssertionError("Connection didn't disappear; "
+ "all subsequent tests will probably fail")
+ except dbus.DBusException, e:
+ pass
+ except Exception, e:
+ traceback.print_exc()
+ error = e
+
+ match_all_signals.remove()
+
+ if error is None:
+ d.addBoth((lambda *args: reactor.crash()))
+ else:
+ # please ignore the POSIX behind the curtain
+ d.addBoth((lambda *args: os._exit(1)))
+
+
+def exec_test(fun, params=None, protocol=None, timeout=None,
+ authenticator=None, num_instances=1, do_connect=True):
+ reactor.callWhenRunning(
+ exec_test_deferred, fun, params, protocol, timeout, authenticator, num_instances,
+ do_connect)
+ reactor.run()
+
+# Useful routines for server-side vCard handling
+current_vcard = domish.Element(('vcard-temp', 'vCard'))
+
+def expect_and_handle_get_vcard(q, stream):
+ get_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP,
+ query_name='vCard', iq_type='get')
+
+ iq = get_vcard_event.stanza
+ vcard = iq.firstChildElement()
+ assert vcard.name == 'vCard', vcard.toXml()
+
+ # Send back current vCard
+ result = make_result_iq(stream, iq, add_query_node=False)
+ result.addChild(current_vcard)
+ stream.send(result)
+
+def expect_and_handle_set_vcard(q, stream, check=None):
+ global current_vcard
+ set_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP,
+ query_name='vCard', iq_type='set')
+ iq = set_vcard_event.stanza
+ vcard = iq.firstChildElement()
+ assert vcard.name == 'vCard', vcard.toXml()
+
+ if check is not None:
+ check(vcard)
+
+ # Update current vCard
+ current_vcard = vcard
+
+ stream.send(make_result_iq(stream, iq))
+
+def _elem_add(elem, *children):
+ for child in children:
+ if isinstance(child, domish.Element):
+ elem.addChild(child)
+ elif isinstance(child, unicode):
+ elem.addContent(child)
+ else:
+ raise ValueError(
+ 'invalid child object %r (must be element or unicode)', child)
+
+def elem(a, b=None, attrs={}, **kw):
+ r"""
+ >>> elem('foo')().toXml()
+ u'<foo/>'
+ >>> elem('foo', x='1')().toXml()
+ u"<foo x='1'/>"
+ >>> elem('foo', x='1')(u'hello').toXml()
+ u"<foo x='1'>hello</foo>"
+ >>> elem('foo', x='1')(u'hello',
+ ... elem('http://foo.org', 'bar', y='2')(u'bye')).toXml()
+ u"<foo x='1'>hello<bar xmlns='http://foo.org' y='2'>bye</bar></foo>"
+ >>> elem('foo', attrs={'xmlns:bar': 'urn:bar', 'bar:cake': 'yum'})(
+ ... elem('bar:e')(u'i')
+ ... ).toXml()
+ u"<foo xmlns:bar='urn:bar' bar:cake='yum'><bar:e>i</bar:e></foo>"
+ """
+
+ class _elem(domish.Element):
+ def __call__(self, *children):
+ _elem_add(self, *children)
+ return self
+
+ if b is not None:
+ elem = _elem((a, b))
+ else:
+ elem = _elem((None, a))
+
+ # Can't just update kw into attrs, because that *modifies the parameter's
+ # default*. Thanks python.
+ allattrs = {}
+ allattrs.update(kw)
+ allattrs.update(attrs)
+
+ # First, let's pull namespaces out
+ realattrs = {}
+ for k, v in allattrs.iteritems():
+ if k.startswith('xmlns:'):
+ abbr = k[len('xmlns:'):]
+ elem.localPrefixes[abbr] = v
+ else:
+ realattrs[k] = v
+
+ for k, v in realattrs.iteritems():
+ if k == 'from_':
+ elem['from'] = v
+ else:
+ elem[k] = v
+
+ return elem
+
+def elem_iq(server, type, **kw):
+ class _iq(IQ):
+ def __call__(self, *children):
+ _elem_add(self, *children)
+ return self
+
+ iq = _iq(server, type)
+
+ for k, v in kw.iteritems():
+ if k == 'from_':
+ iq['from'] = v
+ else:
+ iq[k] = v
+
+ return iq
+
+def make_presence(_from, to='test@localhost', type=None, show=None,
+ status=None, caps=None, photo=None):
+ presence = domish.Element((None, 'presence'))
+ presence['from'] = _from
+ presence['to'] = to
+
+ if type is not None:
+ presence['type'] = type
+
+ if show is not None:
+ presence.addElement('show', content=show)
+
+ if status is not None:
+ presence.addElement('status', content=status)
+
+ if caps is not None:
+ cel = presence.addElement(('http://jabber.org/protocol/caps', 'c'))
+ for key,value in caps.items():
+ cel[key] = value
+
+ # <x xmlns="vcard-temp:x:update"><photo>4a1...</photo></x>
+ if photo is not None:
+ x = presence.addElement((ns.VCARD_TEMP_UPDATE, 'x'))
+ x.addElement('photo').addContent(photo)
+
+ return presence
diff --git a/tests/twisted/hazetest.py b/tests/twisted/hazetest.py
index ea2d68f..e03c58c 100644
--- a/tests/twisted/hazetest.py
+++ b/tests/twisted/hazetest.py
@@ -1,439 +1,19 @@
-
"""
Infrastructure code for testing Haze by pretending to be a Jabber server.
-
-This is based on gabbletest.py in telepathy-gabble. Haze-specific hacks should
-be marked with an 'XXX Haze' comment. This offends me too, but I don't have
-time to do anything better.
"""
-import base64
-import os
-import hashlib
import sys
-import random
-import re
-import traceback
-
-import ns
-import constants as cs
-import servicetest
-from servicetest import (
- assertEquals, assertLength, assertContains, wrap_channel,
- EventPattern, call_async, unwrap, Event)
-import twisted
from twisted.words.xish import domish, xpath
-from twisted.words.protocols.jabber.client import IQ
-from twisted.words.protocols.jabber import xmlstream
-from twisted.internet import reactor, ssl
-
import dbus
-def make_result_iq(stream, iq, add_query_node=True):
- result = IQ(stream, "result")
- result["id"] = iq["id"]
- to = iq.getAttribute('to')
- if to is not None:
- result["from"] = to
- query = iq.firstChildElement()
-
- if query and add_query_node:
- result.addElement((query.uri, query.name))
-
- return result
-
-def acknowledge_iq(stream, iq):
- stream.send(make_result_iq(stream, iq))
-
-def send_error_reply(stream, iq, error_stanza=None):
- result = IQ(stream, "error")
- result["id"] = iq["id"]
- query = iq.firstChildElement()
- to = iq.getAttribute('to')
- if to is not None:
- result["from"] = to
-
- if query:
- result.addElement((query.uri, query.name))
-
- if error_stanza:
- result.addChild(error_stanza)
-
- stream.send(result)
-
-def request_muc_handle(q, conn, stream, muc_jid):
- servicetest.call_async(q, conn, 'RequestHandles', 2, [muc_jid])
- event = q.expect('dbus-return', method='RequestHandles')
- return event.value[0][0]
-
-def make_muc_presence(affiliation, role, muc_jid, alias, jid=None):
- presence = domish.Element((None, 'presence'))
- presence['from'] = '%s/%s' % (muc_jid, alias)
- x = presence.addElement((ns.MUC_USER, 'x'))
- item = x.addElement('item')
- item['affiliation'] = affiliation
- item['role'] = role
- if jid is not None:
- item['jid'] = jid
- return presence
-
-def sync_stream(q, stream):
- """Used to ensure that Gabble has processed all stanzas sent to it."""
-
- iq = IQ(stream, "get")
- id = iq['id']
- iq.addElement(('http://jabber.org/protocol/disco#info', 'query'))
- stream.send(iq)
- q.expect('stream-iq', query_ns='http://jabber.org/protocol/disco#info',
- predicate=(lambda event:
- event.stanza['id'] == id and event.iq_type == 'result'))
-
-class GabbleAuthenticator(xmlstream.Authenticator):
- def __init__(self, username, password, resource=None):
- self.username = username
- self.password = password
- self.resource = resource
- self.bare_jid = None
- self.full_jid = None
- xmlstream.Authenticator.__init__(self)
-
-class JabberAuthenticator(GabbleAuthenticator):
- "Trivial XML stream authenticator that accepts one username/digest pair."
-
- # Patch in fix from http://twistedmatrix.com/trac/changeset/23418.
- # This monkeypatch taken from Gadget source code
- from twisted.words.xish.utility import EventDispatcher
-
- def _addObserver(self, onetime, event, observerfn, priority, *args,
- **kwargs):
- if self._dispatchDepth > 0:
- self._updateQueue.append(lambda: self._addObserver(onetime, event,
- observerfn, priority, *args, **kwargs))
-
- return self._oldAddObserver(onetime, event, observerfn, priority,
- *args, **kwargs)
-
- EventDispatcher._oldAddObserver = EventDispatcher._addObserver
- EventDispatcher._addObserver = _addObserver
-
- def streamStarted(self, root=None):
- if root:
- self.xmlstream.sid = '%x' % random.randint(1, sys.maxint)
-
- self.xmlstream.sendHeader()
- self.xmlstream.addOnetimeObserver(
- "/iq/query[@xmlns='jabber:iq:auth']", self.initialIq)
-
- def initialIq(self, iq):
- result = IQ(self.xmlstream, "result")
- result["id"] = iq["id"]
- query = result.addElement('query')
- query["xmlns"] = "jabber:iq:auth"
- query.addElement('username', content='test')
- query.addElement('password')
- query.addElement('digest')
- query.addElement('resource')
- self.xmlstream.addOnetimeObserver('/iq/query/username', self.secondIq)
- self.xmlstream.send(result)
-
- def secondIq(self, iq):
- username = xpath.queryForNodes('/iq/query/username', iq)
- assert map(str, username) == [self.username]
-
- digest = xpath.queryForNodes('/iq/query/digest', iq)
- expect = hashlib.sha1(self.xmlstream.sid + self.password).hexdigest()
- assert map(str, digest) == [expect]
-
- resource = xpath.queryForNodes('/iq/query/resource', iq)
- assertLength(1, resource)
- if self.resource is not None:
- assertEquals(self.resource, str(resource[0]))
-
- self.bare_jid = '%s@localhost' % self.username
- self.full_jid = '%s/%s' % (self.bare_jid, resource)
-
- result = IQ(self.xmlstream, "result")
- result["id"] = iq["id"]
- self.xmlstream.send(result)
- self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
-
-class XmppAuthenticator(GabbleAuthenticator):
- def __init__(self, username, password, resource=None):
- GabbleAuthenticator.__init__(self, username, password, resource)
- self.authenticated = False
-
- def streamInitialize(self, root):
- if root:
- self.xmlstream.sid = root.getAttribute('id')
-
- if self.xmlstream.sid is None:
- self.xmlstream.sid = '%x' % random.randint(1, sys.maxint)
-
- self.xmlstream.sendHeader()
-
- def streamIQ(self):
- features = elem(xmlstream.NS_STREAMS, 'features')(
- elem(ns.NS_XMPP_BIND, 'bind'),
- elem(ns.NS_XMPP_SESSION, 'session'),
- )
- self.xmlstream.send(features)
-
- self.xmlstream.addOnetimeObserver(
- "/iq/bind[@xmlns='%s']" % ns.NS_XMPP_BIND, self.bindIq)
- self.xmlstream.addOnetimeObserver(
- "/iq/session[@xmlns='%s']" % ns.NS_XMPP_SESSION, self.sessionIq)
-
- def streamSASL(self):
- features = domish.Element((xmlstream.NS_STREAMS, 'features'))
- mechanisms = features.addElement((ns.NS_XMPP_SASL, 'mechanisms'))
- mechanism = mechanisms.addElement('mechanism', content='PLAIN')
- self.xmlstream.send(features)
-
- self.xmlstream.addOnetimeObserver("/auth", self.auth)
-
- def streamStarted(self, root=None):
- self.streamInitialize(root)
-
- if self.authenticated:
- # Initiator authenticated itself, and has started a new stream.
- self.streamIQ()
- else:
- self.streamSASL()
-
- def auth(self, auth):
- assert (base64.b64decode(str(auth)) ==
- '\x00%s\x00%s' % (self.username, self.password))
-
- success = domish.Element((ns.NS_XMPP_SASL, 'success'))
- self.xmlstream.send(success)
- self.xmlstream.reset()
- self.authenticated = True
-
- def bindIq(self, iq):
- resource = xpath.queryForString('/iq/bind/resource', iq)
- if self.resource is not None:
- assertEquals(self.resource, resource)
- else:
- assert resource is not None
-
- result = IQ(self.xmlstream, "result")
- result["id"] = iq["id"]
- bind = result.addElement((ns.NS_XMPP_BIND, 'bind'))
- self.bare_jid = '%s@localhost' % self.username
- self.full_jid = '%s/%s' % (self.bare_jid, resource)
- jid = bind.addElement('jid', content=self.full_jid)
- self.xmlstream.send(result)
-
- self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
-
- def sessionIq(self, iq):
- self.xmlstream.send(make_result_iq(self.xmlstream, iq))
-
-def make_stream_event(type, stanza, stream):
- event = servicetest.Event(type, stanza=stanza)
- event.stream = stream
- event.to = stanza.getAttribute("to")
- return event
-
-def make_iq_event(stream, iq):
- event = make_stream_event('stream-iq', iq, stream)
- event.iq_type = iq.getAttribute("type")
- event.iq_id = iq.getAttribute("id")
- query = iq.firstChildElement()
-
- if query:
- event.query = query
- event.query_ns = query.uri
- event.query_name = query.name
-
- if query.getAttribute("node"):
- event.query_node = query.getAttribute("node")
- else:
- event.query = None
-
- return event
-
-def make_presence_event(stream, stanza):
- event = make_stream_event('stream-presence', stanza, stream)
- event.presence_type = stanza.getAttribute('type')
-
- statuses = xpath.queryForNodes('/presence/status', stanza)
-
- if statuses:
- event.presence_status = str(statuses[0])
-
- return event
-
-def make_message_event(stream, stanza):
- event = make_stream_event('stream-message', stanza, stream)
- event.message_type = stanza.getAttribute('type')
- return event
-
-class StreamFactory(twisted.internet.protocol.Factory):
- def __init__(self, streams, jids):
- self.streams = streams
- self.jids = jids
- self.presences = {}
- self.mappings = dict(map (lambda jid, stream: (jid, stream),
- jids, streams))
-
- # Make a copy of the streams
- self.factory_streams = list(streams)
- self.factory_streams.reverse()
-
- # Do not add observers for single instances because it's unnecessary and
- # some unit tests need to respond to the roster request, and we shouldn't
- # answer it for them otherwise we break compatibility
- if len(streams) > 1:
- # We need to have a function here because lambda keeps a reference on
- # the stream and jid and in the for loop, there is no context
- def addObservers(stream, jid):
- stream.addObserver('/iq', lambda x: \
- self.forward_iq(stream, jid, x))
- stream.addObserver('/presence', lambda x: \
- self.got_presence(stream, jid, x))
-
- for (jid, stream) in self.mappings.items():
- addObservers(stream, jid)
-
- def protocol(self, *args):
- return self.factory_streams.pop()
-
-
- def got_presence (self, stream, jid, stanza):
- stanza.attributes['from'] = jid
- self.presences[jid] = stanza
-
- for dest_jid in self.presences.keys():
- # Dispatch the new presence to other clients
- stanza.attributes['to'] = dest_jid
- self.mappings[dest_jid].send(stanza)
-
- # Don't echo the presence twice
- if dest_jid != jid:
- # Dispatch other client's presence to this stream
- presence = self.presences[dest_jid]
- presence.attributes['to'] = jid
- stream.send(presence)
-
- def lost_presence(self, stream, jid):
- if self.presences.has_key(jid):
- del self.presences[jid]
- for dest_jid in self.presences.keys():
- presence = domish.Element(('jabber:client', 'presence'))
- presence['from'] = jid
- presence['to'] = dest_jid
- presence['type'] = 'unavailable'
- self.mappings[dest_jid].send(presence)
-
- def forward_iq(self, stream, jid, stanza):
- stanza.attributes['from'] = jid
-
- query = stanza.firstChildElement()
-
- # Fake other accounts as being part of our roster
- if query and query.uri == ns.ROSTER:
- roster = make_result_iq(stream, stanza)
- query = roster.firstChildElement()
- for roster_jid in self.mappings.keys():
- if jid != roster_jid:
- item = query.addElement('item')
- item['jid'] = roster_jid
- item['subscription'] = 'both'
- stream.send(roster)
- return
-
- to = stanza.getAttribute('to')
- dest = None
- if to is not None:
- dest = self.mappings.get(to)
-
- if dest is not None:
- dest.send(stanza)
-
-class BaseXmlStream(xmlstream.XmlStream):
- initiating = False
- namespace = 'jabber:client'
- pep_support = True
- disco_features = []
- handle_privacy_lists = True
-
- def __init__(self, event_func, authenticator):
- xmlstream.XmlStream.__init__(self, authenticator)
- self.event_func = event_func
- self.addObserver('//iq', lambda x: event_func(
- make_iq_event(self, x)))
- self.addObserver('//message', lambda x: event_func(
- make_message_event(self, x)))
- self.addObserver('//presence', lambda x: event_func(
- make_presence_event(self, x)))
- self.addObserver('//event/stream/authd', self._cb_authd)
- if self.handle_privacy_lists:
- self.addObserver("/iq/query[@xmlns='%s']" % ns.PRIVACY,
- self._cb_priv_list)
-
- def _cb_priv_list(self, iq):
- send_error_reply(self, iq)
-
- def _cb_authd(self, _):
- # called when stream is authenticated
- assert self.authenticator.full_jid is not None
- assert self.authenticator.bare_jid is not None
-
- self.addObserver(
- "/iq[@to='localhost']/query[@xmlns='http://jabber.org/protocol/disco#info']",
- self._cb_disco_iq)
- self.addObserver(
- "/iq[@to='%s']/query[@xmlns='http://jabber.org/protocol/disco#info']"
- % self.authenticator.bare_jid,
- self._cb_bare_jid_disco_iq)
- # XXX Haze
- self.add_roster_observer()
- self.event_func(servicetest.Event('stream-authenticated'))
-
- def _cb_disco_iq(self, iq):
- nodes = xpath.queryForNodes(
- "/iq/query[@xmlns='http://jabber.org/protocol/disco#info']", iq)
- query = nodes[0]
-
- for feature in self.disco_features:
- query.addChild(elem('feature', var=feature))
-
- iq['type'] = 'result'
- iq['from'] = iq['to']
- self.send(iq)
-
- def _cb_bare_jid_disco_iq(self, iq):
- # advertise PEP support
- nodes = xpath.queryForNodes(
- "/iq/query[@xmlns='http://jabber.org/protocol/disco#info']",
- iq)
- query = nodes[0]
- identity = query.addElement('identity')
- identity['category'] = 'pubsub'
- identity['type'] = 'pep'
-
- iq['type'] = 'result'
- iq['from'] = iq['to']
- self.send(iq)
-
- def onDocumentEnd(self):
- self.event_func(servicetest.Event('stream-closed'))
- # We don't chain up XmlStream.onDocumentEnd() because it will
- # disconnect the TCP connection making tests as
- # connect/disconnect-timeout.py not working
-
- def send_stream_error(self, error='system-shutdown'):
- # Yes, there are meant to be two different STREAMS namespaces.
- go_away = \
- elem(xmlstream.NS_STREAMS, 'error')(
- elem(ns.STREAMS, error)
- )
+import servicetest
+# reexport everything, but override a thing
+from gabbletest import *
- self.send(go_away)
+class EmptyRosterXmppXmlStream(XmppXmlStream):
+ def _cb_authd(self, x):
+ XmppXmlStream._cb_authd(self, x)
- # XXX Haze: the next two methods are Haze-specific.
- def add_roster_observer(self):
self.addObserver(
"/iq/query[@xmlns='jabber:iq:roster']",
self._cb_roster_get)
@@ -444,44 +24,18 @@ class BaseXmlStream(xmlstream.XmlStream):
if iq.getAttribute('type') == 'get':
self.send(make_result_iq(self, iq))
-class JabberXmlStream(BaseXmlStream):
- version = (0, 9)
-
-class XmppXmlStream(BaseXmlStream):
- version = (1, 0)
-
-class GoogleXmlStream(BaseXmlStream):
- version = (1, 0)
-
- pep_support = False
- disco_features = [ns.GOOGLE_ROSTER,
- ns.GOOGLE_JINGLE_INFO,
- ns.GOOGLE_MAIL_NOTIFY,
- ]
-
- def _cb_bare_jid_disco_iq(self, iq):
- # Google talk doesn't support PEP :(
- iq['type'] = 'result'
- iq['from'] = iq['to']
- self.send(iq)
-
-
-def make_connection(bus, event_func, params=None, suffix=''):
+def make_haze_connection(bus, event_func, params=None, suffix=''):
# Gabble accepts a resource in 'account', but the value of 'resource'
- # overrides it if there is one.
- # XXX Haze doesn't.
+ # overrides it if there is one. Haze doesn't.
# account = 'test%s@localhost/%s' % (suffix, re.sub(r'.*/', '', sys.argv[0]))
account = 'test%s@localhost/Resource' % (suffix, )
default_params = {
'account': account,
'password': 'pass',
- # XXX Haze: fd.o#14212.
- #'resource': 'Resource',
'ft-proxies': sys.argv[0],
'server': 'localhost',
'port': dbus.UInt32(4242),
- # XXX Haze
'require-encryption': False,
'auth-plain-in-clear': True,
}
@@ -498,308 +52,25 @@ def make_connection(bus, event_func, params=None, suffix=''):
del default_params['account']
jid = default_params.get('account', None)
- # XXX Haze
conn = servicetest.make_connection(bus, event_func, 'haze', 'jabber',
default_params)
return (conn, jid)
-def make_stream(event_func, authenticator=None, protocol=None,
- resource=None, suffix=''):
- # set up Jabber server
- if authenticator is None:
- authenticator = XmppAuthenticator('test%s' % suffix, 'pass', resource=resource)
-
- if protocol is None:
- protocol = XmppXmlStream
-
- stream = protocol(event_func, authenticator)
- return stream
-
-def disconnect_conn(q, conn, stream, expected_before=[], expected_after=[]):
- call_async(q, conn, 'Disconnect')
-
- tmp = expected_before + [
- EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]),
- EventPattern('stream-closed')]
-
- before_events = q.expect_many(*tmp)
-
- stream.sendFooter()
-
- tmp = expected_after + [EventPattern('dbus-return', method='Disconnect')]
- after_events = q.expect_many(*tmp)
-
- return before_events[:-2], after_events[:-1]
-
-def exec_test_deferred(fun, params, protocol=None, timeout=None,
- authenticator=None, num_instances=1):
- # hack to ease debugging
- domish.Element.__repr__ = domish.Element.toXml
- colourer = None
-
- if sys.stdout.isatty() or 'CHECK_FORCE_COLOR' in os.environ:
- colourer = servicetest.install_colourer()
-
- bus = dbus.SessionBus()
-
- queue = servicetest.IteratingEventQueue(timeout)
- queue.verbose = (
- os.environ.get('CHECK_TWISTED_VERBOSE', '') != ''
- or '-v' in sys.argv)
-
- conns = []
- jids = []
- streams = []
- resource = params.get('resource') if params is not None else None
- for i in range(0, num_instances):
- if i == 0:
- suffix = ''
- else:
- suffix = str(i)
-
- try:
- (conn, jid) = make_connection(bus, queue.append, params, suffix)
- except Exception, e:
- # Crap. This is normally because the connection's still kicking
- # around on the bus. Let's bin any connections we *did* manage to
- # get going and then bail out unceremoniously.
- print e
-
- for conn in conns:
- conn.Disconnect()
-
- os._exit(1)
-
- conns.append(conn)
- jids.append(jid)
- streams.append(make_stream(queue.append, protocol=protocol,
- authenticator=authenticator,
- resource=resource, suffix=suffix))
-
- factory = StreamFactory(streams, jids)
- port = reactor.listenTCP(4242, factory)
-
- def signal_receiver(*args, **kw):
- if kw['path'] == '/org/freedesktop/DBus' and \
- kw['member'] == 'NameOwnerChanged':
- bus_name, old_name, new_name = args
- if new_name == '':
- for i, conn in enumerate(conns):
- stream = streams[i]
- jid = jids[i]
- if conn._requested_bus_name == bus_name:
- factory.lost_presence(stream, jid)
- break
- queue.append(Event('dbus-signal',
- path=unwrap(kw['path']),
- signal=kw['member'], args=map(unwrap, args),
- interface=kw['interface']))
-
- bus.add_signal_receiver(
- signal_receiver,
- None, # signal name
- None, # interface
- None,
- path_keyword='path',
- member_keyword='member',
- interface_keyword='interface',
- byte_arrays=True
- )
-
- error = None
-
- try:
- if len(conns) == 1:
- fun(queue, bus, conns[0], streams[0])
- else:
- fun(queue, bus, conns, streams)
- except Exception, e:
- traceback.print_exc()
- error = e
-
- if colourer:
- sys.stdout = colourer.fh
-
- d = port.stopListening()
-
- # Does the Connection object still exist?
- for i, conn in enumerate(conns):
- if not bus.name_has_owner(conn.object.bus_name):
- # Connection has already been disconnected and destroyed
- continue
- try:
- if conn.Properties.Get(cs.CONN, 'Status') == cs.CONN_STATUS_CONNECTED:
- # Connection is connected, properly disconnect it
- disconnect_conn(queue, conn, streams[i])
- else:
- # Connection is not connected, call Disconnect() to destroy it
- conn.Disconnect()
- except dbus.DBusException, e:
- pass
-
- try:
- conn.Disconnect()
- raise AssertionError("Connection didn't disappear; "
- "all subsequent tests will probably fail")
- except dbus.DBusException, e:
- pass
- except Exception, e:
- traceback.print_exc()
- error = e
-
- if error is None:
- d.addBoth((lambda *args: reactor.crash()))
- else:
- # please ignore the POSIX behind the curtain
- d.addBoth((lambda *args: os._exit(1)))
-
-
-def exec_test(fun, params=None, protocol=None, timeout=None,
- authenticator=None, num_instances=1):
+def expect_kinda_connected(queue):
+ queue.expect('dbus-signal', signal='StatusChanged',
+ args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED])
+ queue.expect('stream-authenticated')
+ # FIXME: unlike Gabble, Haze does not signal a presence update to available
+ # during connect
+ # queue.expect('dbus-signal', signal='PresencesChanged',
+ # args=[{1L: (cs.PRESENCE_AVAILABLE, u'available', '')}])
+ queue.expect('dbus-signal', signal='StatusChanged',
+ args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
+
+# Copy pasta because we need to replace make_connection
+def exec_test(fun, params=None, protocol=EmptyRosterXmppXmlStream, timeout=None,
+ authenticator=None, num_instances=1, do_connect=True):
reactor.callWhenRunning(
- exec_test_deferred, fun, params, protocol, timeout, authenticator, num_instances)
+ exec_test_deferred, fun, params, protocol, timeout, authenticator, num_instances,
+ do_connect, make_haze_connection, expect_kinda_connected)
reactor.run()
-
-# Useful routines for server-side vCard handling
-current_vcard = domish.Element(('vcard-temp', 'vCard'))
-
-def expect_and_handle_get_vcard(q, stream):
- get_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP,
- query_name='vCard', iq_type='get')
-
- iq = get_vcard_event.stanza
- vcard = iq.firstChildElement()
- assert vcard.name == 'vCard', vcard.toXml()
-
- # Send back current vCard
- result = make_result_iq(stream, iq)
- result.addChild(current_vcard)
- stream.send(result)
-
-def expect_and_handle_set_vcard(q, stream, check=None):
- set_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP,
- query_name='vCard', iq_type='set')
- iq = set_vcard_event.stanza
- vcard = iq.firstChildElement()
- assert vcard.name == 'vCard', vcard.toXml()
-
- if check is not None:
- check(vcard)
-
- # Update current vCard
- current_vcard = vcard
-
- stream.send(make_result_iq(stream, iq))
-
-def _elem_add(elem, *children):
- for child in children:
- if isinstance(child, domish.Element):
- elem.addChild(child)
- elif isinstance(child, unicode):
- elem.addContent(child)
- else:
- raise ValueError(
- 'invalid child object %r (must be element or unicode)', child)
-
-def elem(a, b=None, attrs={}, **kw):
- r"""
- >>> elem('foo')().toXml()
- u'<foo/>'
- >>> elem('foo', x='1')().toXml()
- u"<foo x='1'/>"
- >>> elem('foo', x='1')(u'hello').toXml()
- u"<foo x='1'>hello</foo>"
- >>> elem('foo', x='1')(u'hello',
- ... elem('http://foo.org', 'bar', y='2')(u'bye')).toXml()
- u"<foo x='1'>hello<bar xmlns='http://foo.org' y='2'>bye</bar></foo>"
- >>> elem('foo', attrs={'xmlns:bar': 'urn:bar', 'bar:cake': 'yum'})(
- ... elem('bar:e')(u'i')
- ... ).toXml()
- u"<foo xmlns:bar='urn:bar' bar:cake='yum'><bar:e>i</bar:e></foo>"
- """
-
- class _elem(domish.Element):
- def __call__(self, *children):
- _elem_add(self, *children)
- return self
-
- if b is not None:
- elem = _elem((a, b))
- else:
- elem = _elem((None, a))
-
- # Can't just update kw into attrs, because that *modifies the parameter's
- # default*. Thanks python.
- allattrs = {}
- allattrs.update(kw)
- allattrs.update(attrs)
-
- # First, let's pull namespaces out
- realattrs = {}
- for k, v in allattrs.iteritems():
- if k.startswith('xmlns:'):
- abbr = k[len('xmlns:'):]
- elem.localPrefixes[abbr] = v
- else:
- realattrs[k] = v
-
- for k, v in realattrs.iteritems():
- if k == 'from_':
- elem['from'] = v
- else:
- elem[k] = v
-
- return elem
-
-def elem_iq(server, type, **kw):
- class _iq(IQ):
- def __call__(self, *children):
- _elem_add(self, *children)
- return self
-
- iq = _iq(server, type)
-
- for k, v in kw.iteritems():
- if k == 'from_':
- iq['from'] = v
- else:
- iq[k] = v
-
- return iq
-
-def make_presence(_from, to='test@localhost', type=None, show=None,
- status=None, caps=None, photo=None):
- presence = domish.Element((None, 'presence'))
- presence['from'] = _from
- presence['to'] = to
-
- if type is not None:
- presence['type'] = type
-
- if show is not None:
- presence.addElement('show', content=show)
-
- if status is not None:
- presence.addElement('status', content=status)
-
- if caps is not None:
- cel = presence.addElement(('http://jabber.org/protocol/caps', 'c'))
- for key,value in caps.items():
- cel[key] = value
-
- # <x xmlns="vcard-temp:x:update"><photo>4a1...</photo></x>
- if photo is not None:
- x = presence.addElement((ns.VCARD_TEMP_UPDATE, 'x'))
- x.addElement('photo').addContent(photo)
-
- return presence
-
-def close_all_groups(q, bus, conn, stream):
- channels = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'Channels')
- for path, props in channels:
- if props.get(cs.CHANNEL_TYPE) != cs.CHANNEL_TYPE_CONTACT_LIST:
- continue
- if props.get(cs.TARGET_HANDLE_TYPE) != cs.HT_GROUP:
- continue
- wrap_channel(bus.get_object(conn.bus_name, path),
- cs.CHANNEL_TYPE_CONTACT_LIST).Close()
diff --git a/tests/twisted/presence/presence.py b/tests/twisted/presence/presence.py
index dea36a3..d722890 100644
--- a/tests/twisted/presence/presence.py
+++ b/tests/twisted/presence/presence.py
@@ -1,7 +1,5 @@
"""
A simple smoke-test for C.I.SimplePresence
-
-FIXME: test C.I.Presence too
"""
import dbus
@@ -9,13 +7,12 @@ import dbus
from twisted.words.xish import domish, xpath
from twisted.words.protocols.jabber.client import IQ
+from servicetest import assertEquals
from hazetest import exec_test
+import constants as cs
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
-
- amy_handle = conn.RequestHandles(1, ['amy@foo.com'])[0]
+ amy_handle = conn.get_contact_handle_sync('amy@foo.com')
# Divergence from Gabble: hazetest responds to all roster gets with an
# empty roster, so we need to push the roster.
@@ -51,6 +48,15 @@ def test(q, bus, conn, stream):
# produces.
assert event.args[0] == { amy_handle: (2, 'available', 'I may have been drinking') }
+ amy_handle, asv = conn.Contacts.GetContactByID('amy@foo.com',
+ [cs.CONN_IFACE_PRESENCE])
+ assertEquals(event.args[0][amy_handle], asv.get(cs.ATTR_PRESENCE))
+
+ bob_handle, asv = conn.Contacts.GetContactByID('bob@foo.com',
+ [cs.CONN_IFACE_PRESENCE])
+ assertEquals((cs.PRESENCE_UNKNOWN, 'unknown', ''),
+ asv.get(cs.ATTR_PRESENCE))
+
conn.Disconnect()
q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
diff --git a/tests/twisted/roster/groups.py b/tests/twisted/roster/groups.py
index 1e38630..ab74c2a 100644
--- a/tests/twisted/roster/groups.py
+++ b/tests/twisted/roster/groups.py
@@ -8,8 +8,8 @@ from twisted.words.protocols.jabber.client import IQ
from twisted.words.xish import domish, xpath
from servicetest import (EventPattern, wrap_channel, assertLength,
- assertEquals, call_async, sync_dbus, assertContains)
-from hazetest import acknowledge_iq, exec_test, sync_stream, close_all_groups
+ assertEquals, call_async, sync_dbus, assertContains, assertSameSets)
+from hazetest import acknowledge_iq, exec_test, sync_stream
import constants as cs
import ns
@@ -17,25 +17,9 @@ import ns
raise SystemExit(77)
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged',
- args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
- self_handle = conn.GetSelfHandle()
-
- # Close all Group channels to get a clean slate, so we can rely on
- # the NewChannels signal for the default group later
- close_all_groups(q, bus, conn, stream)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'subscribe',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- subscribe = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
-
- romeo, juliet, duncan = conn.RequestHandles(cs.HT_CONTACT,
+ self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
+
+ romeo, juliet, duncan = conn.get_contact_handles_sync(
['romeo@montague.lit', 'juliet@capulet.lit',
'duncan@scotland.lit'])
@@ -70,94 +54,74 @@ def test(q, bus, conn, stream):
sync_dbus(bus, q, conn)
sync_stream(q, stream)
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_GROUP,
- cs.TARGET_ID: 'Still alive',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- still_alive = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_GROUP,
- cs.TARGET_ID: 'Capulets',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- capulets = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
-
# the XMPP prpl puts people into some sort of group, probably called
# Buddies
- channels = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'Channels')
+ groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups')
default_group = None
- default_props = None
-
- for path, props in channels:
- if props.get(cs.CHANNEL_TYPE) != cs.CHANNEL_TYPE_CONTACT_LIST:
- continue
- if props.get(cs.TARGET_HANDLE_TYPE) != cs.HT_GROUP:
- continue
-
- if props.get(cs.TARGET_ID) in ('Capulets', 'Still alive'):
+ for group in groups:
+ if group in ('Capulets', 'Still alive'):
continue
if default_group is not None:
raise AssertionError('Two unexplained groups: %s, %s' %
- (path, default_group.object_path))
+ (group, default_group))
+
+ default_group = group
- default_group = wrap_channel(bus.get_object(conn.bus_name, path),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- default_group_name = props.get(cs.TARGET_ID)
+ call_async(q, conn.ContactList, 'GetContactListAttributes',
+ [cs.CONN_IFACE_CONTACT_GROUPS], False)
+ r = q.expect('dbus-return', method='GetContactListAttributes')
- assertEquals(set([romeo, juliet]), set(still_alive.Group.GetMembers()))
- assertEquals(set([juliet]), set(capulets.Group.GetMembers()))
- assertEquals(set([duncan]), set(default_group.Group.GetMembers()))
+ assertSameSets(['Still alive'], r.value[0][romeo][cs.ATTR_GROUPS])
+ assertSameSets(['Still alive', 'Capulets'],
+ r.value[0][juliet][cs.ATTR_GROUPS])
+ assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS])
# We can't remove Duncan from the default group, because it's his only
# group
- call_async(q, default_group.Group, 'RemoveMembers', [duncan], '')
- q.expect('dbus-error', method='RemoveMembers',
+ call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group,
+ [duncan])
+ q.expect('dbus-error', method='RemoveFromGroup',
name=cs.NOT_AVAILABLE)
+ call_async(q, conn.ContactGroups, 'SetGroupMembers', default_group,
+ [])
+ q.expect('dbus-error', method='SetGroupMembers',
+ name=cs.NOT_AVAILABLE)
+ # SetContactGroups just doesn't do anything in this situation
+ call_async(q, conn.ContactGroups, 'SetContactGroups', duncan, [])
+ q.expect('dbus-return', method='SetContactGroups')
+
+ call_async(q, conn.ContactList, 'GetContactListAttributes',
+ [cs.CONN_IFACE_CONTACT_GROUPS], False)
+ r = q.expect('dbus-return', method='GetContactListAttributes')
+ assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS])
# Make a new group and add Duncan to it
- call_async(q, conn.Requests, 'CreateChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_GROUP,
- cs.TARGET_ID: 'Scots',
- })
- e = q.expect('dbus-return', method='CreateChannel')
- scots = wrap_channel(bus.get_object(conn.bus_name, e.value[0]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- assertEquals(set(), set(scots.Group.GetMembers()))
-
- call_async(q, scots.Group, 'AddMembers', [duncan], '')
+ call_async(q, conn.ContactGroups, 'AddToGroup', 'Scots', [duncan])
iq, _, _ = q.expect_many(
EventPattern('stream-iq', iq_type='set', query_name='query',
query_ns=ns.ROSTER),
- EventPattern('dbus-signal', signal='MembersChanged',
- path=scots.object_path,
- args=['', [duncan], [], [], [], self_handle, cs.GC_REASON_NONE]),
- EventPattern('dbus-return', method='AddMembers'),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ args=[[duncan], ['Scots'], []]),
+ EventPattern('dbus-return', method='AddToGroup'),
)
assertEquals('duncan@scotland.lit', iq.stanza.query.item['jid'])
groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
iq.stanza)])
assertLength(2, groups)
- assertContains(default_group_name, groups)
+ assertContains(default_group, groups)
assertContains('Scots', groups)
# Now we can remove him from the default group. Much rejoicing.
- call_async(q, default_group.Group, 'RemoveMembers', [duncan], '')
+ call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group,
+ [duncan])
iq, _, _ = q.expect_many(
EventPattern('stream-iq', iq_type='set', query_name='query',
query_ns=ns.ROSTER),
- EventPattern('dbus-signal', signal='MembersChanged',
- path=default_group.object_path,
- args=['', [], [duncan], [], [], self_handle, cs.GC_REASON_NONE]),
- EventPattern('dbus-return', method='RemoveMembers'),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ args=[[duncan], [], [default_group]]),
+ EventPattern('dbus-return', method='RemoveFromGroup'),
)
assertEquals('duncan@scotland.lit', iq.stanza.query.item['jid'])
groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
@@ -165,22 +129,49 @@ def test(q, bus, conn, stream):
assertLength(1, groups)
assertContains('Scots', groups)
+ # Test SetContactGroups, which didn't previously have proper coverage
+ call_async(q, conn.ContactGroups, 'SetContactGroups', duncan,
+ ['Scottish former kings'])
+ iq, _, _, _ = q.expect_many(
+ EventPattern('stream-iq', iq_type='set', query_name='query',
+ query_ns=ns.ROSTER),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ args=[[duncan], ['Scottish former kings'], []]),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ args=[[duncan], [], ['Scots']]),
+ EventPattern('dbus-return', method='SetContactGroups'),
+ )
+ assertEquals('duncan@scotland.lit', iq.stanza.query.item['jid'])
+ groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
+ iq.stanza)])
+ assertLength(2, groups)
+ assertContains('Scots', groups)
+ assertContains('Scottish former kings', groups)
+ iq, = q.expect_many(
+ EventPattern('stream-iq', iq_type='set', query_name='query',
+ query_ns=ns.ROSTER),
+ )
+ assertEquals('duncan@scotland.lit', iq.stanza.query.item['jid'])
+ groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
+ iq.stanza)])
+ assertLength(1, groups)
+ assertContains('Scottish former kings', groups)
+
# Romeo dies. If he drops off the roster as a result, that would be
# fd.o #21294. However, to fix that bug, Haze now puts him in the
# default group.
- call_async(q, still_alive.Group, 'RemoveMembers', [romeo], '')
+ call_async(q, conn.ContactGroups, 'RemoveFromGroup', 'Still alive',
+ [romeo])
iq1, iq2, _, _, _ = q.expect_many(
EventPattern('stream-iq', iq_type='set', query_name='query',
query_ns=ns.ROSTER),
EventPattern('stream-iq', iq_type='set', query_name='query',
query_ns=ns.ROSTER),
- EventPattern('dbus-signal', signal='MembersChanged',
- path=still_alive.object_path,
- args=['', [], [romeo], [], [], self_handle, cs.GC_REASON_NONE]),
- EventPattern('dbus-signal', signal='MembersChanged',
- path=default_group.object_path,
- args=['', [romeo], [], [], [], self_handle, cs.GC_REASON_NONE]),
- EventPattern('dbus-return', method='RemoveMembers'),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ args=[[romeo], [default_group], []]),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ args=[[romeo], [], ['Still alive']]),
+ EventPattern('dbus-return', method='RemoveFromGroup'),
)
assertEquals('romeo@montague.lit', iq1.stanza.query.item['jid'])
@@ -188,24 +179,23 @@ def test(q, bus, conn, stream):
iq1.stanza)])
assertLength(2, groups)
assertContains('Still alive', groups)
- assertContains(default_group_name, groups)
+ assertContains(default_group, groups)
assertEquals('romeo@montague.lit', iq2.stanza.query.item['jid'])
groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
iq2.stanza)])
assertLength(1, groups)
- assertContains(default_group_name, groups)
+ assertContains(default_group, groups)
# Juliet dies. She's in another group already, so the workaround for
# fd.o #21294 is not active.
- call_async(q, still_alive.Group, 'RemoveMembers', [juliet], '')
+ call_async(q, conn.ContactGroups, 'SetGroupMembers', 'Still alive', [])
iq, _, _ = q.expect_many(
EventPattern('stream-iq', iq_type='set', query_name='query',
query_ns=ns.ROSTER),
- EventPattern('dbus-signal', signal='MembersChanged',
- path=still_alive.object_path,
- args=['', [], [juliet], [], [], self_handle, cs.GC_REASON_NONE]),
- EventPattern('dbus-return', method='RemoveMembers'),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ args=[[juliet], [], ['Still alive']]),
+ EventPattern('dbus-return', method='SetGroupMembers'),
)
assertEquals('juliet@capulet.lit', iq.stanza.query.item['jid'])
groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group',
@@ -215,17 +205,19 @@ def test(q, bus, conn, stream):
# At the end of a tragedy, everyone dies, so there's no need for this
# group.
- call_async(q, still_alive, 'Close')
- q.expect('dbus-signal', signal='Closed', path=still_alive.object_path)
- q.expect('dbus-return', method='Close')
-
- # Deleting a non-empty group is not allowed.
- call_async(q, capulets, 'Close')
- q.expect('dbus-error', method='Close', name=cs.NOT_AVAILABLE)
-
- # Neither is deleting a List channel.
- call_async(q, subscribe, 'Close')
- q.expect('dbus-error', method='Close', name=cs.NOT_IMPLEMENTED)
+ call_async(q, conn.ContactGroups, 'RemoveGroup', 'Still alive')
+ q.expect('dbus-signal', signal='GroupsRemoved', args=[['Still alive']])
+
+ # Deleting a non-empty group is allowed. (It removes everyone.)
+ call_async(q, conn.ContactGroups, 'RemoveGroup', 'Capulets')
+ q.expect_many(
+ EventPattern('dbus-signal', signal='GroupsRemoved',
+ args=[['Capulets']]),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ args=[[juliet], [default_group], []]),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ args=[[juliet], [], ['Capulets']]),
+ )
if __name__ == '__main__':
exec_test(test)
diff --git a/tests/twisted/roster/initial-roster.py b/tests/twisted/roster/initial-roster.py
index 0fedf63..7fa9c96 100644
--- a/tests/twisted/roster/initial-roster.py
+++ b/tests/twisted/roster/initial-roster.py
@@ -6,18 +6,10 @@ import dbus
from hazetest import exec_test, JabberXmlStream
from servicetest import (assertLength, EventPattern, wrap_channel,
- assertEquals, call_async)
+ assertEquals, call_async, assertSameSets)
import constants as cs
import ns
-# TODO: this needs to be ported to Connection.ContactList
-raise SystemExit(77)
-
-class RosterXmlStream(JabberXmlStream):
- def add_roster_observer(self):
- # don't wait for the roster IQ before continuing into the test
- pass
-
def test(q, bus, conn, stream):
conn.Connect()
@@ -50,97 +42,69 @@ def test(q, bus, conn, stream):
stream.send(event.stanza)
- q.expect('dbus-signal', signal='StatusChanged',
- args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
-
- # Amy, Bob and Chris are all stored on our server-side roster
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'stored',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- stored = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- jids = set(conn.InspectHandles(cs.HT_CONTACT, stored.Group.GetMembers()))
- assertEquals(set(['amy@foo.com', 'bob@foo.com', 'chris@foo.com']), jids)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'subscribe',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- subscribe = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- jids = set(conn.InspectHandles(cs.HT_CONTACT, subscribe.Group.GetMembers()))
- # everyone on our roster is (falsely!) alleged to be on 'subscribe'
- # (in fact this ought to be just Amy and Chris, but libpurple apparently
- # can't represent this)
- assertEquals(set(['amy@foo.com', 'bob@foo.com', 'chris@foo.com']), jids)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'publish',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- publish = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- jids = set(conn.InspectHandles(cs.HT_CONTACT, publish.Group.GetMembers()))
- # the publish list is somewhat imaginary because libpurple doesn't have
- # state-recovery
- assertEquals(set(), jids)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_GROUP,
- cs.TARGET_ID: '3 letter names',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- group_chan = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- jids = set(conn.InspectHandles(cs.HT_CONTACT,
- group_chan.Group.GetMembers()))
- assertEquals(set(['amy@foo.com', 'bob@foo.com']), jids)
+ _, s, _ = q.expect_many(
+ EventPattern('dbus-signal', signal='StatusChanged',
+ args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]),
+ EventPattern('dbus-signal', signal='ContactsChanged',
+ interface=cs.CONN_IFACE_CONTACT_LIST, path=conn.object_path),
+ EventPattern('dbus-signal', signal='ContactListStateChanged',
+ args=[cs.CONTACT_LIST_STATE_SUCCESS]),
+ )
+
+ amy, bob, chris = conn.get_contact_handles_sync(
+ ['amy@foo.com', 'bob@foo.com', 'chris@foo.com'])
+
+ # Amy, Bob and Chris are all stored on our server-side roster.
+ #
+ # Everyone on our roster is (falsely!) alleged to have subscribe=YES
+ # (in fact this ought to be just Amy and Chris, because we're publishing
+ # presence to Bob without being subscribed to his presence, but libpurple
+ # apparently can't represent this).
+ #
+ # The publish value is unknown, because libpurple doesn't have
+ # state-recovery.
+ assertEquals([{
+ amy: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_UNKNOWN, ''),
+ bob: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_UNKNOWN, ''),
+ chris: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_UNKNOWN, ''),
+ },
+ {
+ amy: 'amy@foo.com',
+ bob: 'bob@foo.com',
+ chris: 'chris@foo.com',
+ },
+ {}], s.args)
# the XMPP prpl puts people into some sort of group, probably called
# Buddies
- channels = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'Channels')
+ groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups')
default_group = None
- default_props = None
-
- for path, props in channels:
- if props.get(cs.CHANNEL_TYPE) != cs.CHANNEL_TYPE_CONTACT_LIST:
- continue
- if props.get(cs.TARGET_HANDLE_TYPE) != cs.HT_GROUP:
- continue
-
- if path == group_chan.object_path:
+ for group in groups:
+ if group == '3 letter names':
continue
if default_group is not None:
raise AssertionError('Two unexplained groups: %s, %s' %
- (path, default_group.object_path))
-
- default_group = wrap_channel(bus.get_object(conn.bus_name, path),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- default_props = props
-
- jids = set(conn.InspectHandles(cs.HT_CONTACT,
- default_group.Group.GetMembers()))
- assertEquals(set(['chris@foo.com']), jids)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_GROUP,
- cs.TARGET_ID: default_props[cs.TARGET_ID],
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- assertEquals(False, e.value[0])
- assertEquals(default_group.object_path, e.value[1])
- assertEquals(default_props, e.value[2])
+ (group, default_group))
+
+ default_group = group
+
+ call_async(q, conn.ContactList, 'GetContactListAttributes',
+ [cs.CONN_IFACE_CONTACT_GROUPS])
+ r = q.expect('dbus-return', method='GetContactListAttributes')
+
+ assertEquals(cs.SUBSCRIPTION_STATE_YES, r.value[0][amy][cs.ATTR_SUBSCRIBE])
+ assertEquals(cs.SUBSCRIPTION_STATE_YES, r.value[0][bob][cs.ATTR_SUBSCRIBE])
+ assertEquals(cs.SUBSCRIPTION_STATE_YES, r.value[0][chris][cs.ATTR_SUBSCRIBE])
+
+ assertEquals(cs.SUBSCRIPTION_STATE_UNKNOWN, r.value[0][amy][cs.ATTR_PUBLISH])
+ assertEquals(cs.SUBSCRIPTION_STATE_UNKNOWN, r.value[0][bob][cs.ATTR_PUBLISH])
+ assertEquals(cs.SUBSCRIPTION_STATE_UNKNOWN, r.value[0][chris][cs.ATTR_PUBLISH])
+
+ assertSameSets(['3 letter names'], r.value[0][amy][cs.ATTR_GROUPS])
+ assertSameSets(['3 letter names'], r.value[0][bob][cs.ATTR_GROUPS])
+ assertSameSets([default_group], r.value[0][chris][cs.ATTR_GROUPS])
if __name__ == '__main__':
- exec_test(test, protocol=RosterXmlStream)
+ exec_test(test, protocol=JabberXmlStream, do_connect=False)
diff --git a/tests/twisted/roster/publish.py b/tests/twisted/roster/publish.py
index 69e400f..3b83e18 100644
--- a/tests/twisted/roster/publish.py
+++ b/tests/twisted/roster/publish.py
@@ -8,8 +8,8 @@ from twisted.words.protocols.jabber.client import IQ
from twisted.words.xish import domish
from servicetest import (EventPattern, wrap_channel, assertLength,
- assertEquals, call_async, sync_dbus)
-from hazetest import acknowledge_iq, exec_test, sync_stream, close_all_groups
+ assertEquals, call_async, sync_dbus, assertSameSets)
+from hazetest import acknowledge_iq, exec_test, sync_stream
import constants as cs
import ns
@@ -17,49 +17,13 @@ import ns
raise SystemExit(77)
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged',
- args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
-
- # Close all Group channels to get a clean slate, so we can rely on
- # the NewChannels signal for the default group later
- close_all_groups(q, bus, conn, stream)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'publish',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- publish = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- jids = set(conn.InspectHandles(cs.HT_CONTACT, publish.Group.GetMembers()))
- assertEquals(set(), jids)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'stored',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- stored = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- jids = set(conn.InspectHandles(cs.HT_CONTACT, stored.Group.GetMembers()))
- assertEquals(set(), jids)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'subscribe',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- subscribe = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- jids = set(conn.InspectHandles(cs.HT_CONTACT, subscribe.Group.GetMembers()))
- assertEquals(set(), jids)
+ call_async(q, conn.ContactList, 'GetContactListAttributes',
+ [cs.CONN_IFACE_CONTACT_GROUPS], False)
+ r = q.expect('dbus-return', method='GetContactListAttributes')
+ assertLength(0, r.value[0].keys())
# receive a subscription request
- alice = conn.RequestHandles(cs.HT_CONTACT, ['alice@wonderland.lit'])[0]
+ alice = conn.get_contact_handle_sync('alice@wonderland.lit')
presence = domish.Element(('jabber:client', 'presence'))
presence['from'] = 'alice@wonderland.lit'
@@ -69,31 +33,31 @@ def test(q, bus, conn, stream):
# it seems either libpurple or haze doesn't pass the message through
q.expect_many(
- EventPattern('dbus-signal', path=publish.object_path,
- args=['', [], [], [alice], [], alice,
- cs.GC_REASON_NONE]),
- # In the Conn.I.ContactList world, 'stored' has been
- # re-purposed to mean "we have some reason to care", so she
- # appears here even though she's not on the server-side roster
- # just yet
- EventPattern('dbus-signal', signal='MembersChanged',
- path=stored.object_path,
- args=['', [alice], [], [], [], 0, cs.GC_REASON_NONE]),
+ EventPattern('dbus-signal', signal='ContactsChanged',
+ args=[{
+ alice:
+ (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK,
+ ''),
+ },
+ {alice: 'alice@wonderland.lit'}, {}]),
)
- self_handle = conn.GetSelfHandle()
+ self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
# accept
- call_async(q, publish.Group, 'AddMembers', [alice], '')
+ call_async(q, conn.ContactList, 'AuthorizePublication', [alice])
q.expect_many(
EventPattern('stream-presence', presence_type='subscribed',
to='alice@wonderland.lit'),
- EventPattern('dbus-signal', signal='MembersChanged',
- path=publish.object_path,
- args=['', [alice], [], [], [], self_handle,
- cs.GC_REASON_NONE]),
- EventPattern('dbus-return', method='AddMembers'),
+ EventPattern('dbus-signal', signal='ContactsChanged',
+ args=[{
+ alice:
+ (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_YES,
+ ''),
+ },
+ {alice: 'alice@wonderland.lit'}, {}]),
+ EventPattern('dbus-return', method='AuthorizePublication'),
)
# the server sends us a roster push
@@ -106,33 +70,30 @@ def test(q, bus, conn, stream):
stream.send(iq)
- _, _, new_group = q.expect_many(
+ q.expect_many(
EventPattern('stream-iq', iq_type='result',
predicate=lambda e: e.stanza['id'] == 'roster-push'),
# She's not really on our subscribe list, but this is the closest
# we can guess from libpurple
- # FIXME: TpBaseContactList assumes she's the actor - she must have
- # accepted our request, right? Not actually true in libpurple.
- EventPattern('dbus-signal', signal='MembersChanged',
- path=subscribe.object_path,
- args=['', [alice], [], [], [], alice, cs.GC_REASON_NONE]),
+ EventPattern('dbus-signal', signal='ContactsChanged',
+ args=[{
+ alice:
+ (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES,
+ ''),
+ },
+ {alice: 'alice@wonderland.lit'}, {}]),
# the buddy needs a group, because libpurple
- EventPattern('dbus-signal', signal='NewChannels',
- predicate=lambda e:
- e.args[0][0][1].get(cs.CHANNEL_TYPE) ==
- cs.CHANNEL_TYPE_CONTACT_LIST and
- e.args[0][0][1].get(cs.TARGET_HANDLE_TYPE) ==
- cs.HT_GROUP),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ predicate=lambda e: e.args[0] == [alice]),
)
- def_group = wrap_channel(bus.get_object(conn.bus_name,
- new_group.args[0][0][0]), cs.CHANNEL_TYPE_CONTACT_LIST)
-
- assertEquals(set([alice]), set(def_group.Group.GetMembers()))
+ call_async(q, conn.ContactList, 'GetContactListAttributes',
+ [cs.CONN_IFACE_CONTACT_GROUPS], False)
+ r = q.expect('dbus-return', method='GetContactListAttributes')
+ assertSameSets([alice], r.value[0].keys())
# receive another subscription request
- queen = conn.RequestHandles(cs.HT_CONTACT,
- ['queen.of.hearts@wonderland.lit'])[0]
+ queen = conn.get_contact_handle_sync('queen.of.hearts@wonderland.lit')
presence = domish.Element(('jabber:client', 'presence'))
presence['from'] = 'queen.of.hearts@wonderland.lit'
@@ -141,27 +102,45 @@ def test(q, bus, conn, stream):
stream.send(presence)
# it seems either libpurple or haze doesn't pass the message through
- q.expect('dbus-signal', path=publish.object_path,
- args=['', [], [], [queen], [], queen,
- cs.GC_REASON_NONE])
+ q.expect_many(
+ EventPattern('dbus-signal', signal='ContactsChanged',
+ args=[{
+ queen:
+ (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK,
+ ''),
+ },
+ {queen: 'queen.of.hearts@wonderland.lit'}, {}]),
+ )
+
+ # the contact is temporarily on our roster
+ call_async(q, conn.ContactList, 'GetContactListAttributes',
+ [cs.CONN_IFACE_CONTACT_GROUPS], False)
+ r = q.expect('dbus-return', method='GetContactListAttributes')
+ assertSameSets([alice, queen], r.value[0].keys())
# decline
- call_async(q, publish.Group, 'RemoveMembers', [queen], '')
+ call_async(q, conn.ContactList, 'RemoveContacts', [queen])
q.expect_many(
EventPattern('stream-presence', presence_type='unsubscribed',
to='queen.of.hearts@wonderland.lit'),
- EventPattern('dbus-signal', signal='MembersChanged',
- path=publish.object_path,
- args=['', [], [queen], [], [], 0, cs.GC_REASON_NONE]),
- EventPattern('dbus-return', method='RemoveMembers'),
+ EventPattern('dbus-signal', signal='ContactsChanged',
+ args=[{
+ queen:
+ (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_NO,
+ ''),
+ }, {queen: 'queen.of.hearts@wonderland.lit'}, {}]),
+ EventPattern('dbus-return', method='RemoveContacts'),
)
sync_dbus(bus, q, conn)
sync_stream(q, stream)
- # the declined contact isn't on our roster
- assertEquals(set([alice]), set(def_group.Group.GetMembers()))
+ # the declined contact isn't on our roster any more
+ call_async(q, conn.ContactList, 'GetContactListAttributes',
+ [cs.CONN_IFACE_CONTACT_GROUPS], False)
+ r = q.expect('dbus-return', method='GetContactListAttributes')
+ assertSameSets([alice], r.value[0].keys())
# she's persistent
presence = domish.Element(('jabber:client', 'presence'))
@@ -169,9 +148,17 @@ def test(q, bus, conn, stream):
presence['type'] = 'subscribe'
presence.addElement('status', content='How dare you?')
stream.send(presence)
- q.expect('dbus-signal', path=publish.object_path,
- args=['', [], [], [queen], [], queen,
- cs.GC_REASON_NONE])
+
+ q.expect_many(
+ EventPattern('dbus-signal', signal='ContactsChanged',
+ args=[{
+ queen:
+ (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK,
+ ''),
+ },
+ {queen: 'queen.of.hearts@wonderland.lit'}, {}]),
+ )
+
# disconnect with the request outstanding, to make sure we don't crash
conn.Disconnect()
q.expect('dbus-signal', signal='StatusChanged',
diff --git a/tests/twisted/roster/removed-from-rp-subscribe.py b/tests/twisted/roster/removed-from-rp-subscribe.py
index 339db05..e48ae41 100644
--- a/tests/twisted/roster/removed-from-rp-subscribe.py
+++ b/tests/twisted/roster/removed-from-rp-subscribe.py
@@ -16,38 +16,7 @@ raise SystemExit(77)
jid = 'marco@barisione.lit'
def test(q, bus, conn, stream, remove, local):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged',
- args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'subscribe',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- subscribe = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'stored',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- stored = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'publish',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- publish = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
-
- h = conn.RequestHandles(cs.HT_CONTACT, [jid])[0]
+ h = conn.get_contact_handle_sync(jid)
# Another client logged into our account (Gajim, say) wants to subscribe to
# Marco's presence. First, per RFC 3921 it 'SHOULD perform a "roster set"
@@ -71,12 +40,13 @@ def test(q, bus, conn, stream, remove, local):
# In response, Haze adds Marco to the roster, which we guess (wrongly,
# in this case) also means subscribe
- q.expect_many(
- EventPattern('dbus-signal', signal='MembersChanged',
- args=['', [h], [], [], [], h, 0], path=subscribe.object_path),
- EventPattern('dbus-signal', signal='MembersChanged',
- args=['', [h], [], [], [], 0, 0], path=stored.object_path),
- )
+ q.expect('dbus-signal', signal='ContactsChanged',
+ args=[{
+ h:
+ (cs.SUBSCRIPTION_STATE_YES,
+ cs.SUBSCRIPTION_STATE_UNKNOWN, ''),
+ },
+ {h: jid}, {}])
# Gajim sends a <presence type='subscribe'/> to Marco. 'As a result, the
# user's server MUST initiate a second roster push to all of the user's
@@ -97,8 +67,8 @@ def test(q, bus, conn, stream, remove, local):
if remove:
# ...removes him from the roster...
if local:
- # ...by telling Haze to remove him from stored
- stored.Group.RemoveMembers([h], '')
+ # ...by telling Haze to remove him from the roster
+ conn.ContactList.RemoveContacts([h])
event = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER)
item = event.query.firstChildElement()
@@ -122,15 +92,9 @@ def test(q, bus, conn, stream, remove, local):
stream.send(iq)
# In response, Haze should announce that Marco has been removed from
- # subscribe:remote-pending and stored:members
- q.expect_many(
- EventPattern('dbus-signal', signal='MembersChanged',
- args=['', [], [h], [], [], 0, 0],
- path=subscribe.object_path),
- EventPattern('dbus-signal', signal='MembersChanged',
- args=['', [], [h], [], [], 0, 0],
- path=stored.object_path),
- )
+ # the roster
+ q.expect('dbus-signal', signal='ContactsChanged',
+ args=[{}, {}, {h: jid}])
else:
# ...rescinds the subscription request...
if local:
diff --git a/tests/twisted/roster/subscribe.py b/tests/twisted/roster/subscribe.py
index 539feef..ebd325b 100644
--- a/tests/twisted/roster/subscribe.py
+++ b/tests/twisted/roster/subscribe.py
@@ -8,7 +8,7 @@ from twisted.words.xish import domish
from servicetest import (EventPattern, wrap_channel, assertLength,
assertEquals, call_async, sync_dbus)
-from hazetest import acknowledge_iq, exec_test, sync_stream, close_all_groups
+from hazetest import acknowledge_iq, exec_test, sync_stream
import constants as cs
import ns
@@ -16,55 +16,46 @@ import ns
raise SystemExit(77)
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged',
- args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
- self_handle = conn.GetSelfHandle()
-
- # Close all Group channels to get a clean slate, so we can rely on
- # the NewChannels signal for the default group later
- close_all_groups(q, bus, conn, stream)
-
- call_async(q, conn.Requests, 'EnsureChannel',{
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST,
- cs.TARGET_HANDLE_TYPE: cs.HT_LIST,
- cs.TARGET_ID: 'subscribe',
- })
- e = q.expect('dbus-return', method='EnsureChannel')
- subscribe = wrap_channel(bus.get_object(conn.bus_name, e.value[1]),
- cs.CHANNEL_TYPE_CONTACT_LIST)
- jids = set(conn.InspectHandles(cs.HT_CONTACT, subscribe.Group.GetMembers()))
- assertEquals(set(), jids)
-
- assertLength(0, subscribe.Group.GetMembers())
+ self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
+
+ call_async(q, conn.ContactList, 'GetContactListAttributes',
+ [cs.CONN_IFACE_CONTACT_GROUPS], False)
+ r = q.expect('dbus-return', method='GetContactListAttributes')
+ assertLength(0, r.value[0].keys())
# request subscription
- handle = conn.RequestHandles(cs.HT_CONTACT, ['suggs@night.boat.cairo'])[0]
- call_async(q, subscribe.Group, 'AddMembers', [handle], '')
+ handle = conn.get_contact_handle_sync('suggs@night.boat.cairo')
+ call_async(q, conn.ContactList, 'RequestSubscription', [handle],
+ 'half past monsoon')
# libpurple puts him on our blist as soon as we've asked; there doesn't
# seem to be any concept of remote-pending state.
#
# It also puts him in the default group, probably "Buddies".
- set_iq, _, _, _, new_channels = q.expect_many(
+ set_iq, _, _, _, groups_changed = q.expect_many(
EventPattern('stream-iq', iq_type='set',
query_ns=ns.ROSTER, query_name='query'),
EventPattern('stream-presence', presence_type='subscribe',
to='suggs@night.boat.cairo'),
- EventPattern('dbus-return', method='AddMembers', value=()),
- # FIXME: TpBaseContactList wrongly assumes that he's the actor,
- # because he must have accepted our request... right? Wrong.
- EventPattern('dbus-signal', signal='MembersChanged',
- path=subscribe.object_path,
- args=['', [handle], [], [], [], handle, 0]),
- EventPattern('dbus-signal', signal='NewChannels',
- predicate=lambda e:
- e.args[0][0][1].get(cs.TARGET_HANDLE_TYPE) == cs.HT_GROUP),
+ EventPattern('dbus-return', method='RequestSubscription', value=()),
+ EventPattern('dbus-signal', signal='ContactsChanged',
+ args=[{
+ handle:
+ (cs.SUBSCRIPTION_STATE_YES,
+ cs.SUBSCRIPTION_STATE_UNKNOWN, ''),
+ },
+ {handle: 'suggs@night.boat.cairo'}, {}]),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ predicate=lambda e: e.args[0] == [handle]),
)
assertEquals('suggs@night.boat.cairo', set_iq.query.item['jid'])
acknowledge_iq(stream, set_iq.stanza)
+ assertLength(1, groups_changed.args[1])
+ assertLength(0, groups_changed.args[2])
+ def_group = groups_changed.args[1][0]
+
# Suggs accepts our subscription request
presence = domish.Element(('jabber:client', 'presence'))
presence['from'] = 'suggs@night.boat.cairo'
@@ -74,15 +65,10 @@ def test(q, bus, conn, stream):
# ... but nothing much happens, because there's no concept of pending
# state in libpurple
- def_group = wrap_channel(bus.get_object(conn.bus_name,
- new_channels.args[0][0][0]), cs.CHANNEL_TYPE_CONTACT_LIST)
- handles = set(subscribe.Group.GetMembers())
- assertEquals(set([handle]), handles)
-
# put a contact into the *group* explicitly: this shouldn't ask for
- # subscription, but it does
- handle = conn.RequestHandles(cs.HT_CONTACT, ['ayria@revenge.world'])[0]
- call_async(q, def_group.Group, 'AddMembers', [handle], '')
+ # subscription, but it does, because libpurple
+ handle = conn.get_contact_handle_sync('ayria@revenge.world')
+ call_async(q, conn.ContactGroups, 'AddToGroup', def_group, [handle])
# libpurple puts her on our blist as soon as we've asked; there doesn't
# seem to be any concept of remote-pending state. It also puts her in the
@@ -92,13 +78,16 @@ def test(q, bus, conn, stream):
query_ns=ns.ROSTER, query_name='query'),
EventPattern('stream-presence', presence_type='subscribe',
to='ayria@revenge.world'),
- EventPattern('dbus-return', method='AddMembers', value=()),
- EventPattern('dbus-signal', signal='MembersChanged',
- path=subscribe.object_path,
- args=['', [handle], [], [], [], handle, 0]),
- EventPattern('dbus-signal', signal='MembersChanged',
- path=def_group.object_path,
- args=['', [handle], [], [], [], self_handle, 0]),
+ EventPattern('dbus-return', method='AddToGroup', value=()),
+ EventPattern('dbus-signal', signal='ContactsChanged',
+ args=[{
+ handle:
+ (cs.SUBSCRIPTION_STATE_YES,
+ cs.SUBSCRIPTION_STATE_UNKNOWN, ''),
+ },
+ {handle: 'ayria@revenge.world'}, {}]),
+ EventPattern('dbus-signal', signal='GroupsChanged',
+ args=[[handle], [def_group], []]),
)
acknowledge_iq(stream, set_iq.stanza)
diff --git a/tests/twisted/run-test.sh.in b/tests/twisted/run-test.sh.in
new file mode 100644
index 0000000..11bbaf9
--- /dev/null
+++ b/tests/twisted/run-test.sh.in
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+if test "x$HAZE_TEST_UNINSTALLED" = x; then
+ script_fullname=`readlink -e "@hazetestsdir@/twisted/run-test.sh"`
+ if [ `readlink -e "$0"` != "$script_fullname" ] ; then
+ echo "This script is meant to be installed at $script_fullname" >&2
+ exit 1
+ fi
+
+ test_src="@hazetestsdir@"
+ test_build="@hazetestsdir@"
+ config_file="@hazetestsdir@/twisted/tools/servicedir/tmp-session-bus.conf"
+
+ PYTHONPATH="@hazetestsdir@/twisted"
+ export PYTHONPATH
+
+ HAZE_TWISTED_PATH="@hazetestsdir@/twisted"
+ export HAZE_TWISTED_PATH
+else
+ if test -z "$HAZE_ABS_TOP_SRCDIR"; then
+ echo "HAZE_ABS_TOP_SRCDIR must be set" >&2
+ exit 1
+ fi
+ if test -z "$HAZE_ABS_TOP_BUILDDIR"; then
+ echo "HAZE_ABS_TOP_BUILDDIR must be set" >&2
+ exit 1
+ fi
+
+ test_src="${HAZE_ABS_TOP_SRCDIR}/tests"
+ test_build="${HAZE_ABS_TOP_BUILDDIR}/tests"
+ config_file="${test_build}/twisted/tools/tmp-session-bus.conf"
+
+ PYTHONPATH="${test_src}/twisted:${test_build}/twisted"
+ export PYTHONPATH
+
+ HAZE_TWISTED_PATH="${test_src}/twisted"
+ export HAZE_TWISTED_PATH
+fi
+
+if [ -n "$1" ] ; then
+ list="$1"
+else
+ list=$(cat "${test_build}"/twisted/haze-twisted-tests.list)
+fi
+
+any_failed=0
+for i in $list ; do
+ echo "Testing $i ..."
+ sh "${test_src}/twisted/tools/with-session-bus.sh" \
+ ${HAZE_TEST_SLEEP} \
+ --config-file="${config_file}" \
+ -- \
+ @TEST_PYTHON@ -u "${test_src}/twisted/$i"
+ e=$?
+ case "$e" in
+ (0)
+ echo "PASS: $i"
+ ;;
+ (77)
+ echo "SKIP: $i"
+ ;;
+ (*)
+ any_failed=1
+ echo "FAIL: $i ($e)"
+ ;;
+ esac
+done
+
+exit $any_failed
diff --git a/tests/twisted/sasl/close.py b/tests/twisted/sasl/close.py
new file mode 100644
index 0000000..a7d26f8
--- /dev/null
+++ b/tests/twisted/sasl/close.py
@@ -0,0 +1,43 @@
+"""Test the SASL channel being undispatchable."""
+
+import dbus
+
+from servicetest import EventPattern
+from hazetest import exec_test, assertEquals
+import constants as cs
+from saslutil import connect_and_get_sasl_channel
+
+JID = 'weaver@crobuzon.fic'
+
+def test_no_password(q, bus, conn, stream):
+ chan, props = connect_and_get_sasl_channel(q, bus, conn)
+
+ chan.Close()
+
+ _, _, status_changed = q.expect_many(
+ EventPattern('dbus-signal', path=chan.object_path,
+ signal='Closed'),
+ EventPattern('dbus-signal', path=conn.object_path,
+ signal='ChannelClosed', args=[chan.object_path]),
+ # Unhelpfully prpl-jabber just sets the account to disabled so we
+ # don't get an error.
+ # EventPattern('dbus-signal', path=conn.object_path,
+ # signal='ConnectionError',
+ # predicate=lambda e: e.args[0] == cs.AUTHENTICATION_FAILED),
+ EventPattern('dbus-signal', path=conn.object_path,
+ signal='StatusChanged'),
+ )
+
+ status, reason = status_changed.args
+ assertEquals(cs.CONN_STATUS_DISCONNECTED, status)
+ # We would like to have
+ # assertEquals(cs.CSR_AUTHENTICATION_FAILED, reason)
+
+ # prpl-sipe does actually report a connection error rather
+ # than just disabling the account, so yay. prpl-silc sets
+ # PURPLE_CONNECTION_ERROR_OTHER_ERROR, which also comes out as
+ # AUTHENTICATION_FAILED. No other prpls use
+ # purple_account_request_password().
+
+if __name__ == '__main__':
+ exec_test(test_no_password, {'password': None,'account' : JID}, do_connect=False)
diff --git a/tests/twisted/sasl/saslutil.py b/tests/twisted/sasl/saslutil.py
new file mode 100644
index 0000000..83afe2b
--- /dev/null
+++ b/tests/twisted/sasl/saslutil.py
@@ -0,0 +1,130 @@
+# hey, Python: encoding: utf-8
+from hazetest import XmppAuthenticator
+from base64 import b64decode, b64encode
+from twisted.words.xish import domish
+import constants as cs
+import ns
+from servicetest import (ProxyWrapper, EventPattern, assertEquals,
+ assertLength, Event)
+
+class SaslChannelWrapper(ProxyWrapper):
+ def __init__(self, object, default=cs.CHANNEL, interfaces={
+ "ServerAuthentication" : cs.CHANNEL_TYPE_SERVER_AUTHENTICATION,
+ "SASLAuthentication" : cs.CHANNEL_IFACE_SASL_AUTH}):
+ ProxyWrapper.__init__(self, object, default, interfaces)
+
+class SaslEventAuthenticator(XmppAuthenticator):
+ def __init__(self, jid, mechanisms):
+ XmppAuthenticator.__init__(self, jid, '')
+ self._mechanisms = mechanisms
+
+ def streamSASL(self):
+ XmppAuthenticator.streamSASL(self)
+
+ self.xmlstream.addObserver("/response", self._response)
+ self.xmlstream.addObserver("/abort", self._abort)
+
+ def failure(self, fail_str):
+ reply = domish.Element((ns.NS_XMPP_SASL, 'failure'))
+ reply.addElement(fail_str)
+ self.xmlstream.send(reply)
+ self.xmlstream.reset()
+
+ def abort(self):
+ self.failure('abort')
+
+ def not_authorized(self):
+ self.failure('not-authorized')
+
+ def success(self, data=None):
+ reply = domish.Element((ns.NS_XMPP_SASL, 'success'))
+
+ if data is not None:
+ reply.addContent(b64encode(data))
+
+ self.xmlstream.send(reply)
+ self.authenticated=True
+ self.xmlstream.reset()
+
+ def challenge(self, data):
+ reply = domish.Element((ns.NS_XMPP_SASL, 'challenge'))
+ reply.addContent(b64encode(data))
+ self.xmlstream.send(reply)
+
+ def auth(self, auth):
+ # Special case in XMPP: '=' means a zero-byte blob, whereas an empty
+ # or self-terminating XML element means no initial response.
+ # (RFC 3920 §6.2 (3))
+ if str(auth) == '':
+ self._event_func(Event('sasl-auth', authenticator=self,
+ has_initial_response=False,
+ initial_response=None,
+ xml=auth))
+ elif str(auth) == '=':
+ self._event_func(Event('sasl-auth', authenticator=self,
+ has_initial_response=False,
+ initial_response=None,
+ xml=auth))
+ else:
+ self._event_func(Event('sasl-auth', authenticator=self,
+ has_initial_response=True,
+ initial_response=b64decode(str(auth)),
+ xml=auth))
+
+ def _response(self, response):
+ self._event_func(Event('sasl-response', authenticator=self,
+ response=b64decode(str(response)),
+ xml=response))
+
+ def _abort(self, abort):
+ self._event_func(Event('sasl-abort', authenticator=self,
+ xml=abort))
+
+def connect_and_get_sasl_channel(q, bus, conn):
+ conn.Connect()
+
+ q.expect('dbus-signal', signal='StatusChanged',
+ args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED])
+
+ return expect_sasl_channel(q, bus, conn)
+
+def expect_sasl_channel(q, bus, conn):
+ signal, = q.expect_many(
+ EventPattern('dbus-signal', signal='NewChannels',
+ predicate=lambda e:
+ e.args[0][0][1].get(cs.CHANNEL_TYPE) ==
+ cs.CHANNEL_TYPE_SERVER_AUTHENTICATION),
+ )
+ path = signal.args[0][0][0]
+
+ chan = SaslChannelWrapper(bus.get_object(conn.bus_name, path))
+ assertLength(1, signal.args[0])
+ props = signal.args[0][0][1]
+
+ assertEquals(cs.CHANNEL_IFACE_SASL_AUTH, props.get(cs.AUTH_METHOD))
+ return chan, props
+
+def abort_auth(q, chan, reason, message):
+ reason_err_map = {
+ cs.SASL_ABORT_REASON_USER_ABORT : cs.CANCELLED,
+ cs.SASL_ABORT_REASON_INVALID_CHALLENGE : cs.SERVICE_CONFUSED }
+
+ mapped_error = reason_err_map.get(reason, cs.CANCELLED)
+
+ chan.SASLAuthentication.AbortSASL(reason, message)
+
+ ssc, ce, _ = q.expect_many(
+ EventPattern(
+ 'dbus-signal', signal='SASLStatusChanged',
+ interface=cs.CHANNEL_IFACE_SASL_AUTH,
+ predicate=lambda e: e.args[0] == cs.SASL_STATUS_CLIENT_FAILED),
+ EventPattern('dbus-signal', signal='ConnectionError'),
+ EventPattern(
+ 'dbus-signal', signal="StatusChanged",
+ args=[cs.CONN_STATUS_DISCONNECTED,
+ cs.CSR_AUTHENTICATION_FAILED]))
+
+ assertEquals(cs.SASL_STATUS_CLIENT_FAILED, ssc.args[0])
+ assertEquals(mapped_error, ssc.args[1])
+ assertEquals(message, ssc.args[2].get('debug-message')),
+ assertEquals(mapped_error, ce.args[0])
diff --git a/tests/twisted/sasl/telepathy-password.py b/tests/twisted/sasl/telepathy-password.py
new file mode 100644
index 0000000..f95a3d0
--- /dev/null
+++ b/tests/twisted/sasl/telepathy-password.py
@@ -0,0 +1,53 @@
+"""
+Test the server sasl channel with the X-TELEPATHY-PASSWORD mechanism.
+"""
+
+from servicetest import call_async, EventPattern
+from hazetest import exec_test
+import constants as cs
+from saslutil import connect_and_get_sasl_channel
+
+PASSWORD = "pass"
+
+def test_close_straight_after_accept(q, bus, conn, stream):
+ chan, props = connect_and_get_sasl_channel(q, bus, conn)
+
+ call_async(q, chan.SASLAuthentication, 'StartMechanismWithData',
+ 'X-TELEPATHY-PASSWORD', PASSWORD)
+
+ # In_Progress appears before StartMechanismWithData returns
+ q.expect('dbus-signal', signal='SASLStatusChanged',
+ interface=cs.CHANNEL_IFACE_SASL_AUTH,
+ args=[cs.SASL_STATUS_IN_PROGRESS, '', {}])
+
+ # Different order to Gabble
+ q.expect_many(
+ EventPattern('dbus-return', method='StartMechanismWithData'),
+ EventPattern('dbus-signal', signal='SASLStatusChanged',
+ interface=cs.CHANNEL_IFACE_SASL_AUTH,
+ args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]),
+ )
+
+ # fd.o#32278:
+ # When this was breaking, gabble received AcceptSASL and told the
+ # success_async GAsyncResult to complete in an idle. But, before
+ # the result got its callback called, Close was also received and
+ # the auth manager cleared its channel. When the idle function was
+ # finally reached it saw it no longer had a channel (it had been
+ # cleared in the closed callback) and thought it should be
+ # chaining up to the wocky auth registry but of course it should
+ # be calling the channel finish function.
+ call_async(q, chan.SASLAuthentication, 'AcceptSASL')
+ call_async(q, chan, 'Close')
+
+ q.expect('dbus-signal', signal='SASLStatusChanged',
+ interface=cs.CHANNEL_IFACE_SASL_AUTH,
+ args=[cs.SASL_STATUS_SUCCEEDED, '', {}])
+
+ e = q.expect('dbus-signal', signal='StatusChanged',
+ args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
+
+if __name__ == '__main__':
+ exec_test(test_close_straight_after_accept,
+ {'password': None, 'account' : "test@example.org/Resource"},
+ do_connect=False)
diff --git a/tests/twisted/servicetest.py b/tests/twisted/servicetest.py
index 1b8e073..fcba708 100644
--- a/tests/twisted/servicetest.py
+++ b/tests/twisted/servicetest.py
@@ -8,29 +8,54 @@ from twisted.internet.protocol import Protocol, Factory, ClientFactory
glib2reactor.install()
import sys
import time
+import os
import pprint
import unittest
-import dbus.glib
+import dbus
+from dbus.mainloop.glib import DBusGMainLoop
+DBusGMainLoop(set_as_default=True)
from twisted.internet import reactor
import constants as cs
-tp_name_prefix = 'im.telepathy1'
-tp_path_prefix = '/im/telepathy1'
+tp_name_prefix = cs.PREFIX
+tp_path_prefix = '/' + cs.PREFIX.replace('.', '/')
-class Event:
+class DictionarySupersetOf (object):
+ """Utility class for expecting "a dictionary with at least these keys"."""
+ def __init__(self, dictionary):
+ self._dictionary = dictionary
+ def __repr__(self):
+ return "DictionarySupersetOf(%s)" % self._dictionary
+ def __eq__(self, other):
+ """would like to just do:
+ return set(other.items()).issuperset(self._dictionary.items())
+ but it turns out that this doesn't work if you have another dict
+ nested in the values of your dicts"""
+ try:
+ for k,v in self._dictionary.items():
+ if k not in other or other[k] != v:
+ return False
+ return True
+ except TypeError: # other is not iterable
+ return False
+
+class Event(object):
def __init__(self, type, **kw):
self.__dict__.update(kw)
self.type = type
(self.subqueue, self.subtype) = type.split ("-", 1)
+ def __str__(self):
+ return '\n'.join([ str(type(self)) ] + format_event(self))
+
def format_event(event):
ret = ['- type %s' % event.type]
- for key in dir(event):
+ for key in sorted(dir(event)):
if key != 'type' and not key.startswith('_'):
ret.append('- %s: %s' % (
key, pprint.pformat(getattr(event, key))))
@@ -79,6 +104,14 @@ class EventPattern:
class TimeoutError(Exception):
pass
+class ForbiddenEventOccurred(Exception):
+ def __init__(self, event):
+ Exception.__init__(self)
+ self.event = event
+
+ def __str__(self):
+ return '\n' + '\n'.join(format_event(self.event))
+
class BaseEventQueue:
"""Abstract event queue base class.
@@ -124,13 +157,16 @@ class BaseEventQueue:
"""
self.forbidden_events.difference_update(set(patterns))
+ def unforbid_all(self):
+ """
+ Remove all patterns from the set of forbidden events.
+ """
+ self.forbidden_events.clear()
+
def _check_forbidden(self, event):
for e in self.forbidden_events:
if e.match(event):
- print "forbidden event occurred:"
- for x in format_event(event):
- print x
- assert False
+ raise ForbiddenEventOccurred(event)
def expect(self, type, **kw):
"""
@@ -390,16 +426,21 @@ def call_async(test, proxy, method, *args, **kw):
method_proxy(*args, **kw)
def sync_dbus(bus, q, conn):
- # Dummy D-Bus method call
+ # Dummy D-Bus method call. We can't use DBus.Peer.Ping() because libdbus
+ # replies to that message immediately, rather than handing it up to
+ # dbus-glib and thence Gabble, which means that Ping()ing Gabble doesn't
+ # ensure that it's processed all D-Bus messages prior to our ping.
+ #
# This won't do the right thing unless the proxy has a unique name.
assert conn.object.bus_name.startswith(':')
- root_object = bus.get_object(conn.object.bus_name, '/')
- call_async(
- q, dbus.Interface(root_object, 'org.freedesktop.DBus.Peer'), 'Ping')
- q.expect('dbus-return', method='Ping')
+ root_object = bus.get_object(conn.object.bus_name, '/', introspect=False)
+ call_async(q,
+ dbus.Interface(root_object, cs.PREFIX + '.Tests'),
+ 'DummySyncDBus')
+ q.expect('dbus-error', method='DummySyncDBus')
class ProxyWrapper:
- def __init__(self, object, default, others):
+ def __init__(self, object, default, others={}):
self.object = object
self.default_interface = dbus.Interface(object, default)
self.Properties = dbus.Interface(object, dbus.PROPERTIES_IFACE)
@@ -418,12 +459,29 @@ class ProxyWrapper:
return getattr(self.default_interface, name)
+class ConnWrapper(ProxyWrapper):
+ def inspect_contact_sync(self, handle):
+ return self.inspect_contacts_sync([handle])[0]
+
+ def inspect_contacts_sync(self, handles):
+ h2asv = self.Contacts.GetContactAttributes(handles, [], True)
+ ret = []
+ for h in handles:
+ ret.append(h2asv[h][cs.ATTR_CONTACT_ID])
+ return ret
+
+ def get_contact_handle_sync(self, identifier):
+ return self.Contacts.GetContactByID(identifier, [])[0]
+
+ def get_contact_handles_sync(self, ids):
+ return [self.get_contact_handle_sync(i) for i in ids]
+
def wrap_connection(conn):
- return ProxyWrapper(conn, tp_name_prefix + '.Connection',
+ return ConnWrapper(conn, tp_name_prefix + '.Connection',
dict([
(name, tp_name_prefix + '.Connection.Interface.' + name)
for name in ['Aliasing', 'Avatars', 'Capabilities', 'Contacts',
- 'Presence', 'SimplePresence', 'Requests']] +
+ 'SimplePresence', 'Requests']] +
[('Peer', 'org.freedesktop.DBus.Peer'),
('ContactCapabilities', cs.CONN_IFACE_CONTACT_CAPS),
('ContactInfo', cs.CONN_IFACE_CONTACT_INFO),
@@ -433,6 +491,7 @@ def wrap_connection(conn):
('ContactList', cs.CONN_IFACE_CONTACT_LIST),
('ContactGroups', cs.CONN_IFACE_CONTACT_GROUPS),
('PowerSaving', cs.CONN_IFACE_POWER_SAVING),
+ ('Addressing', cs.CONN_IFACE_ADDRESSING),
]))
def wrap_channel(chan, type_, extra=None):
@@ -448,14 +507,26 @@ def wrap_channel(chan, type_, extra=None):
return ProxyWrapper(chan, tp_name_prefix + '.Channel', interfaces)
+
+def wrap_content(chan, extra=None):
+ interfaces = { }
+
+ if extra:
+ interfaces.update(dict([
+ (name, tp_name_prefix + '.Call1.Content.Interface.' + name)
+ for name in extra]))
+
+ return ProxyWrapper(chan, tp_name_prefix + '.Call1.Content', interfaces)
+
def make_connection(bus, event_func, name, proto, params):
cm = bus.get_object(
tp_name_prefix + '.ConnectionManager.%s' % name,
- tp_path_prefix + '/ConnectionManager/%s' % name)
+ tp_path_prefix + '/ConnectionManager/%s' % name,
+ introspect=False)
cm_iface = dbus.Interface(cm, tp_name_prefix + '.ConnectionManager')
connection_name, connection_path = cm_iface.RequestConnection(
- proto, params)
+ proto, dbus.Dictionary(params, signature='sv'))
conn = wrap_connection(bus.get_object(connection_name, connection_path))
return conn
@@ -568,6 +639,12 @@ def assertFlagsUnset(flags, value):
"expected none of flags %u, but %u are set in %u" % (
flags, masked, value))
+def assertDBusError(name, error):
+ if error.get_dbus_name() != name:
+ raise AssertionError(
+ "expected DBus error named:\n %s\ngot:\n %s\n(with message: %s)"
+ % (name, error.get_dbus_name(), error.message))
+
def install_colourer():
def red(s):
return '\x1b[31m%s\x1b[0m' % s
@@ -596,6 +673,16 @@ def install_colourer():
sys.stdout = Colourer(sys.stdout, patterns)
return sys.stdout
-if __name__ == '__main__':
- unittest.main()
+# this is just to shut up unittest.
+class DummyStream(object):
+ def write(self, s):
+ if 'CHECK_TWISTED_VERBOSE' in os.environ:
+ print s,
+
+ def flush(self):
+ pass
+if __name__ == '__main__':
+ stream = DummyStream()
+ runner = unittest.TextTestRunner(stream=stream)
+ unittest.main(testRunner=runner)
diff --git a/tests/twisted/simple-caps.py b/tests/twisted/simple-caps.py
index e563003..d1657b9 100644
--- a/tests/twisted/simple-caps.py
+++ b/tests/twisted/simple-caps.py
@@ -6,11 +6,9 @@ Make sure ContactCaps works well enough.
from twisted.words.xish import domish
from servicetest import assertEquals, assertContains, EventPattern
-from hazetest import exec_test, sync_stream
+from hazetest import exec_test, sync_stream, JabberXmlStream
import constants as cs
-import config
-
import ns
# assert this list of RCCs is only text
@@ -20,12 +18,8 @@ def check_text_only(rccs):
cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT
}, [cs.TARGET_HANDLE])], rccs)
-# assert GetContactCaps and GetContactAttributes returns just text caps
+# assert just text caps
def check_rccs(conn, handle):
- rccs = conn.ContactCapabilities.GetContactCapabilities([handle])
- assertEquals(1, len(rccs))
- check_text_only(rccs[handle])
-
attrs = conn.Contacts.GetContactAttributes([handle],
[cs.CONN_IFACE_CONTACT_CAPS])
rccs = attrs[handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities']
@@ -33,10 +27,6 @@ def check_rccs(conn, handle):
# do the self handle which will just be text
def test_self_handle(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged',
- args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
-
self_handle = conn.Properties.Get(cs.CONN, 'SelfHandle')
check_rccs(conn, self_handle)
@@ -62,43 +52,13 @@ def test_someone_else(q, bus, conn, stream):
q.expect('dbus-signal', signal='StatusChanged',
args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
- bob_handle = conn.RequestHandles(cs.HT_CONTACT, ['bob@foo.com'])[0]
+ bob_handle = conn.get_contact_handle_sync('bob@foo.com')
check_rccs(conn, bob_handle)
# now a randomer who isn't even in our contact list
- amy_handle = conn.RequestHandles(cs.HT_CONTACT, ['amy@foo.com'])[0]
+ amy_handle = conn.get_contact_handle_sync('amy@foo.com')
check_rccs(conn, amy_handle)
-def test_media(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged',
- args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
-
- sync_stream(q, stream)
-
- conn.ContactCapabilities.UpdateCapabilities([(
- 'im.telepathy1.Client.Foobar',
- [{ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA,
- cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
- cs.INITIAL_AUDIO: True },
- { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA,
- cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
- cs.INITIAL_VIDEO: True }],
- [],
- )])
-
- q.expect('stream-presence') # can't be bothered checking this
-
- conn.ContactCapabilities.UpdateCapabilities([(
- 'im.telepathy1.Client.Foobar',
- [], [])])
-
- q.expect('stream-presence') # can't be bothered checking this
-
if __name__ == '__main__':
exec_test(test_self_handle)
- exec_test(test_someone_else)
-
- if config.MEDIA_ENABLED:
- exec_test(test_media)
-
+ exec_test(test_someone_else, do_connect=False, protocol=JabberXmlStream)
diff --git a/tests/twisted/text/destroy.py b/tests/twisted/text/destroy.py
index 022f136..c6edcff 100644
--- a/tests/twisted/text/destroy.py
+++ b/tests/twisted/text/destroy.py
@@ -8,62 +8,45 @@ import dbus
from twisted.words.xish import domish
from hazetest import exec_test
-from servicetest import call_async, EventPattern, assertEquals
+from servicetest import call_async, EventPattern, assertEquals, assertLength
import constants as cs
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
-
- self_handle = conn.Properties.Get(cs.CONN, 'SelfHandle')
+ self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
jid = 'foo@bar.com'
- call_async(q, conn, 'RequestHandles', 1, [jid])
-
- event = q.expect('dbus-return', method='RequestHandles')
- foo_handle = event.value[0][0]
+ foo_handle = conn.get_contact_handle_sync(jid)
call_async(q, conn.Requests, 'CreateChannel',
- {
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
- cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
- cs.TARGET_HANDLE: foo_handle
+ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.TARGET_HANDLE: foo_handle,
})
- ret, new_sig = q.expect_many(
+ ret, sig = q.expect_many(
EventPattern('dbus-return', method='CreateChannel'),
EventPattern('dbus-signal', signal='NewChannels'),
)
text_chan = bus.get_object(conn.bus_name, ret.value[0])
- chan_iface = dbus.Interface(text_chan,
- 'im.telepathy1.Channel')
- text_iface = dbus.Interface(text_chan,
- 'im.telepathy1.Channel.Type.Text')
- destroyable_iface = dbus.Interface(text_chan,
- 'im.telepathy1.Channel.Interface.Destroyable1')
-
- assert len(new_sig.args) == 1
- assert len(new_sig.args[0]) == 1 # one channel
- assert len(new_sig.args[0][0]) == 2 # two struct members
- assert new_sig.args[0][0][0] == ret.value[0]
- emitted_props = new_sig.args[0][0][1]
- assert emitted_props['im.telepathy1.Channel.ChannelType'] ==\
- 'im.telepathy1.Channel.Type.Text'
- assert emitted_props['im.telepathy1.Channel.'
- 'TargetHandleType'] == 1
- assert emitted_props['im.telepathy1.Channel.TargetHandle'] ==\
- foo_handle
- assert emitted_props['im.telepathy1.Channel.TargetID'] == jid
- assert emitted_props['im.telepathy1.Channel.'
- 'Requested'] == True
- assert emitted_props['im.telepathy1.Channel.'
- 'InitiatorHandle'] == self_handle
- assert emitted_props['im.telepathy1.Channel.'
- 'InitiatorID'] == 'test@localhost'
-
- channel_props = text_chan.GetAll(
- 'im.telepathy1.Channel',
+ chan_iface = dbus.Interface(text_chan, cs.CHANNEL)
+ text_iface = dbus.Interface(text_chan, cs.CHANNEL_TYPE_TEXT)
+ destroyable_iface = dbus.Interface(text_chan, cs.CHANNEL_IFACE_DESTROYABLE)
+
+ assertLength(1, sig.args)
+ assertLength(1, sig.args[0]) # one channel
+ assertLength(2, sig.args[0][0]) # two struct members
+ assertEquals(ret.value, sig.args[0][0])
+ emitted_props = sig.args[0][0][1]
+ assertEquals(cs.CHANNEL_TYPE_TEXT, emitted_props[cs.CHANNEL_TYPE])
+ assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE])
+ assertEquals(foo_handle, emitted_props[cs.TARGET_HANDLE])
+ assertEquals(jid, emitted_props[cs.TARGET_ID])
+ assertEquals(True, emitted_props[cs.REQUESTED])
+ assertEquals(self_handle, emitted_props[cs.INITIATOR_HANDLE])
+ assertEquals('test@localhost', emitted_props[cs.INITIATOR_ID])
+
+ channel_props = text_chan.GetAll(cs.CHANNEL,
dbus_interface=dbus.PROPERTIES_IFACE)
assert channel_props['TargetID'] == jid,\
(channel_props['TargetID'], jid)
@@ -73,14 +56,10 @@ def test(q, bus, conn, stream):
assert channel_props['InitiatorID'] == 'test@localhost',\
channel_props['InitiatorID']
- hey = [
- dbus.Dictionary({ 'message-type': cs.MT_NORMAL,
- }, signature='sv'),
- { 'content-type': 'text/plain',
- 'content': u"hey",
- }
- ]
- text_iface.SendMessage(hey, 0)
+ text_iface.SendMessage([{}, {
+ 'content-type': 'text/plain',
+ 'content': 'hey',
+ }], 0)
event = q.expect('stream-message')
@@ -105,19 +84,19 @@ def test(q, bus, conn, stream):
event = q.expect('dbus-signal', signal='MessageReceived')
- assertEquals(2, len(event.args[0]))
- header, body = event.args[0]
- hello_message_id = header['pending-message-id']
- hello_message_time = header['message-received'],
- assert header['message-sender'] == foo_handle
- # message type: normal
- assert header['message-type'] == cs.MT_NORMAL
- # body
- assert body['content'] == 'hello'
+ message = event.args[0]
+ assertLength(2, message)
+ hello_message_id = message[0]['pending-message-id']
+ assertEquals(foo_handle, message[0]['message-sender'])
+ assertEquals('foo@bar.com', message[0]['message-sender-id'])
+ assertEquals(cs.MT_NORMAL,
+ message[0].get('message-type', cs.MT_NORMAL))
+ assertEquals('text/plain', message[1]['content-type'])
+ assertEquals('hello', message[1]['content'])
messages = text_chan.Get(cs.CHANNEL_TYPE_TEXT, 'PendingMessages',
- dbus_interface=cs.PROPERTIES_IFACE)
- assert messages == [[header, body]], messages
+ dbus_interface=cs.PROPERTIES_IFACE)
+ assertEquals([message], messages)
# destroy the channel without acking the message; it does not come back
diff --git a/tests/twisted/text/ensure.py b/tests/twisted/text/ensure.py
index dcf413e..6896408 100644
--- a/tests/twisted/text/ensure.py
+++ b/tests/twisted/text/ensure.py
@@ -7,26 +7,20 @@ import dbus
from twisted.words.xish import domish
from hazetest import exec_test
-from servicetest import call_async, EventPattern, unwrap
+from servicetest import (call_async, EventPattern, unwrap, assertEquals,
+ assertLength, assertContains)
import pprint
import constants as cs
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
-
- self_handle = conn.Properties.Get(cs.CONN, 'SelfHandle')
+ self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
jids = ['foo@bar.com', 'truc@cafe.fr']
- call_async(q, conn, 'RequestHandles', 1, jids)
-
- event = q.expect('dbus-return', method='RequestHandles')
- handles = event.value[0]
+ handles = conn.get_contact_handles_sync(jids)
- properties = conn.GetAll(
- 'im.telepathy1.Connection.Interface.Requests',
+ properties = conn.GetAll(cs.CONN_IFACE_REQUESTS,
dbus_interface=dbus.PROPERTIES_IFACE)
# Difference from Gabble: Haze's roster channels spring to life even if you
# haven't received the XMPP roster.
@@ -34,13 +28,10 @@ def test(q, bus, conn, stream):
if c[1][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT
]
assert text_channels == [], text_channels
- assert ({'im.telepathy1.Channel.ChannelType':
- 'im.telepathy1.Channel.Type.Text',
- 'im.telepathy1.Channel.TargetHandleType': 1,
+ assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
},
- ['im.telepathy1.Channel.TargetHandle',
- 'im.telepathy1.Channel.TargetID'
- ],
+ [cs.TARGET_HANDLE, cs.TARGET_ID],
) in properties.get('RequestableChannelClasses'),\
properties['RequestableChannelClasses']
@@ -60,7 +51,7 @@ def test_ensure_ensure(q, conn, self_handle, jid, handle):
# Check that Ensuring a channel that doesn't exist succeeds
call_async(q, conn.Requests, 'EnsureChannel', request_props (handle))
- ret, new_sig = q.expect_many(
+ ret, sig = q.expect_many(
EventPattern('dbus-return', method='EnsureChannel'),
EventPattern('dbus-signal', signal='NewChannels'),
)
@@ -74,19 +65,16 @@ def test_ensure_ensure(q, conn, self_handle, jid, handle):
check_props(emitted_props, self_handle, handle, jid)
- assert len(new_sig.args) == 1
- assert len(new_sig.args[0]) == 1 # one channel
- assert len(new_sig.args[0][0]) == 2 # two struct members
- assert new_sig.args[0][0][0] == path
- assert new_sig.args[0][0][1] == emitted_props
+ assertLength(1, sig.args)
+ assertLength(1, sig.args[0]) # one channel
+ assertLength(2, sig.args[0][0]) # two struct members
+ assertEquals(path, sig.args[0][0][0])
+ assertEquals(emitted_props, sig.args[0][0][1])
- properties = conn.GetAll(
- 'im.telepathy1.Connection.Interface.Requests',
+ properties = conn.GetAll(cs.CONN_IFACE_REQUESTS,
dbus_interface=dbus.PROPERTIES_IFACE)
- assert new_sig.args[0][0] in properties['Channels'], \
- (new_sig.args[0][0], properties['Channels'])
-
+ assertContains(sig.args[0][0], properties['Channels'])
# Now try Ensuring a channel which already exists
call_async(q, conn.Requests, 'EnsureChannel', request_props (handle))
@@ -110,7 +98,7 @@ def test_request_ensure(q, conn, self_handle, jid, handle):
call_async(q, conn.Requests, 'CreateChannel', request_props (handle))
- ret, new_sig = q.expect_many(
+ ret, sig = q.expect_many(
EventPattern('dbus-return', method='CreateChannel'),
EventPattern('dbus-signal', signal='NewChannels'),
)
@@ -120,19 +108,16 @@ def test_request_ensure(q, conn, self_handle, jid, handle):
check_props(emitted_props, self_handle, handle, jid)
- assert len(new_sig.args) == 1
- assert len(new_sig.args[0]) == 1 # one channel
- assert len(new_sig.args[0][0]) == 2 # two struct members
- assert new_sig.args[0][0][0] == path
- assert new_sig.args[0][0][1] == emitted_props
+ assertLength(1, sig.args)
+ assertLength(1, sig.args[0]) # one channel
+ assertLength(2, sig.args[0][0]) # two struct members
+ assertEquals(path, sig.args[0][0][0])
+ assertEquals(emitted_props, sig.args[0][0][1])
- properties = conn.GetAll(
- 'im.telepathy1.Connection.Interface.Requests',
+ properties = conn.GetAll(cs.CONN_IFACE_REQUESTS,
dbus_interface=dbus.PROPERTIES_IFACE)
- assert new_sig.args[0][0] in properties['Channels'], \
- (new_sig.args[0][0], properties['Channels'])
-
+ assertContains(sig.args[0][0], properties['Channels'])
# Now try Ensuring that same channel.
call_async(q, conn.Requests, 'EnsureChannel', request_props (handle))
@@ -149,26 +134,19 @@ def test_request_ensure(q, conn, self_handle, jid, handle):
def check_props(props, self_handle, handle, jid):
- assert props['im.telepathy1.Channel.ChannelType'] ==\
- 'im.telepathy1.Channel.Type.Text'
- assert props['im.telepathy1.Channel.'
- 'TargetHandleType'] == 1
- assert props['im.telepathy1.Channel.TargetHandle'] ==\
- handle
- assert props['im.telepathy1.Channel.TargetID'] == jid
- assert props['im.telepathy1.Channel.'
- 'Requested'] == True
- assert props['im.telepathy1.Channel.'
- 'InitiatorHandle'] == self_handle
- assert props['im.telepathy1.Channel.'
- 'InitiatorID'] == 'test@localhost'
+ assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE])
+ assertEquals(cs.HT_CONTACT, props[cs.TARGET_HANDLE_TYPE])
+ assertEquals(handle, props[cs.TARGET_HANDLE])
+ assertEquals(jid, props[cs.TARGET_ID])
+ assertEquals(True, props[cs.REQUESTED])
+ assertEquals(self_handle, props[cs.INITIATOR_HANDLE])
+ assertEquals('test@localhost', props[cs.INITIATOR_ID])
def request_props(handle):
- return { 'im.telepathy1.Channel.ChannelType':
- 'im.telepathy1.Channel.Type.Text',
- 'im.telepathy1.Channel.TargetHandleType': 1,
- 'im.telepathy1.Channel.TargetHandle': handle,
+ return { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.TARGET_HANDLE: handle,
}
diff --git a/tests/twisted/text/initiate-requestotron.py b/tests/twisted/text/initiate-requestotron.py
index 3679df0..c76dd36 100644
--- a/tests/twisted/text/initiate-requestotron.py
+++ b/tests/twisted/text/initiate-requestotron.py
@@ -7,24 +7,20 @@ import dbus
from twisted.words.xish import domish
from hazetest import exec_test
-from servicetest import call_async, EventPattern
+from servicetest import (call_async, EventPattern, unwrap, assertEquals,
+ assertLength, assertContains)
+
+import pprint
import constants as cs
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
-
- self_handle = conn.Properties.Get(cs.CONN, 'SelfHandle')
+ self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
jid = 'foo@bar.com'
- call_async(q, conn, 'RequestHandles', 1, [jid])
-
- event = q.expect('dbus-return', method='RequestHandles')
- foo_handle = event.value[0][0]
+ foo_handle = conn.get_contact_handle_sync(jid)
- properties = conn.GetAll(
- 'im.telepathy1.Connection.Interface.Requests',
+ properties = conn.GetAll(cs.CONN_IFACE_REQUESTS,
dbus_interface=dbus.PROPERTIES_IFACE)
# Difference from Gabble: Haze's roster channels spring to life even if you
# haven't received the XMPP roster.
@@ -32,56 +28,44 @@ def test(q, bus, conn, stream):
if c[1][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT
]
assert text_channels == [], text_channels
- assert ({'im.telepathy1.Channel.ChannelType':
- 'im.telepathy1.Channel.Type.Text',
- 'im.telepathy1.Channel.TargetHandleType': 1,
+ assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
},
- ['im.telepathy1.Channel.TargetHandle',
- 'im.telepathy1.Channel.TargetID'
- ],
+ [cs.TARGET_HANDLE, cs.TARGET_ID],
) in properties.get('RequestableChannelClasses'),\
properties['RequestableChannelClasses']
call_async(q, conn.Requests, 'CreateChannel',
- { 'im.telepathy1.Channel.ChannelType':
- 'im.telepathy1.Channel.Type.Text',
- 'im.telepathy1.Channel.TargetHandleType': 1,
- 'im.telepathy1.Channel.TargetHandle': foo_handle,
- })
+ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.TARGET_HANDLE: foo_handle,
+ })
- ret, new_sig = q.expect_many(
+ ret, sig = q.expect_many(
EventPattern('dbus-return', method='CreateChannel'),
EventPattern('dbus-signal', signal='NewChannels'),
)
assert len(ret.value) == 2
emitted_props = ret.value[1]
- assert emitted_props['im.telepathy1.Channel.ChannelType'] ==\
- 'im.telepathy1.Channel.Type.Text'
- assert emitted_props['im.telepathy1.Channel.'
- 'TargetHandleType'] == 1
- assert emitted_props['im.telepathy1.Channel.TargetHandle'] ==\
- foo_handle
- assert emitted_props['im.telepathy1.Channel.TargetID'] == jid
- assert emitted_props['im.telepathy1.Channel.'
- 'Requested'] == True
- assert emitted_props['im.telepathy1.Channel.'
- 'InitiatorHandle'] == self_handle
- assert emitted_props['im.telepathy1.Channel.'
- 'InitiatorID'] == 'test@localhost'
-
- assert len(new_sig.args) == 1
- assert len(new_sig.args[0]) == 1 # one channel
- assert len(new_sig.args[0][0]) == 2 # two struct members
- assert new_sig.args[0][0][0] == ret.value[0]
- assert new_sig.args[0][0][1] == ret.value[1]
-
- properties = conn.GetAll(
- 'im.telepathy1.Connection.Interface.Requests',
+ assertEquals(cs.CHANNEL_TYPE_TEXT, emitted_props[cs.CHANNEL_TYPE])
+ assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE])
+ assertEquals(foo_handle, emitted_props[cs.TARGET_HANDLE])
+ assertEquals(jid, emitted_props[cs.TARGET_ID])
+ assertEquals(True, emitted_props[cs.REQUESTED])
+ assertEquals(self_handle, emitted_props[cs.INITIATOR_HANDLE])
+ assertEquals('test@localhost', emitted_props[cs.INITIATOR_ID])
+
+ assertLength(1, sig.args)
+ assertLength(1, sig.args[0]) # one channel
+ assertLength(2, sig.args[0][0]) # two struct members
+ assertEquals(ret.value[0], sig.args[0][0][0])
+ assertEquals(ret.value[1], sig.args[0][0][1])
+
+ properties = conn.GetAll(cs.CONN_IFACE_REQUESTS,
dbus_interface=dbus.PROPERTIES_IFACE)
- assert new_sig.args[0][0] in properties['Channels'], \
- (new_sig.args[0][0], properties['Channels'])
+ assertContains(sig.args[0][0], properties['Channels'])
conn.Disconnect()
q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
diff --git a/tests/twisted/text/respawn.py b/tests/twisted/text/respawn.py
index 2afebe2..cdc9105 100644
--- a/tests/twisted/text/respawn.py
+++ b/tests/twisted/text/respawn.py
@@ -7,59 +7,44 @@ import dbus
from twisted.words.xish import domish
from hazetest import exec_test
-from servicetest import call_async, EventPattern, assertEquals
+from servicetest import call_async, EventPattern, assertEquals, assertLength
import constants as cs
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
-
- self_handle = conn.Properties.Get(cs.CONN, 'SelfHandle')
+ self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
jid = 'foo@bar.com'
- call_async(q, conn, 'RequestHandles', 1, [jid])
-
- event = q.expect('dbus-return', method='RequestHandles')
- foo_handle = event.value[0][0]
+ foo_handle = conn.get_contact_handle_sync(jid)
- call_async(q, conn.Requests, 'CreateChannel', {
- cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
- cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
- cs.TARGET_HANDLE: foo_handle
+ call_async(q, conn.Requests, 'CreateChannel',
+ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.TARGET_HANDLE: foo_handle,
})
- ret, new_sig = q.expect_many(
+ ret, sig = q.expect_many(
EventPattern('dbus-return', method='CreateChannel'),
EventPattern('dbus-signal', signal='NewChannels'),
)
text_chan = bus.get_object(conn.bus_name, ret.value[0])
- chan_iface = dbus.Interface(text_chan,
- 'im.telepathy1.Channel')
- text_iface = dbus.Interface(text_chan,
- 'im.telepathy1.Channel.Type.Text')
-
- assert len(new_sig.args) == 1
- assert len(new_sig.args[0]) == 1 # one channel
- assert len(new_sig.args[0][0]) == 2 # two struct members
- assert new_sig.args[0][0][0] == ret.value[0]
- emitted_props = new_sig.args[0][0][1]
- assert emitted_props['im.telepathy1.Channel.ChannelType'] ==\
- 'im.telepathy1.Channel.Type.Text'
- assert emitted_props['im.telepathy1.Channel.'
- 'TargetHandleType'] == 1
- assert emitted_props['im.telepathy1.Channel.TargetHandle'] ==\
- foo_handle
- assert emitted_props['im.telepathy1.Channel.TargetID'] == jid
- assert emitted_props['im.telepathy1.Channel.'
- 'Requested'] == True
- assert emitted_props['im.telepathy1.Channel.'
- 'InitiatorHandle'] == self_handle
- assert emitted_props['im.telepathy1.Channel.'
- 'InitiatorID'] == 'test@localhost'
-
- channel_props = text_chan.GetAll(
- 'im.telepathy1.Channel',
+ chan_iface = dbus.Interface(text_chan, cs.CHANNEL)
+ text_iface = dbus.Interface(text_chan, cs.CHANNEL_TYPE_TEXT)
+
+ assertLength(1, sig.args)
+ assertLength(1, sig.args[0]) # one channel
+ assertLength(2, sig.args[0][0]) # two struct members
+ assertEquals(ret.value, sig.args[0][0])
+ emitted_props = sig.args[0][0][1]
+ assertEquals(cs.CHANNEL_TYPE_TEXT, emitted_props[cs.CHANNEL_TYPE])
+ assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE])
+ assertEquals(foo_handle, emitted_props[cs.TARGET_HANDLE])
+ assertEquals(jid, emitted_props[cs.TARGET_ID])
+ assertEquals(True, emitted_props[cs.REQUESTED])
+ assertEquals(self_handle, emitted_props[cs.INITIATOR_HANDLE])
+ assertEquals('test@localhost', emitted_props[cs.INITIATOR_ID])
+
+ channel_props = text_chan.GetAll(cs.CHANNEL,
dbus_interface=dbus.PROPERTIES_IFACE)
assert channel_props['TargetID'] == jid,\
(channel_props['TargetID'], jid)
@@ -69,14 +54,10 @@ def test(q, bus, conn, stream):
assert channel_props['InitiatorID'] == 'test@localhost',\
channel_props['InitiatorID']
- hey = [
- dbus.Dictionary({ 'message-type': cs.MT_NORMAL,
- }, signature='sv'),
- { 'content-type': 'text/plain',
- 'content': u"hey",
- }
- ]
- text_iface.SendMessage(hey, 0)
+ text_iface.SendMessage([{}, {
+ 'content-type': 'text/plain',
+ 'content': 'hey',
+ }], 0)
event = q.expect('stream-message')
@@ -101,19 +82,19 @@ def test(q, bus, conn, stream):
event = q.expect('dbus-signal', signal='MessageReceived')
- assertEquals(2, len(event.args[0]))
- header, body = event.args[0]
- hello_message_id = header['pending-message-id']
- hello_message_time = header['message-received'],
- assert header['message-sender'] == foo_handle
- # message type: normal
- assert header['message-type'] == cs.MT_NORMAL
- # body
- assert body['content'] == 'hello'
+ message = event.args[0]
+ assertLength(2, message)
+ hello_message_id = message[0]['pending-message-id']
+ assertEquals(foo_handle, message[0]['message-sender'])
+ assertEquals('foo@bar.com', message[0]['message-sender-id'])
+ assertEquals(cs.MT_NORMAL,
+ message[0].get('message-type', cs.MT_NORMAL))
+ assertEquals('text/plain', message[1]['content-type'])
+ assertEquals('hello', message[1]['content'])
messages = text_chan.Get(cs.CHANNEL_TYPE_TEXT, 'PendingMessages',
- dbus_interface=cs.PROPERTIES_IFACE)
- assert messages == [[header, body]], messages
+ dbus_interface=cs.PROPERTIES_IFACE)
+ assertEquals([message], messages)
# close the channel without acking the message; it comes back
@@ -126,20 +107,21 @@ def test(q, bus, conn, stream):
assertEquals(text_chan.object_path, old.path)
assertEquals(text_chan.object_path, new.args[0])
+ # it now behaves as if the message had initiated it
+ new_props = {}
+ for k in emitted_props:
+ new_props[k] = emitted_props[k]
+ new_props[cs.INITIATOR_HANDLE] = foo_handle
+ new_props[cs.INITIATOR_ID] = 'foo@bar.com'
+ new_props[cs.REQUESTED] = False
+
event = q.expect('dbus-signal', signal='NewChannels')
- assertEquals(1, len(event.args[0]))
- path, props = event.args[0][0]
- assert path == text_chan.object_path
- assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT
- assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT
- assert props[cs.TARGET_HANDLE] == foo_handle
+ assertEquals(text_chan.object_path, event.args[0][0][0])
+ assertEquals(new_props, event.args[0][0][1])
event = q.expect('dbus-return', method='Close')
- # it now behaves as if the message had initiated it
-
- channel_props = text_chan.GetAll(
- 'im.telepathy1.Channel',
+ channel_props = text_chan.GetAll(cs.CHANNEL,
dbus_interface=dbus.PROPERTIES_IFACE)
assert channel_props['TargetID'] == jid,\
(channel_props['TargetID'], jid)
@@ -149,21 +131,21 @@ def test(q, bus, conn, stream):
assert channel_props['InitiatorID'] == 'foo@bar.com',\
channel_props['InitiatorID']
- # the message is still there
+ # the message is still there, but is marked as rescued now
+ message[0]['rescued'] = True
- header['rescued'] = True
messages = text_chan.Get(cs.CHANNEL_TYPE_TEXT, 'PendingMessages',
- dbus_interface=cs.PROPERTIES_IFACE)
- assert messages == [[header, body]], messages
+ dbus_interface=cs.PROPERTIES_IFACE)
+ assertEquals([message], messages)
# acknowledge it
text_chan.AcknowledgePendingMessages([hello_message_id],
- dbus_interface='im.telepathy1.Channel.Type.Text')
+ dbus_interface=cs.CHANNEL_TYPE_TEXT)
messages = text_chan.Get(cs.CHANNEL_TYPE_TEXT, 'PendingMessages',
- dbus_interface=cs.PROPERTIES_IFACE)
- assert messages == []
+ dbus_interface=cs.PROPERTIES_IFACE)
+ assertEquals([], messages)
# close the channel again
diff --git a/tests/twisted/text/test-text-delayed.py b/tests/twisted/text/test-text-delayed.py
index 295e01c..59e73d0 100644
--- a/tests/twisted/text/test-text-delayed.py
+++ b/tests/twisted/text/test-text-delayed.py
@@ -12,9 +12,6 @@ from servicetest import EventPattern, assertEquals
import constants as cs
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
-
m = domish.Element((None, 'message'))
m['from'] = 'foo@bar.com'
m['type'] = 'chat'
@@ -27,12 +24,9 @@ def test(q, bus, conn, stream):
stream.send(m)
event = q.expect('dbus-signal', signal='NewChannels')
- assertEquals(1, len(event.args[0]))
- path, props = event.args[0][0]
- assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT
- # check that handle type == contact handle
- assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT
- assert props[cs.TARGET_ID] == 'foo@bar.com'
+ assertEquals(cs.CHANNEL_TYPE_TEXT, event.args[0][0][1][cs.CHANNEL_TYPE])
+ assertEquals(cs.HT_CONTACT, event.args[0][0][1][cs.TARGET_HANDLE_TYPE])
+ assertEquals('foo@bar.com', event.args[0][0][1][cs.TARGET_ID])
message_received = q.expect('dbus-signal', signal='MessageReceived')
diff --git a/tests/twisted/text/test-text-no-body.py b/tests/twisted/text/test-text-no-body.py
index 0ce9c33..c8aacd7 100644
--- a/tests/twisted/text/test-text-no-body.py
+++ b/tests/twisted/text/test-text-no-body.py
@@ -6,16 +6,13 @@ new text channel.
from twisted.words.xish import domish
-from servicetest import assertEquals
from hazetest import exec_test
+from servicetest import assertEquals
import constants as cs
import ns
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
-
# message without body
m = domish.Element((None, 'message'))
m['from'] = 'alice@foo.com'
@@ -33,10 +30,10 @@ def test(q, bus, conn, stream):
# first message should be from Bob, not Alice
event = q.expect('dbus-signal', signal='NewChannels')
- assertEquals(1, len(event.args[0]))
- path, props = event.args[0][0]
- assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE])
- assertEquals('bob@foo.com', props[cs.TARGET_ID])
+ assertEquals(cs.CHANNEL_TYPE_TEXT, event.args[0][0][1][cs.CHANNEL_TYPE])
+ assertEquals(cs.HT_CONTACT, event.args[0][0][1][cs.TARGET_HANDLE_TYPE])
+ assertEquals('bob@foo.com', event.args[0][0][1][cs.TARGET_ID])
+
conn.Disconnect()
q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
diff --git a/tests/twisted/text/test-text.py b/tests/twisted/text/test-text.py
index e33d276..0a33755 100644
--- a/tests/twisted/text/test-text.py
+++ b/tests/twisted/text/test-text.py
@@ -8,12 +8,11 @@ import dbus
from twisted.words.xish import domish
from hazetest import exec_test
-from servicetest import EventPattern, assertEquals
+from servicetest import EventPattern, assertEquals, assertContains
import constants as cs
def test(q, bus, conn, stream):
- conn.Connect()
- q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
+ jid = 'foo@bar.com'
# <message type="chat"><body>hello</body</message>
m = domish.Element((None, 'message'))
@@ -23,41 +22,31 @@ def test(q, bus, conn, stream):
stream.send(m)
event = q.expect('dbus-signal', signal='NewChannels')
- assertEquals(1, len(event.args[0]))
- path, props = event.args[0][0]
- text_chan = bus.get_object(conn.bus_name, path)
- assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE])
- # check that handle type == contact handle
- assertEquals(cs.HT_CONTACT, props[cs.TARGET_HANDLE_TYPE])
- foo_at_bar_dot_com_handle = props[cs.TARGET_HANDLE]
- jid = conn.InspectHandles(1, [foo_at_bar_dot_com_handle])[0]
- assertEquals('foo@bar.com', jid)
- assertEquals(jid, props[cs.TARGET_ID])
+ assertEquals(cs.CHANNEL_TYPE_TEXT, event.args[0][0][1][cs.CHANNEL_TYPE])
+ assertEquals(cs.HT_CONTACT, event.args[0][0][1][cs.TARGET_HANDLE_TYPE])
+ assertEquals(jid, event.args[0][0][1][cs.TARGET_ID])
+ foo_at_bar_dot_com_handle = event.args[0][0][1][cs.TARGET_HANDLE]
+
+ text_chan = bus.get_object(conn.bus_name, event.args[0][0][0])
# Exercise basic Channel Properties from spec 0.17.7
- channel_props = text_chan.GetAll(
- 'im.telepathy1.Channel',
+ channel_props = text_chan.GetAll(cs.CHANNEL,
dbus_interface=dbus.PROPERTIES_IFACE)
- assert channel_props.get('TargetHandle') == props[cs.TARGET_HANDLE],\
- (channel_props.get('TargetHandle'), props[cs.TARGET_HANDLE])
- assert channel_props.get('TargetHandleType') == cs.HT_CONTACT,\
+ assertEquals(foo_at_bar_dot_com_handle, channel_props.get('TargetHandle'))
+ assert channel_props.get('TargetHandleType') == 1,\
channel_props.get('TargetHandleType')
- assert channel_props.get('ChannelType') == cs.CHANNEL_TYPE_TEXT,\
- channel_props.get('ChannelType')
- assert cs.CHANNEL_IFACE_CHAT_STATE in \
- channel_props.get('Interfaces', ()), \
- channel_props.get('Interfaces')
+ assertEquals(cs.CHANNEL_TYPE_TEXT, channel_props.get('ChannelType'))
+ assertContains(cs.CHANNEL_IFACE_CHAT_STATE,
+ channel_props.get('Interfaces', ()))
assert channel_props['TargetID'] == jid,\
(channel_props['TargetID'], jid)
assert channel_props['Requested'] == False
- assert channel_props['InitiatorHandle'] == props[cs.TARGET_HANDLE],\
- (channel_props['InitiatorHandle'], props[cs.TARGET_HANDLE])
+ assertEquals(foo_at_bar_dot_com_handle, channel_props['InitiatorHandle'])
assert channel_props['InitiatorID'] == jid,\
(channel_props['InitiatorID'], jid)
message_received = q.expect('dbus-signal', signal='MessageReceived')
- # Check that MessageReceived looks right.
message = message_received.args[0]
# message should have two parts: the header and one content part
@@ -76,8 +65,7 @@ def test(q, bus, conn, stream):
# PendingMessagesRemoved fires.
message_id = header['pending-message-id']
- dbus.Interface(text_chan,
- u'im.telepathy1.Channel.Type.Text'
+ dbus.Interface(text_chan, cs.CHANNEL_TYPE_TEXT
).AcknowledgePendingMessages([message_id])
removed = q.expect('dbus-signal', signal='PendingMessagesRemoved')
@@ -89,7 +77,7 @@ def test(q, bus, conn, stream):
# Send an action using the Messages API
# In Gabble, this is a Notice, but we don't support those.
greeting = [
- dbus.Dictionary({ 'message-type': 1, # Action
+ dbus.Dictionary({ 'message-type': cs.MT_ACTION,
}, signature='sv'),
{ 'content-type': 'text/plain',
'content': u"waves",
@@ -97,7 +85,7 @@ def test(q, bus, conn, stream):
]
dbus.Interface(text_chan, cs.CHANNEL_TYPE_TEXT
- ).SendMessage(greeting, dbus.UInt32(0))
+ ).SendMessage(greeting, dbus.UInt32(0))
stream_message, message_sent = q.expect_many(
EventPattern('stream-message'),
@@ -124,6 +112,40 @@ def test(q, bus, conn, stream):
assert body['content-type'] == 'text/plain', body
assert body['content'] == u'waves', body
+ # Send a message using Channel.Type.Text API
+ dbus.Interface(text_chan, cs.CHANNEL_TYPE_TEXT
+ ).SendMessage([{}, {
+ 'content-type': 'text/plain',
+ 'content': 'goodbye',
+ }], 0)
+
+ stream_message, message_sent = q.expect_many(
+ EventPattern('stream-message'),
+ EventPattern('dbus-signal', signal='MessageSent'),
+ )
+
+ elem = stream_message.stanza
+ assert elem.name == 'message'
+ assert elem['type'] == 'chat'
+
+ found = False
+ for e in elem.elements():
+ if e.name == 'body':
+ found = True
+ e.children[0] == u'goodbye'
+ break
+ assert found, elem.toXml()
+
+ sent_message = message_sent.args[0]
+ assert len(sent_message) == 2, sent_message
+ header = sent_message[0]
+ # the spec says that message-type "MAY be omitted for normal chat
+ # messages."
+ assert 'message-type' not in header or header['message-type'] == 0, header
+ body = sent_message[1]
+ assert body['content-type'] == 'text/plain', body
+ assert body['content'] == u'goodbye', body
+
conn.Disconnect()
q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
diff --git a/tests/twisted/tools/Makefile.am b/tests/twisted/tools/Makefile.am
new file mode 100644
index 0000000..c64e7c1
--- /dev/null
+++ b/tests/twisted/tools/Makefile.am
@@ -0,0 +1,31 @@
+%.conf: %.conf.in
+ $(AM_V_GEN)sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@
+
+# We don't use the full filename for the .in because > 99 character filenames
+# in tarballs are non-portable (and automake 1.8 doesn't let us build
+# non-archaic tarballs)
+im.telepathy1.ConnectionManager.%.service: %.service.in
+ $(AM_V_GEN)sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" \
+ -e "s|[@]abs_top_srcdir[@]|@abs_top_srcdir@|g" $< > $@
+
+# D-Bus service file for testing
+service_in_files = haze.service.in
+service_files = im.telepathy1.ConnectionManager.haze.service
+
+# D-Bus config file for testing
+conf_in_files = tmp-session-bus.conf.in
+conf_files = $(conf_in_files:.conf.in=.conf)
+
+BUILT_SOURCES = $(service_files) $(conf_files)
+
+EXTRA_DIST = \
+ $(service_in_files) \
+ $(conf_in_files) \
+ exec-with-log.sh \
+ with-session-bus.sh \
+ $(NULL)
+
+CLEANFILES = \
+ $(BUILT_SOURCES) \
+ haze-testing.log \
+ $(NULL)
diff --git a/tests/exec-with-log.sh b/tests/twisted/tools/exec-with-log.sh
index fe25b3c..e6e9379 100755
--- a/tests/exec-with-log.sh
+++ b/tests/twisted/tools/exec-with-log.sh
@@ -9,9 +9,20 @@ cd "${abs_top_builddir}/tests"
export LC_ALL=C
export HAZE_DEBUG=all
+G_MESSAGES_DEBUG=all
+export G_MESSAGES_DEBUG
ulimit -c unlimited
exec >> haze-testing.log 2>&1
+# Avoid using a non-trivial GSettings backend
+GSETTINGS_BACKEND=memory
+export GSETTINGS_BACKEND
+# Avoid libpurple doing "clever" things
+unset KDE_FULL_SESSION
+unset KDEDIR
+unset KDEDIRS
+unset GNOME_DESKTOP_SESSION_ID
+
if test -n "$HAZE_TEST_VALGRIND"; then
export G_DEBUG=${G_DEBUG:+"${G_DEBUG},"}gc-friendly
export G_SLICE=always-malloc
@@ -27,6 +38,8 @@ elif test -n "$HAZE_TEST_REFDBG"; then
if test -z "$HAZE_WRAPPER" ; then
HAZE_WRAPPER="refdbg"
fi
+elif test -n "$HAZE_TEST_BACKTRACE"; then
+ HAZE_WRAPPER="gdb -x ${abs_top_srcdir}/tools/run_and_bt.gdb"
fi
# not suitable for haze:
diff --git a/tests/twisted/tools/haze.service.in b/tests/twisted/tools/haze.service.in
new file mode 100644
index 0000000..8c3cea7
--- /dev/null
+++ b/tests/twisted/tools/haze.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=im.telepathy1.ConnectionManager.haze
+Exec=@abs_top_srcdir@/tests/twisted/tools/exec-with-log.sh @abs_top_srcdir@ @abs_top_builddir@
diff --git a/tests/tmp-session-bus.conf.in b/tests/twisted/tools/tmp-session-bus.conf.in
index c73caac..84d8d65 100644
--- a/tests/tmp-session-bus.conf.in
+++ b/tests/twisted/tools/tmp-session-bus.conf.in
@@ -10,7 +10,7 @@
<listen>unix:tmpdir=/tmp</listen>
- <servicedir>@abs_top_builddir@/tests</servicedir>
+ <servicedir>@abs_top_builddir@/tests/twisted/tools</servicedir>
<policy context="default">
<!-- Allow everything to be sent -->
diff --git a/tools/with-session-bus.sh b/tests/twisted/tools/with-session-bus.sh
index 063bd7e..0afa593 100644
--- a/tools/with-session-bus.sh
+++ b/tests/twisted/tools/with-session-bus.sh
@@ -59,7 +59,9 @@ cleanup ()
{
pid=`head -n1 $me-$$.pid`
if test -n "$pid" ; then
- echo "Killing temporary bus daemon: $pid" >&2
+ if [ -n "$VERBOSE_TESTS" ]; then
+ echo "Killing temporary bus daemon: $pid" >&2
+ fi
kill -INT "$pid"
fi
rm -f $me-$$.address
@@ -69,12 +71,22 @@ cleanup ()
trap cleanup INT HUP TERM
dbus-daemon $dbus_daemon_args
-{ echo -n "Temporary bus daemon is "; cat $me-$$.address; } >&2
-{ echo -n "Temporary bus daemon PID is "; head -n1 $me-$$.pid; } >&2
+if [ -n "$VERBOSE_TESTS" ]; then
+ { echo -n "Temporary bus daemon is "; cat $me-$$.address; } >&2
+ { echo -n "Temporary bus daemon PID is "; head -n1 $me-$$.pid; } >&2
+fi
e=0
+
+# These might be non-null when run from e.g. gnome-terminal 3.8, which uses
+# an activatable service for its windows; we don't want to inherit them either
+unset DBUS_STARTER_ADDRESS
+unset DBUS_STARTER_BUS_TYPE
+
DBUS_SESSION_BUS_ADDRESS="`cat $me-$$.address`"
export DBUS_SESSION_BUS_ADDRESS
+DBUS_SESSION_BUS_PID="`cat $me-$$.pid`"
+export DBUS_SESSION_BUS_PID
if [ -n "$WITH_SESSION_BUS_FORK_DBUS_MONITOR" ] ; then
echo -n "Forking dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT" >&2
diff --git a/tools/Makefile.am b/tools/Makefile.am
index ac54560..715d792 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,5 +1,4 @@
EXTRA_DIST = \
- with-session-bus.sh \
c-constants-gen.py \
doc-generator.xsl \
glib-ginterface-gen.py \
diff --git a/tools/glib-ginterface-gen.py b/tools/glib-ginterface-gen.py
index 13f7f69..6543146 100644
--- a/tools/glib-ginterface-gen.py
+++ b/tools/glib-ginterface-gen.py
@@ -701,7 +701,7 @@ class Generator(object):
self.h('#include <dbus/dbus-glib.h>')
if self.have_properties(nodes):
- self.h('#include <telepathy-glib/dbus-properties-mixin.h>')
+ self.h('#include <telepathy-glib/telepathy-glib.h>')
self.h('')
self.h('G_BEGIN_DECLS')
diff --git a/tools/run_and_bt.gdb b/tools/run_and_bt.gdb
new file mode 100644
index 0000000..201353f
--- /dev/null
+++ b/tools/run_and_bt.gdb
@@ -0,0 +1,7 @@
+run
+echo ---- [bt full] -------------------------------------------\n
+bt full
+echo -----[thread apply all bt full] --------------------------\n
+thread apply all bt full
+echo ----------------------------------------------------------\n
+quit
diff --git a/tools/telepathy.am b/tools/telepathy.am
index 9715026..2b6c430 100644
--- a/tools/telepathy.am
+++ b/tools/telepathy.am
@@ -3,7 +3,7 @@
dist-hook:
chmod u+w ${distdir}/ChangeLog
if test -d ${top_srcdir}/.git; then \
- git log --date=iso $(CHANGELOG_RANGE) > ${distdir}/ChangeLog; \
+ ( cd ${top_srcdir} && git log --date=iso $(CHANGELOG_RANGE) ) > ${distdir}/ChangeLog; \
fi
distcheck-hook:
@@ -26,11 +26,13 @@ _is-release-check:
exit 2; \
;; \
esac
- @if ! git diff --no-ext-diff --quiet --exit-code; then \
+ @cd ${top_srcdir} && \
+ if ! git diff --no-ext-diff --quiet --exit-code; then \
echo "Hey! Your tree is dirty! No release for you." >&2; \
exit 2; \
fi
- @if ! git diff --cached --no-ext-diff --quiet --exit-code; then \
+ @cd ${top_srcdir} && \
+ if ! git diff --cached --no-ext-diff --quiet --exit-code; then \
echo "Hey! You have changes staged! No release for you." >&2; \
exit 2; \
fi
@@ -38,9 +40,16 @@ _is-release-check:
%.tar.gz.asc: %.tar.gz
$(AM_V_GEN)gpg --detach-sign --armor $@
-@PACKAGE@-@VERSION@.tar.gz: _is-release-check check distcheck
+@PACKAGE@-@VERSION@.tar.gz:
+ $(MAKE) _is-release-check
+ $(MAKE) check
+ $(MAKE) distcheck
-maintainer-prepare-release: _is-release-check check distcheck release-mail
+maintainer-prepare-release:
+ $(MAKE) _is-release-check
+ $(MAKE) all
+ $(MAKE) distcheck
+ $(MAKE) release-mail
git tag -s @PACKAGE@-@VERSION@ -m @PACKAGE@' '@VERSION@
gpg --detach-sign --armor @PACKAGE@-@VERSION@.tar.gz
@@ -60,7 +69,9 @@ _maintainer-upload-release: _maintainer-upload-release-check
rsync -vzP @PACKAGE@-@VERSION@.tar.gz telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/releases/@PACKAGE@/@PACKAGE@-@VERSION@.tar.gz
rsync -vzP @PACKAGE@-@VERSION@.tar.gz.asc telepathy.freedesktop.org:/srv/telepathy.freedesktop.org/www/releases/@PACKAGE@/@PACKAGE@-@VERSION@.tar.gz.asc
-maintainer-make-release: maintainer-prepare-release maintainer-upload-release
+maintainer-make-release:
+ $(MAKE) maintainer-prepare-release
+ $(MAKE) maintainer-upload-release
@echo "Now:"
@echo " • bump the nano-version;"
@echo " • push the branch and tags upstream; and"