summaryrefslogtreecommitdiff
path: root/qt4/examples/stream-tubes/tube-initiator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qt4/examples/stream-tubes/tube-initiator.cpp')
-rw-r--r--qt4/examples/stream-tubes/tube-initiator.cpp274
1 files changed, 274 insertions, 0 deletions
diff --git a/qt4/examples/stream-tubes/tube-initiator.cpp b/qt4/examples/stream-tubes/tube-initiator.cpp
new file mode 100644
index 000000000..dd5aa4da5
--- /dev/null
+++ b/qt4/examples/stream-tubes/tube-initiator.cpp
@@ -0,0 +1,274 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2010-2011 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ * 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 "tube-initiator.h"
+
+#include <TelepathyQt4/Account>
+#include <TelepathyQt4/AccountFactory>
+#include <TelepathyQt4/ClientRegistrar>
+#include <TelepathyQt4/ContactFactory>
+#include <TelepathyQt4/Connection>
+#include <TelepathyQt4/ConnectionFactory>
+#include <TelepathyQt4/Constants>
+#include <TelepathyQt4/Contact>
+#include <TelepathyQt4/ContactCapabilities>
+#include <TelepathyQt4/ContactFactory>
+#include <TelepathyQt4/ContactManager>
+#include <TelepathyQt4/Debug>
+#include <TelepathyQt4/OutgoingStreamTubeChannel>
+#include <TelepathyQt4/PendingChannelRequest>
+#include <TelepathyQt4/PendingContacts>
+#include <TelepathyQt4/PendingOperation>
+#include <TelepathyQt4/PendingReady>
+#include <TelepathyQt4/Presence>
+#include <TelepathyQt4/StreamTubeChannel>
+#include <TelepathyQt4/StreamTubeServer>
+
+#include <QDebug>
+#include <QTcpServer>
+#include <QTcpSocket>
+
+TubeInitiator::TubeInitiator(const QString &accountName, const QString &receiver, QObject *parent)
+ : QObject(parent),
+ mReceiver(receiver),
+ mTubeRequested(false)
+{
+ mServer = new QTcpServer(this);
+ connect(mServer, SIGNAL(newConnection()), this, SLOT(onTcpServerNewConnection()));
+ mServer->listen();
+
+ AccountFactoryPtr accountFactory = AccountFactory::create(
+ QDBusConnection::sessionBus(), Account::FeatureCore);
+ // We only care about CONNECTED connections, so let's specify that in a Connection Factory
+ ConnectionFactoryPtr connectionFactory = ConnectionFactory::create(
+ QDBusConnection::sessionBus(), Connection::FeatureCore | Connection::FeatureConnected);
+ ChannelFactoryPtr channelFactory = ChannelFactory::create(QDBusConnection::sessionBus());
+ ContactFactoryPtr contactFactory = ContactFactory::create(Contact::FeatureAlias);
+
+ mTubeServer = StreamTubeServer::create(
+ QStringList() << QLatin1String("tp-qt4-stube-example"), /* one peer-to-peer service */
+ QStringList(), /* no Room tube services */
+ QString(), /* autogenerated Client name */
+ true, /* do monitor connections */
+ accountFactory, connectionFactory, channelFactory, contactFactory);
+
+ connect(mTubeServer.data(),
+ SIGNAL(newTcpConnection(QHostAddress,quint16,Tp::AccountPtr,Tp::ContactPtr,Tp::OutgoingStreamTubeChannelPtr)),
+ SLOT(onTubeNewConnection(QHostAddress,quint16,Tp::AccountPtr,Tp::ContactPtr)));
+ connect(mTubeServer.data(),
+ SIGNAL(tcpConnectionClosed(QHostAddress,quint16,Tp::AccountPtr,Tp::ContactPtr,QString,QString,Tp::OutgoingStreamTubeChannelPtr)),
+ SLOT(onTubeConnectionClosed(QHostAddress,quint16,Tp::AccountPtr,Tp::ContactPtr,QString,QString)));
+
+ mTubeServer->exportTcpSocket(mServer);
+ Q_ASSERT(mTubeServer->isRegistered());
+
+ connect(accountFactory->proxy(
+ TP_QT4_ACCOUNT_MANAGER_BUS_NAME,
+ TP_QT4_ACCOUNT_OBJECT_PATH_BASE + QLatin1Char('/') + accountName,
+ connectionFactory,
+ mTubeServer->registrar()->channelFactory(),
+ mTubeServer->registrar()->contactFactory()),
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(onAccountReady(Tp::PendingOperation *)));
+}
+
+TubeInitiator::~TubeInitiator()
+{
+}
+
+void TubeInitiator::onAccountReady(Tp::PendingOperation *op)
+{
+ if (op->isError()) {
+ qWarning() << "Account cannot become ready - " <<
+ op->errorName() << '-' << op->errorMessage();
+ QCoreApplication::exit(1);
+ return;
+ }
+
+ PendingReady *ready = qobject_cast<PendingReady *>(op);
+ Q_ASSERT(ready != NULL);
+
+ mAccount = AccountPtr::qObjectCast(ready->proxy());
+
+ qDebug() << "Account ready";
+ connect(mAccount.data(),
+ SIGNAL(connectionChanged(Tp::ConnectionPtr)),
+ SLOT(onAccountConnectionChanged(Tp::ConnectionPtr)));
+
+ if (mAccount->connection().isNull()) {
+ qDebug() << "The account given has no Connection. Please set it online to continue.";
+ } else {
+ onAccountConnectionChanged(mAccount->connection());
+ }
+}
+
+void TubeInitiator::onAccountConnectionChanged(const ConnectionPtr &conn)
+{
+ if (!conn) {
+ return;
+ }
+
+ // Connection::FeatureConnected being in the Connection Factory means that we shouldn't get
+ // pre-Connected Connections
+ Q_ASSERT(conn->isValid());
+ Q_ASSERT(conn->status() == ConnectionStatusConnected);
+
+ qDebug() << "Got a Connected Connection!";
+ mConn = conn;
+
+ qDebug() << "Creating contact object for receiver" << mReceiver;
+ connect(mConn->contactManager()->contactsForIdentifiers(QStringList() << mReceiver,
+ Features(Contact::FeatureCapabilities)),
+ SIGNAL(finished(Tp::PendingOperation *)),
+ SLOT(onContactRetrieved(Tp::PendingOperation *)));
+}
+
+void TubeInitiator::onContactRetrieved(PendingOperation *op)
+{
+ if (op->isError()) {
+ qWarning() << "Unable to create contact object for receiver" <<
+ mReceiver << "-" << op->errorName() << ": " << op->errorMessage();
+ return;
+ }
+
+ PendingContacts *pc = qobject_cast<PendingContacts *>(op);
+ Q_ASSERT(pc->contacts().size() == 1);
+ mContact = pc->contacts().first();
+
+ qDebug() << "Checking contact capabilities...";
+ connect(mContact.data(),
+ SIGNAL(capabilitiesChanged(Tp::ContactCapabilities)),
+ SLOT(onContactCapabilitiesChanged()));
+
+ if (mContact->capabilities().streamTubes(QLatin1String("tp-qt4-stube-example"))) {
+ onContactCapabilitiesChanged();
+ } else {
+ qDebug() << "The remote contact needs to be online and have the receiver application running to continue";
+ }
+}
+
+void TubeInitiator::onContactCapabilitiesChanged()
+{
+ if (mTubeRequested) {
+ return;
+ }
+
+ if (mContact->capabilities().streamTubes(QLatin1String("tp-qt4-stube-example"))) {
+ qDebug() << "The remote contact is capable of receiving tubes with service tp-qt4-stube-example now";
+
+ mTubeRequested = true;
+ connect(mAccount->createStreamTube(
+ mContact->id(),
+ QLatin1String("tp-qt4-stube-example")),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onTubeRequestFinished(Tp::PendingOperation*)));
+ }
+}
+
+void TubeInitiator::onTubeRequestFinished(PendingOperation *op)
+{
+ if (op->isError()) {
+ qWarning() << "Unable to request stream tube channel -" <<
+ op->errorName() << ": " << op->errorMessage();
+ return;
+ }
+
+ qDebug() << "Stream tube channel request finished successfully!";
+}
+
+void TubeInitiator::onTubeNewConnection(
+ const QHostAddress &srcAddr,
+ quint16 srcPort,
+ const Tp::AccountPtr &account,
+ const Tp::ContactPtr &contact)
+{
+ qDebug() << "Source port" << srcPort << "is" << contact->alias();
+ mConnAliases.insert(qMakePair(srcAddr, srcPort), contact->alias());
+}
+
+void TubeInitiator::onTubeConnectionClosed(
+ const QHostAddress &srcAddr,
+ quint16 srcPort,
+ const Tp::AccountPtr &account,
+ const Tp::ContactPtr &contact,
+ const QString &error,
+ const QString &message)
+{
+ qDebug() << "Connection from source port" << srcPort << "closed with" << error << ':' << message;
+ mConnAliases.remove(qMakePair(srcAddr, srcPort));
+}
+
+void TubeInitiator::onTcpServerNewConnection()
+{
+ qDebug() << "Pending connection found";
+ QTcpSocket *socket = mServer->nextPendingConnection();
+ connect(socket, SIGNAL(readyRead()), this, SLOT(onDataFromSocket()));
+}
+
+void TubeInitiator::onDataFromSocket()
+{
+ QAbstractSocket *source = qobject_cast<QAbstractSocket *>(sender());
+ QString data = QLatin1String(source->readLine());
+ data.remove(QLatin1Char('\n'));
+
+ // Look up the alias of the remote sending us data based on the tube connection tracking. Of
+ // course, this is slightly overengineered; given that this example uses one-to-one tubes (not
+ // group/MUC tubes), all remote connections are always from the same contact we opened the tube
+ // to in the first place.
+ QPair<QHostAddress, quint16> peerAddr(source->peerAddress(), source->peerPort());
+ if (mConnAliases.contains(peerAddr)) {
+ qDebug() << "New data from contact" << mConnAliases.value(peerAddr) << ':' << data;
+ } else {
+ // We haven't found out the identity for this connection yet. This may happen, because the
+ // TCP server listen socket and the D-Bus connection are on separate sockets and have no
+ // mutual ordering guarantees.
+ //
+ // A GUI application would likely use a placeholder item and lazily replace it with a
+ // representation of the contact when newTcpConnection for this source address has been
+ // received. A non-interactive daemon or alike might, in turn, queue incoming data until the
+ // contact is known, or just carry on if the contact's details aren't needed (yet).
+ qDebug() << "New data from source port" << peerAddr.second << ':' << data;
+ }
+
+ if (data == QLatin1String("Hi there!!")) {
+ source->write(QByteArray("Hey back mate.\n"));
+ } else {
+ source->write(QByteArray("Sorry, I have no time for you right now.\n"));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ if (argc != 3) {
+ qDebug() << "usage:" << argv[0] << "<account name, as in mc-tool list> <receiver contact ID>";
+ return 1;
+ }
+
+ Tp::registerTypes();
+
+ new TubeInitiator(QLatin1String(argv[1]), QLatin1String(argv[2]), &app);
+
+ return app.exec();
+}
+
+#include "_gen/tube-initiator.moc.hpp"