From 64cce5645f12dd5a69c5e34ee33638a55300abc1 Mon Sep 17 00:00:00 2001
From: Nicolas Jager <nicolas.jager@savoirfairelinux.com>
Date: Wed, 24 Jan 2018 12:03:40 -0500
Subject: [PATCH] Implementation of datatransfer
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

add the data transfer functionality to LRC

this patch does not fullfil all the models specifications
and is not fully "concern separated" due to design flaws
(much of the datatransfer is delegated to conversation model).
That will be corrected further in time.

Change-Id: I9bc3d3f8fc544fe97ef43805bad54a5779797234
Reviewed-by: Anthony Léonard <anthony.leonard@savoirfairelinux.com>
---
 src/api/conversationmodel.h      |   6 +-
 src/api/datatransfer.h           |  57 +++++-
 src/api/datatransfermodel.h      |  28 ++-
 src/api/interaction.h            |  55 +++++-
 src/authority/databasehelper.cpp |  24 ++-
 src/authority/databasehelper.h   |   6 +-
 src/callbackshandler.cpp         |  29 ++-
 src/callbackshandler.h           |   8 +-
 src/conversationmodel.cpp        | 309 ++++++++++++++++++++++++++++---
 src/datatransfermodel.cpp        |  99 +++-------
 10 files changed, 498 insertions(+), 123 deletions(-)

diff --git a/src/api/conversationmodel.h b/src/api/conversationmodel.h
index 82c0faa7..a30a141b 100644
--- a/src/api/conversationmodel.h
+++ b/src/api/conversationmodel.h
@@ -148,9 +148,11 @@ public:
      */
     void deleteObsoleteHistory(int date);
 
-    void sendFile(const std::string& uid, const std::string& path, const std::string& filename);
+    void sendFile(const std::string& convUid, const std::string& path, const std::string& filename);
 
-    void acceptFile(const std::string& uid, uint64_t interactionId);
+    void acceptTransfer(const std::string& convUid, uint64_t interactionId, const std::string& path);
+
+    void cancelTransfer(const std::string& convUid, uint64_t interactionId);
 
 Q_SIGNALS:
     /**
diff --git a/src/api/datatransfer.h b/src/api/datatransfer.h
index 1de9fe7f..8e9209f2 100644
--- a/src/api/datatransfer.h
+++ b/src/api/datatransfer.h
@@ -17,6 +17,9 @@
  ***************************************************************************/
 #pragma once
 
+// std
+#include <ctime>
+
 // LRC
 #include "typedefs.h"
 
@@ -33,11 +36,62 @@ enum class Status {
     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)
+    INVALID
 };
 
+static inline const std::string
+to_string(const Status& status)
+{
+    switch(status) {
+    case Status::on_connection:
+        return "on_connection";
+    case Status::on_progress:
+        return "on_progress";
+    case Status::success:
+        return "success";
+    case Status::stop_by_peer:
+        return "stop_by_peer";
+    case Status::stop_by_host:
+        return "stop_by_host";
+    case Status::unjoinable_peer:
+        return "unjoinable_peer";
+    case Status::invalid_pathname:
+        return "invalid_pathname";
+    case Status::unsupported:
+        return "unsupported";
+    case Status::INVALID:
+    default:
+        return "INVALID";
+    }
+}
+
+static inline Status
+to_status(const std::string& status)
+{
+    if (status == "on_connection")
+        return datatransfer::Status::on_connection;
+    else if (status == "on_progress")
+        return datatransfer::Status::on_progress;
+    else if (status == "success")
+        return datatransfer::Status::success;
+    else if (status == "stop_by_peer")
+        return datatransfer::Status::stop_by_peer;
+    else if (status == "stop_by_host")
+        return datatransfer::Status::stop_by_host;
+    else if (status == "unjoinable_peer")
+        return datatransfer::Status::unjoinable_peer;
+    else if (status == "invalid_pathname")
+        return datatransfer::Status::invalid_pathname;
+    else if (status == "unsupported")
+        return datatransfer::Status::unsupported;
+    else
+        return datatransfer::Status::INVALID;
+
+}
+
 struct Info
 {
-    std::string uid; ///< long-term and unique identifier (used for historic)
+    const std::string uid; ///< long-term and unique identifier (used for historic)
     Status status;
     bool isOutgoing;
     std::size_t totalSize;
@@ -46,6 +100,7 @@ struct Info
     std::string displayName;
     std::string accountId;
     std::string peerUri;
+    std::time_t timestamp = 0;
 };
 
 } // namespace lrc::api::datatransfer
