Commit 19dbc124 authored by Guillaume Roguez's avatar Guillaume Roguez Committed by Anthony Léonard

datatransfer: API changes to not throw

GTK client uses D-Bus, so is unable to forward exceptions.
This causes crashes of daemon (unhandled exception).

This patch changes DataTransfer API to not throw (noexcept).
This is an important changes, not backware compatible,
so the API version has been modified consequently.

Change-Id: I9f2a2fe1732b2622ace16225b6e792dc15383ba1
Reviewed-by: default avatarAnthony Léonard <anthony.leonard@savoirfairelinux.com>
parent 66b2627a
......@@ -1409,59 +1409,48 @@
</arg>
</method>
<method name="sendFile" tp:name-for-bindings="sendFile">
<method name="dataTransferList" tp:name-for-bindings="dataTransferList">
<tp:added version="4.2.0"/>
<arg type="t" name="DataTransferId" direction="out">
</arg>
<arg type="s" name="accountID" direction="in">
</arg>
<arg type="s" name="peer_uri" direction="in">
<tp:docstring>
RingID of request's recipient.
</tp:docstring>
</arg>
<arg type="s" name="file_path" direction="in"></arg>
<arg type="s" name="display_name" direction="in"></arg>
<arg type="at" name="dataTransferList" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="VectorULongLong"/>
</method>
<method name="dataTransferList" tp:name-for-bindings="dataTransferList">
<method name="sendFile" tp:name-for-bindings="sendFile">
<tp:added version="4.2.0"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="VectorULongLong"/>
<arg type="at" name="DataTransferList" direction="out">
</arg>
<arg type="u" name="dataTransferError" direction="out"/>
<arg type="(suuxxssss)" name="DataTransferInfo" direction="in"/>
<arg type="t" name="dataTransferId" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="DataTransferInfo"/>
</method>
<method name="dataTransferInfo" tp:name-for-bindings="dataTransferInfo">
<tp:added version="4.2.0"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="DataTransferInfo"/>
<arg type="(buttssss)" name="DataTransferInfo" direction="out">
</arg>
<arg type="t" name="DataTransferId" direction="in">
</arg>
<tp:added version="5.0.0"/>
<arg type="u" name="dataTransferError" direction="out"/>
<arg type="t" name="dataTransferId" direction="in"/>
<arg type="(suuxxssss)" name="dataTransferInfo" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="DataTransferInfo"/>
</method>
<method name="dataTransferBytesProgress" tp:name-for-bindings="dataTransferBytesProgress">
<tp:added version="4.2.0"/>
<arg type="t" name="BytesProgress" direction="out">
</arg>
<arg type="t" name="DataTransferId" direction="in">
</arg>
<tp:added version="5.0.0"/>
<arg type="u" name="dataTransferError" direction="out"/>
<arg type="t" name="dataTransferId" direction="in"/>
<arg type="x" name="totalSize" direction="out"/>
<arg type="x" name="bytesProgress" direction="out"/>
</method>
<method name="acceptFileTransfer" tp:name-for-bindings="acceptFileTransfer">
<tp:added version="4.2.0"/>
<arg type="t" name="DataTransferId" direction="in">
</arg>
<arg type="s" name="file_path" direction="in">
</arg>
<arg type="t" name="offset" direction="in">
</arg>
<tp:added version="5.0.0"/>
<arg type="u" name="dataTransferError" direction="out"/>
<arg type="t" name="dataTransferId" direction="in"/>
<arg type="s" name="filePath" direction="in"/>
<arg type="x" name="offset" direction="in"/>
</method>
<method name="cancelDataTransfer" tp:name-for-bindings="cancelDataTransfer">
<tp:added version="4.2.0"/>
<arg type="t" name="DataTransferId" direction="in">
</arg>
<tp:added version="5.0.0"/>
<arg type="u" name="dataTransferError" direction="out"/>
<arg type="t" name="dataTransferId" direction="in"/>
</method>
<signal name="mediaParametersChanged" tp:name-for-bindings="mediaParametersChanged">
......
......@@ -615,48 +615,66 @@ DBusConfigurationManager::connectivityChanged()
}
auto
DBusConfigurationManager::sendFile(const std::string& account_id, const std::string& peer_uri,
const std::string& file_path, const std::string& display_name) -> decltype(DRing::sendFile(account_id, peer_uri, file_path, display_name))
DBusConfigurationManager::dataTransferList() -> decltype(DRing::dataTransferList())
{
return DRing::sendFile(account_id, peer_uri, file_path, display_name);
return DRing::dataTransferList();
}
DBus::Struct<bool, uint32_t, uint64_t, uint64_t, std::string, std::string, std::string, std::string>
DBusConfigurationManager::dataTransferInfo(const DRing::DataTransferId& id)
{
DBus::Struct<bool, uint32_t, uint64_t, uint64_t, std::string, std::string, std::string, std::string> out;
auto info = DRing::dataTransferInfo(id);
out._1 = info.isOutgoing;
out._2 = uint32_t(info.lastEvent);
out._3 = info.totalSize;
out._4 = info.bytesProgress;
out._5 = info.displayName;
out._6 = info.path;
out._7 = info.accountId;
out._8 = info.peer;
return out;
void
DBusConfigurationManager::sendFile(const RingDBusDataTransferInfo& in,
uint32_t& error,
DRing::DataTransferId& id)
{
DRing::DataTransferInfo info;
info.accountId = in._1;
info.lastEvent = DRing::DataTransferEventCode(in._2);
info.flags = in._3;
info.totalSize = in._4;
info.bytesProgress = in._5;
info.peer = in._6;
info.displayName = in._7;
info.path = in._8;
info.mimetype = in._9;
error = uint32_t(DRing::sendFile(info, id));
}
uint64_t
DBusConfigurationManager::dataTransferBytesProgress(const uint64_t& id)
{
return DRing::dataTransferBytesProgress(id);
void
DBusConfigurationManager::dataTransferInfo(const DRing::DataTransferId& id,
uint32_t& error,
RingDBusDataTransferInfo& out)
{
DRing::DataTransferInfo info;
auto res = DRing::dataTransferInfo(id, info);
if (res == DRing::DataTransferError::success) {
out._1 = info.accountId;
out._2 = uint32_t(info.lastEvent);
out._3 = info.flags;
out._4 = info.totalSize;
out._5 = info.bytesProgress;
out._6 = info.peer;
out._7 = info.displayName;
out._8 = info.path;
out._9 = info.mimetype;
}
error = uint32_t(res);
}
auto
DBusConfigurationManager::dataTransferList() -> decltype(DRing::dataTransferList())
void
DBusConfigurationManager::dataTransferBytesProgress(const uint64_t& id, uint32_t& error,
int64_t& total, int64_t& progress)
{
return DRing::dataTransferList();
error = uint32_t(DRing::dataTransferBytesProgress(id, total, progress));
}
void
DBusConfigurationManager::acceptFileTransfer(const uint64_t& id, const std::string& file_path, const uint64_t& offset)
uint32_t
DBusConfigurationManager::acceptFileTransfer(const uint64_t& id, const std::string& file_path,
const int64_t& offset)
{
DRing::acceptFileTransfer(id, file_path, offset);
return uint32_t(DRing::acceptFileTransfer(id, file_path, offset));
}
void
uint32_t
DBusConfigurationManager::cancelDataTransfer(const uint64_t& id)
{
DRing::cancelDataTransfer(id);
return uint32_t(DRing::cancelDataTransfer(id));
}
......@@ -55,6 +55,8 @@ class DBusConfigurationManager :
public DBus::ObjectAdaptor
{
public:
using RingDBusDataTransferInfo = DBus::Struct<std::string, uint32_t, uint32_t, int64_t, int64_t, std::string, std::string, std::string, std::string>;
DBusConfigurationManager(DBus::Connection& connection);
// Methods
......@@ -153,13 +155,12 @@ class DBusConfigurationManager :
int exportAccounts(const std::vector<std::string>& accountIDs, const std::string& filepath, const std::string& password);
int importAccounts(const std::string& archivePath, const std::string& password);
void connectivityChanged();
DRing::DataTransferId sendFile(const std::string& account_id, const std::string& peer_uri,
const std::string& file_path, const std::string& display_name);
DBus::Struct<bool, uint32_t, uint64_t, uint64_t, std::string, std::string, std::string, std::string> dataTransferInfo(const DRing::DataTransferId& id);
uint64_t dataTransferBytesProgress(const uint64_t& id);
std::vector<uint64_t> dataTransferList();
void acceptFileTransfer(const uint64_t& id, const std::string& file_path, const uint64_t& offset);
void cancelDataTransfer(const uint64_t& id);
void sendFile(const RingDBusDataTransferInfo& info, uint32_t& error, DRing::DataTransferId& id);
void dataTransferInfo(const DRing::DataTransferId& id, uint32_t& error, RingDBusDataTransferInfo& info);
void dataTransferBytesProgress(const uint64_t& id, uint32_t& error, int64_t& total, int64_t& progress);
uint32_t acceptFileTransfer(const uint64_t& id, const std::string& file_path, const int64_t& offset);
uint32_t cancelDataTransfer(const uint64_t& id);
};
#endif // __RING_DBUSCONFIGURATIONMANAGER_H__
......@@ -2,7 +2,7 @@ dnl Ring - configure.ac for automake 1.9 and autoconf 2.59
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ([2.65])
AC_INIT([Ring Daemon],[4.3.0],[ring@gnu.org],[ring])
AC_INIT([Ring Daemon],[5.0.0],[ring@gnu.org],[ring])
AC_COPYRIGHT([[Copyright (c) Savoir-faire Linux 2004-2018]])
AC_REVISION([$Revision$])
......
......@@ -31,7 +31,7 @@ PROJECT_NAME = "Ring Daemon"
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = 4.3.0
PROJECT_NUMBER = 5.0.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer
......
......@@ -42,45 +42,39 @@ registerDataXferHandlers(const std::map<std::string, std::shared_ptr<CallbackWra
}
std::vector<DataTransferId>
dataTransferList()
dataTransferList() noexcept
{
return ring::Manager::instance().dataTransfers->list();
}
DataTransferId
sendFile(const std::string& account_id,
const std::string& peer_uri,
const std::string& file_path,
const std::string& display_name)
DataTransferError
sendFile(const DataTransferInfo& info, DataTransferId& id) noexcept
{
return ring::Manager::instance().dataTransfers->sendFile(
account_id, peer_uri, file_path, display_name.empty() ? file_path : display_name);
return ring::Manager::instance().dataTransfers->sendFile(info, id);
}
void
acceptFileTransfer(const DataTransferId& id,
const std::string& file_path,
std::size_t offset)
DataTransferError
acceptFileTransfer(const DataTransferId& id, const std::string& file_path, int64_t offset) noexcept
{
ring::Manager::instance().dataTransfers->acceptAsFile(id, file_path, offset);
return ring::Manager::instance().dataTransfers->acceptAsFile(id, file_path, offset);
}
void
cancelDataTransfer(const DataTransferId& id)
DataTransferError
cancelDataTransfer(const DataTransferId& id) noexcept
{
ring::Manager::instance().dataTransfers->cancel(id);
return ring::Manager::instance().dataTransfers->cancel(id);
}
std::streamsize
dataTransferBytesProgress(const DataTransferId& id)
DataTransferError
dataTransferBytesProgress(const DataTransferId& id, int64_t& total, int64_t& progress) noexcept
{
return ring::Manager::instance().dataTransfers->bytesProgress(id);
return ring::Manager::instance().dataTransfers->bytesProgress(id, total, progress);
}
DataTransferInfo
dataTransferInfo(const DataTransferId& id)
DataTransferError
dataTransferInfo(const DataTransferId& id, DataTransferInfo& info) noexcept
{
return ring::Manager::instance().dataTransfers->info(id);
return ring::Manager::instance().dataTransfers->info(id, info);
}
} // namespace DRing
......@@ -72,15 +72,15 @@ public:
started_ = false;
}
virtual std::streamsize bytesProgress() const {
void bytesProgress(int64_t& total, int64_t& progress) const {
std::lock_guard<std::mutex> lk {infoMutex_};
return info_.bytesProgress;
total = info_.totalSize;
progress = info_.bytesProgress;
}
DRing::DataTransferInfo info() const {
bytesProgress();
void info(DRing::DataTransferInfo& info) const {
std::lock_guard<std::mutex> lk {infoMutex_};
return info_;
info = info_;
}
void emit(DRing::DataTransferEventCode code) const;
......@@ -133,7 +133,7 @@ FileTransfer::FileTransfer(DRing::DataTransferId tid, const DRing::DataTransferI
throw std::runtime_error("input file open failed");
info_ = info;
info_.isOutgoing = true;
info_.flags &= ~((uint32_t)1 << int(DRing::DataTransferFlags::direction)); // outgoing
// File size?
input_.seekg(0, std::ios_base::end);
......@@ -211,8 +211,6 @@ public:
void close() noexcept override;
std::streamsize bytesProgress() const override;
std::string requestFilename();
void accept(const std::string&, std::size_t offset) override;
......@@ -233,14 +231,7 @@ IncomingFileTransfer::IncomingFileTransfer(DRing::DataTransferId tid,
RING_WARN() << "[FTP] incoming transfert of " << info.totalSize << " byte(s): " << info.displayName;
info_ = info;
info_.isOutgoing = false;
}
std::streamsize
IncomingFileTransfer::bytesProgress() const
{
std::lock_guard<std::mutex> lk {infoMutex_};
return info_.bytesProgress;
info_.flags |= (uint32_t)1 << int(DRing::DataTransferFlags::direction); // incoming
}
std::string
......@@ -290,7 +281,7 @@ IncomingFileTransfer::close() noexcept
RING_DBG() << "[FTP] file closed, rx " << info_.bytesProgress
<< " on " << info_.totalSize;
if (std::size_t(info_.bytesProgress) == info_.totalSize)
if (info_.bytesProgress >= info_.totalSize)
emit(DRing::DataTransferEventCode::finished);
else
emit(DRing::DataTransferEventCode::closed_by_host);
......@@ -327,7 +318,8 @@ public:
mutable std::mutex mapMutex_;
std::unordered_map<DRing::DataTransferId, std::shared_ptr<DataTransfer>> map_;
std::shared_ptr<DataTransfer> createFileTransfer(const DRing::DataTransferInfo&);
std::shared_ptr<DataTransfer> createFileTransfer(const DRing::DataTransferInfo& info,
DRing::DataTransferId& tid);
std::shared_ptr<IncomingFileTransfer> createIncomingFileTransfer(const DRing::DataTransferInfo&);
std::shared_ptr<DataTransfer> getTransfer(const DRing::DataTransferId&);
void cancel(DataTransfer&);
......@@ -351,9 +343,10 @@ DataTransferFacade::Impl::getTransfer(const DRing::DataTransferId& id)
}
std::shared_ptr<DataTransfer>
DataTransferFacade::Impl::createFileTransfer(const DRing::DataTransferInfo& info)
DataTransferFacade::Impl::createFileTransfer(const DRing::DataTransferInfo& info,
DRing::DataTransferId& tid)
{
auto tid = generateUID();
tid = generateUID();
auto transfer = std::make_shared<FileTransfer>(tid, info);
{
std::lock_guard<std::mutex> lk {mapMutex_};
......@@ -396,106 +389,117 @@ DataTransferFacade::Impl::onConnectionRequestReply(const DRing::DataTransferId&
DataTransferFacade::DataTransferFacade() : pimpl_ {std::make_unique<Impl>()}
{
RING_WARN("facade created, pimpl @%p", pimpl_.get());
RING_WARN("[XFER] facade created, pimpl @%p", pimpl_.get());
}
DataTransferFacade::~DataTransferFacade()
{
RING_WARN("facade destroy, pimpl @%p", pimpl_.get());
RING_WARN("[XFER] facade destroy, pimpl @%p", pimpl_.get());
};
std::vector<DRing::DataTransferId>
DataTransferFacade::list() const
DataTransferFacade::list() const noexcept
{
std::lock_guard<std::mutex> lk {pimpl_->mapMutex_};
return map_utils::extractKeys(pimpl_->map_);
}
DRing::DataTransferId
DataTransferFacade::sendFile(const std::string& account_id, const std::string& peer_uri,
const std::string& file_path, const std::string& display_name)
DRing::DataTransferError
DataTransferFacade::sendFile(const DRing::DataTransferInfo& info,
DRing::DataTransferId& tid) noexcept
{
auto account = Manager::instance().getAccount<RingAccount>(account_id);
if (!account)
throw std::invalid_argument("unknown account id");
if (!fileutils::isFile(file_path))
throw std::invalid_argument("invalid input file");
DRing::DataTransferInfo info;
info.accountId = account_id;
info.peer = peer_uri;
info.displayName = display_name;
info.path = file_path;
// remaining fields are overwritten
auto transfer = pimpl_->createFileTransfer(info);
auto tid = transfer->getId();
// IMPLEMENTATION NOTE: requestPeerConnection() may call the given callback a multiple time.
// This happen when multiple agents handle communications of the given peer for the given account.
// Example: Ring account supports multi-devices, each can answer to the request.
account->requestPeerConnection(
peer_uri,
[this, tid] (PeerConnection* connection) {
pimpl_->onConnectionRequestReply(tid, connection);
});
return tid;
auto account = Manager::instance().getAccount<RingAccount>(info.accountId);
if (!account) {
RING_ERR() << "[XFER] unknown id " << tid;
return DRing::DataTransferError::invalid_argument;
}
if (!fileutils::isFile(info.path)) {
RING_ERR() << "[XFER] invalid filename '" << info.path << "'";
return DRing::DataTransferError::invalid_argument;
}
try {
pimpl_->createFileTransfer(info, tid);
} catch (const std::exception& ex) {
RING_ERR() << "[XFER] exception during createFileTransfer(): " << ex.what();
return DRing::DataTransferError::io;
}
try {
// IMPLEMENTATION NOTE: requestPeerConnection() may call the given callback a multiple time.
// This happen when multiple agents handle communications of the given peer for the given account.
// Example: Ring account supports multi-devices, each can answer to the request.
account->requestPeerConnection(
info.peer,
[this, tid] (PeerConnection* connection) {
pimpl_->onConnectionRequestReply(tid, connection);
});
return DRing::DataTransferError::success;
} catch (const std::exception& ex) {
RING_ERR() << "[XFER] exception during sendFile(): " << ex.what();
return DRing::DataTransferError::unknown;
}
}
void
DRing::DataTransferError
DataTransferFacade::acceptAsFile(const DRing::DataTransferId& id,
const std::string& file_path,
std::size_t offset)
int64_t offset) noexcept
{
std::lock_guard<std::mutex> lk {pimpl_->mapMutex_};
const auto& iter = pimpl_->map_.find(id);
if (iter == std::end(pimpl_->map_))
throw std::invalid_argument("not existing DataTransferId");
return DRing::DataTransferError::invalid_argument;;
iter->second->accept(file_path, offset);
return DRing::DataTransferError::success;
}
void
DataTransferFacade::cancel(const DRing::DataTransferId& id)
DRing::DataTransferError
DataTransferFacade::cancel(const DRing::DataTransferId& id) noexcept
{
if (auto transfer = pimpl_->getTransfer(id))
if (auto transfer = pimpl_->getTransfer(id)) {
pimpl_->cancel(*transfer);
else
throw std::invalid_argument("not existing DataTransferId");
return DRing::DataTransferError::success;
}
return DRing::DataTransferError::invalid_argument;
}
std::streamsize
DataTransferFacade::bytesProgress(const DRing::DataTransferId& id) const
DRing::DataTransferError
DataTransferFacade::bytesProgress(const DRing::DataTransferId& id,
int64_t& total, int64_t& progress) const noexcept
{
if (auto transfer = pimpl_->getTransfer(id))
return transfer->bytesProgress();
throw std::invalid_argument("not existing DataTransferId");
try {
if (auto transfer = pimpl_->getTransfer(id)) {
transfer->bytesProgress(total, progress);
return DRing::DataTransferError::success;
}
return DRing::DataTransferError::invalid_argument;
} catch (const std::exception& ex) {
RING_ERR() << "[XFER] exception during bytesProgress(): " << ex.what();
}
return DRing::DataTransferError::unknown;
}
DRing::DataTransferInfo
DataTransferFacade::info(const DRing::DataTransferId& id) const
DRing::DataTransferError
DataTransferFacade::info(const DRing::DataTransferId& id,
DRing::DataTransferInfo& info) const noexcept
{
if (auto transfer = pimpl_->getTransfer(id))
return transfer->info();
throw std::invalid_argument("not existing DataTransferId");
try {
if (auto transfer = pimpl_->getTransfer(id)) {
transfer->info(info);
return DRing::DataTransferError::success;
}
return DRing::DataTransferError::invalid_argument;
} catch (const std::exception& ex) {
RING_ERR() << "[XFER] exception during info(): " << ex.what();
}
return DRing::DataTransferError::unknown;
}
std::shared_ptr<Stream>
DataTransferFacade::onIncomingFileRequest(const std::string& account_id,
const std::string& peer_uri,
const std::string& display_name,
std::size_t total_size,
std::size_t offset)
DataTransferFacade::onIncomingFileRequest(const DRing::DataTransferInfo& info)
{
DRing::DataTransferInfo info;
info.accountId = account_id;
info.peer = peer_uri;
info.displayName = display_name;
info.totalSize = total_size;
info.bytesProgress = offset;
// remaining fields are overwritten
auto transfer = pimpl_->createIncomingFileTransfer(info);
auto filename = transfer->requestFilename();
if (!filename.empty())
......
......@@ -36,46 +36,32 @@ public:
DataTransferFacade();
~DataTransferFacade();
/// Return all known transfer id
std::vector<DRing::DataTransferId> list() const;
/// \see DRing::dataTransferList
std::vector<DRing::DataTransferId> list() const noexcept;
/// Send a file to a peer.
/// Open a file and send its contents over a reliable connection
/// to given peer using the protocol from given account.
/// This method fails immediately if the file cannot be open in binary read mode,
/// if the account doesn't exist or if it doesn't support data transfer.
/// Remaining actions are operated asynchronously, so events are given by signals.
/// \return a unique data transfer identifier.
/// \except std::invalid_argument account doesn't exist or don't support data transfer.
/// \except std::ios_base::failure in case of open file errors.
DRing::DataTransferId sendFile(const std::string& account_id,
const std::string& peer_uri,
const std::string& file_path,
const std::string& display_name);
/// \see DRing::sendFile
DRing::DataTransferError sendFile(const DRing::DataTransferInfo& info,
DRing::DataTransferId& id) noexcept;
/// Accept an incoming transfer and send data into given file.
void acceptAsFile(const DRing::DataTransferId& id,
const std::string& file_path,
std::size_t offset);
/// \see DRing::acceptFileTransfer
DRing::DataTransferError acceptAsFile(const DRing::DataTransferId& id,
const std::string& file_path,
int64_t offset) noexcept;
/// Abort a transfer.
/// The transfer id is abort and removed. The id is not longer valid after the call.
void cancel(const DRing::DataTransferId& id);
/// \see DRing::cancelDataTransfer
DRing::DataTransferError cancel(const DRing::DataTransferId& id) noexcept;
/// \return a copy of all information about a data transfer
DRing::DataTransferInfo info(const DRing::DataTransferId& id) const;
/// \see DRing::dataTransferInfo
DRing::DataTransferError info(const DRing::DataTransferId& id,
DRing::DataTransferInfo& info) const noexcept;
/// \return number of bytes sent/received by a data transfer
/// \note this method is fatest than info()
std::streamsize bytesProgress(const DRing::DataTransferId& id) const;
/// \see DRing::dataTransferBytesProgress