summaryrefslogtreecommitdiff
path: root/qt4/TelepathyQt4/incoming-stream-tube-channel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qt4/TelepathyQt4/incoming-stream-tube-channel.cpp')
-rw-r--r--qt4/TelepathyQt4/incoming-stream-tube-channel.cpp435
1 files changed, 435 insertions, 0 deletions
diff --git a/qt4/TelepathyQt4/incoming-stream-tube-channel.cpp b/qt4/TelepathyQt4/incoming-stream-tube-channel.cpp
new file mode 100644
index 000000000..610c12706
--- /dev/null
+++ b/qt4/TelepathyQt4/incoming-stream-tube-channel.cpp
@@ -0,0 +1,435 @@
+/**
+ * This file is part of TelepathyQt4
+ *
+ * @copyright Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * @license LGPL 2.1
+ *
+ * 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 <TelepathyQt4/IncomingStreamTubeChannel>
+
+#include "TelepathyQt4/_gen/incoming-stream-tube-channel.moc.hpp"
+
+#include "TelepathyQt4/types-internal.h"
+#include "TelepathyQt4/debug-internal.h"
+
+#include <TelepathyQt4/PendingFailure>
+#include <TelepathyQt4/PendingStreamTubeConnection>
+#include <TelepathyQt4/PendingVariant>
+#include <TelepathyQt4/Types>
+
+#include <QHostAddress>
+
+namespace Tp
+{
+
+struct TELEPATHY_QT4_NO_EXPORT IncomingStreamTubeChannel::Private
+{
+ Private(IncomingStreamTubeChannel *parent);
+
+ // Public object
+ IncomingStreamTubeChannel *parent;
+ static bool initRandom;
+};
+
+bool IncomingStreamTubeChannel::Private::initRandom = true;
+
+IncomingStreamTubeChannel::Private::Private(IncomingStreamTubeChannel *parent)
+ : parent(parent)
+{
+}
+
+/**
+ * \class IncomingStreamTubeChannel
+ * \ingroup clientchannel
+ * \headerfile TelepathyQt4/incoming-stream-tube-channel.h <TelepathyQt4/IncomingStreamTubeChannel>
+ *
+ * \brief The IncomingStreamTubeChannel class represents an incoming Telepathy channel
+ * of type StreamTube.
+ *
+ * In particular, this class is meant to be used as a comfortable way for
+ * accepting incoming stream tubes. Tubes can be accepted as TCP and/or Unix sockets with various
+ * access control methods depending on what the service supports using the various overloads of
+ * acceptTubeAsTcpSocket() and acceptTubeAsUnixSocket().
+ *
+ * Once a tube is successfully accepted and open (the PendingStreamTubeConnection returned from the
+ * accepting methods has finished), the application can connect to the socket the address of which
+ * can be retrieved from PendingStreamTubeConnection::ipAddress() and/or
+ * PendingStreamTubeConnection::localAddress() depending on which accepting method was used.
+ * Connecting to this socket will open a tunneled connection to the service listening at the
+ * offering end of the tube.
+ *
+ * For more details, please refer to \telepathy_spec.
+ *
+ * See \ref async_model, \ref shared_ptr
+ */
+
+/**
+ * Feature representing the core that needs to become ready to make the
+ * IncomingStreamTubeChannel object usable.
+ *
+ * This is currently the same as StreamTubeChannel::FeatureCore, but may change to include more.
+ *
+ * When calling isReady(), becomeReady(), this feature is implicitly added
+ * to the requested features.
+ */
+const Feature IncomingStreamTubeChannel::FeatureCore =
+ Feature(QLatin1String(StreamTubeChannel::staticMetaObject.className()), 0); // ST::FeatureCore
+
+/**
+ * Create a new IncomingStreamTubeChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \return A IncomingStreamTubeChannelPtr object pointing to the newly created
+ * IncomingStreamTubeChannel object.
+ */
+IncomingStreamTubeChannelPtr IncomingStreamTubeChannel::create(const ConnectionPtr &connection,
+ const QString &objectPath, const QVariantMap &immutableProperties)
+{
+ return IncomingStreamTubeChannelPtr(new IncomingStreamTubeChannel(connection, objectPath,
+ immutableProperties, IncomingStreamTubeChannel::FeatureCore));
+}
+
+/**
+ * Construct a new IncomingStreamTubeChannel object.
+ *
+ * \param connection Connection owning this channel, and specifying the
+ * service.
+ * \param objectPath The channel object path.
+ * \param immutableProperties The channel immutable properties.
+ * \param coreFeature The core feature of the channel type, if any. The corresponding introspectable should
+ * depend on IncomingStreamTubeChannel::FeatureCore.
+ */
+IncomingStreamTubeChannel::IncomingStreamTubeChannel(const ConnectionPtr &connection,
+ const QString &objectPath,
+ const QVariantMap &immutableProperties,
+ const Feature &coreFeature)
+ : StreamTubeChannel(connection, objectPath,
+ immutableProperties, coreFeature),
+ mPriv(new Private(this))
+{
+}
+
+/**
+ * Class destructor.
+ */
+IncomingStreamTubeChannel::~IncomingStreamTubeChannel()
+{
+ delete mPriv;
+}
+
+/**
+ * Accept an incoming stream tube as a TCP socket.
+ *
+ * This method accepts an incoming connection request for a stream tube. It can be called
+ * only if the tube is in the #TubeStateLocalPending state.
+ *
+ * The connection manager will open a TCP socket for the application to connect to. The address of
+ * the socket will be returned in PendingStreamTubeConnection::ipAddress() once the operation has
+ * finished successfully.
+ *
+ * This overload lets you specify an allowed address/port combination for connecting to the CM
+ * socket. Connections with other source addresses won't be accepted. The accessors
+ * supportsIPv4SocketsWithSpecifiedAddress() and supportsIPv6SocketsWithSpecifiedAddress() can be
+ * used to verify that the connection manager supports this kind of access control; otherwise, this
+ * method will always fail unless QHostAddress::Any or QHostAddress::AnyIPv6 is passed, in which
+ * case the behavior is identical to the always supported acceptTubeAsTcpSocket() overload.
+ *
+ * Note that when using QHostAddress::Any or QHostAddress::AnyIPv6, \a allowedPort is ignored.
+ *
+ * This method requires IncomingStreamTubeChannel::FeatureCore to be ready.
+ *
+ * \param allowedAddress An allowed address for connecting to the socket.
+ * \param allowedPort An allowed port for connecting to the socket.
+ * \return A PendingStreamTubeConnection which will emit PendingStreamTubeConnection::finished
+ * when the stream tube is ready to be used
+ * (hence in the #TubeStateOpen state).
+ */
+PendingStreamTubeConnection *IncomingStreamTubeChannel::acceptTubeAsTcpSocket(
+ const QHostAddress &allowedAddress,
+ quint16 allowedPort)
+{
+ if (!isReady(IncomingStreamTubeChannel::FeatureCore)) {
+ warning() << "IncomingStreamTubeChannel::FeatureCore must be ready before "
+ "calling acceptTubeAsTcpSocket";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ // The tube must be in local pending state
+ if (state() != TubeChannelStateLocalPending) {
+ warning() << "You can accept tubes only when they are in LocalPending state";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ QVariant controlParameter;
+ SocketAccessControl accessControl;
+
+ // Now, let's check what we need to do with accessControl. There is just one special case, Port.
+ if (allowedAddress != QHostAddress::Any && allowedAddress != QHostAddress::AnyIPv6) {
+ // We need to have a valid QHostAddress AND Port.
+ if (allowedAddress.isNull() || allowedPort == 0) {
+ warning() << "You have to set a valid allowed address+port to use Port access control";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("The supplied allowed address and/or port was invalid"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ accessControl = SocketAccessControlPort;
+
+ // IPv4 or IPv6?
+ if (allowedAddress.protocol() == QAbstractSocket::IPv4Protocol) {
+ // IPv4 case
+ SocketAddressIPv4 addr;
+ addr.address = allowedAddress.toString();
+ addr.port = allowedPort;
+
+ controlParameter = QVariant::fromValue(addr);
+ } else if (allowedAddress.protocol() == QAbstractSocket::IPv6Protocol) {
+ // IPv6 case
+ SocketAddressIPv6 addr;
+ addr.address = allowedAddress.toString();
+ addr.port = allowedPort;
+
+ controlParameter = QVariant::fromValue(addr);
+ } else {
+ // We're handling an IPv4/IPv6 socket only
+ warning() << "acceptTubeAsTcpSocket can be called only with a QHostAddress "
+ "representing an IPv4 or IPv6 address";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid host given"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+ } else {
+ // We have to do no special stuff here
+ accessControl = SocketAccessControlLocalhost;
+ // Since QDBusMarshaller does not like null variants, just add an empty string.
+ controlParameter = QVariant(QString());
+ }
+
+ // Set the correct address type and access control
+ setAddressType(allowedAddress.protocol() == QAbstractSocket::IPv4Protocol ?
+ SocketAddressTypeIPv4 :
+ SocketAddressTypeIPv6);
+ setAccessControl(accessControl);
+
+ // Fail early if the combination is not supported
+ if ((accessControl == SocketAccessControlLocalhost &&
+ addressType() == SocketAddressTypeIPv4 &&
+ !supportsIPv4SocketsOnLocalhost()) ||
+ (accessControl == SocketAccessControlPort &&
+ addressType() == SocketAddressTypeIPv4 &&
+ !supportsIPv4SocketsWithSpecifiedAddress()) ||
+ (accessControl == SocketAccessControlLocalhost &&
+ addressType() == SocketAddressTypeIPv6 &&
+ !supportsIPv6SocketsOnLocalhost()) ||
+ (accessControl == SocketAccessControlPort &&
+ addressType() == SocketAddressTypeIPv6 &&
+ !supportsIPv6SocketsWithSpecifiedAddress())) {
+ warning() << "You requested an address type/access control combination "
+ "not supported by this channel";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("The requested address type/access control "
+ "combination is not supported"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ // Perform the actual call
+ PendingVariant *pv = new PendingVariant(
+ interface<Client::ChannelTypeStreamTubeInterface>()->Accept(
+ addressType(),
+ accessControl,
+ QDBusVariant(controlParameter)),
+ IncomingStreamTubeChannelPtr(this));
+
+ PendingStreamTubeConnection *op = new PendingStreamTubeConnection(pv, addressType(),
+ false, 0, IncomingStreamTubeChannelPtr(this));
+ return op;
+}
+
+/**
+ * Accept an incoming stream tube as a TCP socket.
+ *
+ * This method accepts an incoming connection request for a stream tube. It can be called
+ * only if the tube is in the #TubeStateLocalPending state.
+ *
+ * The connection manager will open a TCP socket for the application to connect to. The address of
+ * the socket will be returned in PendingStreamTubeConnection::ipAddress() once the operation has
+ * finished successfully.
+ *
+ * Using this overload, the connection manager will accept every incoming connection from localhost.
+ *
+ * This accept method must be supported by all connection managers adhering to the \telepathy_spec.
+ *
+ * This method requires IncomingStreamTubeChannel::FeatureCore to be ready.
+ *
+ * \return A PendingStreamTubeConnection which will emit PendingStreamTubeConnection::finished
+ * when the stream tube is ready to be used
+ * (hence in the #TubeStateOpen state).
+ */
+PendingStreamTubeConnection *IncomingStreamTubeChannel::acceptTubeAsTcpSocket()
+{
+ return acceptTubeAsTcpSocket(QHostAddress::Any, 0);
+}
+
+/**
+ * Accept an incoming stream tube as a Unix socket.
+ *
+ * This method accepts an incoming connection request for a stream tube. It can be called
+ * only if the tube is in the #TubeStateLocalPending state.
+ *
+ * An Unix socket (can be used with QLocalSocket or alike) will be opened by the connection manager
+ * as the local tube endpoint. This is only supported if supportsUnixSocketsOnLocalhost() is \c
+ * true.
+ *
+ * You can also specify whether the CM should require an SCM_CREDS or SCM_CREDENTIALS message
+ * upon connection instead of accepting every incoming connection from localhost. This provides
+ * additional security, but requires sending the byte retrieved from
+ * PendingStreamTubeConnection::credentialByte() in-line in the socket byte stream (in a credentials
+ * message if available on the platform), which might not be compatible with all protocols or
+ * libraries. Also, only connection managers for which supportsUnixSocketsWithCredentials() is \c
+ * true support this type of access control.
+ *
+ * This method requires IncomingStreamTubeChannel::FeatureCore to be ready.
+ *
+ * \param requireCredentials Whether the CM should require an SCM_CREDS or SCM_CREDENTIALS message
+ * upon connection.
+ * \return A PendingStreamTubeConnection which will emit PendingStreamTubeConnection::finished
+ * when the stream tube is ready to be used
+ * (hence in the #TubeStateOpen state).
+ * \sa StreamTubeChannel::supportsUnixSocketsOnLocalhost(),
+ * StreamTubeChannel::supportsUnixSocketsWithCredentials(),
+ * StreamTubeChannel::supportsAbstractUnixSocketsOnLocalhost(),
+ * StreamTubeChannel::supportsAbstractUnixSocketsWithCredentials()
+ */
+PendingStreamTubeConnection *IncomingStreamTubeChannel::acceptTubeAsUnixSocket(
+ bool requireCredentials)
+{
+ if (!isReady(IncomingStreamTubeChannel::FeatureCore)) {
+ warning() << "IncomingStreamTubeChannel::FeatureCore must be ready before "
+ "calling acceptTubeAsUnixSocket";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ // The tube must be in local pending state
+ if (state() != TubeChannelStateLocalPending) {
+ warning() << "You can accept tubes only when they are in LocalPending state";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_AVAILABLE),
+ QLatin1String("Channel not ready"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ SocketAccessControl accessControl = requireCredentials ?
+ SocketAccessControlCredentials :
+ SocketAccessControlLocalhost;
+ setAddressType(SocketAddressTypeUnix);
+ setAccessControl(accessControl);
+
+ // Fail early if the combination is not supported
+ if ((accessControl == SocketAccessControlLocalhost &&
+ addressType() == SocketAddressTypeUnix &&
+ !supportsUnixSocketsOnLocalhost()) ||
+ (accessControl == SocketAccessControlCredentials &&
+ addressType() == SocketAddressTypeUnix &&
+ !supportsUnixSocketsWithCredentials()) ||
+ (accessControl == SocketAccessControlLocalhost &&
+ addressType() == SocketAddressTypeAbstractUnix &&
+ !supportsAbstractUnixSocketsOnLocalhost()) ||
+ (accessControl == SocketAccessControlCredentials &&
+ addressType() == SocketAddressTypeAbstractUnix &&
+ !supportsAbstractUnixSocketsWithCredentials())) {
+ warning() << "You requested an address type/access control combination "
+ "not supported by this channel";
+ return new PendingStreamTubeConnection(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("The requested address type/access control "
+ "combination is not supported"),
+ IncomingStreamTubeChannelPtr(this));
+ }
+
+ QDBusVariant accessControlParam;
+ uchar credentialByte = 0;
+ if (accessControl == SocketAccessControlLocalhost) {
+ accessControlParam.setVariant(qVariantFromValue(static_cast<uint>(0)));
+ } else if (accessControl == SocketAccessControlCredentials) {
+ if (mPriv->initRandom) {
+ qsrand(QTime::currentTime().msec());
+ mPriv->initRandom = false;
+ }
+ credentialByte = static_cast<uchar>(qrand());
+ accessControlParam.setVariant(qVariantFromValue(credentialByte));
+ } else {
+ Q_ASSERT(false);
+ }
+
+ // Perform the actual call
+ PendingVariant *pv = new PendingVariant(
+ interface<Client::ChannelTypeStreamTubeInterface>()->Accept(
+ addressType(),
+ accessControl,
+ accessControlParam),
+ IncomingStreamTubeChannelPtr(this));
+
+ PendingStreamTubeConnection *op = new PendingStreamTubeConnection(pv, addressType(),
+ requireCredentials, credentialByte, IncomingStreamTubeChannelPtr(this));
+ return op;
+}
+
+/**
+ * Return the local address of the opened stream tube.
+ *
+ * Calling this method when the tube has not been opened will cause it
+ * to return an undefined value. The same will happen if the tube has been accepted as a TCP
+ * socket. Use ipAddress() if that is the case.
+ *
+ * \return Unix socket address if using an Unix socket,
+ * or an undefined value otherwise.
+ * \sa acceptTubeAsUnixSocket(), ipAddress()
+ */
+QString IncomingStreamTubeChannel::localAddress() const
+{
+ return StreamTubeChannel::localAddress();
+}
+
+/**
+ * Return the IP address/port combination of the opened stream tube.
+ *
+ * Calling this method when the tube has not been opened will cause it
+ * to return an undefined value. The same will happen if the tube has been accepted as an Unix
+ * socket. Use localAddress() if that is the case.
+ *
+ * \return Pair of IP address as QHostAddress and port if using a TCP socket,
+ * or an undefined value otherwise.
+ * \sa acceptTubeAsTcpSocket(), localAddress()
+ */
+QPair<QHostAddress, quint16> IncomingStreamTubeChannel::ipAddress() const
+{
+ return StreamTubeChannel::ipAddress();
+}
+
+void IncomingStreamTubeChannel::onNewLocalConnection(uint connectionId)
+{
+ addConnection(connectionId);
+}
+
+}