diff --git a/src/api/datatransfermodel.h b/src/api/datatransfermodel.h
index de0edb85..fc171a96 100644
--- a/src/api/datatransfermodel.h
+++ b/src/api/datatransfermodel.h
@@ -41,6 +41,10 @@ namespace api {
 
 class BehaviorController;
 
+namespace datatransfer {
+class Info;
+} // namespace datatransfer
+
 /**
  *  @brief Class that manages data transfer.
  */
@@ -52,32 +56,26 @@ public:
                       const CallbacksHandler& callbacksHandler);
     ~DataTransferModel();
 
-    std::vector<std::string> transferIdList() const;
-
-    std::string sendFile(const std::string& account_id, const std::string& peer_uri,
+    void 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);
+    std::streamsize bytesProgress(int interactionId);
+
+    void accept(int interactionId, const std::string& file_path, std::size_t offset);
 
-    void acceptFile(const std::string& id, const std::string& file_path, std::size_t offset);
+    void cancel(int interactionId);
 
-    void cancel(const std::string& id);
+    void registerTransferId(long long dringId, int interactionId);
+
+    int getInteractionIdFromDringId(long long dringId);
 
 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);
+    void incomingTransfer(api::datatransfer::Info dataTransferInfo);
 
     /**
      * Connect this signal to know when an existing data transfer has changed of status.
diff --git a/src/api/interaction.h b/src/api/interaction.h
index 71f50705..f0871b4c 100644
--- a/src/api/interaction.h
+++ b/src/api/interaction.h
@@ -35,7 +35,9 @@ enum class Type {
     INVALID,
     TEXT,
     CALL,
-    CONTACT
+    CONTACT,
+    OUTGOING_DATA_TRANSFER,
+    INCOMING_DATA_TRANSFER
 };
 
 static inline const std::string
@@ -48,6 +50,10 @@ to_string(const Type& type)
         return "CALL";
     case Type::CONTACT:
         return "CONTACT";
+    case Type::OUTGOING_DATA_TRANSFER:
+        return "OUTGOING_DATA_TRANSFER";
+    case Type::INCOMING_DATA_TRANSFER:
+        return "INCOMING_DATA_TRANSFER";
     case Type::INVALID:
     default:
         return "INVALID";
@@ -63,6 +69,10 @@ to_type(const std::string& type)
         return interaction::Type::CALL;
     else if (type == "CONTACT")
         return interaction::Type::CONTACT;
+    else if (type == "OUTGOING_DATA_TRANSFER")
+        return interaction::Type::OUTGOING_DATA_TRANSFER;
+    else if (type == "INCOMING_DATA_TRANSFER")
+        return interaction::Type::INCOMING_DATA_TRANSFER;
     else
         return interaction::Type::INVALID;
 }
@@ -75,7 +85,14 @@ enum class Status {
     FAILED,
     SUCCEED,
     READ,
-    UNREAD
+    UNREAD,
+    TRANSFER_CREATED, /*[jn] mettre à jour les fonctions de conversion */
+    TRANSFER_ACCEPTED,
+    TRANSFER_CANCELED,
+    TRANSFER_ERROR,
+    TRANSFER_ONGOING,
+    TRANSFER_AWAITING,
+    TRANSFER_FINISHED
 };
 
 static inline const std::string
@@ -94,7 +111,20 @@ to_string(const Status& status)
         return "READ";
     case Status::UNREAD:
         return "UNREAD";
-    case Status::INVALID:
+    case Status::TRANSFER_CREATED:
+        return "TRANSFER_CREATED";
+    case Status::TRANSFER_ACCEPTED:
+        return "TRANSFER_ACCEPTED";
+    case Status::TRANSFER_CANCELED:
+        return "TRANSFER_CANCELED";
+    case Status::TRANSFER_ERROR:
+        return "TRANSFER_ERROR";
+    case Status::TRANSFER_ONGOING:
+        return "TRANSFER_ONGOING";
+    case Status::TRANSFER_AWAITING:
+        return "TRANSFER_AWAITING";
+    case Status::TRANSFER_FINISHED:
+        return "TRANSFER_FINISHED";
     default:
         return "INVALID";
     }
@@ -115,6 +145,20 @@ to_status(const std::string& status)
         return interaction::Status::READ;
     else if (status == "UNREAD")
         return interaction::Status::UNREAD;
