/** * This file is part of TelepathyQt4 * * @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 "TelepathyQt4/stream-tube-server-internal.h" #include "TelepathyQt4/_gen/stream-tube-server.moc.hpp" #include "TelepathyQt4/_gen/stream-tube-server-internal.moc.hpp" #include "TelepathyQt4/debug-internal.h" #include "TelepathyQt4/simple-stream-tube-handler.h" #include #include #include #include #include #include #include namespace Tp { /** * \class StreamTubeServer::ParametersGenerator * \ingroup serverclient * \headerfile TelepathyQt4/stream-tube-server.h * * \brief The StreamTubeServer::ParametersGenerator abstract interface allows sending a different * set of parameters with each tube offer. * * %Tube parameters are arbitrary data sent with the tube offer, which can be retrieved in the * receiving end with IncomingStreamTubeChannel::parameters(). They can be used to transfer * e.g. session identification information, authentication credentials or alike, for bootstrapping * the protocol used for communicating over the tube. * * For usecases where the parameters don't need to change between each tube, just passing a fixed * set of parameters to a suitable StreamTubeServer::exportTcpSocket() overload is usually more * convenient than implementing a ParametersGenerator. Note that StreamTubeServer::exportTcpSocket() * can be called multiple times to change the parameters for future tubes when e.g. configuration * settings have been changed, so a ParametersGenerator only needs to be implemented if each and * every tube must have a different set of parameters. */ /** * \fn QVariantMap StreamTubeServer::ParametersGenerator::nextParameters(const AccountPtr &, const * OutgoingStreamTubeChannelPtr &, const ChannelRequestHints &) * * Return the parameters to send when offering the given \a tube. * * \param account The account from which the tube originates. * \param tube The tube channel which is going to be offered by the StreamTubeServer. * \param hints The hints associated with the request that led to the creation of this tube, if any. * * \return Parameters to send with the offer, or an empty QVariantMap if none are needed for this * tube. */ /** * \fn StreamTubeServer::ParametersGenerator::~ParametersGenerator * * Class destructor. Protected, because StreamTubeServer never deletes a ParametersGenerator passed * to it. */ class TELEPATHY_QT4_NO_EXPORT FixedParametersGenerator : public StreamTubeServer::ParametersGenerator { public: FixedParametersGenerator(const QVariantMap ¶ms) : mParams(params) {} QVariantMap nextParameters(const AccountPtr &, const OutgoingStreamTubeChannelPtr &, const ChannelRequestHints &) { return mParams; } private: QVariantMap mParams; }; struct TELEPATHY_QT4_NO_EXPORT StreamTubeServer::RemoteContact::Private : public QSharedData { // empty placeholder for now }; /** * \class StreamTubeServer::RemoteContact * \ingroup serverclient * \headerfile TelepathyQt4/stream-tube-server.h * * \brief The StreamTubeServer::RemoteContact class represents a contact from which a socket * connection to our exported socket originates. */ /** * Constructs a new invalid RemoteContact instance. */ StreamTubeServer::RemoteContact::RemoteContact() { // invalid instance } /** * Constructs a new RemoteContact for the given \a contact object from the given \a account. * * \param account A pointer to the account which this contact can be reached through. * \param contact A pointer to the contact object. */ StreamTubeServer::RemoteContact::RemoteContact( const AccountPtr &account, const ContactPtr &contact) : QPair(account, contact), mPriv(new Private) { } /** * Copy constructor. */ StreamTubeServer::RemoteContact::RemoteContact( const RemoteContact &other) : QPair(other.account(), other.contact()), mPriv(other.mPriv) { } /** * Class destructor. */ StreamTubeServer::RemoteContact::~RemoteContact() { // mPriv deleted automatically } /** * Assignment operator. */ StreamTubeServer::RemoteContact &StreamTubeServer::RemoteContact::operator=( const RemoteContact &other) { if (&other == this) { return *this; } first = other.account(); second = other.contact(); mPriv = other.mPriv; return *this; } /** * \fn bool StreamTubeServer::RemoteContact::isValid() const * * Return whether or not the contact is valid or is just the null object created using the default * constructor. * * \return \c true if valid, \c false otherwise. */ /** * \fn AccountPtr StreamTubeServer::RemoteContact::account() const * * Return the account through which the contact can be reached. * * \return A pointer to the account object. */ /** * \fn ContactPtr StreamTubeServer::RemoteContact::contact() const * * Return the actual contact object. * * \return A pointer to the object. */ struct TELEPATHY_QT4_NO_EXPORT StreamTubeServer::Tube::Private : public QSharedData { // empty placeholder for now }; /** * \class StreamTubeServer::Tube * \ingroup serverclient * \headerfile TelepathyQt4/stream-tube-server.h * * \brief The StreamTubeServer::Tube class represents a tube being handled by the server. */ /** * Constructs a new invalid Tube instance. */ StreamTubeServer::Tube::Tube() { // invalid instance } /** * Constructs a Tube instance for the given tube \a channel originating from the given \a account. * * \param account A pointer to the account object. * \param channel A pointer to the tube channel object. */ StreamTubeServer::Tube::Tube( const AccountPtr &account, const OutgoingStreamTubeChannelPtr &channel) : QPair(account, channel), mPriv(new Private) { } /** * Copy constructor. */ StreamTubeServer::Tube::Tube( const Tube &other) : QPair(other.account(), other.channel()), mPriv(other.mPriv) { } /** * Class destructor. */ StreamTubeServer::Tube::~Tube() { // mPriv deleted automatically } /** * Assignment operator. */ StreamTubeServer::Tube &StreamTubeServer::Tube::operator=( const Tube &other) { if (&other == this) { return *this; } first = other.account(); second = other.channel(); mPriv = other.mPriv; return *this; } /** * \fn bool StreamTubeServer::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 StreamTubeServer::Tube::account() const * * Return the account from which the tube originates. * * \return A pointer to the account object. */ /** * \fn OutgoingStreamTubeChannelPtr StreamTubeServer::Tube::channel() const * * Return the actual tube channel. * * \return A pointer to the channel. */ struct StreamTubeServer::Private { Private(const ClientRegistrarPtr ®istrar, const QStringList &p2pServices, const QStringList &roomServices, const QString &maybeClientName, bool monitorConnections) : registrar(registrar), handler(SimpleStreamTubeHandler::create(p2pServices, roomServices, true, monitorConnections)), clientName(maybeClientName), isRegistered(false), exportedPort(0), generator(0) { if (clientName.isEmpty()) { clientName = QString::fromLatin1("TpQt4STubeServer_%1_%2") .arg(registrar->dbusConnection().baseService() .replace(QLatin1Char(':'), QLatin1Char('_')) .replace(QLatin1Char('.'), QLatin1Char('_'))) .arg((intptr_t) this, 0, 16); } } void ensureRegistered() { if (isRegistered) { return; } debug() << "Register StreamTubeServer with name " << clientName; if (registrar->registerClient(handler, clientName)) { isRegistered = true; } else { warning() << "StreamTubeServer" << clientName << "registration failed"; } } ClientRegistrarPtr registrar; SharedPtr handler; QString clientName; bool isRegistered; QHostAddress exportedAddr; quint16 exportedPort; ParametersGenerator *generator; QScopedPointer fixedGenerator; QHash tubes; }; StreamTubeServer::TubeWrapper::TubeWrapper(const AccountPtr &acc, const OutgoingStreamTubeChannelPtr &tube, const QHostAddress &exportedAddr, quint16 exportedPort, const QVariantMap ¶ms, StreamTubeServer *parent) : QObject(parent), mAcc(acc), mTube(tube) { connect(tube->offerTcpSocket(exportedAddr, exportedPort, params), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onTubeOffered(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 StreamTubeServer::TubeWrapper::onTubeOffered(Tp::PendingOperation *op) { emit offerFinished(this, op); } void StreamTubeServer::TubeWrapper::onNewConnection(uint conn) { emit newConnection(this, conn); } void StreamTubeServer::TubeWrapper::onConnectionClosed(uint conn, const QString &error, const QString &message) { emit connectionClosed(this, conn, error, message); } /** * \class StreamTubeServer * \ingroup serverclient * \headerfile TelepathyQt4/stream-tube-server.h * * \brief The StreamTubeServer class is a Handler implementation for outgoing %Stream %Tube channels, * allowing an application to easily export a TCP network server 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 StreamTubeServer class exports such a bytestream socket \b server over * the tubes it \em handles as a Telepathy Handler %Client; the StreamTubeClient class is the * counterpart, enabling TCP/UNIX socket clients to connect to services from such exported servers * offered to them via tubes. * * 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. It is also possible to not advertise handling capability for ANY tube * service; instead just using the StreamTubeServer to handle tubes on an one-off basis by passing * its corresponding %Client service name as the \a preferredHandler when requesting tubes via the * Account::createStreamTube() methods (or equivalent). * * %Connection monitoring allows associating incoming connections on the exported server socket with * the corresponding remote contacts. This allows an application to show the details of and/or * initiate further communication with the remote contacts, without considering the actual tube * channels the connections are being made through at all (in particular, their * Channel::targetContact() accessor for peer-to-peer and the * OutgoingStreamTubeChannel::connectionsForSourceAddresses() accessor for group 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. * Additionally, some protocol backends or environments they're running in might not support the * ::SocketAccessControlPort mechanism, in which case the source address won't be reported for * connections through them. Even in this case, the remote contacts can be associated by accepting * one incoming socket connection at a time, and waiting for the corresponding contact to be * signaled (although its source address will be invalid, it's the only possibility given its the * only accepted connection). However, it's not necessary to do this e.g. with the Gabble XMPP * backend, because it fully supports the required mechanism. * * A service activated Handler can be implemented using StreamTubeServer 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. * * StreamTubeServer 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 service 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 * OutgoingStreamTubeChannel instances or subclasses thereof for all channel classes corresponding * to the tube services to handle. This is the default; overriding it without obeying these * constraints using ChannelFactory::setSubclassForOutgoingStreamTubes() or the related methods * for room tubes prevents StreamTubeServer from operating correctly. * * \todo Coin up a small Python script or alike to easily generate the .client and .service files. * (fd.o #41614) * \todo Support exporting Unix sockets as well. (fd.o #41615) */ /** * Create a new StreamTubeServer, 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 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 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. */ StreamTubeServerPtr StreamTubeServer::create( const QStringList &p2pServices, const QStringList &roomServices, const QString &clientName, bool monitorConnections, const AccountFactoryConstPtr &accountFactory, const ConnectionFactoryConstPtr &connectionFactory, const ChannelFactoryConstPtr &channelFactory, const ContactFactoryConstPtr &contactFactory) { return create( QDBusConnection::sessionBus(), accountFactory, connectionFactory, channelFactory, contactFactory, p2pServices, roomServices, clientName, monitorConnections); } /** * Create a new StreamTubeServer, 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. */ StreamTubeServerPtr StreamTubeServer::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) { return create( ClientRegistrar::create( bus, accountFactory, connectionFactory, channelFactory, contactFactory), p2pServices, roomServices, clientName, monitorConnections); } /** * Create a new StreamTubeServer, 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. */ StreamTubeServerPtr StreamTubeServer::create( const AccountManagerPtr &accountManager, const QStringList &p2pServices, const QStringList &roomServices, const QString &clientName, bool monitorConnections) { return create( accountManager->dbusConnection(), accountManager->accountFactory(), accountManager->connectionFactory(), accountManager->channelFactory(), accountManager->contactFactory(), p2pServices, roomServices, clientName, monitorConnections); } /** * Create a new StreamTubeServer, 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. */ StreamTubeServerPtr StreamTubeServer::create( const ClientRegistrarPtr ®istrar, const QStringList &p2pServices, const QStringList &roomServices, const QString &clientName, bool monitorConnections) { return StreamTubeServerPtr( new StreamTubeServer(registrar, p2pServices, roomServices, clientName, monitorConnections)); } StreamTubeServer::StreamTubeServer( const ClientRegistrarPtr ®istrar, const QStringList &p2pServices, const QStringList &roomServices, const QString &clientName, bool monitorConnections) : mPriv(new Private(registrar, p2pServices, roomServices, clientName, monitorConnections)) { connect(mPriv->handler.data(), SIGNAL(invokedForTube( Tp::AccountPtr, Tp::StreamTubeChannelPtr, QDateTime, Tp::ChannelRequestHints)), SLOT(onInvokedForTube( Tp::AccountPtr, Tp::StreamTubeChannelPtr, QDateTime, Tp::ChannelRequestHints))); } /** * Class destructor. */ StreamTubeServer::~StreamTubeServer() { if (isRegistered()) { mPriv->registrar->unregisterClient(mPriv->handler); } delete mPriv; } /** * Return the client registrar used by the server to register itself as a Handler client. * * This is the registrar originally passed to * create(const ClientRegistrarPtr &, const QStringList &, const QStringList &, const QString &, bool) * if that was used, and an internally constructed one otherwise. In any case, it can be used to * e.g. register further clients like any other ClientRegistrar. * * \return A pointer to the registrar. */ ClientRegistrarPtr StreamTubeServer::registrar() const { return mPriv->registrar; } /** * Return the Telepathy %Client name of the server. * * \return The name, without the \c org.freedesktop.Telepathy.Client. prefix of the full D-Bus service name. */ QString StreamTubeServer::clientName() const { return mPriv->clientName; } /** * Return whether the server has been successfully registered or not. * * Registration is attempted, at the latest, when a socket is first exported using exportTcpSocket(). * 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 exporting their socket. * * \return \c true if the server has been successfully registered, \c false if not. */ bool StreamTubeServer::isRegistered() const { return mPriv->isRegistered; } /** * Return whether connection monitoring is enabled on this server. * * For technical reasons, connection monitoring can't be enabled when the server 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, newTcpConnection() and tcpConnectionClosed() won't be * emitted and tcpConnections() won't be populated. * * \return \c true if monitoring is enabled, \c false if not. */ bool StreamTubeServer::monitorsConnections() const { return mPriv->handler->monitorsConnections(); } /** * Return the host address and port of the currently exported TCP socket, if any. * * QHostAddress::Null is reported as the address and 0 as the port if no TCP socket has yet been * successfully exported. * * \return The host address and port values in a pair structure. */ QPair StreamTubeServer::exportedTcpSocketAddress() const { return qMakePair(mPriv->exportedAddr, mPriv->exportedPort); } /** * Return the fixed parameters, if any, which are sent along when offering the exported socket on * all handled tubes. * * To prevent accidentally leaving the current parameters to be sent when offering a different * socket, or vice versa, the parameters can only be set together with the socket using * exportTcpSocket(). Parameters often contain sensitive information such as session identifiers or * authentication credentials, which could then be used to maliciously access the service listening * on the other socket. * * If a custom dynamic ParametersGenerator was passed to exportTcpSocket() instead of a set of fixed * parameters, an empty set of parameters is returned. * * \return The parameters in a string-variant map. */ QVariantMap StreamTubeServer::exportedParameters() const { if (!mPriv->generator) { return QVariantMap(); } FixedParametersGenerator *generator = dynamic_cast(mPriv->generator); if (generator) { return generator->nextParameters(AccountPtr(), OutgoingStreamTubeChannelPtr(), ChannelRequestHints()); } else { return QVariantMap(); } } /** * Set the server to offer the socket listening at the given (\a address, \a port) combination as the * local endpoint of tubes handled in the future. * * A fixed set of protocol bootstrapping \a parameters can optionally be set to be sent along with all * tube offers until the next call to exportTcpSocket(). See the ParametersGenerator documentation * for an in-depth description of the parameter transfer mechanism, and a more flexible way to vary * the parameters between each handled tube. * * The handler is registered on the bus at the latest when this method or another exportTcpSocket() * overload is called for the first time, so one should check the return value of isRegistered() at * that point to verify that was successful. * * \param address The listen address of the socket. * \param port The port of the socket. * \param parameters The bootstrapping parameters in a string-value map. */ void StreamTubeServer::exportTcpSocket( const QHostAddress &address, quint16 port, const QVariantMap ¶meters) { if (address.isNull() || port == 0) { warning() << "Attempted to export null TCP socket address or zero port, ignoring"; return; } mPriv->exportedAddr = address; mPriv->exportedPort = port; mPriv->generator = 0; if (!parameters.isEmpty()) { mPriv->fixedGenerator.reset(new FixedParametersGenerator(parameters)); mPriv->generator = mPriv->fixedGenerator.data(); } mPriv->ensureRegistered(); } /** * Set the StreamTubeServer to offer the already listening TCP \a server as the local endpoint of tubes * handled in the future. * * This is just a convenience wrapper around * exportTcpSocket(const QHostAddress &, quint16, const QVariantMap &) to be used when the TCP * server code is implemented using the QtNetwork facilities. * * A fixed set of protocol bootstrapping \a parameters can optionally be set to be sent along with all * tube offers until the next call to exportTcpSocket(). See the ParametersGenerator documentation * for an in-depth description of the parameter transfer mechanism, and a more flexible way to vary * the parameters between each handled tube. * * \param server A pointer to the TCP server. * \param parameters The bootstrapping parameters in a string-value map. */ void StreamTubeServer::exportTcpSocket( const QTcpServer *server, const QVariantMap ¶meters) { if (!server->isListening()) { warning() << "Attempted to export non-listening QTcpServer, ignoring"; return; } if (server->serverAddress() == QHostAddress::Any) { return exportTcpSocket(QHostAddress::LocalHost, server->serverPort(), parameters); } else if (server->serverAddress() == QHostAddress::AnyIPv6) { return exportTcpSocket(QHostAddress::LocalHostIPv6, server->serverPort(), parameters); } else { return exportTcpSocket(server->serverAddress(), server->serverPort(), parameters); } } /** * Set the server to offer the socket listening at the given \a address - \a port combination as the * local endpoint of tubes handled in the future, sending the parameters from the given \a generator * along with the offers. * * The handler is registered on the bus at the latest when this method or another exportTcpSocket() * overload is called for the first time, so one should check the return value of isRegistered() at * that point to verify that was successful. * * \param address The listen address of the socket. * \param port The port of the socket. * \param generator A pointer to the bootstrapping parameters generator. */ void StreamTubeServer::exportTcpSocket( const QHostAddress &address, quint16 port, ParametersGenerator *generator) { if (address.isNull() || port == 0) { warning() << "Attempted to export null TCP socket address or zero port, ignoring"; return; } mPriv->exportedAddr = address; mPriv->exportedPort = port; mPriv->generator = generator; mPriv->ensureRegistered(); } /** * Set the server to offer the already listening TCP \a server as the local endpoint of tubes * handled in the future, sending the parameters from the given \a generator along with the offers. * * This is just a convenience wrapper around * exportTcpSocket(const QHostAddress &, quint16, ParametersGenerator *) to be used when the TCP * server code is implemented using the QtNetwork facilities. * * \param server A pointer to the TCP server. * \param generator A pointer to the bootstrapping parameters generator. */ void StreamTubeServer::exportTcpSocket( const QTcpServer *server, ParametersGenerator *generator) { if (!server->isListening()) { warning() << "Attempted to export non-listening QTcpServer, ignoring"; return; } if (server->serverAddress() == QHostAddress::Any) { return exportTcpSocket(QHostAddress::LocalHost, server->serverPort(), generator); } else if (server->serverAddress() == QHostAddress::AnyIPv6) { return exportTcpSocket(QHostAddress::LocalHostIPv6, server->serverPort(), generator); } else { return exportTcpSocket(server->serverAddress(), server->serverPort(), generator); } } /** * Return the tubes currently handled by the server. * * \return A list of Tube structures containing pointers to the account and tube channel for each * tube. */ QList StreamTubeServer::tubes() const { QList tubes; foreach (TubeWrapper *wrapper, mPriv->tubes.values()) { tubes.push_back(Tube(wrapper->mAcc, wrapper->mTube)); } return tubes; } /** * Return the ongoing TCP connections over tubes handled by this server. * * The returned mapping has the connection source addresses as keys and the contacts along with the * accounts which can be used to reach them as values. Connections through protocol backends which * don't support SocketAccessControlPort will be included as the potentially many values for the * null source address key, the pair (\c QHostAddress::Null, 0). * * This is effectively a state recovery accessor corresponding to the change notification signals * newTcpConnection() and tcpConnectionClosed(). * * The mapping is only populated if connection monitoring was requested when creating the server (so * monitorsConnections() returns \c true). * * \return The connections in a mapping with pairs of their source host addresses and ports as keys * and structures containing pointers to the account and remote contacts they're from as values. */ QHash, StreamTubeServer::RemoteContact> StreamTubeServer::tcpConnections() const { QHash, RemoteContact> conns; if (!monitorsConnections()) { warning() << "StreamTubeServer::tcpConnections() used, but connection monitoring is disabled"; return conns; } foreach (const Tube &tube, tubes()) { // Ignore invalid and non-Open tubes to prevent a few useless warnings in corner cases where // a tube is still being opened, or has been invalidated but we haven't processed that event // yet. if (!tube.channel()->isValid() || tube.channel()->state() != TubeChannelStateOpen) { continue; } if (tube.channel()->addressType() != SocketAddressTypeIPv4 && tube.channel()->addressType() != SocketAddressTypeIPv6) { continue; } QHash, uint> srcAddrConns = tube.channel()->connectionsForSourceAddresses(); QHash connContacts = tube.channel()->contactsForConnections(); QPair srcAddr; foreach (srcAddr, srcAddrConns.keys()) { ContactPtr contact = connContacts.take(srcAddrConns.value(srcAddr)); conns.insert(srcAddr, RemoteContact(tube.account(), contact)); } // The remaining values in our copy of connContacts are those which didn't have a // corresponding source address, probably because the service doesn't properly implement // Port AC foreach (const ContactPtr &contact, connContacts.values()) { // Insert them with an invalid source address as the key conns.insertMulti(qMakePair(QHostAddress(QHostAddress::Null), quint16(0)), RemoteContact(tube.account(), contact)); } } return conns; } void StreamTubeServer::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 OutgoingStreamTubeChannelPtr outgoing = OutgoingStreamTubeChannelPtr::qObjectCast(tube); if (outgoing) { emit tubeRequested(acc, outgoing, time, hints); } else { warning() << "The ChannelFactory used by StreamTubeServer must construct" << "OutgoingStreamTubeChannel subclasses for Requested=true StreamTubes"; tube->requestClose(); return; } if (!mPriv->tubes.contains(tube)) { debug().nospace() << "Offering socket " << mPriv->exportedAddr << ":" << mPriv->exportedPort << " on tube " << tube->objectPath(); QVariantMap params; if (mPriv->generator) { params = mPriv->generator->nextParameters(acc, outgoing, hints); } Q_ASSERT(!mPriv->exportedAddr.isNull() && mPriv->exportedPort != 0); TubeWrapper *wrapper = new TubeWrapper(acc, outgoing, mPriv->exportedAddr, mPriv->exportedPort, params, this); connect(wrapper, SIGNAL(offerFinished(TubeWrapper*,Tp::PendingOperation*)), SLOT(onOfferFinished(TubeWrapper*,Tp::PendingOperation*))); 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(outgoing, wrapper); } } void StreamTubeServer::onOfferFinished( TubeWrapper *wrapper, Tp::PendingOperation *op) { OutgoingStreamTubeChannelPtr tube = wrapper->mTube; if (op->isError()) { warning() << "Offer() failed, closing tube" << tube->objectPath() << '-' << op->errorName() << ':' << op->errorMessage(); if (wrapper->mTube->isValid()) { wrapper->mTube->requestClose(); } wrapper->mTube->disconnect(this); emit tubeClosed(wrapper->mAcc, wrapper->mTube, op->errorName(), op->errorMessage()); mPriv->tubes.remove(wrapper->mTube); wrapper->deleteLater(); } else { debug() << "Tube" << tube->objectPath() << "offered successfully"; } } void StreamTubeServer::onTubeInvalidated( Tp::DBusProxy *proxy, const QString &error, const QString &message) { OutgoingStreamTubeChannelPtr tube(qobject_cast(proxy)); Q_ASSERT(!tube.isNull()); TubeWrapper *wrapper = mPriv->tubes.value(tube); if (!wrapper) { // Offer finish with error already removed it return; } debug() << "Tube" << tube->objectPath() << "invalidated with" << error << ':' << message; emit tubeClosed(wrapper->mAcc, wrapper->mTube, error, message); mPriv->tubes.remove(tube); delete wrapper; } void StreamTubeServer::onNewConnection( TubeWrapper *wrapper, uint conn) { Q_ASSERT(monitorsConnections()); if (wrapper->mTube->addressType() == SocketAddressTypeIPv4 || wrapper->mTube->addressType() == SocketAddressTypeIPv6) { QHash, uint> srcAddrConns = wrapper->mTube->connectionsForSourceAddresses(); QHash connContacts = wrapper->mTube->contactsForConnections(); QPair srcAddr = srcAddrConns.key(conn); emit newTcpConnection(srcAddr.first, srcAddr.second, wrapper->mAcc, connContacts.value(conn), wrapper->mTube); } else { // No UNIX socket should ever have been offered yet Q_ASSERT(false); } } void StreamTubeServer::onConnectionClosed( TubeWrapper *wrapper, uint conn, const QString &error, const QString &message) { Q_ASSERT(monitorsConnections()); if (wrapper->mTube->addressType() == SocketAddressTypeIPv4 || wrapper->mTube->addressType() == SocketAddressTypeIPv6) { QHash, uint> srcAddrConns = wrapper->mTube->connectionsForSourceAddresses(); QHash connContacts = wrapper->mTube->contactsForConnections(); QPair srcAddr = srcAddrConns.key(conn); emit tcpConnectionClosed(srcAddr.first, srcAddr.second, wrapper->mAcc, connContacts.value(conn), error, message, wrapper->mTube); } else { // No UNIX socket should ever have been offered yet Q_ASSERT(false); } } /** * \fn void StreamTubeServer::tubeRequested(const AccountPtr &account, const * OutgoingStreamTubeChannelPtr &tube, const QDateTime &userActionTime, const ChannelRequestHints * &hints) * * Emitted when a tube has been requested for one of our services, and we've began handling it. * * This is emitted before invoking the ParametersGenerator, if any, for the tube. * * \param account A pointer to the account from which the tube was requested from. * \param tube A pointer to the actual tube channel. * \param userActionTime The time the request occurred at, if it was an user action. Should be used * for focus stealing prevention. * \param hints The hints passed to the request, if any. */ /** * \fn void StreamTubeServer::tubeClosed(const AccountPtr &account, const * OutgoingStreamTubeChannelPtr &tube, const QString &error, const QString &message) * * Emitted when a tube we've been handling (previously announced with tubeRequested()) has * encountered an error or has otherwise been closed from further communication. * * \param account A pointer to the account from which the tube was requested from. * \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 StreamTubeServer::newTcpConnection(const QHostAddress &sourceAddress, quint16 * sourcePort, const AccountPtr &account, const ContactPtr &contact, const * OutgoingStreamTubeChannelPtr &tube) * * Emitted when we have picked up a new TCP connection to the (current or previous) exported server * socket. This can be used to associate connections the protocol backend relays to the exported * socket with the remote contact who originally initiated them in the other end of the tube. * * This is only emitted if connection monitoring was enabled when creating the StreamTubeServer. * Additionally, if the protocol backend the connection is from doesn't support the * ::SocketAccessControlPort mechanism, the source address and port will always be invalid. * * \param sourceAddress The source address of the connection, or QHostAddress::Null if it can't be * resolved. * \param sourcePort The source port of the connection, or 0 if it can't be resolved. * \param account A pointer to the account through which the remote contact can be reached. * \param contact A pointer to the remote contact object. * \param tube A pointer to the tube channel through which the connection has been made. */ /** * \fn void StreamTubeServer::tcpConnectionClosed(const QHostAddress &sourceAddress, quint16 * sourcePort, const AccountPtr &account, const ContactPtr &contact, conts QString &error, const * QString &message, const OutgoingStreamTubeChannelPtr &tube) * * Emitted when a TCP connection (previously announced with newTcpConnection()) 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_QT4_ERROR_DISCONNECTED). * * This is only emitted if connection monitoring was enabled when creating the StreamTubeServer. * Additionally, if the protocol backend the connection is from doesn't support the * ::SocketAccessControlPort mechanism, the source address and port will always be invalid. * * \param sourceAddress The source address of the connection, or QHostAddress::Null if it couldn't * be resolved. * \param sourcePort The source port of the connection, or 0 if it couldn't be resolved. * \param account A pointer to the account through which the remote contact can be reached. * \param contact A pointer to the remote contact object. * \param error The D-Bus error name corresponding to the reason for the closure. * \param message A freeform debug message associated with the error. * \param tube A pointer to the tube channel through which the connection has been made. */ } // Tp