/** * This file is part of TelepathyQt * * @copyright Copyright (C) 2011 Collabora Ltd. * @copyright Copyright (C) 2011 Nokia Corporation * @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 #include "TelepathyQt/stream-tube-client-internal.h" #include "TelepathyQt/_gen/stream-tube-client.moc.hpp" #include "TelepathyQt/_gen/stream-tube-client-internal.moc.hpp" #include "TelepathyQt/debug-internal.h" #include "TelepathyQt/simple-stream-tube-handler.h" #include #include #include #include #include #include #include namespace Tp { /** * \class StreamTubeClient::TcpSourceAddressGenerator * \ingroup serverclient * \headerfile TelepathyQt/stream-tube-client.h * * \brief The StreamTubeClient::TcpSourceAddressGenerator abstract interface allows using socket * source address/port based access control for connecting to tubes accepted as TCP sockets. * * By default, every application on the local computer is allowed to connect to the socket created * by the protocol backend as the local endpoint of the tube. This is not always desirable, as that * includes even other users. * * Note that since every TCP connection must have an unique source address, only one simultaneous * connection can be made through each tube for which this type of access control has been used. */ /** * \fn QPair StreamTubeClient::TcpSourceAddressGenerator::nextSourceAddress(const AccountPtr &, const * IncomingStreamTubeChannelPtr &) * * Return the source address from which connections will be allowed to the given \a tube once it has * been accepted. * * Returning the pair (QHostAddress::Any, 0) (or alternatively (QHostAddress::AnyIPv4, 0) in Qt5) * makes the protocol backend allow connections from any address on the local computer. * This can be used on a tube-by-tube basis if for some tubes its known that multiple connections * need to be made, so a single source address doesn't suffice. * * The \a account and \a tube parameters can be inspected to make the decision; typically by looking * at the tube's service type, parameters and/or initiator contact. * * The general pattern for implementing this method is: *
    *
  1. Determine whether \a tube needs to allow multiple connections, and if so, skip source address * access control completely
  2. *
  3. Otherwise, create a socket and bind it to a free address
  4. *
  5. Return this socket's address
  6. *
  7. Keep the socket bound so that no other process can (accidentally or maliciously) take the * address until it's used to connect to the tube when StreamTubeClient::tubeAcceptedAsTcp() is * emitted for the tube
  8. *
* * \param account The account from which the tube originates. * \param tube The tube channel which is going to be accepted by the StreamTubeClient. * * \return A pair containing the host address and port allowed to connect. */ /** * \fn StreamTubeClient::TcpSourceAddressGenerator::~TcpSourceAddressGenerator * * Class destructor. Protected, because StreamTubeClient never deletes a TcpSourceAddressGenerator passed * to it. */ struct TP_QT_NO_EXPORT StreamTubeClient::Tube::Private : public QSharedData { // empty placeholder for now }; /** * \class StreamTubeClient::Tube * \ingroup serverclient * \headerfile TelepathyQt/stream-tube-client.h * * \brief The StreamTubeClient::Tube class represents a tube being handled by the client. */ /** * Constructs a new invalid Tube instance. */ StreamTubeClient::Tube::Tube() { // invalid instance } /** * Constructs a Tube instance for the given tube \a channel from the given \a account. * * \param account A pointer to the account the online connection of which the tube originates from. * \param channel A pointer to the tube channel object. */ StreamTubeClient::Tube::Tube( const AccountPtr &account, const IncomingStreamTubeChannelPtr &channel) : QPair(account, channel), mPriv(new Private) { } /** * Copy constructor. */ StreamTubeClient::Tube::Tube( const Tube &other) : QPair(other.account(), other.channel()), mPriv(other.mPriv) { } /** * Class destructor. */ StreamTubeClient::Tube::~Tube() { // mPriv deleted automatically } /** * Assignment operator. */ StreamTubeClient::Tube &StreamTubeClient::Tube::operator=( const Tube &other) { if (&other == this) { return *this; } first = other.account(); second = other.channel(); mPriv = other.mPriv; return *this; } /** * \fn bool StreamTubeClient::Tube::isValid() const * * Return whether or not the tube is valid or is just the null object created using the default * constructor. * * \return \c true if valid, \c false otherwise. */ /** * \fn AccountPtr StreamTubeClient::Tube::account() const * * Return the account from which the tube originates. * * \return A pointer to the account object. */ /** * \fn IncomingStreamTubeChannelPtr StreamTubeClient::Tube::channel() const * * Return the actual tube channel. * * \return A pointer to the channel. */ struct TP_QT_NO_EXPORT StreamTubeClient::Private { Private(const ClientRegistrarPtr ®istrar, const QStringList &p2pServices, const QStringList &roomServices, const QString &maybeClientName, bool monitorConnections, bool bypassApproval) : registrar(registrar), handler(SimpleStreamTubeHandler::create( p2pServices, roomServices, false, monitorConnections, bypassApproval)), clientName(maybeClientName), isRegistered(false), acceptsAsTcp(false), acceptsAsUnix(false), tcpGenerator(0), requireCredentials(false) { if (clientName.isEmpty()) { clientName = QString::fromLatin1("TpQtSTubeClient_%1_%2") .arg(registrar->dbusConnection().baseService() .replace(QLatin1Char(':'), QLatin1Char('_')) .replace(QLatin1Char('.'), QLatin1Char('_'))) .arg((quintptr) this, 0, 16); } } void ensureRegistered() { if (isRegistered) { return; } debug() << "Register StreamTubeClient with name " << clientName; if (registrar->registerClient(handler, clientName)) { isRegistered = true; } else { warning() << "StreamTubeClient" << clientName << "registration failed"; } } ClientRegistrarPtr registrar; SharedPtr handler; QString clientName; bool isRegistered; bool acceptsAsTcp, acceptsAsUnix; TcpSourceAddressGenerator *tcpGenerator; bool requireCredentials; QHash tubes; }; StreamTubeClient::TubeWrapper::TubeWrapper( const AccountPtr &acc, const IncomingStreamTubeChannelPtr &tube, const QHostAddress &sourceAddress, quint16 sourcePort, StreamTubeClient *parent) : QObject(parent), mAcc(acc), mTube(tube), mSourceAddress(sourceAddress), mSourcePort(sourcePort) { QHostAddress hostAddress = sourceAddress; if (sourcePort != 0) { #if QT_VERSION >= 0x050000 if (hostAddress == QHostAddress::Any || hostAddress == QHostAddress::LocalHost) { hostAddress = QHostAddress::AnyIPv4; } #endif if ((hostAddress.protocol() == QAbstractSocket::IPv4Protocol && !tube->supportsIPv4SocketsWithSpecifiedAddress()) || (hostAddress.protocol() == QAbstractSocket::IPv6Protocol && !tube->supportsIPv6SocketsWithSpecifiedAddress())) { debug() << "StreamTubeClient falling back to Localhost AC for tube" << tube->objectPath(); mSourceAddress = sourceAddress.protocol() == QAbstractSocket::IPv4Protocol ? QHostAddress::Any : QHostAddress::AnyIPv6; mSourcePort = 0; } } connect(tube->acceptTubeAsTcpSocket(mSourceAddress, mSourcePort), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onTubeAccepted(Tp::PendingOperation*))); connect(tube.data(), SIGNAL(newConnection(uint)), SLOT(onNewConnection(uint))); connect(tube.data(), SIGNAL(connectionClosed(uint,QString,QString)), SLOT(onConnectionClosed(uint,QString,QString))); } StreamTubeClient::TubeWrapper::TubeWrapper( const AccountPtr &acc, const IncomingStreamTubeChannelPtr &tube, bool requireCredentials, StreamTubeClient *parent) : QObject(parent), mAcc(acc), mTube(tube), mSourcePort(0) { if (requireCredentials && !tube->supportsUnixSocketsWithCredentials()) { debug() << "StreamTubeClient falling back to Localhost AC for tube" << tube->objectPath(); requireCredentials = false; } connect(tube->acceptTubeAsUnixSocket(requireCredentials), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onTubeAccepted(Tp::PendingOperation*))); connect(tube.data(), SIGNAL(newConnection(uint)), SLOT(onNewConnection(uint))); connect(tube.data(), SIGNAL(connectionClosed(uint,QString,QString)), SLOT(onConnectionClosed(uint,QString,QString))); } void StreamTubeClient::TubeWrapper::onTubeAccepted(Tp::PendingOperation *op) { emit acceptFinished(this, qobject_cast(op)); } void StreamTubeClient::TubeWrapper::onNewConnection(uint conn) { emit newConnection(this, conn); } void StreamTubeClient::TubeWrapper::onConnectionClosed(uint conn, const QString &error, const QString &message) { emit connectionClosed(this, conn, error, message); } /** * \class StreamTubeClient * \ingroup serverclient * \headerfile TelepathyQt/stream-tube-client.h * * \brief The StreamTubeClient class is a Handler implementation for incoming %Stream %Tube channels, * allowing an application to easily get notified about services they can connect to offered to them * over Telepathy Tubes without worrying about the channel dispatching details. * * Telepathy Tubes is a technology for connecting arbitrary applications together through the IM * network (and sometimes with direct peer-to-peer connections), such that issues like firewall/NAT * traversal are automatically handled. Stream Tubes in particular offer properties similar to * SOCK_STREAM sockets. The StreamTubeClient class negotiates tubes offered to us so that an * application can connect such bytestream sockets of theirs to them. The StreamTubeServer class is * the counterpart, offering services from a bytestream socket server to tubes requested to be * initiated. * * Both peer-to-peer (\c TargetHandleType == \ref HandleTypeContact) and group (\c TargetHandleType * == \ref HandleTypeRoom) channels are supported, and it's possible to specify the tube services to * handle for each separately. There must be at least one service in total declared, as it never * makes sense to handle stream tubes without considering the protocol of the service offered * through them. * * %Connection monitoring allows fine-grained error reporting for connections made through tubes, * and observing connections being made and broken even if the application code running * StreamTubeClient can't easily get this information from the code actually connecting through it. * Such a setting might occur e.g. when a wrapper application is developed to connect some existing * "black box" networked application through TCP tubes by launching it with the appropriate command * line arguments or alike for accepted tubes. * * Enabling connection monitoring adds a small overhead and latency to handling each incoming tube * and signaling each new incoming connection over them, though, so use it only when needed. * * A service activated Handler can be implemented using StreamTubeClient by passing a predefined \a * clientName manually to the chosen create() method, and installing Telepathy \c .client and D-Bus * \c .service files declaring the implemented tube services as channel classes and a path to the * executable. If this is not needed, the \a clientName can be omitted, in which case a random * unique client name is generated and used instead. However, then the tube client application must * already be running for remote contacts to be able to offer services to us over tubes. * * Whether the Handler application implemented using StreamTubeClient is service activatable or not, * incoming channels will typically first be given to an Approver, if there is one for tube services * corresponding to the tube in question. Only if the Approver decides that the tube communication * should be allowed (usually by asking the user), or if there is no matching Approver at all, is * the channel given to the actual Handler tube client. This can be overridden by setting \a * bypassApproval to \c true, which skips approval for the given services completely and directs * them straight to the Handler. * * StreamTubeClient shares Account, Connection and Channel proxies and Contact objects with the * rest of the application as long as a reference to the AccountManager, ClientRegistrar, or the * factories used elsewhere is passed to the create() method. A stand-alone tube client Handler can * get away without passing these however, or just passing select factories to make the desired * features prepared and subclasses employed for these objects for their own convenience. * * Whichever method is used, the ChannelFactory (perhaps indirectly) given must construct * IncomingStreamTubeChannel instances or subclasses thereof for all channel classes corresponding * to the tube services the client should be able to connect to. This is the default; overriding it * without obeying these constraints using ChannelFactory::setSubclassForIncomingStreamTubes() or * the related methods for room tubes prevents StreamTubeClient from operating correctly. * * \todo Coin up a small Python script or alike to easily generate the .client and .service files. * (fd.o #41614) */ /** * Create a new StreamTubeClient, which will register itself on the session bus using an internal * ClientRegistrar and use the given factories. * * \param p2pServices Names of the tube services to accept on peer-to-peer tube channels. * \param roomServices Names of the tube services to accept on room/group tube channels. * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix). * \param monitorConnections Whether to enable connection monitoring or not. * \param bypassApproval \c true to skip approval, \c false to invoke an Approver for incoming * channels if there is one. * \param accountFactory The account factory to use. * \param connectionFactory The connection factory to use. * \param channelFactory The channel factory to use. * \param contactFactory The contact factory to use. */ StreamTubeClientPtr StreamTubeClient::create( const QStringList &p2pServices, const QStringList &roomServices, const QString &clientName, bool monitorConnections, bool bypassApproval, const AccountFactoryConstPtr &accountFactory, const ConnectionFactoryConstPtr &connectionFactory, const ChannelFactoryConstPtr &channelFactory, const ContactFactoryConstPtr &contactFactory) { return create( QDBusConnection::sessionBus(), accountFactory, connectionFactory, channelFactory, contactFactory, p2pServices, roomServices, clientName, monitorConnections, bypassApproval); } /** * Create a new StreamTubeClient, which will register itself on the given \a bus using an internal * ClientRegistrar and use the given factories. * * The factories must all be created for the given \a bus. * * \param bus Connection to the bus to register on. * \param accountFactory The account factory to use. * \param connectionFactory The connection factory to use. * \param channelFactory The channel factory to use. * \param contactFactory The contact factory to use. * \param p2pServices Names of the tube services to handle on peer-to-peer tube channels. * \param roomServices Names of the tube services to handle on room/group tube channels. * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix). * \param monitorConnections Whether to enable connection monitoring or not. * \param bypassApproval \c true to skip approval, \c false to invoke an Approver for incoming * channels if there is one. */ StreamTubeClientPtr StreamTubeClient::create( const QDBusConnection &bus, const AccountFactoryConstPtr &accountFactory, const ConnectionFactoryConstPtr &connectionFactory, const ChannelFactoryConstPtr &channelFactory, const ContactFactoryConstPtr &contactFactory, const QStringList &p2pServices, const QStringList &roomServices, const QString &clientName, bool monitorConnections, bool bypassApproval) { return create( ClientRegistrar::create( bus, accountFactory, connectionFactory, channelFactory, contactFactory), p2pServices, roomServices, clientName, monitorConnections, bypassApproval); } /** * Create a new StreamTubeClient, which will register itself on the bus of and share objects with * the given \a accountManager, creating an internal ClientRegistrar. * * \param accountManager A pointer to the account manager to link up with. * \param p2pServices Names of the tube services to handle on peer-to-peer tube channels. * \param roomServices Names of the tube services to handle on room/group tube channels. * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix). * \param monitorConnections Whether to enable connection monitoring or not. * \param bypassApproval \c true to skip approval, \c false to invoke an Approver for incoming * channels if there is one. */ StreamTubeClientPtr StreamTubeClient::create( const AccountManagerPtr &accountManager, const QStringList &p2pServices, const QStringList &roomServices, const QString &clientName, bool monitorConnections, bool bypassApproval) { return create( accountManager->dbusConnection(), accountManager->accountFactory(), accountManager->connectionFactory(), accountManager->channelFactory(), accountManager->contactFactory(), p2pServices, roomServices, clientName, monitorConnections, bypassApproval); } /** * Create a new StreamTubeClient, which will register itself on the bus of and using the given * client \a registrar, and share objects with it. * * \param registrar The client registrar to use. * \param p2pServices Names of the tube services to handle on peer-to-peer tube channels. * \param roomServices Names of the tube services to handle on room/group tube channels. * \param clientName The client name (without the \c org.freedesktop.Telepathy.Client. prefix). * \param monitorConnections Whether to enable connection monitoring or not. * \param bypassApproval \c true to skip approval, \c false to invoke an Approver for incoming * channels if there is one. */ StreamTubeClientPtr StreamTubeClient::create( const ClientRegistrarPtr ®istrar, const QStringList &p2pServices, const QStringList &roomServices, const QString &clientName, bool monitorConnections, bool bypassApproval) { if (p2pServices.isEmpty() && roomServices.isEmpty()) { warning() << "Tried to create a StreamTubeClient with no services, returning NULL"; return StreamTubeClientPtr(); } return StreamTubeClientPtr( new StreamTubeClient(registrar, p2pServices, roomServices, clientName, monitorConnections, bypassApproval)); } StreamTubeClient::StreamTubeClient( const ClientRegistrarPtr ®istrar, const QStringList &p2pServices, const QStringList &roomServices, const QString &clientName, bool monitorConnections, bool bypassApproval) : mPriv(new Private(registrar, p2pServices, roomServices, clientName, monitorConnections, bypassApproval)) { connect(mPriv->handler.data(), SIGNAL(invokedForTube( Tp::AccountPtr, Tp::StreamTubeChannelPtr, QDateTime, Tp::ChannelRequestHints)), SLOT(onInvokedForTube( Tp::AccountPtr, Tp::StreamTubeChannelPtr, QDateTime, Tp::ChannelRequestHints))); } /** * Class destructor. */ StreamTubeClient::~StreamTubeClient() { if (isRegistered()) { mPriv->registrar->unregisterClient(mPriv->handler); } delete mPriv; } /** * Return the client registrar used by the client to register itself as a Telepathy channel Handler * %Client. * * This is the registrar originally passed to * create(const ClientRegistrarPtr &, const QStringList &, const QStringList &, const QString &, bool, bool) * if that was used, and an internally constructed one otherwise. In any case, it can be used to * e.g. register further clients, just like any other ClientRegistrar. * * \return A pointer to the registrar. */ ClientRegistrarPtr StreamTubeClient::registrar() const { return mPriv->registrar; } /** * Return the Telepathy %Client name of the client. * * \return The name, without the \c org.freedesktop.Telepathy.Client. prefix of the full D-Bus service name. */ QString StreamTubeClient::clientName() const { return mPriv->clientName; } /** * Return whether the client has been successfully registered or not. * * Registration is attempted, at the latest, when the client is first set to accept incoming tubes, * either as TCP sockets (setToAcceptAsTcp()) or Unix ones (setToAcceptAsUnix()). It can fail e.g. * because the connection to the bus has failed, or a predefined \a clientName has been passed to * create(), and a %Client with the same name is already registered. Typically, failure registering * would be a fatal error for a stand-alone tube handler, but only a warning event for an * application serving other purposes. In any case, a high-quality user of the API will check the * return value of this accessor after choosing the desired address family. * * \return \c true if the client has been successfully registered, \c false if not. */ bool StreamTubeClient::isRegistered() const { return mPriv->isRegistered; } /** * Return whether connection monitoring is enabled on this client. * * For technical reasons, connection monitoring can't be enabled when the client is already running, * so there is no corresponding setter method. It has to be enabled by passing \c true as the \a * monitorConnections parameter to the create() method. * * If connection monitoring isn't enabled, newConnection() and connectionClosed() won't be * emitted and connections() won't be populated. * * \return \c true if monitoring is enabled, \c false if not. */ bool StreamTubeClient::monitorsConnections() const { return mPriv->handler->monitorsConnections(); } /** * Return whether the client is currently set to accept incoming tubes as TCP sockets. * * \return \c true if the client will accept tubes as TCP sockets, \c false if it will accept them * as Unix ones or hasn't been set to accept at all yet. */ bool StreamTubeClient::acceptsAsTcp() const { return mPriv->acceptsAsTcp; } /** * Return the TCP source address generator, if any, set by setToAcceptAsTcp() previously. * * \return A pointer to the generator instance. */ StreamTubeClient::TcpSourceAddressGenerator *StreamTubeClient::tcpGenerator() const { if (!acceptsAsTcp()) { warning() << "StreamTubeClient::tcpGenerator() used, but not accepting as TCP, returning 0"; return 0; } return mPriv->tcpGenerator; } /** * Return whether the client is currently set to accept incoming tubes as Unix sockets. * * \return \c true if the client will accept tubes as Unix sockets, \c false if it will accept them * as TCP ones or hasn't been set to accept at all yet. */ bool StreamTubeClient::acceptsAsUnix() const { return mPriv->acceptsAsUnix; } /** * Set the client to accept tubes received to handle in the future in a fashion which will yield a * TCP socket as the local endpoint to connect to. * * A source address generator can optionally be set. If non-null, it will be invoked for each new * tube received to handle and an attempt is made to restrict connections to the tube's local socket * endpoint to those from that source address. * * However, if the protocol backend doesn't actually support source address based access control, * tubeAcceptedAsTcp() will be emitted with QHostAddress::Any * as the allowed source address to signal that it doesn't matter where we connect from, but more * importantly, that anybody else on the same host could have, and can, connect to the tube. * The tube can be closed at this point if this would be unacceptable security-wise. * To totally prevent the tube from being accepted in the first place, one can close it already when * tubeOffered() is emitted for it - support for the needed security mechanism can be queried using * its supportsIPv4SocketsWithSpecifiedAddress() accessor. * * The handler is registered on the bus at the latest when this method or setToAcceptAsUnix() is * called for the first time, so one should check the return value of isRegistered() at that point * to verify that was successful. * * \param generator A pointer to the source address generator to use, or 0 to allow all * connections from the local host. * * \todo Make it possible to set the tube client to auto-close tubes if the desired access control * level is not achieved, as an alternative to the current best-effort behavior. */ void StreamTubeClient::setToAcceptAsTcp(TcpSourceAddressGenerator *generator) { mPriv->tcpGenerator = generator; mPriv->acceptsAsTcp = true; mPriv->acceptsAsUnix = false; mPriv->ensureRegistered(); } /** * Set the client to accept tubes received to handle in the future in a fashion which will yield a * Unix socket as the local endpoint to connect to. * * If that doesn't cause problems for the payload protocol, it's possible to increase security by * restricting the processes allowed to connect to the local endpoint socket to those from the same * user ID as the protocol backend is running as by setting \a requireCredentials to \c true. This * requires transmitting a single byte, signaled as the \a credentialByte parameter to the * tubeAcceptedAsUnix() signal, in a \c SCM_CREDS or SCM_CREDENTIALS message, whichever is supported * by the platform, as the first thing after having connected to the socket. Even if the platform * doesn't implement either concept, the byte must still be sent. * * However, not all protocol backends support the credential passing based access control on all the * platforms they can run on. If a tube is offered through such a backend, tubeAcceptedAsUnix() will * be emitted with \a requiresCredentials set to \c false, to signal that a credential byte should * NOT be sent for that tube, and that any local process can or could have connected to the tube * already. The tube can be closed at this point if this would be unacceptable security-wise. To * totally prevent the tube from being accepted in the first place, one can close it already when * tubeOffered() is emitted for it - support for the needed security mechanism can be queried using * its supportsIPv4SocketsWithSpecifiedAddress() * accessor. * * The handler is registered on the bus at the latest when this method or setToAcceptAsTcp() is * called for the first time, so one should check the return value of isRegistered() at that point * to verify that was successful. * * \param requireCredentials \c true to try and restrict connecting by UID, \c false to allow all * connections. * * \todo Make it possible to set the tube client to auto-close tubes if the desired access control * level is not achieved, as an alternative to the current best-effort behavior. */ void StreamTubeClient::setToAcceptAsUnix(bool requireCredentials) { mPriv->tcpGenerator = 0; mPriv->acceptsAsTcp = false; mPriv->acceptsAsUnix = true; mPriv->requireCredentials = requireCredentials; mPriv->ensureRegistered(); } /** * Return the tubes currently handled by the client. * * \return A list of Tube structures containing pointers to the account and tube channel for each * tube. */ QList StreamTubeClient::tubes() const { QList tubes; foreach (TubeWrapper *wrapper, mPriv->tubes.values()) { tubes.push_back(Tube(wrapper->mAcc, wrapper->mTube)); } return tubes; } /** * Return the ongoing connections established through tubes signaled by this client. * * The returned mapping has for each Tube a structure containing pointers to the account and tube * channel objects as keys, with the integer identifiers for the current connections on them as the * values. The IDs are unique amongst the connections active on a single tube at any given time, but * not globally. * * This is effectively a state recovery accessor corresponding to the change notification signals * newConnection() and connectionClosed(). * * The mapping is only populated if connection monitoring was requested when creating the client (so * monitorsConnections() returns \c true). * * \return The connections in a mapping with Tube structures containing pointers to the account and * channel objects for each tube as keys, and the sets of numerical IDs as values. */ QHash > StreamTubeClient::connections() const { QHash > conns; if (!monitorsConnections()) { warning() << "StreamTubeClient::connections() used, but connection monitoring is disabled"; return conns; } foreach (const Tube &tube, tubes()) { if (!tube.channel()->isValid()) { // The tube has been invalidated, so skip it to avoid warnings // We're going to get rid of the wrapper in the next mainloop iteration when we get the // invalidation signal continue; } QSet tubeConns = tube.channel()->connections(); if (!tubeConns.empty()) { conns.insert(tube, tubeConns); } } return conns; } void StreamTubeClient::onInvokedForTube( const AccountPtr &acc, const StreamTubeChannelPtr &tube, const QDateTime &time, const ChannelRequestHints &hints) { Q_ASSERT(isRegistered()); // our SSTH shouldn't be receiving any channels unless it's registered Q_ASSERT(!tube->isRequested()); Q_ASSERT(tube->isValid()); // SSTH won't emit invalid tubes if (mPriv->tubes.contains(tube)) { debug() << "Ignoring StreamTubeClient reinvocation for tube" << tube->objectPath(); return; } IncomingStreamTubeChannelPtr incoming = IncomingStreamTubeChannelPtr::qObjectCast(tube); if (!incoming) { warning() << "The ChannelFactory used by StreamTubeClient must construct" << "IncomingStreamTubeChannel subclasses for Requested=false StreamTubes"; tube->requestClose(); return; } TubeWrapper *wrapper = 0; if (mPriv->acceptsAsTcp) { QPair srcAddr = qMakePair(QHostAddress(QHostAddress::Any), quint16(0)); if (mPriv->tcpGenerator) { srcAddr = mPriv->tcpGenerator->nextSourceAddress(acc, incoming); } wrapper = new TubeWrapper(acc, incoming, srcAddr.first, srcAddr.second, this); } else { Q_ASSERT(mPriv->acceptsAsUnix); // we should only be registered when we're set to accept as either TCP or Unix wrapper = new TubeWrapper(acc, incoming, mPriv->requireCredentials, this); } connect(wrapper, SIGNAL(acceptFinished(TubeWrapper*,Tp::PendingStreamTubeConnection*)), SLOT(onAcceptFinished(TubeWrapper*,Tp::PendingStreamTubeConnection*))); connect(tube.data(), SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)), SLOT(onTubeInvalidated(Tp::DBusProxy*,QString,QString))); if (monitorsConnections()) { connect(wrapper, SIGNAL(newConnection(TubeWrapper*,uint)), SLOT(onNewConnection(TubeWrapper*,uint))); connect(wrapper, SIGNAL(connectionClosed(TubeWrapper*,uint,QString,QString)), SLOT(onConnectionClosed(TubeWrapper*,uint,QString,QString))); } mPriv->tubes.insert(tube, wrapper); emit tubeOffered(acc, incoming); } void StreamTubeClient::onAcceptFinished(TubeWrapper *wrapper, PendingStreamTubeConnection *conn) { Q_ASSERT(wrapper != NULL); Q_ASSERT(conn != NULL); if (!mPriv->tubes.contains(wrapper->mTube)) { debug() << "StreamTubeClient ignoring Accept result for invalidated tube" << wrapper->mTube->objectPath(); return; } if (conn->isError()) { warning() << "StreamTubeClient couldn't accept tube" << wrapper->mTube->objectPath() << '-' << conn->errorName() << ':' << conn->errorMessage(); if (wrapper->mTube->isValid()) { wrapper->mTube->requestClose(); } wrapper->mTube->disconnect(this); emit tubeClosed(wrapper->mAcc, wrapper->mTube, conn->errorName(), conn->errorMessage()); mPriv->tubes.remove(wrapper->mTube); wrapper->deleteLater(); return; } debug() << "StreamTubeClient accepted tube" << wrapper->mTube->objectPath(); if (conn->addressType() == SocketAddressTypeIPv4 || conn->addressType() == SocketAddressTypeIPv6) { QPair addr = conn->ipAddress(); emit tubeAcceptedAsTcp(addr.first, addr.second, wrapper->mSourceAddress, wrapper->mSourcePort, wrapper->mAcc, wrapper->mTube); } else { emit tubeAcceptedAsUnix(conn->localAddress(), conn->requiresCredentials(), conn->credentialByte(), wrapper->mAcc, wrapper->mTube); } } void StreamTubeClient::onTubeInvalidated(Tp::DBusProxy *proxy, const QString &error, const QString &message) { StreamTubeChannelPtr tube(qobject_cast(proxy)); Q_ASSERT(!tube.isNull()); TubeWrapper *wrapper = mPriv->tubes.value(tube); if (!wrapper) { // Accept finish with error already removed it return; } debug() << "Client StreamTube" << tube->objectPath() << "invalidated - " << error << ':' << message; emit tubeClosed(wrapper->mAcc, wrapper->mTube, error, message); mPriv->tubes.remove(tube); delete wrapper; } void StreamTubeClient::onNewConnection( TubeWrapper *wrapper, uint conn) { Q_ASSERT(monitorsConnections()); emit newConnection(wrapper->mAcc, wrapper->mTube, conn); } void StreamTubeClient::onConnectionClosed( TubeWrapper *wrapper, uint conn, const QString &error, const QString &message) { Q_ASSERT(monitorsConnections()); emit connectionClosed(wrapper->mAcc, wrapper->mTube, conn, error, message); } /** * \fn void StreamTubeClient::tubeOffered(const AccountPtr &account, const * IncomingStreamTubeChannelPtr &tube) * * Emitted when one of the services we're interested in connecting to has been offered by us as a * tube, which we've began handling. * * This is emitted before invoking the TcpSourceAddressGenerator, if any, for the tube. * * \param account A pointer to the account through which the tube was offered. * \param tube A pointer to the actual tube channel. */ /** * \fn void StreamTubeClient::tubeAcceptedAsTcp(const QHostAddress &listenAddress, quint16 * listenPort, const QHostAddress &sourceAddress, quint16 sourcePort, const AccountPtr &account, * const IncomingStreamTubeChannelPtr &tube) * * Emitted when a tube offered to us (previously announced with tubeOffered()) has been successfully * accepted and a TCP socket established as the local endpoint, as specified by setToAcceptAsTcp(). * * The allowed source address and port are signaled here if there was a TcpSourceAddressGenerator set * at the time of accepting this tube, it yielded a non-zero address, and the protocol backend * supports source address based access control. This enables the application to use the correct one * of the sockets it currently has bound to the generated addresses. * * \param listenAddress The listen address of the local endpoint socket. * \param listenPort The listen port of the local endpoint socket. * \param sourceAddress The host address allowed to connect to the tube, or QHostAddress::Any * if source address based access control is not in use. * \param sourcePort The port from which connections are allowed to the tube, or 0 if source address * based access control is not in use. * \param account A pointer to the account object through which the tube was offered. * \param tube A pointer to the actual tube channel object. */ // Explicitly specifying Tp:: for this signal's argument types works around a doxygen bug causing it // to confuse the doc here being for StreamTubeServer::tubeClosed :/ /** * \fn void StreamTubeClient::tubeClosed(const Tp::AccountPtr &account, const * Tp::IncomingStreamTubeChannelPtr &tube, const QString &error, const QString &message) * * Emitted when a tube we've been handling (previously announced with tubeOffered()) has * encountered an error or has otherwise been closed from further communication. * * \param account A pointer to the account through which the tube was offered. * \param tube A pointer to the actual tube channel. * \param error The D-Bus error name corresponding to the reason for the closure. * \param message A freeform debug message associated with the error. */ /** * \fn void StreamTubeClient::tubeAcceptedAsUnix(const QHostAddress &listenAddress, quint16 * listenPort, const QHostAddress &sourceAddress, quint16 sourcePort, const AccountPtr &account, * const IncomingStreamTubeChannelPtr &tube) * * Emitted when a tube offered to us (previously announced with tubeOffered()) has been successfully * accepted and a Unix socket established as the local endpoint, as specified by setToAcceptAsUnix(). * * The credential byte which should be sent after connecting to the tube (in a SCM_CREDENTIALS or * SCM_CREDS message if supported by the platform) will be signaled here if the client was set to * attempt requiring credentials at the time of accepting this tube, and the protocol backend * supports credential passing based access control. Otherwise, \a requiresCredentials will be false * and no byte or associated out-of-band credentials metadata should be sent. * * \param listenAddress The listen address of the local endpoint socket. * \param requiresCredentials \c true if \a credentialByte should be sent after connecting to the * socket, \c false if not. * \param credentialByte The byte to send if \a requiresCredentials is \c true. * \param account A pointer to the account object through which the tube was offered. * \param tube A pointer to the actual tube channel object. */ /** * \fn void StreamTubeClient::newConnection(const AccountPtr &account, const * IncomingStreamTubeChannelPtr &tube, uint connectionId) * * Emitted when a new connection has been made to the local endpoint socket for \a tube. * * This can be used to later associate connection errors reported by connectionClosed() with the * corresponding application sockets. However, establishing the association generally requires * connecting only one socket at a time, waiting for newConnection() to be emitted, and only then * proceeding, as there is no identification for the connections unlike the incoming connections in * StreamTubeServer. * * Note that the connection IDs are only unique within a given tube, so identification of the tube * channel must also be recorded together with the ID to establish global uniqueness. Even then, the * a connection ID can be reused after the previous connection identified by it having been * signaled as closed with connectionClosed(). * * This is only emitted if connection monitoring was enabled when creating the StreamTubeClient. * * \param account A pointer to the account through which the tube was offered. * \param tube A pointer to the tube channel through which the connection has been made. * \param connectionId The integer ID of the new connection. */ /** * \fn void StreamTubeClient::connectionClosed(const AccountPtr &account, const * IncomingStreamTubeChannelPtr &tube, uint connectionId, const QString &error, const * QString &message) * * Emitted when a connection (previously announced with newConnection()) through one of our * handled tubes has been closed due to an error or by a graceful disconnect (in which case the * error is ::TP_QT_ERROR_CANCELLED). * * This is only emitted if connection monitoring was enabled when creating the StreamTubeClient. * * \param account A pointer to the account through which the tube was offered. * \param tube A pointer to the tube channel through which the connection had been made. * \param connectionId The integer ID of the connection closed. * \param error The D-Bus error name corresponding to the reason for the closure. * \param message A freeform debug message associated with the error. */ } // Tp