+    else if (status == "TRANSFER_CREATED")
+        return interaction::Status::TRANSFER_CREATED;
+    else if (status == "TRANSFER_ACCEPTED")
+        return interaction::Status::TRANSFER_ACCEPTED;
+    else if (status == "TRANSFER_CANCELED")
+        return interaction::Status::TRANSFER_CANCELED;
+    else if (status == "TRANSFER_ERROR")
+        return interaction::Status::TRANSFER_ERROR;
+    else if (status == "TRANSFER_ONGOING")
+        return interaction::Status::TRANSFER_ONGOING;
+    else if (status == "TRANSFER_AWAITING")
+        return interaction::Status::TRANSFER_AWAITING;
+    else if (status == "TRANSFER_FINISHED")
+        return interaction::Status::TRANSFER_FINISHED;
     else
         return interaction::Status::INVALID;
 
@@ -130,8 +174,9 @@ struct Info
 };
 
 static inline bool isOutgoing(const Info& interaction) {
-    return interaction.status != lrc::api::interaction::Status::READ
-    && interaction.status != lrc::api::interaction::Status::UNREAD;
+    return (interaction.status != lrc::api::interaction::Status::READ
+    && interaction.status != lrc::api::interaction::Status::UNREAD)
+    || interaction.type == lrc::api::interaction::Type::OUTGOING_DATA_TRANSFER;
 }
 
 } // namespace interaction
diff --git a/src/authority/databasehelper.cpp b/src/authority/databasehelper.cpp
index 2a2b1933..67b3343a 100644
--- a/src/authority/databasehelper.cpp
+++ b/src/authority/databasehelper.cpp
@@ -18,6 +18,7 @@
  ***************************************************************************/
 #include "databasehelper.h"
 #include "api/profile.h"
+#include "api/datatransfer.h"
 
 namespace lrc
 {
@@ -209,6 +210,27 @@ addMessageToConversation(Database& db,
                           {":status", to_string(msg.status)}});
 }
 
+int
+addDataTransferToConversation(Database& db,
+                              const std::string& accountProfileId,
+                              const std::string& conversationId,
+                              const DataTransferInfo& infoFromDaemon)
+{
+    auto peerProfileId = getProfileId(db, infoFromDaemon.peer.toStdString());
+    auto authorId = (infoFromDaemon.isOutgoing) ? peerProfileId : peerProfileId;
+
+    return db.insertInto("interactions",
+                         {{":account_id", "account_id"}, {":author_id", "author_id"},
+                         {":conversation_id", "conversation_id"}, {":timestamp", "timestamp"},
+                         {":body", "body"}, {":type", "type"},
+                         {":status", "status"}},
+                         {{":account_id", accountProfileId}, {":author_id", authorId},
+                         {":conversation_id", conversationId},
+                         {":timestamp", std::to_string(std::time(nullptr))},
+                         {":body", infoFromDaemon.displayName.toStdString()}, {":type", (infoFromDaemon.isOutgoing)?"OUTGOING_DATA_TRANSFER":"INCOMING_DATA_TRANSFER"},
+                         {":status", "TRANSFER_CREATED"}});
+}
+
 int
 addOrUpdateMessage(Database& db,
                          const std::string& accountProfile,
@@ -267,7 +289,7 @@ std::string getInteractionIdByDaemonId(Database& db, const std::string& id)
 
 
 void updateInteractionStatus(Database& db, unsigned int id,
-                             api::interaction::Status& newStatus)
+                             api::interaction::Status newStatus)
 {
     db.update("interactions", "status=:status",
               {{":status", api::interaction::to_string(newStatus)}},
diff --git a/src/authority/databasehelper.h b/src/authority/databasehelper.h
index f4bcbe69..9ee85493 100644
--- a/src/authority/databasehelper.h
+++ b/src/authority/databasehelper.h
@@ -177,7 +177,7 @@ std::string getInteractionIdByDaemonId(Database& db, const std::string& id);
  * @param newStatus
  */
 void updateInteractionStatus(Database& db, unsigned int id,
-                             api::interaction::Status& newStatus);
+                             api::interaction::Status newStatus);
 
 /**
  * Clear history but not the conversation started interaction
@@ -229,6 +229,10 @@ void addContact(Database& db, const std::string& accountUri, const std::string&
  */
 int countUnreadFromInteractions(Database& db, const std::string& conversationId);
 
+int addDataTransferToConversation(Database& db,
+                                  const std::string& accountProfileId,
+                                  const std::string& conversationId,
+                                  const DataTransferInfo& infoFromDaemon);
 } // namespace database
 
 } // namespace authority
