Commit 02f40929 authored by Guillaume Roguez's avatar Guillaume Roguez Committed by Anthony Léonard

add data transfer API

Add an API to support the new libring data-transfer feature.
This includes libring API connections and a new DataTransferModel class
usable by clients.

Change-Id: I944f6e6bbc6d6ef4a9ce91c9f7503c0210fbb390
Reviewed-by: default avatarAnthony Léonard <anthony.leonard@savoirfairelinux.com>
parent f9c73752
......@@ -316,6 +316,7 @@ SET( libringclient_LIB_SRCS
src/newaccountmodel.cpp
src/callbackshandler.cpp
src/behaviorcontroller.cpp
src/datatransfermodel.cpp
#Data collections
src/transitionalpersonbackend.cpp
......@@ -482,6 +483,8 @@ SET(libringclient_api_LIB_HDRS
src/api/conversationmodel.h
src/api/profile.h
src/api/behaviorcontroller.h
src/api/datatransfermodel.h
src/api/datatransfer.h
)
......
/****************************************************************************
* Copyright (C) 2018 Savoir-faire Linux *
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> *
* *
* 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 General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#pragma once
// LRC
#include "typedefs.h"
namespace lrc { namespace api {
namespace datatransfer {
enum class Status {
on_connection, // outgoing tx: wait for connection/acceptance, incoming tx: wait for local acceptance
on_progress, // connected, data transfer progress reporting
success, // transfer finished with success, all data sent
stop_by_peer, // error: transfer terminated by peer
stop_by_host, // eror: transfer terminated by local host
unjoinable_peer, // error: (outgoing only) peer connection failed
invalid_pathname, // error: (file transfer only) given file is not a valid
unsupported, // error: unable to do the transfer (generic error)
};
struct Info
{
std::string uid; ///< long-term and unique identifier (used for historic)
Status status;
bool isOutgoing;
std::size_t totalSize;
std::size_t progress; ///< if status >= on_progress, gives number of bytes tx/rx until now
std::string path;
std::string displayName;
};
} // namespace lrc::api::datatransfer
}} // namespace lrc::api
/****************************************************************************
* Copyright (C) 2018 Savoir-faire Linux *
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> *
* *
* 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 General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#pragma once
// Std
#include <string>
#include <memory>
#include <ios>
// Qt
#include <qobject.h>
// Data
#include "api/datatransfer.h"
#include "api/account.h"
// LRC
#include "typedefs.h"
namespace lrc {
class CallbacksHandler;
class Database;
namespace api {
class BehaviorController;
/**
* @brief Class that manages data transfer.
*/
class LIB_EXPORT DataTransferModel : public QObject {
Q_OBJECT
public:
DataTransferModel(Database& database,
const CallbacksHandler& callbacksHandler,
const api::BehaviorController& behaviorController);
~DataTransferModel();
std::vector<std::string> transferIdList() const;
std::string sendFile(const std::string& account_id, const std::string& peer_uri,
const std::string& file_path, const std::string& display_name);
datatransfer::Info transferInfo(const std::string& uid);
std::streamsize bytesProgress(const std::string& id);
void acceptFile(const std::string& id, const std::string& file_path, std::size_t offset);
void cancel(const std::string& id);
Q_SIGNALS:
/**
* Connect this signal to know when a data transfer is incoming.
* \note the unique identification is generated by the libring and its unicity scope is limited
* to the libring process life.
*
* @param transfer_id unique identification of incoming data transfer.
* @param display_name a free identification string given by sender.
* @oaram size total number of bytes of the transfer (including offset).
* @oaram offset offset of first given bytes for continued transfer.
*/
void incomingTransfer(const std::string& uid, const std::string& display_name,
const std::size_t size, const std::size_t offset);
/**
* Connect this signal to know when an existing data transfer has changed of status.
* @param transfer_id unique identification of incoming data transfer.
* @param status reported status.
*/
void transferStatusChanged(const std::string& uid, datatransfer::Status status);
private:
class Impl;
std::unique_ptr<Impl> pimpl_;
};
}} // namespace lrc::api
/****************************************************************************
* Copyright (C) 2018 Savoir-faire Linux *
* Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> *
* *
* 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 General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
// LRC
#include "api/datatransfermodel.h"
#include "api/behaviorcontroller.h"
#include "callbackshandler.h"
#include "database.h"
// Dbus
#include "dbus/configurationmanager.h"
// DRing
#include <datatransfer_interface.h>
// Std
#include <map>
#include <stdexcept>
// Qt
#include <QUuid>
namespace lrc { namespace api {
/// DRING to LRC event code conversion
static inline
datatransfer::Status
convertDataTransferEvent(DRing::DataTransferEventCode event)
{
switch (event) {
case DRing::DataTransferEventCode::created: return datatransfer::Status::on_connection;
case DRing::DataTransferEventCode::unsupported: return datatransfer::Status::unsupported;
case DRing::DataTransferEventCode::wait_peer_acceptance: return datatransfer::Status::on_connection;
case DRing::DataTransferEventCode::wait_host_acceptance: return datatransfer::Status::on_connection;
case DRing::DataTransferEventCode::ongoing: return datatransfer::Status::on_progress;
case DRing::DataTransferEventCode::finished: return datatransfer::Status::success;
case DRing::DataTransferEventCode::closed_by_host: return datatransfer::Status::stop_by_host;
case DRing::DataTransferEventCode::closed_by_peer: return datatransfer::Status::stop_by_peer;
case DRing::DataTransferEventCode::invalid_pathname: return datatransfer::Status::invalid_pathname;
case DRing::DataTransferEventCode::unjoinable_peer: return datatransfer::Status::unjoinable_peer;
}
throw std::runtime_error("BUG: broken convertDataTransferEvent() switch");
}
class DataTransferModel::Impl : public QObject
{
Q_OBJECT
public:
Impl(DataTransferModel& up_link,
Database& database,
const CallbacksHandler& callbacksHandler,
const api::BehaviorController& behaviorController);
DataTransferModel& upLink;
std::map<DRing::DataTransferId, std::string> dring2lrcIdMap;
std::map<std::string, DRing::DataTransferId> lrc2dringIdMap; // stricly the reverse map of dring2lrcIdMap
Database& database;
const CallbacksHandler& callbacksHandler;
const BehaviorController& behaviorController;
std::string registerTransferId(DRing::DataTransferId id);
public Q_SLOTS:
void slotDataTransferEvent(qulonglong id, uint code);
};
DataTransferModel::Impl::Impl(DataTransferModel& up_link,
Database& database,
const CallbacksHandler& callbacksHandler,
const api::BehaviorController& behaviorController)
: QObject {}
, behaviorController {behaviorController}
, callbacksHandler {callbacksHandler}
, database {database}
, upLink {up_link}
{
connect(&ConfigurationManager::instance(), &ConfigurationManagerInterface::dataTransferEvent,
this, &DataTransferModel::Impl::slotDataTransferEvent);
}
std::string
DataTransferModel::Impl::registerTransferId(DRing::DataTransferId dring_id)
{
const auto& iter = dring2lrcIdMap.find(dring_id);
if (iter != std::cend(dring2lrcIdMap))
return iter->second;
while (true) {
auto res = dring2lrcIdMap.emplace(dring_id, QUuid::createUuid().toString().toStdString());
if (res.second) {
lrc2dringIdMap.emplace(res.first->second, dring_id);
return res.first->second;
}
}
}
void
DataTransferModel::Impl::slotDataTransferEvent(qulonglong dring_id, uint code)
{
auto lrc_id = registerTransferId(dring_id);
auto event = DRing::DataTransferEventCode(code);
if (event == DRing::DataTransferEventCode::created) {
auto info = static_cast<DataTransferInfo>(ConfigurationManager::instance().dataTransferInfo(dring_id));
if (!info.isOutgoing) {
emit upLink.incomingTransfer(lrc_id, info.displayName.toStdString(), info.totalSize, info.bytesProgress);
return;
}
}
emit upLink.transferStatusChanged(lrc_id, convertDataTransferEvent(event));
}
DataTransferModel::DataTransferModel(Database& database,
const CallbacksHandler& callbacksHandler,
const api::BehaviorController& behaviorController)
: QObject()
, pimpl_ { std::make_unique<Impl>(*this, database, callbacksHandler, behaviorController) }
{}
DataTransferModel::~DataTransferModel() = default;
std::vector<std::string>
DataTransferModel::transferIdList() const
{
VectorULongLong dring_list = ConfigurationManager::instance().dataTransferList();
for (auto dring_id : dring_list) {
pimpl_->registerTransferId(dring_id);
}
std::vector<std::string> result;
result.reserve(dring_list.size());
for (auto& item : pimpl_->lrc2dringIdMap) {
result.push_back(item.first);
}
return result;
}
std::string
DataTransferModel::sendFile(const std::string& account_id, const std::string& peer_uri,
const std::string& file_path, const std::string& display_name)
{
auto dring_id = static_cast<DRing::DataTransferId>(ConfigurationManager::instance().sendFile(
QString::fromStdString(account_id),
QString::fromStdString(peer_uri),
QString::fromStdString(file_path),
QString::fromStdString(display_name)));
return pimpl_->registerTransferId(dring_id);
}
datatransfer::Info
DataTransferModel::transferInfo(const std::string& lrc_id)
{
auto dring_id = pimpl_->lrc2dringIdMap.at(lrc_id);
auto dring_info = static_cast<DataTransferInfo>(ConfigurationManager::instance().dataTransferInfo(dring_id));
datatransfer::Info lrc_info;
lrc_info.uid = lrc_id;
lrc_info.isOutgoing = dring_info.lastEvent;
lrc_info.progress = dring_info.lastEvent;
lrc_info.path = dring_info.displayName.toStdString();
lrc_info.displayName = dring_info.displayName.toStdString();
lrc_info.status = convertDataTransferEvent(DRing::DataTransferEventCode(dring_info.lastEvent));
return lrc_info;
}
std::streamsize
DataTransferModel::bytesProgress(const std::string& lrc_id)
{
return ConfigurationManager::instance().dataTransferBytesSent(pimpl_->lrc2dringIdMap.at(lrc_id));
}
void
DataTransferModel::acceptFile(const std::string& lrc_id,
const std::string& file_path,
std::size_t offset)
{
auto dring_id = pimpl_->lrc2dringIdMap.at(lrc_id);
ConfigurationManager::instance().acceptFileTransfer(dring_id, QString::fromStdString(file_path), offset);
}
void
DataTransferModel::cancel(const std::string& lrc_id)
{
auto dring_id = pimpl_->lrc2dringIdMap.at(lrc_id);
ConfigurationManager::instance().cancelDataTransfer(dring_id);
}
}} // namespace lrc::api
#include "api/moc_datatransfermodel.cpp"
#include "datatransfermodel.moc"
......@@ -37,6 +37,7 @@ Q_DECLARE_METATYPE(VectorMapStringString)
Q_DECLARE_METATYPE(MapStringMapStringStringList)
Q_DECLARE_METATYPE(VectorInt)
Q_DECLARE_METATYPE(VectorUInt)
Q_DECLARE_METATYPE(VectorULongLong)
Q_DECLARE_METATYPE(VectorString)
Q_DECLARE_METATYPE(MapStringVectorString)
Q_DECLARE_METATYPE(VectorVectorByte)
......@@ -53,6 +54,7 @@ inline void registerCommTypes() {
qDBusRegisterMetaType<MapStringMapStringVectorString>();
qDBusRegisterMetaType<VectorInt> ();
qDBusRegisterMetaType<VectorUInt> ();
qDBusRegisterMetaType<VectorULongLong> ();
qDBusRegisterMetaType<VectorString> ();
qDBusRegisterMetaType<MapStringVectorString> ();
qDBusRegisterMetaType<VectorVectorByte> ();
......
......@@ -30,6 +30,7 @@
#include <future>
#include <configurationmanager_interface.h>
#include <datatransfer_interface.h>
#include <account_const.h>
#include "typedefs.h"
......@@ -44,12 +45,14 @@ class ConfigurationManagerInterface: public QObject
public:
std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> confHandlers;
std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> dataXferHandlers;
ConfigurationManagerInterface() {
setObjectName("ConfigurationManagerInterface");
using DRing::exportable_callback;
using DRing::ConfigurationSignal;
using DRing::AudioSignal;
using DRing::DataTransferSignal;
setObjectName("ConfigurationManagerInterface");
confHandlers = {
......@@ -151,6 +154,13 @@ public:
Q_EMIT this->contactRemoved(QString(account_id.c_str()), QString(uri.c_str()), banned);
}),
};
dataXferHandlers = {
exportable_callback<DataTransferSignal::DataTransferEvent>(
[this] (const uint64_t& transfer_id, const uint32_t& code) {
Q_EMIT this->dataTransferEvent(transfer_id, code);
}),
};
}
~ConfigurationManagerInterface() {}
......@@ -576,6 +586,38 @@ public Q_SLOTS: // METHODS
return convertMap(DRing::getContactDetails(accountID.toStdString(), uri.toStdString()));
}
VectorULongLong dataTransferList() {
return convertVectorULongLong(DRing::dataTransferList());
}
uint64_t sendFile(const QString& account_id, const QString& peer_uri, const QString& file_path, const QString& display_name) {
return DRing::sendFile(account_id.toStdString(), peer_uri.toStdString(), file_path.toStdString(), display_name.toStdString());
}
DataTransferInfo dataTransferInfo(uint64_t transfer_id) {
auto dring_info = DRing::dataTransferInfo(transfer_id);
DataTransferInfo lrc_info;
lrc_info.isOutgoing = dring_info.isOutgoing;
lrc_info.lastEvent = uint(dring_info.lastEvent);
lrc_info.totalSize = dring_info.totalSize;
lrc_info.bytesProgress = dring_info.bytesProgress;
lrc_info.displayName = QString::fromStdString(dring_info.displayName);
lrc_info.path = QString::fromStdString(dring_info.path);
return lrc_info;
}
uint64_t dataTransferBytesSent(uint64_t transfer_id) {
return DRing::dataTransferBytesSent(transfer_id);
}
void acceptFileTransfer(uint64_t transfer_id, const QString& file_path, uint64_t offset) {
DRing::acceptFileTransfer(transfer_id, file_path.toStdString(), offset);
}
void cancelDataTransfer(uint64_t transfer_id) {
DRing::cancelDataTransfer(transfer_id);
}
Q_SIGNALS: // SIGNALS
void volumeChanged(const QString& device, double value);
void accountsChanged();
......@@ -601,8 +643,7 @@ Q_SIGNALS: // SIGNALS
void migrationEnded(const QString &accountID, const QString &result);
void contactAdded(const QString &accountID, const QString &uri, bool banned);
void contactRemoved(const QString &accountID, const QString &uri, bool banned);
void dataTransferEvent(qulonglong transfer_id, uint code);
};
namespace org { namespace ring { namespace Ring {
......
......@@ -79,6 +79,14 @@ inline VectorString convertVectorString(const std::vector<std::string>& v) {
return temp;
}
inline VectorULongLong convertVectorULongLong(const std::vector<uint64_t>& v) {
VectorULongLong temp;
for (const auto& x : v) {
temp.push_back(x);
}
return temp;
}
inline std::vector<std::string> convertStringList(const QStringList& v) {
std::vector<std::string> temp;
for (const auto& x : v) {
......
......@@ -38,6 +38,7 @@ InstanceManagerInterface::InstanceManagerInterface() : m_pTimer(nullptr)
using DRing::CallSignal;
using DRing::ConfigurationSignal;
using DRing::PresenceSignal;
using DRing::DataTransferSignal;
#ifdef ENABLE_VIDEO
using DRing::VideoSignal;
......@@ -62,6 +63,7 @@ InstanceManagerInterface::InstanceManagerInterface() : m_pTimer(nullptr)
registerCallHandlers(CallManager::instance().callHandlers);
registerConfHandlers(ConfigurationManager::instance().confHandlers);
registerPresHandlers(PresenceManager::instance().presHandlers);
registerDataXferHandlers(ConfigurationManager::instance().dataXferHandlers);
#ifdef ENABLE_VIDEO
registerVideoHandlers(VideoManager::instance().videoHandlers);
#endif
......
......@@ -30,6 +30,7 @@ typedef QMap<QString, QString> MapStringString
typedef QMap<QString, int> MapStringInt ;
typedef QVector<int> VectorInt ;
typedef QVector<uint> VectorUInt ;
typedef QVector<qulonglong> VectorULongLong ;
typedef QVector< QMap<QString, QString> > VectorMapStringString ;
typedef QVector< QString > VectorString ;
typedef QMap< QString, QMap< QString, QVector<QString> > > MapStringMapStringVectorString;
......
......@@ -648,6 +648,38 @@ public Q_SLOTS: // METHODS
emit incomingTrustRequest(accountId, from, payload, timestamp);
}
VectorULongLong dataTransferList() {
return {};
}
uint64_t sendFile(const QString& account_id, const QString& peer_uri, const QString& file_path, const QString& display_name) {
(void)account_id;
(void)peer_uri;
(void)file_path;
(void)display_name;
return 0;
}
DataTransferInfo dataTransferInfo(uint64_t transfer_id) {
(void)transfer_id;
return {};
}
uint64_t dataTransferBytesSent(uint64_t transfer_id) {
(void)transfer_id;
return 0;
}
void acceptFileTransfer(uint64_t transfer_id, const QString& file_path, uint64_t offset) {
(void)transfer_id;
(void)file_path;
(void)offset;
}
void cancelDataTransfer(uint64_t transfer_id) {
(void)transfer_id;
}
Q_SIGNALS: // SIGNALS
void volumeChanged(const QString& device, double value);
void accountsChanged();
......@@ -673,8 +705,7 @@ Q_SIGNALS: // SIGNALS
void migrationEnded(const QString &accountId, const QString &result);
void contactAdded(const QString &accountId, const QString &uri, bool banned);
void contactRemoved(const QString &accountId, const QString &uri, bool banned);
void dataTransferEvent(uint64_t transfer_id, uint32_t code);
};
namespace org {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment