From 19dbc12486135ef30a88e38447596ccc718aae69 Mon Sep 17 00:00:00 2001
From: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
Date: Thu, 8 Feb 2018 10:06:40 -0500
Subject: [PATCH] datatransfer: API changes to not throw
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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: Anthony Léonard <anthony.leonard@savoirfairelinux.com>
---
 .../cx.ring.Ring.ConfigurationManager.xml     |  63 +++----
 bin/dbus/dbusconfigurationmanager.cpp         |  76 +++++---
 bin/dbus/dbusconfigurationmanager.h           |  13 +-
 configure.ac                                  |   2 +-
 doc/doxygen/core-doc.cfg.in                   |   2 +-
 src/client/datatransfer.cpp                   |  38 ++--
 src/data_transfer.cpp                         | 172 +++++++++---------
 src/data_transfer.h                           |  50 ++---
 src/dring/datatransfer_interface.h            |  85 ++++++---
 src/ftp_server.cpp                            |  12 +-
 10 files changed, 268 insertions(+), 245 deletions(-)

diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
index 6f693e193d..78119db0c2 100644
--- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
+++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
@@ -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">
diff --git a/bin/dbus/dbusconfigurationmanager.cpp b/bin/dbus/dbusconfigurationmanager.cpp
index 8023f720fe..696ed15d48 100644
--- a/bin/dbus/dbusconfigurationmanager.cpp
+++ b/bin/dbus/dbusconfigurationmanager.cpp
@@ -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));
 }
diff --git a/bin/dbus/dbusconfigurationmanager.h b/bin/dbus/dbusconfigurationmanager.h
index df0ea761b3..f37ac08ec4 100644
--- a/bin/dbus/dbusconfigurationmanager.h
+++ b/bin/dbus/dbusconfigurationmanager.h
@@ -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__
diff --git a/configure.ac b/configure.ac
index 4365a1a6b9..7f4cbf3cf9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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$])
diff --git a/doc/doxygen/core-doc.cfg.in b/doc/doxygen/core-doc.cfg.in
index 2e72e59662..eb3fa6b388 100644
--- a/doc/doxygen/core-doc.cfg.in
+++ b/doc/doxygen/core-doc.cfg.in
@@ -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
diff --git a/src/client/datatransfer.cpp b/src/client/datatransfer.cpp
index a4545e9edb..b4882fb955 100644
--- a/src/client/datatransfer.cpp
+++ b/src/client/datatransfer.cpp
@@ -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
diff --git a/src/data_transfer.cpp b/src/data_transfer.cpp
index f695511996..19d51e51dc 100644
--- a/src/data_transfer.cpp
+++ b/src/data_transfer.cpp
@@ -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())
diff --git a/src/data_transfer.h b/src/data_transfer.h
index 6a74bff6c6..746e524535 100644
--- a/src/data_transfer.h
+++ b/src/data_transfer.h
@@ -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
+    DRing::DataTransferError bytesProgress(const DRing::DataTransferId& id, int64_t& total,
+                                           int64_t& progress) const noexcept;
 
     /// Create an IncomingFileTransfer object.
     /// \return a shared pointer on created Stream object, or nullptr in case of error
-    std::shared_ptr<Stream> 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);
+    std::shared_ptr<Stream> onIncomingFileRequest(const DRing::DataTransferInfo& info);
 
 private:
     class Impl;
diff --git a/src/dring/datatransfer_interface.h b/src/dring/datatransfer_interface.h
index 4919cae49a..4fe464cd8a 100644
--- a/src/dring/datatransfer_interface.h
+++ b/src/dring/datatransfer_interface.h
@@ -27,8 +27,7 @@
 #include <map>
 #include <vector>
 #include <memory>
-#include <cstdlib> // std::size_t
-#include <ios> // std::streamsize
+#include <bitset>
 
 namespace DRing {
 
@@ -36,6 +35,7 @@ using DataTransferId = uint64_t;
 
 enum class DataTransferEventCode : uint32_t
 {
+    invalid=0,
     created,
     unsupported,
     wait_peer_acceptance,
@@ -48,19 +48,34 @@ enum class DataTransferEventCode : uint32_t
     unjoinable_peer,
 };
 
+enum class DataTransferError : uint32_t
+{
+    success=0,
+    unknown,
+    io,
+    invalid_argument,
+};
+
+/// Bit definition for DataTransferInfo.flags field
+enum class DataTransferFlags
+{
+    direction=0, ///< 0: outgoing, 1: incoming
+};
+
 struct DataTransferInfo
 {
-    bool isOutgoing; ///< Outgoing or Incoming?
-    DataTransferEventCode lastEvent { DataTransferEventCode::created }; ///< Latest event code sent to the user
-    std::size_t totalSize {0} ; ///< Total number of bytes to sent/receive, 0 if not known
-    std::streamsize bytesProgress {0}; ///< Number of bytes sent/received
-    std::string displayName; ///< Human oriented transfer name
-    std::string path; ///< associated local file path if supported (empty, if not)
     std::string accountId; ///< Identifier of the emiter/receiver account
+    DataTransferEventCode lastEvent { DataTransferEventCode::invalid }; ///< Latest event code sent to the user
+    uint32_t flags {0}; ///< Transfer global information.
+    int64_t totalSize {0} ; ///< Total number of bytes to sent/receive, 0 if not known
+    int64_t bytesProgress {0}; ///< Number of bytes sent/received
     std::string peer; ///< Identifier of the remote peer (in the semantic of the associated account)
+    std::string displayName; ///< Human oriented transfer name
+    std::string path; ///< associated local file path if supported (empty, if not)
+    std::string mimetype; ///< MimeType of transfered data (https://www.iana.org/assignments/media-types/media-types.xhtml)
 };
 
-std::vector<DataTransferId> dataTransferList();
+std::vector<DataTransferId> dataTransferList() noexcept;
 
 /// Asynchronously send a file to a peer using given account connection.
 ///
@@ -68,24 +83,25 @@ std::vector<DataTransferId> dataTransferList();
 /// an internal data transfer and return its identification.
 /// This identity code is used by signals and APIs to follow the transfer progress.
 ///
-/// \param account_id existing account ID with file transfer support
-/// \param peer_uri peer address suitable for the given account
-/// \param file_path pathname of file to transfer
-/// \param display_name optional textual representation given to the peer when the file is proposed.
-/// When empty (or not given), \a file_path is used.
+/// Following the \a info structure fields usage:
+///     - accountId [mandatory] existing account ID with file transfer support
+///     - peer [mandatory] peer address suitable for the given account
+///     - path [mandatory] pathname of file to transfer
+///     - mimetype [optional] file type
+///     - displayName [optional] textual representation given to the peer when the file is proposed
 ///
-/// \return DataTransferId value representing the internal transfer.
+/// Other fields are not used, but you must keep the default assigned value for compatibility.
 ///
-/// \exception std::invalid_argument not existing account
-/// \exception std::ios_base::failure file opening failures
+/// \param info a DataTransferInfo structure filled with information usefull for a file transfer.
+/// \param[out] id data transfer identifiant if function succeed, usable with other APIs. Undefined value in case of error.
 ///
+/// \return DataTransferError::success if file is accepted for transfer, any other value in case of errors
 /// \note If the account is valid but doesn't support file transfer, or if the peer is unjoignable,
-/// or at events during the transfer, the function returns a valid DataTransferId and the user
-/// will be signaled throught DataTransferEvent signal for such event.
-/// There is no reserved or special values on DataTransferId type.
+/// or at any further events during the transfer, the function returns a valid DataTransferId as
+/// the processing is asynchronous. Application will be signaled throught DataTransferEvent signal
+/// for such event. There is no reserved or special values on DataTransferId type.
 ///
-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;
 
 /// Accept an incoming file transfer.
 ///
@@ -99,7 +115,11 @@ DataTransferId sendFile(const std::string& account_id, const std::string& peer_u
 /// \param offset used to indicate the remote side about the number of bytes already received in
 /// a previous transfer session, usefull in transfer continuation mode.
 ///
-void acceptFileTransfer(const DataTransferId& id, const std::string& file_path, std::size_t offset);
+/// \return DataTransferError::invalid_argument if id is unknown.
+/// \note unknown \a id results to a no-op call.
+///
+DataTransferError acceptFileTransfer(const DataTransferId& id, const std::string& file_path,
+                                     int64_t offset) noexcept;
 
 /// Refuse or abort an outgoing or an incoming file transfer.
 ///
@@ -110,23 +130,32 @@ void acceptFileTransfer(const DataTransferId& id, const std::string& file_path,
 ///
 /// \param id data transfer identification value as given by a DataTransferEvent signal.
 ///
-void cancelDataTransfer(const DataTransferId& id);
+/// \return DataTransferError::invalid_argument if id is unknown.
+/// \note unknown \a id results to a no-op call.
+///
+DataTransferError cancelDataTransfer(const DataTransferId& id) noexcept;
 
 /// Return some information on given data transfer.
 ///
 /// \param id data transfer identification value as given by a DataTransferEvent signal.
+/// \param[out] info data transfer information.
 ///
-/// \return transfer information.
+/// \return DataTransferError::invalid_argument if id is unknown.
+/// \note \a info structure is in undefined state in case of error.
 ///
-DataTransferInfo dataTransferInfo(const DataTransferId& id);
+DataTransferError dataTransferInfo(const DataTransferId& id, DataTransferInfo& info) noexcept;
 
 /// Return the amount of sent/received bytes of an existing data transfer.
 ///
 /// \param id data transfer identification value as given by a DataTransferEvent signal.
+/// \param[out] total positive number of bytes to sent/received, or -1 if unknown.
+/// \param[out] progress positive number of bytes already sent/received.
 ///
-/// \return number of successfuly transfered bytes.
+/// \return DataTransferError::success if \a total and \a progress is set with valid values.
+/// DataTransferError::invalid_argument if the id is unknown.
 ///
-std::streamsize dataTransferBytesProgress(const DataTransferId& id);
+DataTransferError dataTransferBytesProgress(const DataTransferId& id, int64_t& total,
+                                            int64_t& progress) noexcept;
 
 // Signal handlers registration
 void registerDataXferHandlers(const std::map<std::string, std::shared_ptr<CallbackWrapperBase>>&);
diff --git a/src/ftp_server.cpp b/src/ftp_server.cpp
index 501170477a..f0f8675ec7 100644
--- a/src/ftp_server.cpp
+++ b/src/ftp_server.cpp
@@ -59,11 +59,13 @@ bool
 FtpServer::startNewFile()
 {
     // Request filename from client (WARNING: synchrone call!)
-    out_ = Manager::instance().dataTransfers->onIncomingFileRequest(accountId_,
-                                                                    peerUri_,
-                                                                    displayName_,
-                                                                    fileSize_,
-                                                                    0 /* TODO: offset */);
+    DRing::DataTransferInfo info {};
+    info.accountId = accountId_;
+    info.peer = peerUri_;
+    info.displayName = displayName_;
+    info.totalSize = fileSize_;
+    info.bytesProgress = 0;
+    out_ = Manager::instance().dataTransfers->onIncomingFileRequest(info);
     return true;
 }
 
-- 
GitLab