diff --git a/src/callbackshandler.cpp b/src/callbackshandler.cpp
index 7781cf27..f2e5fa92 100644
--- a/src/callbackshandler.cpp
+++ b/src/callbackshandler.cpp
@@ -282,9 +282,34 @@ CallbacksHandler::slotAccountMessageStatusChanged(const QString& accountId,
 }
 
 void
-CallbacksHandler::slotDataTransferEvent(qulonglong dring_id, uint code)
+CallbacksHandler::slotDataTransferEvent(qulonglong dringId, uint codeStatus)
 {
-    emit incomingTransfer(-1, -1);
+    auto event = DRing::DataTransferEventCode(codeStatus);
+
+    switch (event) {
+    case DRing::DataTransferEventCode::created:
+        emit incomingTransfer(static_cast<long long>(dringId));
+        break;
+    case DRing::DataTransferEventCode::closed_by_host:
+    case DRing::DataTransferEventCode::closed_by_peer:
+        emit transferStatusCanceled(static_cast<long long>(dringId));
+        break;
+    case DRing::DataTransferEventCode::wait_peer_acceptance:
+    case DRing::DataTransferEventCode::wait_host_acceptance:
+        emit transferStatusAwaiting(static_cast<long long>(dringId));
+        break;
+    case DRing::DataTransferEventCode::ongoing:
+        emit transferStatusOngoing(static_cast<long long>(dringId));
+        break;
+    case DRing::DataTransferEventCode::finished:
+        emit transferStatusFinished(static_cast<long long>(dringId));
+        break;
+    case DRing::DataTransferEventCode::invalid_pathname:
+    case DRing::DataTransferEventCode::unjoinable_peer:
+    case DRing::DataTransferEventCode::unsupported:
+        emit transferStatusError(static_cast<long long>(dringId));
+        break;
+    }
 }
 
 } // namespace lrc
diff --git a/src/callbackshandler.h b/src/callbackshandler.h
index a1e4a65d..adf4d2bd 100644
--- a/src/callbackshandler.h
+++ b/src/callbackshandler.h
@@ -160,7 +160,13 @@ Q_SIGNALS:
                                      const uint64_t id,
                                      const std::string& to, int status);
 
-    void incomingTransfer(long long dring_id, uint code);
+    void incomingTransfer(long long dringId);
+    //~ void transferStatusChanged(const long long dringId, uint codeStatus);
+    void transferStatusCanceled(long long dringId);
+    void transferStatusAwaiting(long long dringId);
+    void transferStatusOngoing(long long dringId);
+    void transferStatusFinished(long long dringId);
+    void transferStatusError(long long dringId);
 
 private Q_SLOTS:
     /**
diff --git a/src/conversationmodel.cpp b/src/conversationmodel.cpp
index 9dd7ce5b..5ac5ea65 100644
--- a/src/conversationmodel.cpp
+++ b/src/conversationmodel.cpp
@@ -214,13 +214,16 @@ public Q_SLOTS:
      */
     void slotConferenceRemoved(const std::string& confId);
 
-    void slotIncomingTransfer(const std::string& uid,
-                              const std::string& display_name,
-                              const std::size_t size,
-                              const std::size_t offset);
+    void slotIncomingTransfer(long long dringId);
 
-    void slotTransferStatusChanged(const std::string& uid,
-                               datatransfer::Status status);
+    //~ void slotTransferStatusChanged(long long dringId, uint codeStatus);
+
+    void slotCancelTransfer(long long dringId);
+
+    void slotTransferStatusAwaiting(long long dringId);
+    void slotTransferStatusOngoing(long long dringId);
+    void slotTransferStatusFinished(long long dringId);
+    void slotTransferStatusError(long long dringId);
 
 };
 
@@ -712,16 +715,31 @@ ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked,
             this,
             &ConversationModelPimpl::slotConferenceRemoved);
 
-    connect(&dataTransferModel,
-            &DataTransferModel::incomingTransfer,
+    // data transfer
+    connect(&callbacksHandler,
+            &CallbacksHandler::incomingTransfer,
             this,
             &ConversationModelPimpl::slotIncomingTransfer);
-
-    connect(&dataTransferModel,
-            &DataTransferModel::transferStatusChanged,
+    connect(&callbacksHandler,
+            &CallbacksHandler::transferStatusCanceled,
             this,
-            &ConversationModelPimpl::slotTransferStatusChanged);
-
+            &ConversationModelPimpl::slotCancelTransfer);
+    connect(&callbacksHandler,
+            &CallbacksHandler::transferStatusAwaiting,
+            this,
+            &ConversationModelPimpl::slotTransferStatusAwaiting);
+    connect(&callbacksHandler,
+            &CallbacksHandler::transferStatusOngoing,
+            this,
+            &ConversationModelPimpl::slotTransferStatusOngoing);
+    connect(&callbacksHandler,
+            &CallbacksHandler::transferStatusFinished,
+            this,
+            &ConversationModelPimpl::slotTransferStatusFinished);
+    connect(&callbacksHandler,
+            &CallbacksHandler::transferStatusError,
+            this,
+            &ConversationModelPimpl::slotTransferStatusError);
 }
 
 ConversationModelPimpl::~ConversationModelPimpl()
