diff options
Diffstat (limited to 'TelepathyQt/base-channel.cpp')
-rw-r--r-- | TelepathyQt/base-channel.cpp | 571 |
1 files changed, 499 insertions, 72 deletions
diff --git a/TelepathyQt/base-channel.cpp b/TelepathyQt/base-channel.cpp index c13e8f04..6ea739d4 100644 --- a/TelepathyQt/base-channel.cpp +++ b/TelepathyQt/base-channel.cpp @@ -39,6 +39,8 @@ #include <QDateTime> #include <QString> +#include <QTcpServer> +#include <QTcpSocket> #include <QVariantMap> namespace Tp @@ -782,28 +784,39 @@ QString BaseChannelMessagesInterface::sendMessage(const Tp::MessagePartList &mes // Chan.T.FileTransfer // The BaseChannelFileTransferType code is fully or partially generated by the TelepathyQt-Generator. struct TP_QT_NO_EXPORT BaseChannelFileTransferType::Private { + Private(BaseChannelFileTransferType *parent, - const QString &contentType, - const QString &filename, - qulonglong size, - uint contentHashType, - const QString &contentHash, - const QString &description, - const QDateTime &date, - const Tp::SupportedSocketMap &availableSocketTypes) + const QVariantMap &request) : state(Tp::FileTransferStatePending), - contentType(contentType), - filename(filename), - size(size), - contentHashType(contentHashType), - contentHash(contentHash), - description(description), - date(date), - availableSocketTypes(availableSocketTypes), transferredBytes(0), initialOffset(0), + deviceOffset(0), + device(0), + weOpenedDevice(false), + serverSocket(0), + clientSocket(0), adaptee(new BaseChannelFileTransferType::Adaptee(parent)) { + contentType = request.value(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".ContentType")).toString(); + filename = request.value(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".Filename")).toString(); + size = request.value(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".Size")).toULongLong(); + contentHashType = request.value(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".ContentHashType")).toUInt(); + contentHash = request.value(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".ContentHash")).toString(); + description = request.value(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".Description")).toString(); + qint64 dbusDataValue = request.value(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".Date")).value<qint64>(); + if (dbusDataValue != 0) { + date.setTime_t(dbusDataValue); + } + + if (request.contains(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".URI"))) { + uri = request.value(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".URI")).toString(); + } + + if (request.value(TP_QT_IFACE_CHANNEL + QLatin1String(".Requested")).toBool()) { + direction = BaseChannelFileTransferType::Outgoing; + } else { + direction = BaseChannelFileTransferType::Incoming; + } } uint state; @@ -814,14 +827,20 @@ struct TP_QT_NO_EXPORT BaseChannelFileTransferType::Private { QString contentHash; QString description; QDateTime date; - Tp::SupportedSocketMap availableSocketTypes; qulonglong transferredBytes; qulonglong initialOffset; + qulonglong deviceOffset; QString uri; QString fileCollection; - AcceptFileCallback acceptFileCB; - ProvideFileCallback provideFileCB; + QIODevice *device; // A socket to read or write file to underlying connection manager + bool weOpenedDevice; + QTcpServer *serverSocket; // Server socket is an implementation detail. + QIODevice *clientSocket; // A socket to communicate with a Telepathy client + BaseChannelFileTransferType::Direction direction; BaseChannelFileTransferType::Adaptee *adaptee; + + friend class BaseChannelFileTransferType::Adaptee; + }; BaseChannelFileTransferType::Adaptee::Adaptee(BaseChannelFileTransferType *interface) @@ -899,29 +918,55 @@ QString BaseChannelFileTransferType::Adaptee::fileCollection() const return mInterface->fileCollection(); } +void BaseChannelFileTransferType::Adaptee::setUri(const QString &uri) +{ + mInterface->setUri(uri); +} + void BaseChannelFileTransferType::Adaptee::acceptFile(uint addressType, uint accessControl, const QDBusVariant &accessControlParam, qulonglong offset, const Tp::Service::ChannelTypeFileTransferAdaptor::AcceptFileContextPtr &context) { - qDebug() << "BaseChannelFileTransferType::Adaptee::acceptFile"; + debug() << "BaseChannelFileTransferType::Adaptee::acceptFile"; + + if (mInterface->mPriv->device) { + context->setFinishedWithError(TP_QT_ERROR_NOT_AVAILABLE, QLatin1String("File transfer can only be started once in the same channel")); + return; + } + DBusError error; - QDBusVariant address = mInterface->acceptFile(addressType, accessControl, accessControlParam, offset, &error); + mInterface->createSocket(addressType, accessControl, accessControlParam, &error); + if (error.isValid()) { context->setFinishedWithError(error.name(), error.message()); return; } + + QDBusVariant address = mInterface->socketAddress(); + + mInterface->setState(Tp::FileTransferStateAccepted, Tp::FileTransferStateChangeReasonNone); + + mInterface->mPriv->initialOffset = offset; + QMetaObject::invokeMethod(this, "initialOffsetDefined", Q_ARG(qulonglong, offset)); + context->setFinished(address); } void BaseChannelFileTransferType::Adaptee::provideFile(uint addressType, uint accessControl, const QDBusVariant &accessControlParam, const Tp::Service::ChannelTypeFileTransferAdaptor::ProvideFileContextPtr &context) { - qDebug() << "BaseChannelFileTransferType::Adaptee::provideFile"; + debug() << "BaseChannelFileTransferType::Adaptee::provideFile"; + DBusError error; - QDBusVariant address = mInterface->provideFile(addressType, accessControl, accessControlParam, &error); + mInterface->createSocket(addressType, accessControl, accessControlParam, &error); + if (error.isValid()) { context->setFinishedWithError(error.name(), error.message()); return; } + + QDBusVariant address = mInterface->socketAddress(); + + mInterface->tryToOpenAndTransfer(); context->setFinished(address); } @@ -930,23 +975,250 @@ void BaseChannelFileTransferType::Adaptee::provideFile(uint addressType, uint ac * \ingroup servicecm * \headerfile TelepathyQt/base-channel.h <TelepathyQt/BaseChannel> * - * \brief Base class for implementations of Channel.Type.FileTransfer + * \brief Base class of Channel.Type.FileTransfer channel type. + * + * Default implementation currently support only IPv4 and IPv6 sockets with localhost access control. + * + * Usage: + * -# Add FileTransfer to the list of the protocol requestable channel classes. + * -# Add FileTransfer to the list of the connection requestable channel classes. + * -# Setup ContactCapabilities interface and ensure that FileTransfer requestable channel class presence matches to + * actual local (!) and remote contacts capabilities. + * -# Implement initial FileTransfer channel support in createChannel callback. + * -# The channel of interest are those with channelType TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER. + * -# Create BaseChannel and plug BaseChannelFileTransferType interface. + * -# If transferInterface->direction() is Outgoing, notify the remote side. + * -# Implement incoming file request handler: + * -# Properly setup the request details, take care on TargetHandle and InitiatorHandle. + * -# Call BaseConnection::createChannel() with the details. Do not suppress handler! + * -# Use remoteProvideFile() to pass the input device and its offset. + * -# transferredBytes property will be updated automatically on bytes written to the client socket. + * -# Implement "remote side accepted transfer" handler: + * -# Use remoteAcceptFile() to pass the requested initial offset and output device. + * -# Update transferredBytes property on bytes written to the remote side. + * + * Incoming transfer process: + * -# Connection manager creates not requested channel with ChannelType = TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER and + * other properties, such as Filename, Size and ContentType. + * -# The channel initial state is Pending. + * -# At any time: + * -# Client calls AcceptFile method to configure the socket and request an initial offset. The implementation + * calls createSocket(), which should trigger (now or later) a call to setClientSocket() to setup the client + * socket. socketAddress() method used to return the socket address. This changes the state to Accepted. + * -# The connection manager calls remoteProvideFile() method to pass the input device and it's offset. The device + * offset is a number of bytes, already skipped by the device. The interface would skip remaining + * initialOffset - deviceOffset bytes. + * -# Client connects to the socket and triggers setClientSocket() call. + * -# The channel state is Open now. + * -# If the device is already ready to read, or emit readyRead() signal, the interface reads data from the device and + * write it to the clientSocket. + * -# Client socket emit bytesWritten() signal, the interface updates transferredBytes count. + * -# If transferredBytes == size, then the channel state changes to Completed. + * Otherwise the interface waits for further data from the device socket. + * + * Outgoing transfer process: + * -# Client requests a channel with ChannelType = TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER and other properties, such as + * Filename, Size and ContentType. + * -# Connection manager creates the requested channel with initial state Pending. + * -# Connection manager asks remote contact to accept the transfer. + * -# At any time: + * -# Remote contact accept file, connection manager calls remoteAcceptFile() method to pass the output device + * and an initial offset. This changes the state to Accepted. + * -# Client calls ProvideFile method to configure a socket. The implementation calls createSocket(), which should + * trigger (now or later) a call to setClientSocket() to setup the client socket. socketAddress() method used + * to return the socket address. + * -# Client connects to the socket and triggers setClientSocket() call. + * -# The channel state is Open now. + * -# Client writes data to the socket. + * -# The clientSocket emits readyRead() signal, the interface reads the data from the clientSocket and write it to the + * io device. + * -# Connection manager calls updates transferredBytes property on actual data write. + * -# If transferredBytes == size, then the channel state changes to Completed. + * Otherwise the interface waits for further data from the client socket. + * + * Subclassing: + * + Reimplement a public virtual method availableSocketTypes() to expose extra socket types. + * + Overload protected createSocket() method to provide own socket address type, access control and its param + * implementation. + * + Custom createSocket() implementation MUST be paired with custom socketAddress() method implementation. + * + Use setClientSocket() method to pass the client socket. + * */ /** * Class constructor. */ -BaseChannelFileTransferType::BaseChannelFileTransferType(const QString &contentType, - const QString &filename, - qulonglong size, - uint contentHashType, - const QString &contentHash, - const QString &description, - const QDateTime &date, - const Tp::SupportedSocketMap &availableSocketTypes) +BaseChannelFileTransferType::BaseChannelFileTransferType(const QVariantMap &request) : AbstractChannelInterface(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER), - mPriv(new Private(this, contentType, filename, size, contentHashType, contentHash, description, date, availableSocketTypes)) + mPriv(new Private(this, request)) +{ +} + +bool BaseChannelFileTransferType::createSocket(uint addressType, uint accessControl, const QDBusVariant &accessControlParam, Tp::DBusError *error) +{ + Q_UNUSED(accessControlParam); + + if (accessControl != Tp::SocketAccessControlLocalhost) { + error->set(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Requested access control mechanism is not supported.")); + return false; + } + + QHostAddress address; + + switch (addressType) { + case Tp::SocketAddressTypeIPv4: + address = QHostAddress(QHostAddress::LocalHost); + break; + case Tp::SocketAddressTypeIPv6: + address = QHostAddress(QHostAddress::LocalHostIPv6); + break; + default: + error->set(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Requested address type is not supported.")); + return false; + } + + if (mPriv->serverSocket) { + error->set(TP_QT_ERROR_NOT_AVAILABLE, QLatin1String("File transfer can only be started once in the same channel")); + return false; + } + + mPriv->serverSocket = new QTcpServer(this); + mPriv->serverSocket->setMaxPendingConnections(1); + + connect(mPriv->serverSocket, SIGNAL(newConnection()), this, SLOT(onSocketConnection())); + + bool result = mPriv->serverSocket->listen(address); + if (!result) { + error->set(TP_QT_ERROR_NETWORK_ERROR, mPriv->serverSocket->errorString()); + } + + return result; +} + +QDBusVariant BaseChannelFileTransferType::socketAddress() const +{ + if (!mPriv->serverSocket) { + return QDBusVariant(); + } + + switch (mPriv->serverSocket->serverAddress().protocol()) { + case QAbstractSocket::IPv4Protocol: { + SocketAddressIPv4 a; + a.address = mPriv->serverSocket->serverAddress().toString(); + a.port = mPriv->serverSocket->serverPort(); + return QDBusVariant(QVariant::fromValue(a)); + } + case QAbstractSocket::IPv6Protocol: { + SocketAddressIPv6 a; + a.address = mPriv->serverSocket->serverAddress().toString(); + a.port = mPriv->serverSocket->serverPort(); + return QDBusVariant(QVariant::fromValue(a)); + } + default: + break; + } + + return QDBusVariant(); +} + +void BaseChannelFileTransferType::setTransferredBytes(qulonglong count) +{ + if (mPriv->transferredBytes == count) { + return; + } + + mPriv->transferredBytes = count; + QMetaObject::invokeMethod(mPriv->adaptee, "transferredBytesChanged", Q_ARG(qulonglong, count)); //Can simply use emit in Qt5 + + if (transferredBytes() == size()) { + mPriv->clientSocket->close(); + mPriv->serverSocket->close(); + setState(Tp::FileTransferStateCompleted, Tp::FileTransferStateChangeReasonNone); + } +} + +void BaseChannelFileTransferType::setClientSocket(QIODevice *socket) +{ + mPriv->clientSocket = socket; + + if (!socket) { + warning() << "BaseChannelFileTransferType::setClientSocket() called with a null socket."; + return; + } + + switch (mPriv->direction) { + case BaseChannelFileTransferType::Outgoing: + connect(mPriv->clientSocket, SIGNAL(readyRead()), this, SLOT(doTransfer())); + break; + case BaseChannelFileTransferType::Incoming: + connect(mPriv->clientSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(onBytesWritten(qint64))); + break; + default: + // Should not be ever possible + Q_ASSERT(0); + break; + } + + tryToOpenAndTransfer(); +} + +void BaseChannelFileTransferType::onSocketConnection() +{ + setClientSocket(mPriv->serverSocket->nextPendingConnection()); +} + +void BaseChannelFileTransferType::doTransfer() +{ + if (!mPriv->clientSocket || !mPriv->device) { + return; + } + + QIODevice *input = 0; + QIODevice *output = 0; + + switch (mPriv->direction) { + case BaseChannelFileTransferType::Outgoing: + input = mPriv->clientSocket; + output = mPriv->device; + break; + case BaseChannelFileTransferType::Incoming: + input = mPriv->device; + output = mPriv->clientSocket; + break; + default: + // Should not be ever possible + Q_ASSERT(0); + break; + } + + static const int c_blockSize = 16 * 1024; + char buffer[c_blockSize]; + char *inputPointer = buffer; + + qint64 length = input->read(buffer, sizeof(buffer)); + + if (length) { + // deviceOffset is the number of already skipped bytes + if (mPriv->deviceOffset + length > initialOffset()) { + if (mPriv->deviceOffset < initialOffset()) { + qint64 diff = initialOffset() - mPriv->deviceOffset; + length -= diff; + inputPointer += diff; + mPriv->deviceOffset += diff; + } + output->write(inputPointer, length); + } + mPriv->deviceOffset += length; + } + + if (input->bytesAvailable() > 0) { + QMetaObject::invokeMethod(this, "doTransfer", Qt::QueuedConnection); + } +} + +void BaseChannelFileTransferType::onBytesWritten(qint64 count) { + setTransferredBytes(transferredBytes() + count); } /** @@ -984,9 +1256,19 @@ QVariantMap BaseChannelFileTransferType::immutableProperties() const QVariant::fromValue(date().toTime_t())); map.insert(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".AvailableSocketTypes"), QVariant::fromValue(availableSocketTypes())); + + if (mPriv->direction == Outgoing) { + map.insert(TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER + QLatin1String(".URI"), QVariant::fromValue(uri())); + } + return map; } +BaseChannelFileTransferType::Direction BaseChannelFileTransferType::direction() const +{ + return mPriv->direction; +} + uint BaseChannelFileTransferType::state() const { return mPriv->state; @@ -994,8 +1276,13 @@ uint BaseChannelFileTransferType::state() const void BaseChannelFileTransferType::setState(uint state, uint reason) { + if (mPriv->state == state) { + return; + } + mPriv->state = state; QMetaObject::invokeMethod(mPriv->adaptee, "fileTransferStateChanged", Q_ARG(uint, state), Q_ARG(uint, reason)); //Can simply use emit in Qt5 + emit stateChanged(state, reason); } QString BaseChannelFileTransferType::contentType() const @@ -1035,7 +1322,10 @@ QDateTime BaseChannelFileTransferType::date() const Tp::SupportedSocketMap BaseChannelFileTransferType::availableSocketTypes() const { - return mPriv->availableSocketTypes; + Tp::SupportedSocketMap types; + types.insert(Tp::SocketAddressTypeIPv4, Tp::UIntList() << Tp::SocketAccessControlLocalhost); + + return types; } qulonglong BaseChannelFileTransferType::transferredBytes() const @@ -1043,22 +1333,11 @@ qulonglong BaseChannelFileTransferType::transferredBytes() const return mPriv->transferredBytes; } -void BaseChannelFileTransferType::setTransferredBytes(qulonglong count) -{ - mPriv->transferredBytes = count; - QMetaObject::invokeMethod(mPriv->adaptee, "transferredBytesChanged", Q_ARG(qulonglong, count)); //Can simply use emit in Qt5 -} - qulonglong BaseChannelFileTransferType::initialOffset() const { return mPriv->initialOffset; } -void BaseChannelFileTransferType::setInitialOffset(qulonglong initialOffset) -{ - mPriv->initialOffset = initialOffset; -} - QString BaseChannelFileTransferType::uri() const { return mPriv->uri; @@ -1066,9 +1345,21 @@ QString BaseChannelFileTransferType::uri() const void BaseChannelFileTransferType::setUri(const QString &uri) { + if (mPriv->direction == Outgoing) { + warning() << "BaseChannelFileTransferType::setUri(): Failed to set URI property for outgoing transfer."; + return; + } + + // The property can be written only before AcceptFile. + if (state() != FileTransferStatePending) { + warning() << "BaseChannelFileTransferType::setUri(): Failed to set URI property after AcceptFile call."; + return; + } + mPriv->uri = uri; + QMetaObject::invokeMethod(mPriv->adaptee, "uriDefined", Q_ARG(QString, uri)); //Can simply use emit in Qt5 + emit uriDefined(uri); } - QString BaseChannelFileTransferType::fileCollection() const { return mPriv->fileCollection; @@ -1085,47 +1376,183 @@ void BaseChannelFileTransferType::createAdaptor() mPriv->adaptee, dbusObject()); } -void BaseChannelFileTransferType::setAcceptFileCallback(const AcceptFileCallback &cb) +bool BaseChannelFileTransferType::remoteAcceptFile(QIODevice *output, qulonglong offset) { - mPriv->acceptFileCB = cb; -} + QString errorText; + bool deviceIsAlreadynOpened = output && output->isOpen(); -QDBusVariant BaseChannelFileTransferType::acceptFile(uint addressType, uint accessControl, const QDBusVariant &accessControlParam, qulonglong offset, DBusError *error) -{ - if (!mPriv->acceptFileCB.isValid()) { - error->set(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented")); - return QDBusVariant(); + if (!output) { + errorText = QLatin1String("The device must not be null."); + goto errorLabel; } - return mPriv->acceptFileCB(addressType, accessControl, accessControlParam, offset, error); -} -void BaseChannelFileTransferType::setProvideFileCallback(const ProvideFileCallback &cb) -{ - mPriv->provideFileCB = cb; -} + if (mPriv->state != Tp::FileTransferStatePending) { + errorText = QLatin1String("The state should be Pending."); + goto errorLabel; + } -QDBusVariant BaseChannelFileTransferType::provideFile(uint addressType, uint accessControl, const QDBusVariant &accessControlParam, DBusError *error) -{ - if (!mPriv->provideFileCB.isValid()) { - error->set(TP_QT_ERROR_NOT_IMPLEMENTED, QLatin1String("Not implemented")); - return QDBusVariant(); + if (mPriv->direction != Outgoing) { + errorText = QLatin1String("The direction should be Outgoing."); + goto errorLabel; + } + + if (offset > size()) { + errorText = QLatin1String("The offset should be less than the size."); + goto errorLabel; + } + + if (mPriv->device) { + errorText = QLatin1String("The device is already set."); + goto errorLabel; + } + + if (!deviceIsAlreadynOpened) { + if (!output->open(QIODevice::WriteOnly)) { + errorText = QLatin1String("Unable to open the device ."); + goto errorLabel; + } + + if (!output->isSequential()) { + if (!output->seek(offset)) { + errorText = QLatin1String("Unable to seek the device to the offset."); + goto errorLabel; + } + } } - return mPriv->provideFileCB(addressType, accessControl, accessControlParam, error); + + if (!output->isWritable()) { + errorText = QLatin1String("The device is not writable."); + goto errorLabel; + } + + if (!errorText.isEmpty()) { + errorLabel: + warning() << "BaseChannelFileTransferType::remoteAcceptFile(): Invalid call:" << errorText; + setState(Tp::FileTransferStateCancelled, Tp::FileTransferStateChangeReasonLocalError); + + return false; + } + + mPriv->device = output; + mPriv->deviceOffset = offset; + mPriv->weOpenedDevice = !deviceIsAlreadynOpened; + mPriv->initialOffset = offset; + + QMetaObject::invokeMethod(mPriv->adaptee, "initialOffsetDefined", Q_ARG(qulonglong, offset)); //Can simply use emit in Qt5 + setState(Tp::FileTransferStateAccepted, Tp::FileTransferStateChangeReasonNone); + + return true; } -void BaseChannelFileTransferType::fileTransferStateChanged(uint state, uint reason) +/*! + * + * Connection manager should call this method to pass the input device and its offset. + * The interface would skip remaining initialOffset - deviceOffset bytes. + * + * \param input The input device + * \param deviceOffset The number of bytes, already skipped by the device. + * + * \return True if success, false otherwise. + */ +bool BaseChannelFileTransferType::remoteProvideFile(QIODevice *input, qulonglong deviceOffset) { - QMetaObject::invokeMethod(mPriv->adaptee, "fileTransferStateChanged", Q_ARG(uint, state), Q_ARG(uint, reason)); //Can simply use emit in Qt5 + QString errorText; + bool deviceIsAlreadyOpened = input && input->isOpen(); + + if (!input) { + errorText = QLatin1String("The device must not be null."); + goto errorLabel; + } + + switch (mPriv->state) { + case Tp::FileTransferStatePending: + case Tp::FileTransferStateAccepted: + break; + default: + errorText = QLatin1String("The state should be Pending or Accepted."); + goto errorLabel; + break; + } + + if (mPriv->direction != Incoming) { + errorText = QLatin1String("The direction should be Incoming."); + goto errorLabel; + } + + if (deviceOffset > initialOffset()) { + errorText = QLatin1String("The deviceOffset should be less or equal to the initialOffset."); + goto errorLabel; + } + + if (mPriv->device) { + errorText = QLatin1String("The device is already set."); + goto errorLabel; + } + + if (!deviceIsAlreadyOpened) { + if (!input->open(QIODevice::ReadOnly)) { + errorText = QLatin1String("Unable to open the device ."); + goto errorLabel; + } + + if (!input->isSequential()) { + if (!input->seek(initialOffset())) { + errorText = QLatin1String("Unable to seek the device to the initial offset."); + goto errorLabel; + } + deviceOffset = initialOffset(); + } + } + + if (!input->isReadable()) { + errorText = QLatin1String("The device is not readable."); + goto errorLabel; + } + + if (!errorText.isEmpty()) { + errorLabel: + warning() << "BaseChannelFileTransferType::remoteProvideFile(): Invalid call:" << errorText; + setState(Tp::FileTransferStateCancelled, Tp::FileTransferStateChangeReasonLocalError); + + return false; + } + + mPriv->deviceOffset = deviceOffset; + + mPriv->device = input; + mPriv->weOpenedDevice = !deviceIsAlreadyOpened; + + connect(mPriv->device, SIGNAL(readyRead()), this, SLOT(doTransfer())); + + tryToOpenAndTransfer(); + + return true; } -void BaseChannelFileTransferType::initialOffsetDefined(qulonglong initialOffset) +void BaseChannelFileTransferType::tryToOpenAndTransfer() { - QMetaObject::invokeMethod(mPriv->adaptee, "initialOffsetDefined", Q_ARG(qulonglong, initialOffset)); //Can simply use emit in Qt5 + if (state() == Tp::FileTransferStateAccepted) { + setState(Tp::FileTransferStateOpen, Tp::FileTransferStateChangeReasonNone); + setTransferredBytes(initialOffset()); + } + + if (state() == Tp::FileTransferStateOpen) { + if (mPriv->clientSocket && mPriv->device) { + QMetaObject::invokeMethod(this, "doTransfer", Qt::QueuedConnection); + + } + } } -void BaseChannelFileTransferType::uriDefined(const QString &uri) +void BaseChannelFileTransferType::close() { - QMetaObject::invokeMethod(mPriv->adaptee, "uriDefined", Q_ARG(QString, uri)); //Can simply use emit in Qt5 + uint transferState = state(); + if (transferState == FileTransferStatePending || + transferState == FileTransferStateAccepted || + transferState == FileTransferStateOpen) { + // The file transfer was cancelled + setState(Tp::FileTransferStateCancelled, Tp::FileTransferStateChangeReasonLocalStopped); + } } // Chan.T.RoomList |