[Telepathy] tp-qt4 ContactCapabilities and RequestableChannelClasses API proposal
Simon McVittie
simon.mcvittie at collabora.co.uk
Thu Sep 17 07:18:14 PDT 2009
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Here are some sketches of how I think capabilities-discovery
(ContactCapabilities and RequestableChannelClasses) should look in
telepathy-qt4; see my contact-caps-sketch branch for a starting point for
implementation.
Thoughts?
Subject: [PATCH 1/2] Add ensureAudioCall(), ensureVideoCall() (bindings for the InitialAudio/InitialVideo properties)
- ---
TelepathyQt4/account.cpp | 134 +++++++++++++++++++++++++++++++++++++++++++++-
TelepathyQt4/account.h | 20 +++++++
2 files changed, 152 insertions(+), 2 deletions(-)
diff --git a/TelepathyQt4/account.cpp b/TelepathyQt4/account.cpp
index 2d82469..c8c166d 100644
- --- a/TelepathyQt4/account.cpp
+++ b/TelepathyQt4/account.cpp
@@ -828,11 +828,49 @@ PendingChannelRequest *Account::ensureMediaCall(
}
/**
- - * Start a request to ensure that a media channel with the given
+ * Start a request to ensure that an audio call with the given
+ * contact \a contactIdentifier exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * This will only work on relatively modern connection managers.
+ *
+ * \param contactIdentifier The identifier of the contact to call.
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureAudioCall(
+ const QString &contactIdentifier,
+ QDateTime userActionTime,
+ const QString &preferredHandler)
+{
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) Tp::HandleTypeContact);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitialAudio"),
+ true);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
+ contactIdentifier);
+ return new PendingChannelRequest(dbusConnection(), objectPath(),
+ request, userActionTime, preferredHandler, false, this);
+}
+
+/**
+ * Start a request to ensure that an audio call with the given
* contact \a contact exists, creating it if necessary.
*
* See ensureChannel() for more details.
*
+ * This will only work on relatively modern connection managers.
+ *
* \param contact The contact to call.
* \param userActionTime The time at which user action occurred, or QDateTime()
* if this channel request is for some reason not
@@ -843,8 +881,92 @@ PendingChannelRequest *Account::ensureMediaCall(
* indicate that any handler would be acceptable.
* \sa ensureChannel(), createChannel()
*/
- -PendingChannelRequest *Account::ensureMediaCall(
+PendingChannelRequest *Account::ensureAudioCall(
+ const ContactPtr &contact,
+ QDateTime userActionTime,
+ const QString &preferredHandler)
+{
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) Tp::HandleTypeContact);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitialAudio"),
+ true);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
+ contact ? contact->handle().at(0) : (uint) 0);
+ return new PendingChannelRequest(dbusConnection(), objectPath(),
+ request, userActionTime, preferredHandler, false, this);
+}
+
+/**
+ * Start a request to ensure that a video call with the given
+ * contact \a contactIdentifier exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * This will only work on relatively modern connection managers.
+ *
+ * \param contactIdentifier The identifier of the contact to call.
+ * \param withAudio true if both audio and video are required, false for a
+ * video-only call
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureAudioCall(
+ const QString &contactIdentifier,
+ bool withAudio,
+ QDateTime userActionTime,
+ const QString &preferredHandler)
+{
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) Tp::HandleTypeContact);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitialVideo"),
+ true);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
+ contactIdentifier);
+
+ if (withAudio) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitialAudio"),
+ true);
+ }
+
+ return new PendingChannelRequest(dbusConnection(), objectPath(),
+ request, userActionTime, preferredHandler, false, this);
+}
+
+/**
+ * Start a request to ensure that a video call with the given
+ * contact \a contact exists, creating it if necessary.
+ *
+ * See ensureChannel() for more details.
+ *
+ * This will only work on relatively modern connection managers.
+ *
+ * \param contact The contact to call.
+ * \param withAudio true if both audio and video are required, false for a
+ * video-only call
+ * \param userActionTime The time at which user action occurred, or QDateTime()
+ * if this channel request is for some reason not
+ * involving user action.
+ * \param preferredHandler Either the well-known bus name (starting with
+ * org.freedesktop.Telepathy.Client.) of the preferred
+ * handler for this channel, or an empty string to
+ * indicate that any handler would be acceptable.
+ * \sa ensureChannel(), createChannel()
+ */
+PendingChannelRequest *Account::ensureVideoCall(
const ContactPtr &contact,
+ bool withAudio,
QDateTime userActionTime,
const QString &preferredHandler)
{
@@ -853,8 +975,16 @@ PendingChannelRequest *Account::ensureMediaCall(
TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA);
request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
(uint) Tp::HandleTypeContact);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitialVideo"),
+ true);
request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
contact ? contact->handle().at(0) : (uint) 0);
+
+ if (withAudio) {
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".InitialAudio"),
+ true);
+ }
+
return new PendingChannelRequest(dbusConnection(), objectPath(),
request, userActionTime, preferredHandler, false, this);
}
diff --git a/TelepathyQt4/account.h b/TelepathyQt4/account.h
index 3630540..4debaa0 100644
- --- a/TelepathyQt4/account.h
+++ b/TelepathyQt4/account.h
@@ -159,6 +159,26 @@ public:
QDateTime userActionTime = QDateTime::currentDateTime(),
const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureAudioCall(
+ const QString &contactIdentifier,
+ QDateTime userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureAudioCall(
+ const ContactPtr &contact,
+ QDateTime userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+
+ PendingChannelRequest *ensureVideoCall(
+ const QString &contactIdentifier,
+ bool withAudio = true,
+ QDateTime userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+ PendingChannelRequest *ensureVideoCall(
+ const ContactPtr &contact,
+ bool withAudio = true,
+ QDateTime userActionTime = QDateTime::currentDateTime(),
+ const QString &preferredHandler = QString());
+
// advanced
PendingChannelRequest *createChannel(
const QVariantMap &requestedProperties,
Subject: [PATCH 2/2] Sketch an API for connection and contact capabilities, based on the Requests and ContactCapabilities D-Bus APIs
- ---
TelepathyQt4/ConnectionCapabilities | 13 +++
TelepathyQt4/ContactCapabilities | 13 +++
TelepathyQt4/capabilities.cpp | 184 +++++++++++++++++++++++++++++++++++
TelepathyQt4/capabilities.h | 111 +++++++++++++++++++++
TelepathyQt4/connection.h | 8 ++
TelepathyQt4/contact.h | 12 +++
6 files changed, 341 insertions(+), 0 deletions(-)
create mode 100644 TelepathyQt4/ConnectionCapabilities
create mode 100644 TelepathyQt4/ContactCapabilities
create mode 100644 TelepathyQt4/capabilities.cpp
create mode 100644 TelepathyQt4/capabilities.h
diff --git a/TelepathyQt4/ConnectionCapabilities b/TelepathyQt4/ConnectionCapabilities
new file mode 100644
index 0000000..c2ebc24
- --- /dev/null
+++ b/TelepathyQt4/ConnectionCapabilities
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt4_ConnectionCapabilities_HEADER_GUARD_
+#define _TelepathyQt4_ConnectionCapabilities_HEADER_GUARD_
+
+#ifndef IN_TELEPATHY_QT4_HEADER
+#define IN_TELEPATHY_QT4_HEADER
+#endif
+
+#include <TelepathyQt4/capabilities.h>
+
+#undef IN_TELEPATHY_QT4_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt4/ContactCapabilities b/TelepathyQt4/ContactCapabilities
new file mode 100644
index 0000000..d6a3e8a
- --- /dev/null
+++ b/TelepathyQt4/ContactCapabilities
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt4_ContactCapabilities_HEADER_GUARD_
+#define _TelepathyQt4_ContactCapabilities_HEADER_GUARD_
+
+#ifndef IN_TELEPATHY_QT4_HEADER
+#define IN_TELEPATHY_QT4_HEADER
+#endif
+
+#include <TelepathyQt4/capabilities.h>
+
+#undef IN_TELEPATHY_QT4_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt4/capabilities.cpp b/TelepathyQt4/capabilities.cpp
new file mode 100644
index 0000000..5f60734
- --- /dev/null
+++ b/TelepathyQt4/capabilities.cpp
@@ -0,0 +1,184 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2009 Nokia Corporation
[... standard LGPL header elided ...]
+ */
+
+#include <TelepathyQt4/ConnectionCapabilities>
+#include <TelepathyQt4/ContactCapabilities>
+
+/**
+ * Return true if private text channels can be established by providing
+ * a contact identifier.
+ *
+ * If the protocol is such that text chats can be established, but only via
+ * a more elaborate D-Bus API than normal (because more information is needed),
+ * then this method will return false.
+ *
+ * \return true if Tp::Account::ensureTextChat() can be expected to work
+ */
+bool CapabilitiesBase::supportsTextChats()
+{
+ return FIXME;
+}
+
+/**
+ * Return true if private audio and/or video calls can be established by
+ * providing a contact identifier.
+ *
+ * If the protocol is such that these calls can be established, but only via
+ * a more elaborate D-Bus API than normal (because more information is needed),
+ * then this method will return false.
+ *
+ * \return true if Tp::Account::ensureMediaCall() can be expected to work
+ */
+bool CapabilitiesBase::supportsMediaCalls()
+{
+ return FIXME;
+}
+
+/**
+ * Return true if private audio calls can be established by providing a
+ * contact identifier.
+ *
+ * Call mediaCallsHaveImmutableStreams() to determine whether such calls are
+ * likely to be upgradable to have a video stream later.
+ *
+ * If the protocol is such that these calls can be established, but only via
+ * a more elaborate D-Bus API than normal (because more information is needed),
+ * then this method will return false.
+ *
+ * In some older connection managers, supportsAudioCalls() and
+ * supportsVideoCalls() might both return false, even though
+ * supportsMediaCalls() returns true. This indicates that only an older
+ * API is supported - clients of these connection managers must call
+ * ensureMediaCall() to get an empty call, then add audio and/or video
+ * streams to it.
+ *
+ * \return true if Tp::Account::ensureAudioCall() can be expected to work
+ */
+bool CapabilitiesBase::supportsAudioCalls()
+{
+ return FIXME;
+}
+
+/**
+ * Return true if private video calls can be established by providing a
+ * contact identifier.
+ *
+ * The same comments as for supportsAudioCalls() apply to this method.
+ *
+ * \param withAudio If true (the default), check for the ability to make calls
+ * with both audio and video; if false, do not require the ability to
+ * include an audio stream
+ * \return true if Tp::Account::ensureVideoCall() can be expected to work,
+ * if given the same withAudio parameter
+ */
+bool CapabilitiesBase::supportsVideoCalls(bool withAudio)
+{
+ return withAudio ? FIXME : FIXME;
+}
+
+/**
+ * Return whether the protocol supports adding streams of a different type
+ * to ongoing media calls.
+ *
+ * In some protocols and clients (such as XMPP Jingle), all calls potentially
+ * support both audio and video. This is indicated by returning true.
+ *
+ * In other protocols and clients (such as MSN, and the variant of XMPP Jingle
+ * used by Google clients), the streams are fixed at the time the call is
+ * started, so if you will ever want video, you have to ask for it at the
+ * beginning, for instance with ensureVideoCall(). This is indicated by
+ * returning false.
+ *
+ * User interfaces can use this method as a UI hint. If it returns false,
+ * then a UI wishing to support both audio and video calls will have to
+ * provide separate "audio call" and "video call" buttons or menu items;
+ * if it returns true, a single button that makes an audio call is sufficient,
+ * because video can be added later.
+ *
+ * (The underlying Telepathy feature is the ImmutableStreams property; if this
+ * method returns true, then ImmutableStreams is false, and vice versa).
+ *
+ * \return true if audio calls can be upgraded to audio + video
+ */
+bool CapabilitiesBase::supportsUpgradingCalls()
+{
+ return FIXME;
+}
+
+/**
+ * Return true if this object accurately describes the capabilities of a
+ * particular Tp::Contact, or false if it's only a guess based on the
+ * capabilities of the underlying Tp::Connection.
+ *
+ * In protocols like XMPP where each contact advertises their capabilities
+ * to others, Tp::Contact::capabilities will generally return an object where
+ * this method returns true.
+ *
+ * In protocols like SIP where contacts' capabilities are not known,
+ * Tp::Contact::Capabilities will return an object where this method returns
+ * false, whose methods supportsTextChats() etc. are based on what the
+ * underlying Tp::Connection supports.
+ *
+ * This reflects the fact that the best assumption an application can make is
+ * that every contact supports every channel type supported by the connection,
+ * while indicating that requests to communicate might fail if the contact
+ * does not actually have the necessary functionality.
+ */
+bool ContactCapabilities::isSpecificToContact() const
+{
+ return FIXME;
+}
+
+/**
+ * Return true if named text chatrooms can be joined by providing a
+ * chatroom identifier.
+ *
+ * If the protocol is such that chatrooms can be joined, but only via
+ * a more elaborate D-Bus API than normal (because more information is needed),
+ * then this method will return false.
+ *
+ * \return true if Tp::Account::ensureTextChatroom() can be expected to work
+ */
+bool ConnectionCapabilities::supportsTextChatrooms() const
+{
+ return FIXME;
+}
+
+/**
+ * Return the underlying data structure used by Telepathy to represent
+ * the requests that can succeed.
+ *
+ * This can be used by advanced clients to determine whether an unusually
+ * complex request would succeed. See the Telepathy D-Bus API Specification
+ * for details of how to interpret the returned list of QVariantMap objects.
+ *
+ * The higher-level methods like supportsTextChats() are likely to be more
+ * useful to the majority of clients.
+ *
+ * \return a RequestableChannelClassList indicating the parameters to
+ * Tp::Account::createChannel, Tp::Account::ensureChannel,
+ * Tp::Connection::createChannel and Tp::Connection::ensureChannel
+ * that can be expected to work
+ */
+RequestableChannelClassList CapabilitiesBase::requestableChannelClasses() const
+{
+ return mPriv->classes;
+}
diff --git a/TelepathyQt4/capabilities.h b/TelepathyQt4/capabilities.h
new file mode 100644
index 0000000..0982cb6
- --- /dev/null
+++ b/TelepathyQt4/capabilities.h
@@ -0,0 +1,111 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2009 Nokia Corporation
+ *
[... standard LGPL header elided ...]
+ */
+
+#ifndef _TelepathyQt4_capabilities_h_HEADER_GUARD_
+#define _TelepathyQt4_capabilities_h_HEADER_GUARD_
+
+#ifndef IN_TELEPATHY_QT4_HEADER
+#error IN_TELEPATHY_QT4_HEADER
+#endif
+
+#include <TelepathyQt4/Types>
+
+namespace Tp
+{
+
+class Connection;
+class Contact;
+
+// This is the part common to contacts (for things a particular contact
+// can do) and connections (for things contacts in general might be able to
+// do)
+class CapabilitiesBase
+{
+public:
+ virtual ~CapabilitiesBase();
+
+ RequestableChannelClassList requestableChannelClasses() const;
+
+ bool supportsTextChats() const;
+ bool supportsMediaCalls() const;
+ bool supportsAudioCalls() const;
+ bool supportsVideoCalls(bool withAudio = true) const;
+ bool supportsUpgradingCalls() const;
+
+ // later:
+ // bool supportsFileTransfers() const;
+ // QList<FileTransferHashType> fileTransfersRequireHash() const;
+ //
+ // bool supportsStreamTubes() const;
+ // bool supportsDBusTubes() const;
+
+protected:
+ CapabilitiesBase(const RequestableChannelClassList &classes);
+
+private:
+ friend class Connection;
+ friend class Contact;
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+class ContactCapabilities : public CapabilitiesBase
+{
+public:
+ virtual ~ContactCapabilities();
+
+ bool isSpecificToContact() const;
+
+ // later:
+ // QStringList supportedStreamTubeServices() const;
+ // QStringList supportedDBusTubeServices() const;
+ // (those don't make much sense for ConnectionCapabilities)
+
+private:
+ friend class Connection;
+ friend class Contact;
+
+ CapabilitiesBase(const RequestableChannelClassList &classes,
+ bool isSpecificToContact);
+
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+class ConnectionCapabilities : public CapabilitiesBase
+{
+public:
+ virtual ~ConnectionCapabilities();
+
+ bool supportsTextChatrooms() const;
+
+private:
+ struct Private;
+ friend struct Private;
+ Private *mPriv;
+};
+
+} // Tp namespace
+
+#endif
diff --git a/TelepathyQt4/connection.h b/TelepathyQt4/connection.h
index 6382b67..baea9eb 100644
- --- a/TelepathyQt4/connection.h
+++ b/TelepathyQt4/connection.h
@@ -28,6 +28,7 @@
#include <TelepathyQt4/_gen/cli-connection.h>
+#include <TelepathyQt4/ConnectionCapabilities>
#include <TelepathyQt4/Contact>
#include <TelepathyQt4/DBus>
#include <TelepathyQt4/DBusProxy>
@@ -71,6 +72,7 @@ public:
static const Feature FeatureSimplePresence;
static const Feature FeatureRoster;
static const Feature FeatureRosterGroups;
+ static const Feature FeatureCapabilities;
enum Status {
StatusDisconnected = ConnectionStatusDisconnected,
@@ -95,6 +97,11 @@ public:
SimpleStatusSpecMap allowedPresenceStatuses() const;
PendingOperation *setSelfPresence(const QString &status, const QString &statusMessage);
+ // all the requests that could potentially succeed on this Connection
+ // (in the case of contacts: all the requests that could possibly succeed,
+ // to any contact)
+ ConnectionCapabilities capabilities() const;
+
PendingChannel *createChannel(const QVariantMap &request);
PendingChannel *ensureChannel(const QVariantMap &request);
@@ -155,6 +162,7 @@ Q_SIGNALS:
void selfHandleChanged(uint newHandle);
// FIXME: might not need this when Renaming is fixed and mapped to Contacts
void selfContactChanged();
+ void capabilitiesChanged(const ConnectionCapabilities &capabilities);
protected:
Connection(const QString &busName, const QString &objectPath);
diff --git a/TelepathyQt4/contact.h b/TelepathyQt4/contact.h
index a0ea274..ed079d3 100644
- --- a/TelepathyQt4/contact.h
+++ b/TelepathyQt4/contact.h
@@ -31,6 +31,7 @@
#include <QSharedPointer>
#include <QVariantMap>
+#include <TelepathyQt4/ContactCapabilities>
#include <TelepathyQt4/Types>
namespace Tp
@@ -50,6 +51,7 @@ public:
FeatureAlias,
FeatureAvatarToken,
FeatureSimplePresence,
+ FeatureCapabilities,
_Padding = 0xFFFFFFFF
};
@@ -79,6 +81,14 @@ public:
PresenceState subscriptionState() const;
PresenceState publishState() const;
+ // all the requests that are likely to succeed for this specific Contact.
+ // If possible, this is from the ContactCapabilities interface,
+ // and capabilities()->isSpecificToContact() will be true; if that
+ // interface isn't present, this is the subset of
+ // manager()->connection()->capabilities() that have handle type Contact,
+ // and capabilities()->isSpecificToContact() will be false.
+ ContactCapabilities capabilities() const;
+
PendingOperation *requestPresenceSubscription(const QString &message = QString());
PendingOperation *removePresenceSubscription(const QString &message = QString());
PendingOperation *authorizePresencePublication(const QString &message = QString());
@@ -105,6 +115,8 @@ Q_SIGNALS:
void addedToGroup(const QString &group);
void removedFromGroup(const QString &group);
+ void capabilitiesChanged(const ContactCapabilities &capabilities);
+
// TODO: consider how the Renaming interface should work and map to Contacts
// I guess it would be something like:
// void renamedTo(Tp::ContactPtr)
-----BEGIN PGP SIGNATURE-----
iQIVAwUBSrJFHU3o/ypjx8yQAQj8Aw//S9qC8djUyry1OZEiLBPkmf64WfzkOecl
OWcwcb7E+3TfkQmhFjTHhj8bqjzdA8IIeaYln0ReTXBm6FrIzejCT4SyGQLWsp4h
I5wn/byATqKwCJdLWfk6Zryr3382Fyv96oE2DEOm5miYe3aSYtk+IoESj4atXjfc
PdAFHcvPMfUoPxnQZe8n4IDE7HPYjmU4DnFnbm0mBEMofPlqcmXbo9icSyQ8cxxC
PX4gliIl4hHKca8UlUMWPta126qVHGS+dfB5YwXFerywEOa9n6/iOZyGaaIgBM8l
AwL0L6/XsvmYDOWNfR2lhq6vUlyE7vEfsihY1EKm6L9zz5WZOUEXFsKqrIdxNxxB
ArdZPTzuXvXgvEaYdtw2aWRGIOzTFDzlJvqy4D0+EOMimdJo5kawLA2GWM+VpqoK
WCBNaLwFFTOj5MjFB9dwMk2AIugAOxGCaBZ7lnAqHa+7U9b1dn+EJX7QsYMutJgp
ixnREuAT3sjLPZxH4QmwvoyDGhbYu1eUcR/FZ+bshzyU5HpJHd/87uXR7/eY/XRQ
/eys9OweVk1WQayTF44be8VduQv4+YlIgMbR3Bk+nEWR6GHO+TLMF5ZkJ/im97Dr
jDmzrxq0wkIiOfB8Z2I4LjknQ9F+QiCg+LcRT38QaoIvQo++175UFoUeV6A9zolY
66NFoz+5vGk=
=F0aC
-----END PGP SIGNATURE-----
More information about the telepathy
mailing list