@@ -948,6 +966,14 @@ ConversationModelPimpl::addConversationWith(const std::string& convId,
             }
             slotUpdateInteractionStatus(linked.owner.id, std::stoull(id),
                                         contactUri, status);
+        } else if (interaction.second.status == interaction::Status::TRANSFER_CREATED
+                   || interaction.second.status == interaction::Status::TRANSFER_AWAITING
+                   || interaction.second.status == interaction::Status::TRANSFER_ONGOING
+                   || interaction.second.status == interaction::Status::TRANSFER_ACCEPTED) {
+            // If a datatransfer was left in a non-terminal status in DB, we switch this status to ERROR
+            // TODO : Improve for DBus clients as daemon and transfer may still be ongoing
+            database::updateInteractionStatus(db, interaction.first, interaction::Status::TRANSFER_ERROR);
+            interaction.second.status = interaction::Status::TRANSFER_ERROR;
         }
     }
 
@@ -1226,18 +1252,80 @@ ConversationModelPimpl::getNumberOfUnreadMessagesFor(const std::string& uid)
 }
 
 void
-ConversationModelPimpl::slotIncomingTransfer(const std::string& uid,
-                                             const std::string& display_name,
-                                             const std::size_t size,
-                                             const std::size_t offset)
+ConversationModelPimpl::slotIncomingTransfer(long long dringId)
 {
-    emit linked.newInteraction("", -1, {});
+    // no auto
+    DataTransferInfo infoFromDaemon = ConfigurationManager::instance().dataTransferInfo(dringId);
+
+    auto accountProfileId = database::getProfileId(db, linked.owner.profileInfo.uri);
+    auto contactProfileId = database::getProfileId(db, infoFromDaemon.peer.toStdString());
+
+    // get the conversation if any
+    auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
+
+    if (conv.empty())
+        return;
+
+    // add interaction to the db
+    auto interactionId = database::addDataTransferToConversation(db, accountProfileId, conv[0], infoFromDaemon);
+
+    // add transfert to the transfer model
+    dataTransferModel.registerTransferId(dringId, interactionId);
+
+    // prepare interaction Info and emit signal for the client
+    auto conversationIdx = indexOf(conv[0]);
+    if (conversationIdx != -1) {
+        auto& interactions = conversations[conversationIdx].interactions;
+        auto it = interactions.find(interactionId);
+
+        if (it != interactions.end()) {
+            return;
+        }
+
+        auto interaction = interaction::Info {contactProfileId, infoFromDaemon.displayName.toStdString(),
+                                              std::time(nullptr),
+                                              (infoFromDaemon.isOutgoing)?interaction::Type::OUTGOING_DATA_TRANSFER:interaction::Type::INCOMING_DATA_TRANSFER,
+                                              interaction::Status::TRANSFER_CREATED};
+
+        auto it2 = conversations[conversationIdx].interactions.emplace(interactionId, interaction);
+        conversations[conversationIdx].lastMessageUid = interactionId;
+        dirtyConversations = true;
+        emit linked.newInteraction(conv[0], interactionId, interaction);
+        sortConversations();
+        emit linked.modelSorted();
+    }
 }
 
 void
-ConversationModelPimpl::slotTransferStatusChanged(const std::string& uid, datatransfer::Status status)
+ConversationModelPimpl::slotCancelTransfer(long long dringId)
 {
-    emit linked.interactionStatusUpdated("", -1, {});
+    // no auto [jn] facto le code
+    DataTransferInfo infoFromDaemon = ConfigurationManager::instance().dataTransferInfo(dringId);
+
+    auto accountProfileId = database::getProfileId(db, linked.owner.profileInfo.uri);
+    auto contactProfileId = database::getProfileId(db, infoFromDaemon.peer.toStdString());
+
+    // get the conversation if any
+    auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
+
+    if (conv.empty())
+        return;
+
+    // get interaction id from data transfer model
+    auto interactionId = dataTransferModel.getInteractionIdFromDringId(dringId);
+
+    // update information in the db
+    database::updateInteractionStatus(db, interactionId, interaction::Status::TRANSFER_CANCELED);
+
+    // prepare interaction Info and emit signal for the client
+    auto conversationIdx = indexOf(conv[0]);
+    if (conversationIdx != -1) {
+        auto& interactions = conversations[conversationIdx].interactions;
+        auto it = interactions.find(interactionId);
+        if (it != interactions.end()) {
+            emit linked.interactionStatusUpdated(conv[0], interactionId, it->second);
+        }
+    }
 }
 
 void
