[telepathy-qt4/master] OutgoingFileTransferChannel: Added specialized class for outgoing file transfer channels.

Andre Moreira Magalhaes (andrunko) andre.magalhaes at collabora.co.uk
Wed Sep 16 18:51:51 PDT 2009


---
 TelepathyQt4/OutgoingFileTransferChannel        |   13 +
 TelepathyQt4/outgoing-file-transfer-channel.cpp |  292 +++++++++++++++++++++++
 TelepathyQt4/outgoing-file-transfer-channel.h   |   76 ++++++
 3 files changed, 381 insertions(+), 0 deletions(-)
 create mode 100644 TelepathyQt4/OutgoingFileTransferChannel
 create mode 100644 TelepathyQt4/outgoing-file-transfer-channel.cpp
 create mode 100644 TelepathyQt4/outgoing-file-transfer-channel.h

diff --git a/TelepathyQt4/OutgoingFileTransferChannel b/TelepathyQt4/OutgoingFileTransferChannel
new file mode 100644
index 0000000..792225b
--- /dev/null
+++ b/TelepathyQt4/OutgoingFileTransferChannel
@@ -0,0 +1,13 @@
+#ifndef _TelepathyQt4_OutgoingFileTransferChannel_HEADER_GUARD_
+#define _TelepathyQt4_OutgoingFileTransferChannel_HEADER_GUARD_
+
+#ifndef IN_TELEPATHY_QT4_HEADER
+#define IN_TELEPATHY_QT4_HEADER
+#endif
+
+#include <TelepathyQt4/outgoing-file-transfer-channel.h>
+
+#undef IN_TELEPATHY_QT4_HEADER
+
+#endif
+// vim:set ft=cpp:
diff --git a/TelepathyQt4/outgoing-file-transfer-channel.cpp b/TelepathyQt4/outgoing-file-transfer-channel.cpp
new file mode 100644
index 0000000..aa79127
--- /dev/null
+++ b/TelepathyQt4/outgoing-file-transfer-channel.cpp
@@ -0,0 +1,292 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2009 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 <TelepathyQt4/OutgoingFileTransferChannel>
+
+#include "TelepathyQt4/_gen/outgoing-file-transfer-channel.moc.hpp"
+
+#include "TelepathyQt4/debug-internal.h"
+
+#include <TelepathyQt4/Connection>
+#include <TelepathyQt4/PendingFailure>
+#include <TelepathyQt4/PendingVariant>
+#include <TelepathyQt4/Types>
+
+#include <QIODevice>
+#include <QTcpSocket>
+
+#define BUFFER_SIZE 4096
+
+namespace Tp
+{
+
+struct OutgoingFileTransferChannel::Private
+{
+    Private(OutgoingFileTransferChannel *parent);
+    ~Private();
+
+    // Public object
+    OutgoingFileTransferChannel *parent;
+
+    // Introspection
+    QIODevice *input;
+    QTcpSocket *socket;
+    SocketAddressIPv4 addr;
+};
+
+OutgoingFileTransferChannel::Private::Private(OutgoingFileTransferChannel *parent)
+    : parent(parent),
+      input(0),
+      socket(0)
+{
+}
+
+OutgoingFileTransferChannel::Private::~Private()
+{
+}
+
+/**
+ * \class OutgoingFileTransferChannel
+ * \ingroup clientchannel
+ * \headerfile <TelepathyQt4/outgoing-file-transfer-channel.h> <TelepathyQt4/OutgoingFileTransferChannel>
+ *
+ * High-level proxy object for accessing remote %Channel objects of the
+ * FileTransfer channel type. These channels can be used to transfer one file
+ * to or from a contact.
+ *
+ * This subclass of Channel will eventually provide a high-level API for the
+ * FileTransfer interface. Until then, it's just a Channel.
+ */
+
+const Feature OutgoingFileTransferChannel::FeatureCore = Feature(OutgoingFileTransferChannel::staticMetaObject.className(), 0);
+
+OutgoingFileTransferChannelPtr OutgoingFileTransferChannel::create(const ConnectionPtr &connection,
+        const QString &objectPath, const QVariantMap &immutableProperties)
+{
+    return OutgoingFileTransferChannelPtr(new OutgoingFileTransferChannel(connection, objectPath,
+                immutableProperties));
+}
+
+/**
+ * Creates a OutgoingFileTransferChannel associated with the given object on the
+ * same service as the given connection.
+ *
+ * \param connection Connection owning this OutgoingFileTransferChannel, and
+ *                   specifying the service.
+ * \param objectPath Path to the object on the service.
+ * \param immutableProperties The immutable properties of the channel, as
+ *                            signalled by NewChannels or returned by
+ *                            CreateChannel or EnsureChannel
+ */
+OutgoingFileTransferChannel::OutgoingFileTransferChannel(
+        const ConnectionPtr &connection,
+        const QString &objectPath,
+        const QVariantMap &immutableProperties)
+    : FileTransferChannel(connection, objectPath, immutableProperties),
+      mPriv(new Private(this))
+{
+}
+
+/**
+ * Class destructor.
+ */
+OutgoingFileTransferChannel::~OutgoingFileTransferChannel()
+{
+    delete mPriv;
+}
+
+/**
+ * Provide the file for an outgoing file transfer which has been offered.
+ * The state will change to %FileTransferStateOpen as soon as the transfer
+ * starts.
+ * The given input device should not be destroyed until the state()
+ * changes to %FileTransferStateCompleted or %FileTransferStateCancelled.
+ * If input is a sequential device QIODevice::isSequential(), it should be
+ * closed when no more data is available, so we know when to stop reading.
+ *
+ * \param input A QIODevice object where the data will be read from.
+ * \return A PendingOperation object which will emit PendingOperation::finished
+ *         when the call has finished.
+ * \sa stateChanged(), state(), stateReason()
+ */
+PendingOperation *OutgoingFileTransferChannel::provideFile(QIODevice *input)
+{
+    // let's fail here direclty as we may only have one device to handle
+    if (mPriv->input) {
+        warning() << "File transfer can only be started once in the same "
+            "channel";
+        return new PendingFailure(this, TELEPATHY_ERROR_NOT_AVAILABLE,
+                "File transfer can only be started once in the same channel");
+    }
+
+    if ((!input->isOpen() && !input->open(QIODevice::ReadOnly)) &&
+        !input->isReadable()) {
+        warning() << "Unable to open IO device for reading";
+        return new PendingFailure(this, TELEPATHY_ERROR_PERMISSION_DENIED,
+                "Unable to open IO device for reading");
+    }
+
+    mPriv->input = input;
+    connect(input,
+            SIGNAL(aboutToClose()),
+            SLOT(onInputAboutToClose()));
+
+    PendingVariant *pv = new PendingVariant(
+            fileTransferInterface(BypassInterfaceCheck)->ProvideFile(
+                SocketAddressTypeIPv4,
+                SocketAccessControlLocalhost,
+                QDBusVariant(QVariant(QString()))),
+            this);
+    connect(pv,
+            SIGNAL(finished(Tp::PendingOperation*)),
+            SLOT(onProvideFileFinished(Tp::PendingOperation*)));
+    return pv;
+}
+
+void OutgoingFileTransferChannel::onProvideFileFinished(PendingOperation *op)
+{
+    if (op->isError()) {
+        warning() << "Error providing file transfer " <<
+            op->errorName() << ":" << op->errorMessage();
+        return;
+    }
+
+    PendingVariant *pv = qobject_cast<PendingVariant *>(op);
+    mPriv->addr = qdbus_cast<SocketAddressIPv4>(pv->result());
+    debug().nospace() << "Got address " << mPriv->addr.address <<
+        ":" << mPriv->addr.port;
+
+    if (state() == FileTransferStateOpen) {
+        connectToHost();
+    }
+}
+
+void OutgoingFileTransferChannel::connectToHost()
+{
+    if (isConnected() || mPriv->addr.address.isNull()) {
+        return;
+    }
+
+    mPriv->socket = new QTcpSocket(this);
+
+    connect(mPriv->socket, SIGNAL(connected()),
+            SLOT(onSocketConnected()));
+    connect(mPriv->socket, SIGNAL(disconnected()),
+            SLOT(onSocketDisconnected()));
+    connect(mPriv->socket, SIGNAL(error(QAbstractSocket::SocketError)),
+            SLOT(onSocketError(QAbstractSocket::SocketError)));
+    connect(mPriv->socket, SIGNAL(bytesWritten(qint64)),
+            SLOT(doTransfer()));
+
+    debug().nospace() << "Connecting to host " <<
+        mPriv->addr.address << ":" << mPriv->addr.port << "...";
+    mPriv->socket->connectToHost(mPriv->addr.address, mPriv->addr.port);
+}
+
+void OutgoingFileTransferChannel::onSocketConnected()
+{
+    debug() << "Connected to host!";
+    setConnected();
+
+    connect(mPriv->input, SIGNAL(readyRead()),
+            SLOT(doTransfer()));
+
+    debug() << "Starting transfer...";
+    doTransfer();
+}
+
+void OutgoingFileTransferChannel::onSocketDisconnected()
+{
+    debug() << "Disconnected from host!";
+    setFinished();
+}
+
+void OutgoingFileTransferChannel::onSocketError(QAbstractSocket::SocketError error)
+{
+    debug() << "Socket error" << error;
+    setFinished();
+}
+
+void OutgoingFileTransferChannel::onInputAboutToClose()
+{
+    debug() << "Input closed!";
+
+    // in case of sequential devices, we should read everything from it and
+    // write to the socket. Let's not do this for non-sequential devices as we
+    // don't want to read a whole file into memory.
+    if (isConnected() && mPriv->input->isSequential()) {
+        QByteArray data;
+        data = mPriv->input->readAll();
+        mPriv->socket->write(data); // never fails
+    }
+
+    setFinished();
+}
+
+void OutgoingFileTransferChannel::doTransfer()
+{
+    // read 16k each time, as input can be a QFile, we don't want to block
+    // reading the whole file
+    char buffer[16 * 1024];
+    qint64 len = mPriv->input->read(buffer, sizeof(buffer));
+
+    if (len > 0) {
+        mPriv->socket->write(buffer, len); // never fails
+    }
+
+    if (len == -1 || (!mPriv->input->isSequential() && mPriv->input->atEnd())) {
+        // error or EOF
+        setFinished();
+        return;
+    }
+}
+
+void OutgoingFileTransferChannel::setFinished()
+{
+    if (isFinished()) {
+        // it shouldn't happen but let's make sure
+        return;
+    }
+
+    if (mPriv->socket) {
+        disconnect(mPriv->socket, SIGNAL(connected()),
+                   this, SLOT(onSocketConnected()));
+        disconnect(mPriv->socket, SIGNAL(disconnected()),
+                   this, SLOT(onSocketDisconnected()));
+        disconnect(mPriv->socket, SIGNAL(error(QAbstractSocket::SocketError)),
+                   this, SLOT(onSocketError(QAbstractSocket::SocketError)));
+        disconnect(mPriv->socket, SIGNAL(bytesWritten(qint64)),
+                   this, SLOT(doTransfer()));
+        mPriv->socket->close();
+    }
+
+    if (mPriv->input) {
+        disconnect(mPriv->input, SIGNAL(aboutToClose()),
+                   this, SLOT(onInputAboutToClose()));
+        disconnect(mPriv->input, SIGNAL(readyRead()),
+                   this, SLOT(doTransfer()));
+        mPriv->input->close();
+    }
+
+    FileTransferChannel::setFinished();
+}
+
+} // Tp
diff --git a/TelepathyQt4/outgoing-file-transfer-channel.h b/TelepathyQt4/outgoing-file-transfer-channel.h
new file mode 100644
index 0000000..9a4dcba
--- /dev/null
+++ b/TelepathyQt4/outgoing-file-transfer-channel.h
@@ -0,0 +1,76 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2009 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
+ */
+
+#ifndef _TelepathyQt4_outgoing_file_transfer_channel_h_HEADER_GUARD_
+#define _TelepathyQt4_outgoing_file_transfer_channel_h_HEADER_GUARD_
+
+#ifndef IN_TELEPATHY_QT4_HEADER
+#error IN_TELEPATHY_QT4_HEADER
+#endif
+
+#include <TelepathyQt4/FileTransferChannel>
+
+#include <QAbstractSocket>
+
+namespace Tp
+{
+
+class OutgoingFileTransferChannel : public FileTransferChannel
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(OutgoingFileTransferChannel)
+
+public:
+    static const Feature FeatureCore;
+
+    static OutgoingFileTransferChannelPtr create(const ConnectionPtr &connection,
+            const QString &objectPath, const QVariantMap &immutableProperties);
+
+    virtual ~OutgoingFileTransferChannel();
+
+    PendingOperation *provideFile(QIODevice *input);
+
+protected:
+    OutgoingFileTransferChannel(const ConnectionPtr &connection,
+            const QString &objectPath,
+            const QVariantMap &immutableProperties);
+
+private Q_SLOTS:
+    void onProvideFileFinished(Tp::PendingOperation *op);
+
+    void onSocketConnected();
+    void onSocketDisconnected();
+    void onSocketError(QAbstractSocket::SocketError error);
+    void onInputAboutToClose();
+    void doTransfer();
+
+private:
+    void connectToHost();
+    void setFinished();
+
+    struct Private;
+    friend struct Private;
+    Private *mPriv;
+};
+
+} // Tp
+
+#endif
-- 
1.5.6.5




More information about the telepathy-commits mailing list