From 4742556c51273739f65d8a60fe3eb4e68ea34bab Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Wed, 10 Nov 2010 18:56:53 +0000 Subject: [PATCH] Implement telepathy tubes support. svn path=/trunk/KDE/kdenetwork/krfb/; revision=1195279 --- CMakeLists.txt | 1 + cmake/modules/FindTelepathyQt4.cmake | 39 ++ krfb/CMakeLists.txt | 28 ++ krfb/krfb_rfb_handler.client | 7 + krfb/main.cpp | 11 + ...lepathy.Client.krfb_rfb_handler.service.in | 3 + krfb/tubesclienthandler.cpp | 95 +++++ krfb/tubesclienthandler.h | 45 +++ krfb/tubesrfbserver.cpp | 353 ++++++++++++++++++ krfb/tubesrfbserver.h | 52 +++ 10 files changed, 634 insertions(+) create mode 100644 cmake/modules/FindTelepathyQt4.cmake create mode 100644 krfb/krfb_rfb_handler.client create mode 100644 krfb/org.freedesktop.Telepathy.Client.krfb_rfb_handler.service.in create mode 100644 krfb/tubesclienthandler.cpp create mode 100644 krfb/tubesclienthandler.h create mode 100644 krfb/tubesrfbserver.cpp create mode 100644 krfb/tubesrfbserver.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c2f5079a..9d2fc000 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ if(NOT INSIDE_KDENETWORK) include(CheckSymbolExists) find_package(LibVNCServer REQUIRED) + find_package(TelepathyQt4) set(CMAKE_REQUIRED_DEFINITIONS ${_KDE_PLATFORM_DEFINITIONS}) diff --git a/cmake/modules/FindTelepathyQt4.cmake b/cmake/modules/FindTelepathyQt4.cmake new file mode 100644 index 00000000..33381a94 --- /dev/null +++ b/cmake/modules/FindTelepathyQt4.cmake @@ -0,0 +1,39 @@ +# Try to find the Qt4 binding of the Telepathy library +# TELEPATHY_QT4_FOUND - system has TelepathyQt4 +# TELEPATHY_QT4_INCLUDE_DIR - the TelepathyQt4 include directory +# TELEPATHY_QT4_LIBRARIES - Link these to use TelepathyQt4 + +# Copyright (c) 2008, Allen Winter +# Copyright (c) 2009, Andre Moreira Magalhaes +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +set(TELEPATHY_QT4_FIND_REQUIRED ${TelepathyQt4_FIND_REQUIRED}) +if(TELEPATHY_QT4_INCLUDE_DIR AND TELEPATHY_QT4_LIBRARIES) + # Already in cache, be silent + set(TELEPATHY_QT4_FIND_QUIETLY TRUE) +endif(TELEPATHY_QT4_INCLUDE_DIR AND TELEPATHY_QT4_LIBRARIES) + +find_package(PkgConfig) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_TELEPATHY_QT4 QUIET TelepathyQt4>=0.1.8) +endif(PKG_CONFIG_FOUND) + +find_path(TELEPATHY_QT4_INCLUDE_DIR + NAMES TelepathyQt4/Types + HINTS + ${PC_TELEPATHY_QT4_INCLUDEDIR} + ${PC_TELEPATHY_QT4_INCLUDE_DIRS} +) + +find_library(TELEPATHY_QT4_LIBRARIES + NAMES telepathy-qt4 + HINTS + ${PC_TELEPATHY_QT4_LIBDIR} + ${PC_TELEPATHY_QT4_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(TELEPATHY_QT4 DEFAULT_MSG + TELEPATHY_QT4_LIBRARIES TELEPATHY_QT4_INCLUDE_DIR) diff --git a/krfb/CMakeLists.txt b/krfb/CMakeLists.txt index 44dc6e82..d60fb228 100644 --- a/krfb/CMakeLists.txt +++ b/krfb/CMakeLists.txt @@ -41,6 +41,11 @@ install (FILES # Second target: krfb - the app # itself. +if(TELEPATHY_QT4_FOUND) + add_definitions(-DKRFB_WITH_TELEPATHY_TUBES) + include_directories(${TELEPATHY_QT4_INCLUDE_DIR}) +endif(TELEPATHY_QT4_FOUND) + set (krfb_SRCS connectiondialog.cpp events.cpp @@ -59,6 +64,14 @@ set (krfb_SRCS invitationsrfbserver.cpp ) +if (TELEPATHY_QT4_FOUND) + set (krfb_SRCS + ${krfb_SRCS} + tubesrfbserver.cpp + tubesclienthandler.cpp + ) +endif (TELEPATHY_QT4_FOUND) + kde4_add_kcfg_files (krfb_SRCS krfbconfig.kcfgc ) @@ -88,6 +101,12 @@ target_link_libraries (krfb ${KDE4_KDEUI_LIBS} ) +if (TELEPATHY_QT4_FOUND) + target_link_libraries(krfb + ${TELEPATHY_QT4_LIBRARIES} + ) +endif (TELEPATHY_QT4_FOUND) + if (X11_XTest_FOUND) target_link_libraries (krfb ${X11_XTest_LIB} @@ -98,6 +117,15 @@ install (TARGETS krfb ${INSTALL_TARGETS_DEFAULT_ARGS} ) +if (TELEPATHY_QT4_FOUND) + configure_file(org.freedesktop.Telepathy.Client.krfb_rfb_handler.service.in + org.freedesktop.Telepathy.Client.krfb_rfb_handler.service) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Telepathy.Client.krfb_rfb_handler.service + DESTINATION ${DBUS_SERVICES_INSTALL_DIR}) + + install(FILES krfb_rfb_handler.client DESTINATION ${CMAKE_INSTALL_PREFIX}/share/telepathy/clients/) +endif (TELEPATHY_QT4_FOUND) + ########### install files ############### install (PROGRAMS krfb.desktop diff --git a/krfb/krfb_rfb_handler.client b/krfb/krfb_rfb_handler.client new file mode 100644 index 00000000..07a26701 --- /dev/null +++ b/krfb/krfb_rfb_handler.client @@ -0,0 +1,7 @@ +[org.freedesktop.Telepathy.Client] +Interfaces=org.freedesktop.Telepathy.Client.Handler; + +[org.freedesktop.Telepathy.Client.Handler.HandlerChannelFilter 0] +org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.StreamTube +org.freedesktop.Telepathy.Channel.Type.StreamTube.Service s=rfb +org.freedesktop.Telepathy.Channel.Requested b=true diff --git a/krfb/main.cpp b/krfb/main.cpp index e68a1d7c..cc780f28 100644 --- a/krfb/main.cpp +++ b/krfb/main.cpp @@ -32,6 +32,11 @@ #include #include +#ifdef KRFB_WITH_TELEPATHY_TUBES +# include "tubesclienthandler.h" +# include +#endif + #include #include @@ -104,6 +109,12 @@ int main(int argc, char *argv[]) //init the core InvitationsRfbServer::init(); +#ifdef KRFB_WITH_TELEPATHY_TUBES + Tp::ClientRegistrarPtr clientRegistrar = Tp::ClientRegistrar::create(); + clientRegistrar->registerClient(Tp::AbstractClientPtr(new TubesClientHandler), + "krfb_rfb_handler"); +#endif + //init the GUI ManageInvitationsDialog invitationsDialog; TrayIcon trayicon(&invitationsDialog); diff --git a/krfb/org.freedesktop.Telepathy.Client.krfb_rfb_handler.service.in b/krfb/org.freedesktop.Telepathy.Client.krfb_rfb_handler.service.in new file mode 100644 index 00000000..46189632 --- /dev/null +++ b/krfb/org.freedesktop.Telepathy.Client.krfb_rfb_handler.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.freedesktop.Telepathy.Client.krfb_rfb_handler +Exec=@CMAKE_INSTALL_PREFIX@/bin/krfb --nodialog diff --git a/krfb/tubesclienthandler.cpp b/krfb/tubesclienthandler.cpp new file mode 100644 index 00000000..e54de174 --- /dev/null +++ b/krfb/tubesclienthandler.cpp @@ -0,0 +1,95 @@ +/* This file is part of the KDE project + Copyright (C) 2009 Collabora Ltd + @author George Goldberg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "tubesclienthandler.h" +#include "tubesrfbserver.h" + +#include +#include + +#include + +using namespace Tp; + +static inline Tp::ChannelClassList channelClassList() +{ + QMap filter0; + filter0[TELEPATHY_INTERFACE_CHANNEL ".ChannelType"] = + QDBusVariant(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE); + filter0[TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE ".Service"] = QDBusVariant("rfb"); + filter0[TELEPATHY_INTERFACE_CHANNEL ".Requested"] = QDBusVariant(true); + + return Tp::ChannelClassList() << Tp::ChannelClass(filter0); +} + +TubesClientHandler::TubesClientHandler() + : AbstractClientHandler(channelClassList(), false) +{ + kDebug(); + + Tp::enableDebug(false); + Tp::enableWarnings(true); + + /* Registering telepathy types */ + registerTypes(); +} + +TubesClientHandler::~TubesClientHandler() +{ + kDebug(); +} + +bool TubesClientHandler::bypassApproval() const +{ + // Don't bypass approval of channels. + return false; +} + +void TubesClientHandler::handleChannels(const Tp::MethodInvocationContextPtr<> &context, + const Tp::AccountPtr &account, + const Tp::ConnectionPtr &connection, + const QList &channels, + const QList &requestsSatisfied, + const QDateTime &userActionTime, + const QVariantMap &handlerInfo) +{ + kDebug(); + + Q_UNUSED(account); + Q_UNUSED(connection); + Q_UNUSED(requestsSatisfied); + Q_UNUSED(userActionTime); + Q_UNUSED(handlerInfo); + + foreach(const Tp::ChannelPtr &channel, channels) { + kDebug() << "Incoming channel: " << channel; + + QVariantMap properties = channel->immutableProperties(); + + if (properties[TELEPATHY_INTERFACE_CHANNEL ".ChannelType"] == + TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAM_TUBE) { + + kDebug() << "Channel is a stream tube. Handling: " << channel; + new TubesRfbServer(channel); + } + } + context->setFinished(); +} + diff --git a/krfb/tubesclienthandler.h b/krfb/tubesclienthandler.h new file mode 100644 index 00000000..c03864d5 --- /dev/null +++ b/krfb/tubesclienthandler.h @@ -0,0 +1,45 @@ +/* This file is part of the KDE project + Copyright (C) 2009 Collabora Ltd + @author George Goldberg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KRFB_TUBESCLIENTHANDLER_H +#define KRFB_TUBESCLIENTHANDLER_H + +#include + +class TubesClientHandler : public Tp::AbstractClientHandler +{ +public: + TubesClientHandler(); + virtual ~TubesClientHandler(); + + virtual bool bypassApproval() const; + virtual void handleChannels(const Tp::MethodInvocationContextPtr<> &context, + const Tp::AccountPtr &account, + const Tp::ConnectionPtr &connection, + const QList &channels, + const QList &requestsSatisfied, + const QDateTime &userActionTime, + const QVariantMap &handlerInfo); + +}; + + +#endif // Header guard + diff --git a/krfb/tubesrfbserver.cpp b/krfb/tubesrfbserver.cpp new file mode 100644 index 00000000..fe666fa1 --- /dev/null +++ b/krfb/tubesrfbserver.cpp @@ -0,0 +1,353 @@ +/* + Copyright (C) 2009-2010 Collabora Ltd. + @author George Goldberg + @author George Kiagiadakis + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ +#include "tubesrfbserver.h" +#include "sockethelpers.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* workaround for QtDBus bug */ +struct StreamTubeAddress +{ + QString address; + uint port; +}; +Q_DECLARE_METATYPE(StreamTubeAddress); + +//Marshall the StreamTubeAddress data into a D-Bus argument +QDBusArgument &operator<<(QDBusArgument &argument, + const StreamTubeAddress &streamTubeAddress) +{ + argument.beginStructure(); + argument << streamTubeAddress.address << streamTubeAddress.port; + argument.endStructure(); + return argument; +} + +// Retrieve the StreamTubeAddress data from the D-Bus argument +const QDBusArgument &operator>>(const QDBusArgument &argument, + StreamTubeAddress &streamTubeAddress) +{ + argument.beginStructure(); + argument >> streamTubeAddress.address >> streamTubeAddress.port; + argument.endStructure(); + return argument; +} + +//************** + +class TubesRfbClient : public RfbClient +{ +public: + TubesRfbClient(rfbClientPtr client, QObject* parent = 0); + + virtual QString name(); + void setContact(const Tp::ContactPtr & contact); + +protected: + virtual rfbNewClientAction doHandle(); + +private: + Tp::ContactPtr m_contact; + bool m_doHandleCalled; +}; + +TubesRfbClient::TubesRfbClient(rfbClientPtr client, QObject* parent) + : RfbClient(client, parent), m_doHandleCalled(false) +{ +} + +QString TubesRfbClient::name() +{ + if (m_contact) { + return m_contact->alias(); + } else { + return RfbClient::name(); + } +} + +void TubesRfbClient::setContact(const Tp::ContactPtr & contact) +{ + m_contact = contact; + if (m_doHandleCalled) { + //doHandle has already been called, so we need to call the parent's implementation here + rfbNewClientAction action = RfbClient::doHandle(); + + //we were previously on hold, so we now need to act if + //the parent's doHandle() says we must do something else + if (action == RFB_CLIENT_ACCEPT) { + rfbStartOnHoldClient(rfbClient()); + } else if (action == RFB_CLIENT_REFUSE) { + rfbRefuseOnHoldClient(rfbClient()); + } //else if action == RFB_CLIENT_ON_HOLD there is nothing to do + } +} + +rfbNewClientAction TubesRfbClient::doHandle() +{ + if (!m_contact) { + //no associated contact yet, hold. + m_doHandleCalled = true; //act when a contact is set + return RFB_CLIENT_ON_HOLD; + } else { + //we have a contact, begin handling + return RfbClient::doHandle(); + } +} + +//************** + +struct TubesRfbServer::Private +{ + Tp::ChannelPtr channel; + QHash contactsPerPort; + QHash clientsPerPort; +}; + +TubesRfbServer::TubesRfbServer(const Tp::ChannelPtr & channel, QObject *parent) + : RfbServer(parent), d(new Private) +{ + kDebug() << "starting "; + + /* Registering struct containing the tube address */ + qDBusRegisterMetaType(); + + d->channel = channel; + connect(d->channel->becomeReady(), + SIGNAL(finished(Tp::PendingOperation *)), + SLOT(onChannelReady(Tp::PendingOperation *))); + + setListeningPort(6789); + setListeningAddress("127.0.0.1"); // Listen only on the loopback network interface +} + +TubesRfbServer::~TubesRfbServer() +{ + kDebug(); + delete d; +} + +RfbClient* TubesRfbServer::newClient(rfbClientPtr client) +{ + kDebug() << "new tubes client"; + + TubesRfbClient *c = new TubesRfbClient(client, this); + int port = peerPort(client->sock); + + d->clientsPerPort[port] = c; + if (d->contactsPerPort.contains(port)) { + kDebug() << "already have a contact"; + c->setContact(d->contactsPerPort[port]); + } + + return c; +} + +/************************** TP TUBES CODE ************************************/ + +void TubesRfbServer::close() +{ + kDebug(); + d->channel->requestClose(); +} + +void TubesRfbServer::cleanup() +{ + kDebug(); + + d->clientsPerPort.clear(); + d->contactsPerPort.clear(); + + stop(); + deleteLater(); +} + +void TubesRfbServer::onChannelReady(Tp::PendingOperation *op) +{ + kDebug(); + + if (op->isError()) { + kWarning() << "Getting channel ready faied:" << op->errorName() << op->errorMessage(); + KMessageBox::error(QApplication::activeWindow(), + i18n("An error occurred sharing your desktop."), + i18n("Error")); + cleanup(); + return; + } + + Tp::Contacts contacts = d->channel->groupContacts(); + + Tp::ContactManager *contactManager = d->channel->connection()->contactManager(); + + if (!contactManager) { + kWarning() << "Invalid Contact Manager."; + KMessageBox::error(QApplication::activeWindow(), + i18n("An unknown error occurred sharing your desktop."), + i18n("Error")); + close(); + return; + } + + QSet features; + features << Tp::Contact::FeatureAlias; + + connect(contactManager->upgradeContacts(contacts.toList(), features), + SIGNAL(finished(Tp::PendingOperation*)), + SLOT(onContactsUpgraded(Tp::PendingOperation*))); +} + +void TubesRfbServer::onContactsUpgraded(Tp::PendingOperation *op) +{ + kDebug(); + + if (op->isError()) { + kWarning() << "Upgrading contacts failed:" << op->errorName() << op->errorMessage(); + KMessageBox::error(QApplication::activeWindow(), + i18n("An unknown error occurred sharing your desktop."), + i18n("Error")); + close(); + return; + } + + offerTube(); +} + +void TubesRfbServer::offerTube() +{ + kDebug() << "Channel is ready!"; + + //start the rfb server + if (!start()) { + kWarning() << "Could not start rfb server"; + KMessageBox::error(QApplication::activeWindow(), + i18n("Failed to activate the rfb server."), + i18n("Error")); + close(); + return; + } + + connect(d->channel.data(), + SIGNAL(invalidated(Tp::DBusProxy*, const QString&, const QString&)), + SLOT(onChannelInvalidated(Tp::DBusProxy*, const QString&, + const QString&))); + + /* Interface used to control the tube state */ + Tp::Client::ChannelInterfaceTubeInterface *tubeInterface = d->channel->tubeInterface(); + + /* Interface used to control stream tube */ + Tp::Client::ChannelTypeStreamTubeInterface *streamTubeInterface = d->channel->streamTubeInterface(); + + if (streamTubeInterface && tubeInterface) { + kDebug() << "Offering tube"; + + connect(tubeInterface, + SIGNAL(TubeChannelStateChanged(uint)), + SLOT(onTubeStateChanged(uint))); + + // Offer the stream tube + StreamTubeAddress streamTubeAddress; + streamTubeAddress.address = listeningAddress(); + streamTubeAddress.port = listeningPort(); + + kDebug() << "Offering:" << streamTubeAddress.port << streamTubeAddress.address; + + QDBusVariant address; + address.setVariant(qVariantFromValue(streamTubeAddress)); + + QDBusPendingReply<> ret = streamTubeInterface->Offer( + uint(Tp::SocketAddressTypeIPv4), + address, + uint(Tp::SocketAccessControlPort), + QVariantMap()); + + connect(new QDBusPendingCallWatcher(ret, this), SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(onOfferTubeFinished(QDBusPendingCallWatcher*))); + connect(streamTubeInterface, + SIGNAL(NewRemoteConnection(uint, QDBusVariant, uint)), + SLOT(onNewRemoteConnection(uint, QDBusVariant, uint))); + } +} + +void TubesRfbServer::onOfferTubeFinished(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply reply = *watcher; + if (reply.isError()) { + kWarning() << "Offer tube failed:" << reply.error(); + + if (reply.error().name() == TELEPATHY_ERROR_NOT_AVAILABLE) { + KMessageBox::error(QApplication::activeWindow(), + i18n("An error occurred sharing your desktop. The person you are " + "trying to share your desktop with does not have the required " + "software installed to access it."), + i18n("Error")); + } else { + KMessageBox::error(QApplication::activeWindow(), + i18n("An unknown error occurred sharing your desktop."), + i18n("Error")); + } + } else { + kDebug() << "Offer Tube succeeded."; + } +} + +void TubesRfbServer::onTubeStateChanged(uint state) +{ + kDebug() << "Tube state changed:" << state; +} + +void TubesRfbServer::onNewRemoteConnection(uint handle, QDBusVariant connectionParam, uint connectionId) +{ + Q_UNUSED(connectionId); + + QVariant v = connectionParam.variant(); + kDebug() << "variant:" << v; + StreamTubeAddress ipv4address = qdbus_cast(v); + + kDebug() << "NewRemoteConnection: port:" << ipv4address.port << ipv4address.address; + + Q_FOREACH(const Tp::ContactPtr & c, d->channel->groupContacts()) { + if (c->handle().value(0) == handle) { + d->contactsPerPort[ipv4address.port] = c; + if (d->clientsPerPort.contains(ipv4address.port)) { + kDebug() << "client already exists"; + d->clientsPerPort[ipv4address.port]->setContact(c); + } + break; + } + } +} + +void TubesRfbServer::onChannelInvalidated(Tp::DBusProxy *proxy, + const QString &errorName, const QString &errorMessage) +{ + Q_UNUSED(proxy); + kDebug() << "Tube channel invalidated:" << errorName << errorMessage; + cleanup(); +} + +#include "tubesrfbserver.moc" diff --git a/krfb/tubesrfbserver.h b/krfb/tubesrfbserver.h new file mode 100644 index 00000000..e23e6192 --- /dev/null +++ b/krfb/tubesrfbserver.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2009-2010 Collabora Ltd. + @author George Goldberg + @author George Kiagiadakis + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ +#ifndef TUBESRFBSERVER_H +#define TUBESRFBSERVER_H + +#include "rfbserver.h" +#include + +class TubesRfbServer : public RfbServer +{ + Q_OBJECT +public: + TubesRfbServer(const Tp::ChannelPtr & channel, QObject* parent = 0); + virtual ~TubesRfbServer(); + + virtual RfbClient* newClient(rfbClientPtr client); + +private Q_SLOTS: + void close(); + void cleanup(); + void onChannelReady(Tp::PendingOperation *op); + void onContactsUpgraded(Tp::PendingOperation *op); + void offerTube(); + void onOfferTubeFinished(QDBusPendingCallWatcher *watcher); + void onTubeStateChanged(uint state); + void onNewRemoteConnection(uint handle, QDBusVariant connectionParam, uint connectionId); + void onChannelInvalidated(Tp::DBusProxy *proxy, + const QString &errorName, + const QString &errorMessage); + +private: + struct Private; + Private *const d; +}; + +#endif // TUBESRFBSERVER_H