@@ -1246,19 +1334,188 @@ ConversationModel::sendFile(const std::string& uid, const std::string& path, con
     auto conversationIdx = pimpl_->indexOf(uid);
     if (conversationIdx != -1) {
         auto& peerUri = pimpl_->conversations[conversationIdx].participants.front();
-        if (not peerUri.empty())
+        if (not peerUri.empty()) {
             pimpl_->dataTransferModel.sendFile(owner.id.c_str(), peerUri.c_str(), path.c_str(), filename.c_str());
+        }
     }
 }
 
 void
-ConversationModel::acceptFile(const std::string& uid, uint64_t interactionId)
+ConversationModel::acceptTransfer(const std::string& convUid, uint64_t interactionId, const std::string& path)
 {
-    auto conversationIdx = pimpl_->indexOf(uid);
+    pimpl_->dataTransferModel.accept(interactionId, path, 0);
+    database::updateInteractionStatus(pimpl_->db, interactionId, interaction::Status::TRANSFER_ACCEPTED);
+
+    // prepare interaction Info and emit signal for the client
+    auto conversationIdx = pimpl_->indexOf(convUid);
+    if (conversationIdx != -1) {
+        auto& interactions = pimpl_->conversations[conversationIdx].interactions;
+        auto it = interactions.find(interactionId);
+        if (it != interactions.end()) {
+            it->second.status = interaction::Status::TRANSFER_ACCEPTED;
+            pimpl_->dirtyConversations = true;
+            emit interactionStatusUpdated(convUid, interactionId, it->second);
+        }
+    }
+}
+
+void
+ConversationModel::cancelTransfer(const std::string& convUid, uint64_t interactionId)
+{
+    // For this action, we change interaction status before effective canceling as daemon will
+    // emit Finished event code immediatly (before leaving this method) in non-DBus mode.
+    auto conversationIdx = pimpl_->indexOf(convUid);
+    if (conversationIdx != -1) {
+        auto& interactions = pimpl_->conversations[conversationIdx].interactions;
+        auto it = interactions.find(interactionId);
+        if (it != interactions.end()) {
+            it->second.status = interaction::Status::TRANSFER_CANCELED;
+
+            // update information in the db
+            database::updateInteractionStatus(pimpl_->db, interactionId, interaction::Status::TRANSFER_CANCELED);
+
+            // Forward cancel action to daemon
+            pimpl_->dataTransferModel.cancel(interactionId);
+            pimpl_->dirtyConversations = true;
+            emit interactionStatusUpdated(convUid, interactionId, it->second);
+        }
+    }
+}
+
+void
+ConversationModelPimpl::slotTransferStatusAwaiting(long long dringId)
+{
+    // no auto [jn] facto le code
+    DataTransferInfo infoFromDaemon = ConfigurationManager::instance().dataTransferInfo(dringId);
+
+    auto accountProfileId = database::getProfileId(db, linked.owner.profileInfo.uri);
+    auto contactProfileId = database::getProfileId(db, infoFromDaemon.peer.toStdString());
+
+    // get the conversation if any
+    auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
+
+    if (conv.empty())
+        return;
+
+    // get interaction id from data transfer model
+    auto interactionId = dataTransferModel.getInteractionIdFromDringId(dringId);
+
+    // update information in the db
+    database::updateInteractionStatus(db, interactionId, interaction::Status::TRANSFER_AWAITING);
+
+    // prepare interaction Info and emit signal for the client
+    auto conversationIdx = indexOf(conv[0]);
+    if (conversationIdx != -1) {
+        auto& interactions = conversations[conversationIdx].interactions;
+        auto it = interactions.find(interactionId);
+        if (it != interactions.end()) {
+            it->second.status = interaction::Status::TRANSFER_AWAITING;
+            dirtyConversations = true;
+            emit linked.interactionStatusUpdated(conv[0], interactionId, it->second);
+        }
+    }
+}
+
+void
+ConversationModelPimpl::slotTransferStatusOngoing(long long dringId)
+{
+    // no auto [jn] facto le code
+    DataTransferInfo infoFromDaemon = ConfigurationManager::instance().dataTransferInfo(dringId);
+
+    auto accountProfileId = database::getProfileId(db, linked.owner.profileInfo.uri);
+    auto contactProfileId = database::getProfileId(db, infoFromDaemon.peer.toStdString());
+
+    // get the conversation if any
+    auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
+
+    if (conv.empty())
+        return;
+
+    // get interaction id from data transfer model
+    auto interactionId = dataTransferModel.getInteractionIdFromDringId(dringId);
+
+    // update information in the db
+    database::updateInteractionStatus(db, interactionId, interaction::Status::TRANSFER_ONGOING);
+
+    // prepare interaction Info and emit signal for the client
+    auto conversationIdx = indexOf(conv[0]);
     if (conversationIdx != -1) {
-        auto& dataTransferUid = pimpl_->dataTransferModel.transferIdList()[interactionId];
-        if (not dataTransferUid.empty())
-            pimpl_->dataTransferModel.acceptFile(dataTransferUid, "~", 0);
+        auto& interactions = conversations[conversationIdx].interactions;
+        auto it = interactions.find(interactionId);
+        if (it != interactions.end()) {
+            it->second.status = interaction::Status::TRANSFER_ONGOING;
+            dirtyConversations = true;
+            emit linked.interactionStatusUpdated(conv[0], interactionId, it->second);
+        }
+    }
+}
+
+void
+ConversationModelPimpl::slotTransferStatusFinished(long long dringId)
+{
+    // no auto [jn] facto le code
+    DataTransferInfo infoFromDaemon = ConfigurationManager::instance().dataTransferInfo(dringId);
+
+    auto accountProfileId = database::getProfileId(db, linked.owner.profileInfo.uri);
+    auto contactProfileId = database::getProfileId(db, infoFromDaemon.peer.toStdString());
+
+    // get the conversation if any
+    auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
+
+    if (conv.empty())
+        return;
+
+    // get interaction id from data transfer model
+    auto interactionId = dataTransferModel.getInteractionIdFromDringId(dringId);
+
+    // prepare interaction Info and emit signal for the client
+    auto conversationIdx = indexOf(conv[0]);
+    if (conversationIdx != -1) {
+        auto& interactions = conversations[conversationIdx].interactions;
+        auto it = interactions.find(interactionId);
+        if (it != interactions.end()) {
+            // We need to check if current status is ONGOING as CANCELED must not be transformed into FINISHED
+            if (it->second.status == interaction::Status::TRANSFER_ONGOING) {
+                // update information in the db
+                database::updateInteractionStatus(db, interactionId, interaction::Status::TRANSFER_FINISHED);
+                it->second.status = interaction::Status::TRANSFER_FINISHED;
+                dirtyConversations = true;
+                emit linked.interactionStatusUpdated(conv[0], interactionId, it->second);
+            }
+        }
+    }
+}
+
+void
+ConversationModelPimpl::slotTransferStatusError(long long dringId)
+{
+    // no auto [jn] facto le code
+    DataTransferInfo infoFromDaemon = ConfigurationManager::instance().dataTransferInfo(dringId);
+
+    auto accountProfileId = database::getProfileId(db, linked.owner.profileInfo.uri);
+    auto contactProfileId = database::getProfileId(db, infoFromDaemon.peer.toStdString());
+
+    // get the conversation if any
+    auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
+
+    if (conv.empty())
+        return;
+
+    // get interaction id from data transfer model
+    auto interactionId = dataTransferModel.getInteractionIdFromDringId(dringId);
+
+    // update information in the db
+    database::updateInteractionStatus(db, interactionId, interaction::Status::TRANSFER_ERROR);
+
+    // prepare interaction Info and emit signal for the client
+    auto conversationIdx = indexOf(conv[0]);
+    if (conversationIdx != -1) {
+        auto& interactions = conversations[conversationIdx].interactions;
+        auto it = interactions.find(interactionId);
+        if (it != interactions.end()) {
+            dirtyConversations = true;
+            emit linked.interactionStatusUpdated(conv[0], interactionId, it->second);
+        }
     }
 }
 
diff --git a/src/datatransfermodel.cpp b/src/datatransfermodel.cpp
index 82d4f1d2..3bc088f6 100644
--- a/src/datatransfermodel.cpp
+++ b/src/datatransfermodel.cpp
@@ -65,16 +65,13 @@ public:
          Database& database,
          const CallbacksHandler& callbacksHandler);
 
+    std::vector<std::string> transferIdList() const;
+
     DataTransferModel& upLink;
-    std::map<DRing::DataTransferId, std::string> dring2lrcIdMap;
-    std::map<std::string, DRing::DataTransferId> lrc2dringIdMap; // stricly the reverse map of dring2lrcIdMap
+    std::map<long long, int> dring2lrcIdMap;
+    std::map<int, long long> lrc2dringIdMap; // stricly the reverse map of dring2lrcIdMap
     Database& database;
     const CallbacksHandler& callbacksHandler;
-
-    std::string registerTransferId(DRing::DataTransferId id);
-
-public Q_SLOTS:
-    void slotDataTransferEvent(long long dring_id, uint code);
 };
 
 DataTransferModel::Impl::Impl(DataTransferModel& up_link,
@@ -84,41 +81,18 @@ DataTransferModel::Impl::Impl(DataTransferModel& up_link,
     , callbacksHandler {callbacksHandler}
     , database {database}
     , upLink {up_link}
-{
-    connect(&callbacksHandler, &CallbacksHandler::incomingTransfer,
-            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(long long dring_id, uint code)
+DataTransferModel::registerTransferId(long long dringId, int interactionId)
 {
-    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("", "", 0, 0);
-            return;
-        }
-    }
 
-    emit upLink.transferStatusChanged("", convertDataTransferEvent(event));
+    pimpl_->dring2lrcIdMap.emplace(dringId, interactionId);
+    pimpl_->lrc2dringIdMap.emplace(interactionId, dringId);
 }
 
+
 DataTransferModel::DataTransferModel(Database& database,
                                      const CallbacksHandler& callbacksHandler)
     : QObject()
@@ -128,21 +102,21 @@ DataTransferModel::DataTransferModel(Database& database,
 DataTransferModel::~DataTransferModel() = default;
 
 std::vector<std::string>
-DataTransferModel::transferIdList() const
+DataTransferModel::Impl::transferIdList() const
 {
     VectorULongLong dring_list = ConfigurationManager::instance().dataTransferList();
-    for (auto dring_id : dring_list) {
-         pimpl_->registerTransferId(dring_id);
-    }
+    //~ 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);
-    }
+    //~ result.reserve(dring_list.size());
+    //~ for (auto& item : pimpl_->lrc2dringIdMap) {
+        //~ result.push_back(item.first);
+    //~ }
     return result;
 }
 
-std::string
+void
 DataTransferModel::sendFile(const std::string& account_id, const std::string& peer_uri,
                             const std::string& file_path, const std::string& display_name)
 {
@@ -151,49 +125,36 @@ DataTransferModel::sendFile(const std::string& account_id, const std::string& pe
                                                            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.isOutgoing;
-    lrc_info.totalSize = dring_info.totalSize;
-    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));
-    lrc_info.accountId = dring_info.accountId.toStdString();
-    lrc_info.peerUri = dring_info.peer.toStdString();
-    return lrc_info;
 }
 
 std::streamsize
-DataTransferModel::bytesProgress(const std::string& lrc_id)
+DataTransferModel::bytesProgress(int interactionId)
 {
-    return ConfigurationManager::instance().dataTransferBytesProgress(pimpl_->lrc2dringIdMap.at(lrc_id));
+    return ConfigurationManager::instance().dataTransferBytesProgress(pimpl_->lrc2dringIdMap.at(interactionId));
 }
 
 void
-DataTransferModel::acceptFile(const std::string& lrc_id,
+DataTransferModel::accept(int interactionId,
                                       const std::string& file_path,
                                       std::size_t offset)
 {
-    auto dring_id = pimpl_->lrc2dringIdMap.at(lrc_id);
+    auto dring_id = pimpl_->lrc2dringIdMap.at(interactionId);
     ConfigurationManager::instance().acceptFileTransfer(dring_id, QString::fromStdString(file_path), offset);
 }
 
 void
-DataTransferModel::cancel(const std::string& lrc_id)
+DataTransferModel::cancel(int interactionId)
 {
-    auto dring_id = pimpl_->lrc2dringIdMap.at(lrc_id);
+    auto dring_id = pimpl_->lrc2dringIdMap.at(interactionId);
     ConfigurationManager::instance().cancelDataTransfer(dring_id);
 }
 
+int
+DataTransferModel::getInteractionIdFromDringId(long long dringId)
+{
+    return pimpl_->dring2lrcIdMap.at(dringId);
+}
+
 }} // namespace lrc::api
 
 #include "api/moc_datatransfermodel.cpp"
-- 
GitLab