From 44a3d0711fe408700a3e69761e7256da2fab910a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Anthony=20L=C3=A9onard?=
 <anthony.leonard@savoirfairelinux.com>
Date: Mon, 12 Feb 2018 16:55:14 -0500
Subject: [PATCH] fix code for datatransfer

Many fixes to adapt LRC to the new datatransfer.

Change-Id: Ibbe322a32d44800c54e9a7ffd99c42a57200deb7
Signed-off-by: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
---
 src/api/conversationmodel.h               |   6 +
 src/api/datatransfer.h                    |   2 +-
 src/api/datatransfermodel.h               |  11 +-
 src/api/interaction.h                     |   1 +
 src/api/lrc.h                             |   6 +
 src/api/newaccountmodel.h                 |   5 +-
 src/authority/databasehelper.cpp          |  56 +++-
 src/authority/databasehelper.h            |   9 +-
 src/callbackshandler.cpp                  |  24 +-
 src/callbackshandler.h                    |  14 +-
 src/contactmodel.cpp                      |  32 --
 src/conversationmodel.cpp                 | 360 ++++++++++++----------
 src/datatransfermodel.cpp                 |  86 ++++--
 src/dbus/metatypes.h                      |  54 ++--
 src/lrc.cpp                               |  15 +-
 src/newaccountmodel.cpp                   |  24 +-
 src/qtwrapper/configurationmanager_wrap.h |  47 +--
 src/typedefs.h                            |  13 +-
 test/mocks/configurationmanager_mock.h    |  23 +-
 19 files changed, 450 insertions(+), 338 deletions(-)

diff --git a/src/api/conversationmodel.h b/src/api/conversationmodel.h
index a30a141b..e7a909f7 100644
--- a/src/api/conversationmodel.h
+++ b/src/api/conversationmodel.h
@@ -33,6 +33,7 @@
 // Data
 #include "api/conversation.h"
 #include "api/profile.h"
+#include "api/datatransfer.h"
 
 namespace lrc
 {
@@ -46,6 +47,8 @@ namespace api
 
 namespace account { struct Info; }
 namespace interaction { struct Info; }
+
+class Lrc;
 class BehaviorController;
 class NewAccountModel;
 
@@ -60,6 +63,7 @@ public:
     const account::Info& owner;
 
     ConversationModel(const account::Info& owner,
+                      Lrc& lrc,
                       Database& db,
                       const CallbacksHandler& callbacksHandler,
                       const api::BehaviorController& behaviorController);
@@ -154,6 +158,8 @@ public:
 
     void cancelTransfer(const std::string& convUid, uint64_t interactionId);
 
+    void getTransferInfo(uint64_t interactionId, api::datatransfer::Info& info);
+
 Q_SIGNALS:
     /**
      * Emitted when a conversation receives a new interaction
diff --git a/src/api/datatransfer.h b/src/api/datatransfer.h
index 8e9209f2..5dcf4798 100644
--- a/src/api/datatransfer.h
+++ b/src/api/datatransfer.h
@@ -91,7 +91,7 @@ to_status(const std::string& status)
 
 struct Info
 {
-    const std::string uid; ///< long-term and unique identifier (used for historic)
+    std::string uid; ///< long-term and unique identifier (used for historic)
     Status status;
     bool isOutgoing;
     std::size_t totalSize;
diff --git a/src/api/datatransfermodel.h b/src/api/datatransfermodel.h
index fc171a96..02f9bda5 100644
--- a/src/api/datatransfermodel.h
+++ b/src/api/datatransfermodel.h
@@ -52,16 +52,15 @@ class LIB_EXPORT DataTransferModel : public QObject {
     Q_OBJECT
 
 public:
-    DataTransferModel(Database& database,
-                      const CallbacksHandler& callbacksHandler);
+    DataTransferModel();
     ~DataTransferModel();
 
     void sendFile(const std::string& account_id, const std::string& peer_uri,
-                         const std::string& file_path, const std::string& display_name);
+                  const std::string& file_path, const std::string& display_name);
 
-    datatransfer::Info transferInfo(const std::string& uid);
+    void transferInfo(long long ringId, datatransfer::Info& lrc_info);
 
-    std::streamsize bytesProgress(int interactionId);
+    void bytesProgress(int interactionId, int64_t& total, int64_t& progress);
 
     void accept(int interactionId, const std::string& file_path, std::size_t offset);
 
@@ -71,6 +70,8 @@ public:
 
     int getInteractionIdFromDringId(long long dringId);
 
+    long long getDringIdFromInteractionId(int interactionId);
+
 Q_SIGNALS:
     /**
      * Connect this signal to know when a data transfer is incoming.
diff --git a/src/api/interaction.h b/src/api/interaction.h
index f0871b4c..708ca184 100644
--- a/src/api/interaction.h
+++ b/src/api/interaction.h
@@ -125,6 +125,7 @@ to_string(const Status& status)
         return "TRANSFER_AWAITING";
     case Status::TRANSFER_FINISHED:
         return "TRANSFER_FINISHED";
+    case Status::INVALID:
     default:
         return "INVALID";
     }
diff --git a/src/api/lrc.h b/src/api/lrc.h
index 0bcad859..ae401962 100644
--- a/src/api/lrc.h
+++ b/src/api/lrc.h
@@ -34,6 +34,7 @@ namespace api
 
 class BehaviorController;
 class NewAccountModel;
+class DataTransferModel;
 
 class LIB_EXPORT Lrc {
 public:
@@ -49,6 +50,11 @@ public:
      * @return a BehaviorController&.
      */
     const BehaviorController& getBehaviorController() const;
+    /**
+     * get a reference on the DataTransfer controller.
+     * @return a DataTransferModel&.
+     */
+    DataTransferModel& getDataTransferModel() const;
 
 private:
     std::unique_ptr<LrcPimpl> lrcPimpl_;
diff --git a/src/api/newaccountmodel.h b/src/api/newaccountmodel.h
index 24888e15..5b4b091e 100644
--- a/src/api/newaccountmodel.h
+++ b/src/api/newaccountmodel.h
@@ -39,6 +39,8 @@ class NewAccountModelPimpl;
 
 namespace api
 {
+
+class Lrc;
 class BehaviorController;
 
 namespace account { struct Info; }
@@ -51,7 +53,8 @@ class LIB_EXPORT NewAccountModel : public QObject {
 public:
     using AccountInfoMap = std::map<std::string, account::Info>;
 
-    NewAccountModel(Database& database,
+    NewAccountModel(Lrc& lrc,
+                    Database& database,
                     const CallbacksHandler& callbackHandler,
                     const api::BehaviorController& behaviorController);
 
diff --git a/src/authority/databasehelper.cpp b/src/authority/databasehelper.cpp
index 67b3343a..f97db6ea 100644
--- a/src/authority/databasehelper.cpp
+++ b/src/authority/databasehelper.cpp
@@ -20,6 +20,8 @@
 #include "api/profile.h"
 #include "api/datatransfer.h"
 
+#include <datatransfer_interface.h>
+
 namespace lrc
 {
 
@@ -181,7 +183,7 @@ getHistory(Database& db, api::conversation::Info& conversation)
                                     {{":conversation_id", conversation.uid}});
     if (interactionsResult.nbrOfCols == 6) {
         auto payloads = interactionsResult.payloads;
-        for (auto i = 0; i < payloads.size(); i += 6) {
+        for (decltype(payloads.size()) i = 0; i < payloads.size(); i += 6) {
             auto msg = api::interaction::Info({payloads[i + 1], payloads[i + 2],
                                          std::stoi(payloads[i + 3]),
                                          api::interaction::to_type(payloads[i + 4]),
@@ -214,21 +216,30 @@ int
 addDataTransferToConversation(Database& db,
                               const std::string& accountProfileId,
                               const std::string& conversationId,
-                              const DataTransferInfo& infoFromDaemon)
+                              const api::datatransfer::Info& 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"}});
+    auto peerProfileId = getProfileId(db, infoFromDaemon.peerUri);
+    auto authorId = 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},
+            {":type", infoFromDaemon.isOutgoing ?
+                    "OUTGOING_DATA_TRANSFER" :
+                    "INCOMING_DATA_TRANSFER"},
+            {":status", "TRANSFER_CREATED"}
+        });
 }
 
 int
@@ -296,6 +307,21 @@ void updateInteractionStatus(Database& db, unsigned int id,
               "id=:id", {{":id", std::to_string(id)}});
 }
 
+std::string
+conversationIdFromInteractionId(Database& db, unsigned int interactionId)
+{
+    auto result = db.select("conversation_id",
+                            "interactions",
+                            "id=:interaction_id",
+                            {{":interaction_id", std::to_string(interactionId)}});
+    if (result.nbrOfCols == 1) {
+        auto payloads = result.payloads;
+        return payloads[0];
+    }
+
+    return {};
+}
+
 void clearHistory(Database& db,
                   const std::string& conversationId)
 {
diff --git a/src/authority/databasehelper.h b/src/authority/databasehelper.h
index 9ee85493..535e766c 100644
--- a/src/authority/databasehelper.h
+++ b/src/authority/databasehelper.h
@@ -29,6 +29,10 @@
 namespace lrc
 {
 
+namespace api { namespace datatransfer {
+struct Info;
+}}
+
 namespace authority
 {
 
@@ -232,7 +236,10 @@ int countUnreadFromInteractions(Database& db, const std::string& conversationId)
 int addDataTransferToConversation(Database& db,
                                   const std::string& accountProfileId,
                                   const std::string& conversationId,
-                                  const DataTransferInfo& infoFromDaemon);
+                                  const api::datatransfer::Info& infoFromDaemon);
+
+std::string conversationIdFromInteractionId(Database& db, unsigned int interactionId);
+
 } // namespace database
 
 } // namespace authority
diff --git a/src/callbackshandler.cpp b/src/callbackshandler.cpp
index f2e5fa92..f1821c56 100644
--- a/src/callbackshandler.cpp
+++ b/src/callbackshandler.cpp
@@ -22,7 +22,7 @@
 #include "api/account.h"
 #include "api/lrc.h"
 #include "api/newaccountmodel.h"
-#include "api/datatransfer.h"
+#include "api/datatransfermodel.h"
 
 // Lrc
 #include "account.h"
@@ -218,8 +218,9 @@ CallbacksHandler::slotRegistrationStateChanged(const QString& accountId,
                                                unsigned detail_code,
                                                const QString& detail_str)
 {
+    (void) detail_code;
+    (void) detail_str;
     emit accountStatusChanged(accountId.toStdString(), lrc::api::account::to_status(registration_state.toStdString()));
-
 }
 
 void
@@ -286,28 +287,35 @@ CallbacksHandler::slotDataTransferEvent(qulonglong dringId, uint codeStatus)
 {
     auto event = DRing::DataTransferEventCode(codeStatus);
 
+    api::datatransfer::Info info;
+    parent.getDataTransferModel().transferInfo(dringId, info);
+
+    // WARNING: info.status could be INVALID in case of async signaling
+    // So listeners must only take account of dringId in such case.
+    // Is useful for "termination" status like unjoinable_peer.
+
     switch (event) {
     case DRing::DataTransferEventCode::created:
-        emit incomingTransfer(static_cast<long long>(dringId));
+        emit transferStatusCreated(static_cast<long long>(dringId), info);
         break;
     case DRing::DataTransferEventCode::closed_by_host:
     case DRing::DataTransferEventCode::closed_by_peer:
-        emit transferStatusCanceled(static_cast<long long>(dringId));
+        emit transferStatusCanceled(static_cast<long long>(dringId), info);
         break;
     case DRing::DataTransferEventCode::wait_peer_acceptance:
     case DRing::DataTransferEventCode::wait_host_acceptance:
-        emit transferStatusAwaiting(static_cast<long long>(dringId));
+        emit transferStatusAwaiting(static_cast<long long>(dringId), info);
         break;
     case DRing::DataTransferEventCode::ongoing:
-        emit transferStatusOngoing(static_cast<long long>(dringId));
+        emit transferStatusOngoing(static_cast<long long>(dringId), info);
         break;
     case DRing::DataTransferEventCode::finished:
-        emit transferStatusFinished(static_cast<long long>(dringId));
+        emit transferStatusFinished(static_cast<long long>(dringId), info);
         break;
     case DRing::DataTransferEventCode::invalid_pathname:
     case DRing::DataTransferEventCode::unjoinable_peer:
     case DRing::DataTransferEventCode::unsupported:
-        emit transferStatusError(static_cast<long long>(dringId));
+        emit transferStatusError(static_cast<long long>(dringId), info);
         break;
     }
 }
diff --git a/src/callbackshandler.h b/src/callbackshandler.h
index adf4d2bd..23ebd44d 100644
--- a/src/callbackshandler.h
+++ b/src/callbackshandler.h
@@ -27,6 +27,7 @@
 // Lrc
 #include "typedefs.h"
 #include "namedirectory.h"
+#include "api/datatransfer.h"
 
 namespace lrc
 {
@@ -160,13 +161,12 @@ Q_SIGNALS:
                                      const uint64_t id,
                                      const std::string& to, int status);
 
-    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);
+    void transferStatusCreated(long long dringId, api::datatransfer::Info info);
+    void transferStatusCanceled(long long dringId, api::datatransfer::Info info);
+    void transferStatusAwaiting(long long dringId, api::datatransfer::Info info);
+    void transferStatusOngoing(long long dringId, api::datatransfer::Info info);
+    void transferStatusFinished(long long dringId, api::datatransfer::Info info);
+    void transferStatusError(long long dringId, api::datatransfer::Info info);
 
 private Q_SLOTS:
     /**
diff --git a/src/contactmodel.cpp b/src/contactmodel.cpp
index e715025c..7e784352 100644
--- a/src/contactmodel.cpp
+++ b/src/contactmodel.cpp
@@ -136,11 +136,6 @@ public Q_SLOTS:
     void slotNewAccountMessage(std::string& accountId,
                                std::string& from,
                                std::map<std::string,std::string> payloads);
-    /**
-     * Listen from callbacksHandler for new account file transfer and add pending contact if not present
-     * @param dringId Id of transfer from daemon. Used to retrieve transfer informations.
-     */
-    void slotNewAccountTransfer(long long dringId);
 };
 
 using namespace authority;
@@ -368,9 +363,6 @@ ContactModelPimpl::ContactModelPimpl(const ContactModel& linked,
             this, &ContactModelPimpl::slotIncomingCall);
     connect(&callbacksHandler, &lrc::CallbacksHandler::newAccountMessage,
             this, &ContactModelPimpl::slotNewAccountMessage);
-    connect(&callbacksHandler, &lrc::CallbacksHandler::incomingTransfer,
-            this, &ContactModelPimpl::slotNewAccountTransfer);
-
 }
 
 ContactModelPimpl::~ContactModelPimpl()
@@ -596,30 +588,6 @@ ContactModelPimpl::slotNewAccountMessage(std::string& accountId,
     emit linked.newAccountMessage(accountId, from, payloads);
 }
 
-void
-ContactModelPimpl::slotNewAccountTransfer(long long dringId)
-{
-    DataTransferInfo infoFromDaemon = ConfigurationManager::instance().dataTransferInfo(dringId);
-
-    auto accountId = infoFromDaemon.accountId.toStdString();
-    auto peerId = infoFromDaemon.peer.toStdString(); // TODO valeur à checker
-
-    if (accountId != linked.owner.id) return;
-    auto* account = AccountModel::instance().getById(linked.owner.id.c_str());
-    if (not account) {
-        qDebug() << "ContactModel::slotNewAccountTransfer(), nullptr";
-        return;
-    }
-
-    if (contacts.find(peerId) == contacts.end()) {
-        // Contact not found, load profile from database.
-        // The conversation model will create an entry and link the incomingCall.
-        auto* cm = PhoneDirectoryModel::instance().getNumber(infoFromDaemon.peer, account);
-        addToContacts(cm, profile::Type::PENDING);
-    }
-    emit linked.newAccountTransfer(dringId, infoFromDaemon);
-}
-
 } // namespace lrc
 
 #include "api/moc_contactmodel.cpp"
diff --git a/src/conversationmodel.cpp b/src/conversationmodel.cpp
index b4e615ad..50f9e973 100644
--- a/src/conversationmodel.cpp
+++ b/src/conversationmodel.cpp
@@ -19,14 +19,19 @@
  ***************************************************************************/
 #include "api/conversationmodel.h"
 
+//Qt
+#include <QtCore/QTimer>
+
 // daemon
 #include <account_const.h>
+#include <datatransfer_interface.h>
 
 // std
 #include <regex>
 #include <algorithm>
 
 // LRC
+#include "api/lrc.h"
 #include "api/behaviorcontroller.h"
 #include "api/contactmodel.h"
 #include "api/newcallmodel.h"
@@ -58,6 +63,7 @@ class ConversationModelPimpl : public QObject
     Q_OBJECT
 public:
     ConversationModelPimpl(const ConversationModel& linked,
+                           Lrc& lrc,
                            Database& db,
                            const CallbacksHandler& callbacksHandler,
                            const BehaviorController& behaviorController);
@@ -132,12 +138,21 @@ public:
      */
     int getNumberOfUnreadMessagesFor(const std::string& uid);
 
+    /**
+     * Handle data transfer progression
+     */
+    void updateTransfer(QTimer* timer, const std::string& conversation, int conversationIdx,
+                        int interactionId);
+
+    bool usefulDataFromDataTransfer(long long dringId, const datatransfer::Info& info,
+                                    int& interactionId, std::string& convId);
+
     const ConversationModel& linked;
+    Lrc& lrc;
     Database& db;
     const CallbacksHandler& callbacksHandler;
     const std::string accountProfileId;
     const BehaviorController& behaviorController;
-    DataTransferModel dataTransferModel;
 
     ConversationModel::ConversationQueue conversations; ///< non-filtered conversations
     ConversationModel::ConversationQueue filteredConversations;
@@ -214,25 +229,21 @@ public Q_SLOTS:
      */
     void slotConferenceRemoved(const std::string& confId);
 
-    void slotIncomingTransfer(long long dringId, const DataTransferInfo& transferInfo);
-
-    //~ 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);
-
+    void slotTransferStatusCreated(long long dringId, api::datatransfer::Info info);
+    void slotTransferStatusCanceled(long long dringId, api::datatransfer::Info info);
+    void slotTransferStatusAwaiting(long long dringId, api::datatransfer::Info info);
+    void slotTransferStatusOngoing(long long dringId, api::datatransfer::Info info);
+    void slotTransferStatusFinished(long long dringId, api::datatransfer::Info info);
+    void slotTransferStatusError(long long dringId, api::datatransfer::Info info);
 };
 
 ConversationModel::ConversationModel(const account::Info& owner,
+                                     Lrc& lrc,
                                      Database& db,
                                      const CallbacksHandler& callbacksHandler,
                                      const BehaviorController& behaviorController)
 : QObject()
-, pimpl_(std::make_unique<ConversationModelPimpl>(*this, db, callbacksHandler, behaviorController))
+, pimpl_(std::make_unique<ConversationModelPimpl>(*this, lrc, db, callbacksHandler, behaviorController))
 , owner(owner)
 {
 
@@ -657,16 +668,17 @@ ConversationModel::setInteractionRead(const std::string& convId, const uint64_t&
 }
 
 ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked,
+                                               Lrc& lrc,
                                                Database& db,
                                                const CallbacksHandler& callbacksHandler,
                                                const BehaviorController& behaviorController)
 : linked(linked)
+, lrc {lrc}
 , db(db)
 , callbacksHandler(callbacksHandler)
 , typeFilter(profile::Type::INVALID)
 , accountProfileId(database::getProfileId(db, linked.owner.profileInfo.uri))
 , behaviorController(behaviorController)
-, dataTransferModel(db, callbacksHandler)
 {
     initConversations();
 
@@ -716,14 +728,14 @@ ConversationModelPimpl::ConversationModelPimpl(const ConversationModel& linked,
             &ConversationModelPimpl::slotConferenceRemoved);
 
     // data transfer
-    connect(&*linked.owner.contactModel,
-            &ContactModel::newAccountTransfer,
+    connect(&callbacksHandler,
+            &CallbacksHandler::transferStatusCreated,
             this,
-            &ConversationModelPimpl::slotIncomingTransfer);
+            &ConversationModelPimpl::slotTransferStatusCreated);
     connect(&callbacksHandler,
             &CallbacksHandler::transferStatusCanceled,
             this,
-            &ConversationModelPimpl::slotCancelTransfer);
+            &ConversationModelPimpl::slotTransferStatusCanceled);
     connect(&callbacksHandler,
             &CallbacksHandler::transferStatusAwaiting,
             this,
@@ -1258,108 +1270,31 @@ ConversationModelPimpl::getNumberOfUnreadMessagesFor(const std::string& uid)
     return database::countUnreadFromInteractions(db, uid);
 }
 
-void
-ConversationModelPimpl::slotIncomingTransfer(long long dringId, const DataTransferInfo& transferInfo)
-{
-    // check if transfer is for the current account
-    if (transferInfo.accountId.toStdString() != linked.owner.id)
-        return;
-
-    auto accountProfileId = database::getProfileId(db, linked.owner.profileInfo.uri);
-    auto contactProfileId = database::getProfileId(db, transferInfo.peer.toStdString());
-
-    // get the conversation if any
-    auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
-
-    if (conv.empty()) {
-        conv.emplace_back(database::beginConversationsBetween(
-            db, accountProfileId, contactProfileId,
-            QObject::tr("Invitation received").toStdString()
-        ));
-    }
-
-    // add interaction to the db
-    auto interactionId = database::addDataTransferToConversation(db, accountProfileId, conv[0], transferInfo);
-
-    // 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) {
-        addConversationWith(conv[0], transferInfo.peer.toStdString());
-        emit linked.newConversation(conv[0]);
-    } else {
-        auto& interactions = conversations[conversationIdx].interactions;
-        auto it = interactions.find(interactionId);
-
-        if (it != interactions.end()) {
-            return;
-        }
-
-        auto interaction = interaction::Info {contactProfileId, transferInfo.displayName.toStdString(),
-                                              std::time(nullptr),
-                                              (transferInfo.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::slotCancelTransfer(long long dringId)
+ConversationModel::sendFile(const std::string& convUid,
+                            const std::string& path,
+                            const std::string& filename)
 {
-    // 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())
+    auto conversationIdx = pimpl_->indexOf(convUid);
+    if (conversationIdx == -1)
         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);
-        }
-    }
-}
+    const auto& peerUri = pimpl_->conversations[conversationIdx].participants.front();
+    if (peerUri.empty())
+        return;
 
-void
-ConversationModel::sendFile(const std::string& uid, const std::string& path, const std::string& filename)
-{
-    auto conversationIdx = pimpl_->indexOf(uid);
-    if (conversationIdx != -1) {
-        auto& peerUri = pimpl_->conversations[conversationIdx].participants.front();
-        if (not peerUri.empty()) {
-            pimpl_->sendContactRequest(peerUri);
-            pimpl_->dataTransferModel.sendFile(owner.id.c_str(), peerUri.c_str(), path.c_str(), filename.c_str());
-        }
-    }
+    pimpl_->sendContactRequest(peerUri);
+    pimpl_->lrc.getDataTransferModel().sendFile(owner.id.c_str(),
+                                                peerUri.c_str(),
+                                                path.c_str(),
+                                                filename.c_str());
 }
 
 void
 ConversationModel::acceptTransfer(const std::string& convUid, uint64_t interactionId, const std::string& path)
 {
-    pimpl_->dataTransferModel.accept(interactionId, path, 0);
+    pimpl_->lrc.getDataTransferModel().accept(interactionId, path, 0);
     database::updateInteractionStatus(pimpl_->db, interactionId, interaction::Status::TRANSFER_ACCEPTED);
 
     // prepare interaction Info and emit signal for the client
@@ -1392,7 +1327,7 @@ ConversationModel::cancelTransfer(const std::string& convUid, uint64_t interacti
             database::updateInteractionStatus(pimpl_->db, interactionId, interaction::Status::TRANSFER_CANCELED);
 
             // Forward cancel action to daemon
-            pimpl_->dataTransferModel.cancel(interactionId);
+            pimpl_->lrc.getDataTransferModel().cancel(interactionId);
             pimpl_->dirtyConversations = true;
             emit interactionStatusUpdated(convUid, interactionId, it->second);
         }
@@ -1400,93 +1335,145 @@ ConversationModel::cancelTransfer(const std::string& convUid, uint64_t interacti
 }
 
 void
-ConversationModelPimpl::slotTransferStatusAwaiting(long long dringId)
+ConversationModel::getTransferInfo(uint64_t interactionId, datatransfer::Info& info)
+{
+    try {
+        auto dringId = pimpl_->lrc.getDataTransferModel().getDringIdFromInteractionId(interactionId);
+        pimpl_->lrc.getDataTransferModel().transferInfo(dringId, info);
+    } catch (...) {
+        info.status = datatransfer::Status::INVALID;
+    }
+}
+
+bool
+ConversationModelPimpl::usefulDataFromDataTransfer(long long dringId, const datatransfer::Info& info,
+                                                   int& interactionId, std::string& convId)
+{
+    try {
+        interactionId = lrc.getDataTransferModel().getInteractionIdFromDringId(dringId);
+    } catch (...) {
+        return false;
+    }
+
+    convId = database::conversationIdFromInteractionId(db, interactionId);
+    return true;
+}
+
+void
+ConversationModelPimpl::slotTransferStatusCreated(long long dringId, datatransfer::Info info)
 {
-    // no auto [jn] facto le code
-    DataTransferInfo infoFromDaemon = ConfigurationManager::instance().dataTransferInfo(dringId);
+    // check if transfer is for the current account
+    if (info.accountId != linked.owner.id) return;
+
+    const auto* account = AccountModel::instance().getById(info.accountId.c_str());
+    if (not account)
+        return;
 
     auto accountProfileId = database::getProfileId(db, linked.owner.profileInfo.uri);
-    auto contactProfileId = database::getProfileId(db, infoFromDaemon.peer.toStdString());
+    auto contactProfileId = database::getProfileId(db, info.peerUri);
+
+    // create a new conversation if needed
+    auto conversation_list = database::getConversationsBetween(db, accountProfileId, contactProfileId);
+    if (conversation_list.empty()) {
+        conversation_list.emplace_back(database::beginConversationsBetween(
+                                           db, accountProfileId, contactProfileId,
+                                           QObject::tr("Invitation received").toStdString()));
+    }
 
-    // get the conversation if any
-    auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
+    // add interaction to the db
+    const auto& convId = conversation_list[0];
+    auto interactionId = database::addDataTransferToConversation(db, accountProfileId, convId, info);
 
-    if (conv.empty())
-        return;
+    // map dringId and interactionId for latter retrivial from client (that only known the interactionId)
+    lrc.getDataTransferModel().registerTransferId(dringId, interactionId);
 
-    // get interaction id from data transfer model
-    auto interactionId = dataTransferModel.getInteractionIdFromDringId(dringId);
+    // prepare interaction Info and emit signal for the client
+    auto conversationIdx = indexOf(convId);
+    if (conversationIdx == -1) {
+        addConversationWith(conversation_list[0], info.peerUri);
+        emit linked.newConversation(convId);
+    } else {
+        auto& interactions = conversations[conversationIdx].interactions;
+        auto it = interactions.find(interactionId);
+        if (it != interactions.end())
+            return;
+
+        auto interactioType = info.isOutgoing ?
+            interaction::Type::OUTGOING_DATA_TRANSFER :
+            interaction::Type::INCOMING_DATA_TRANSFER;
+        auto interaction = interaction::Info {contactProfileId,
+                                              info.displayName,
+                                              std::time(nullptr),
+                                              interactioType,
+                                              interaction::Status::TRANSFER_CREATED};
+        interactions.emplace(interactionId, interaction);
+        conversations[conversationIdx].lastMessageUid = interactionId;
+        dirtyConversations = true;
+        emit linked.newInteraction(convId, interactionId, interaction);
+    }
+    sortConversations();
+    emit linked.modelSorted();
+}
+
+void
+ConversationModelPimpl::slotTransferStatusAwaiting(long long dringId, datatransfer::Info info)
+{
+    int interactionId;
+    std::string convId;
+    if (not usefulDataFromDataTransfer(dringId, info, interactionId, convId))
+        return;
 
-    // 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]);
+    auto conversationIdx = indexOf(convId);
     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);
+            emit linked.interactionStatusUpdated(convId, interactionId, it->second);
         }
     }
 }
 
 void
-ConversationModelPimpl::slotTransferStatusOngoing(long long dringId)
+ConversationModelPimpl::slotTransferStatusOngoing(long long dringId, datatransfer::Info info)
 {
-    // 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())
+    int interactionId;
+    std::string convId;
+    if (not usefulDataFromDataTransfer(dringId, info, interactionId, convId))
         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]);
+    auto conversationIdx = indexOf(convId);
     if (conversationIdx != -1) {
         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);
+            auto* timer = new QTimer();
+            connect(timer, &QTimer::timeout,
+                    [=] { updateTransfer(timer, convId, conversationIdx, interactionId); });
+            timer->start(1000);
+
+            emit linked.interactionStatusUpdated(convId, interactionId, it->second);
         }
     }
 }
 
 void
-ConversationModelPimpl::slotTransferStatusFinished(long long dringId)
+ConversationModelPimpl::slotTransferStatusFinished(long long dringId, datatransfer::Info info)
 {
-    // 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())
+    int interactionId;
+    std::string convId;
+    if (not usefulDataFromDataTransfer(dringId, info, interactionId, convId))
         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]);
+    auto conversationIdx = indexOf(convId);
     if (conversationIdx != -1) {
         auto& interactions = conversations[conversationIdx].interactions;
         auto it = interactions.find(interactionId);
@@ -1497,45 +1484,76 @@ ConversationModelPimpl::slotTransferStatusFinished(long long dringId)
                 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);
+                emit linked.interactionStatusUpdated(convId, interactionId, it->second);
             }
         }
     }
 }
 
 void
-ConversationModelPimpl::slotTransferStatusError(long long dringId)
+ConversationModelPimpl::slotTransferStatusCanceled(long long dringId, datatransfer::Info info)
 {
-    // no auto [jn] facto le code
-    DataTransferInfo infoFromDaemon = ConfigurationManager::instance().dataTransferInfo(dringId);
+    int interactionId;
+    std::string convId;
+    if (not usefulDataFromDataTransfer(dringId, info, interactionId, convId))
+        return;
 
-    auto accountProfileId = database::getProfileId(db, linked.owner.profileInfo.uri);
-    auto contactProfileId = database::getProfileId(db, infoFromDaemon.peer.toStdString());
+    database::updateInteractionStatus(db, interactionId, interaction::Status::TRANSFER_CANCELED);
 
-    // get the conversation if any
-    auto conv = database::getConversationsBetween(db, accountProfileId, contactProfileId);
+    auto conversationIdx = indexOf(convId);
+    if (conversationIdx != -1) {
+        auto& interactions = conversations[conversationIdx].interactions;
+        auto it = interactions.find(interactionId);
+        if (it != interactions.end()) {
+            it->second.status = interaction::Status::TRANSFER_CANCELED;
+            dirtyConversations = true;
+            emit linked.interactionStatusUpdated(convId, interactionId, it->second);
+        }
+    }
+}
 
-    if (conv.empty())
+void
+ConversationModelPimpl::slotTransferStatusError(long long dringId, datatransfer::Info info)
+{
+    int interactionId;
+    std::string convId;
+    if (not usefulDataFromDataTransfer(dringId, info, interactionId, convId))
         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]);
+    auto conversationIdx = indexOf(convId);
     if (conversationIdx != -1) {
         auto& interactions = conversations[conversationIdx].interactions;
         auto it = interactions.find(interactionId);
         if (it != interactions.end()) {
+            it->second.status = interaction::Status::TRANSFER_ERROR;
             dirtyConversations = true;
-            emit linked.interactionStatusUpdated(conv[0], interactionId, it->second);
+            emit linked.interactionStatusUpdated(convId, interactionId, it->second);
         }
     }
 }
 
+void
+ConversationModelPimpl::updateTransfer(QTimer* timer, const std::string& conversation,
+                                       int conversationIdx, int interactionId)
+{
+    try {
+        const auto& interactions = conversations[conversationIdx].interactions;
+        const auto& it = interactions.find(interactionId);
+        if (it != std::cend(interactions)
+            and it->second.status == interaction::Status::TRANSFER_ONGOING) {
+            emit linked.interactionStatusUpdated(conversation, interactionId, it->second);
+            return;
+        }
+    } catch (...) {}
+
+    timer->stop();
+    delete timer;
+}
+
 } // namespace lrc
 
 #include "api/moc_conversationmodel.cpp"
diff --git a/src/datatransfermodel.cpp b/src/datatransfermodel.cpp
index 3bc088f6..b7c65e3a 100644
--- a/src/datatransfermodel.cpp
+++ b/src/datatransfermodel.cpp
@@ -18,8 +18,6 @@
 
 // LRC
 #include "api/datatransfermodel.h"
-#include "callbackshandler.h"
-#include "database.h"
 
 // Dbus
 #include "dbus/configurationmanager.h"
@@ -30,18 +28,20 @@
 // Std
 #include <map>
 #include <stdexcept>
+#include <type_traits>
 
 // Qt
 #include <QUuid>
 
 namespace lrc { namespace api {
 
-/// DRING to LRC event code conversion
+// DRING to LRC event code conversion
 static inline
 datatransfer::Status
 convertDataTransferEvent(DRing::DataTransferEventCode event)
 {
     switch (event) {
+        case DRing::DataTransferEventCode::invalid: return datatransfer::Status::INVALID;
         case DRing::DataTransferEventCode::created: return datatransfer::Status::on_connection;
         case DRing::DataTransferEventCode::unsupported: return datatransfer::Status::unsupported;
         case DRing::DataTransferEventCode::wait_peer_acceptance: return datatransfer::Status::on_connection;
@@ -61,25 +61,17 @@ class DataTransferModel::Impl : public QObject
     Q_OBJECT
 
 public:
-    Impl(DataTransferModel& up_link,
-         Database& database,
-         const CallbacksHandler& callbacksHandler);
+    Impl(DataTransferModel& up_link);
 
     std::vector<std::string> transferIdList() const;
 
     DataTransferModel& upLink;
     std::map<long long, int> dring2lrcIdMap;
     std::map<int, long long> lrc2dringIdMap; // stricly the reverse map of dring2lrcIdMap
-    Database& database;
-    const CallbacksHandler& callbacksHandler;
 };
 
-DataTransferModel::Impl::Impl(DataTransferModel& up_link,
-                              Database& database,
-                              const CallbacksHandler& callbacksHandler)
+DataTransferModel::Impl::Impl(DataTransferModel& up_link)
     : QObject {}
-    , callbacksHandler {callbacksHandler}
-    , database {database}
     , upLink {up_link}
 {}
 
@@ -93,10 +85,9 @@ DataTransferModel::registerTransferId(long long dringId, int interactionId)
 }
 
 
-DataTransferModel::DataTransferModel(Database& database,
-                                     const CallbacksHandler& callbacksHandler)
+DataTransferModel::DataTransferModel()
     : QObject()
-    , pimpl_ { std::make_unique<Impl>(*this, database, callbacksHandler) }
+    , pimpl_ { std::make_unique<Impl>(*this) }
 {}
 
 DataTransferModel::~DataTransferModel() = default;
@@ -116,27 +107,64 @@ DataTransferModel::Impl::transferIdList() const
     return result;
 }
 
+void
+DataTransferModel::transferInfo(long long ringId, datatransfer::Info& lrc_info)
+{
+    DataTransferInfo infoFromDaemon;
+    if (ConfigurationManager::instance().dataTransferInfo(ringId, infoFromDaemon) == 0) {
+#if 0
+        int interactionId;
+        try {
+            interactionId = pimpl_->dring2lrcIdMap.at(ringId);
+        } catch (...) {
+            interactionId = -1;
+        }
+#endif
+        //lrc_info.uid = ?
+        lrc_info.status = convertDataTransferEvent(DRing::DataTransferEventCode(infoFromDaemon.lastEvent));
+        lrc_info.isOutgoing = !(infoFromDaemon.flags & (1 << uint32_t(DRing::DataTransferFlags::direction)));
+        lrc_info.totalSize = infoFromDaemon.totalSize;
+        lrc_info.progress = infoFromDaemon.bytesProgress;
+        lrc_info.path = infoFromDaemon.displayName.toStdString();
+        lrc_info.displayName = infoFromDaemon.displayName.toStdString();
+        lrc_info.accountId = infoFromDaemon.accountId.toStdString();
+        lrc_info.peerUri = infoFromDaemon.peer.toStdString();
+        //lrc_info.timestamp = ?
+        return;
+    }
+
+    lrc_info.status = datatransfer::Status::INVALID;
+}
+
 void
 DataTransferModel::sendFile(const std::string& account_id, const std::string& peer_uri,
                             const std::string& file_path, const std::string& display_name)
 {
-    auto dring_id = static_cast<DRing::DataTransferId>(ConfigurationManager::instance().sendFile(
-                                                           QString::fromStdString(account_id),
-                                                           QString::fromStdString(peer_uri),
-                                                           QString::fromStdString(file_path),
-                                                           QString::fromStdString(display_name)));
+    DataTransferInfo info;
+    qulonglong id;
+    info.accountId = QString::fromStdString(account_id);
+    info.peer = QString::fromStdString(peer_uri);
+    info.path = QString::fromStdString(file_path);
+    info.displayName = QString::fromStdString(display_name);
+    info.bytesProgress = 0;
+    if (ConfigurationManager::instance().sendFile(info, id) != 0) {
+        qDebug() << "DataTransferModel::sendFile(), error";
+        return;
+    }
 }
 
-std::streamsize
-DataTransferModel::bytesProgress(int interactionId)
+void
+DataTransferModel::bytesProgress(int interactionId, int64_t& total, int64_t& progress)
 {
-    return ConfigurationManager::instance().dataTransferBytesProgress(pimpl_->lrc2dringIdMap.at(interactionId));
+    ConfigurationManager::instance().dataTransferBytesProgress(pimpl_->lrc2dringIdMap.at(interactionId),
+                                                               reinterpret_cast<qlonglong&>(total),
+                                                               reinterpret_cast<qlonglong&>(progress));
 }
 
 void
 DataTransferModel::accept(int interactionId,
-                                      const std::string& file_path,
-                                      std::size_t offset)
+                          const std::string& file_path,
+                          std::size_t offset)
 {
     auto dring_id = pimpl_->lrc2dringIdMap.at(interactionId);
     ConfigurationManager::instance().acceptFileTransfer(dring_id, QString::fromStdString(file_path), offset);
@@ -155,6 +183,12 @@ DataTransferModel::getInteractionIdFromDringId(long long dringId)
     return pimpl_->dring2lrcIdMap.at(dringId);
 }
 
+long long
+DataTransferModel::getDringIdFromInteractionId(int interactionId)
+{
+    return pimpl_->lrc2dringIdMap.at(interactionId);
+}
+
 }} // namespace lrc::api
 
 #include "api/moc_datatransfermodel.cpp"
diff --git a/src/dbus/metatypes.h b/src/dbus/metatypes.h
index b2c03281..97350683 100644
--- a/src/dbus/metatypes.h
+++ b/src/dbus/metatypes.h
@@ -43,38 +43,19 @@ Q_DECLARE_METATYPE(MapStringVectorString)
 Q_DECLARE_METATYPE(VectorVectorByte)
 Q_DECLARE_METATYPE(DataTransferInfo)
 
-#ifndef ENABLE_LIBWRAP
-static bool dbus_metaTypeInit = false;
-#endif
-inline void registerCommTypes() {
-#ifndef ENABLE_LIBWRAP
-   qDBusRegisterMetaType<MapStringString>               ();
-   qDBusRegisterMetaType<MapStringInt>                  ();
-   qDBusRegisterMetaType<VectorMapStringString>         ();
-   qDBusRegisterMetaType<MapStringMapStringVectorString>();
-   qDBusRegisterMetaType<VectorInt>                     ();
-   qDBusRegisterMetaType<VectorUInt>                    ();
-   qDBusRegisterMetaType<VectorULongLong>               ();
-   qDBusRegisterMetaType<VectorString>                  ();
-   qDBusRegisterMetaType<MapStringVectorString>         ();
-   qDBusRegisterMetaType<VectorVectorByte>              ();
-   qDBusRegisterMetaType<DataTransferInfo>              ();
-   dbus_metaTypeInit = true;
-#endif
-}
-
 #ifndef ENABLE_LIBWRAP
 static inline QDBusArgument &operator<<(QDBusArgument& argument, const DataTransferInfo& info)
 {
     argument.beginStructure();
-    argument << info.isOutgoing;
+    argument << info.accountId;
     argument << info.lastEvent;
+    argument << info.flags;
     argument << info.totalSize;
     argument << info.bytesProgress;
+    argument << info.peer;
     argument << info.displayName;
     argument << info.path;
-    argument << info.accountId;
-    argument << info.peer;
+    argument << info.mimetype;
     argument.endStructure();
 
     return argument;
@@ -83,19 +64,40 @@ static inline QDBusArgument &operator<<(QDBusArgument& argument, const DataTrans
 static inline const QDBusArgument &operator>>(const QDBusArgument& argument, DataTransferInfo& info)
 {
     argument.beginStructure();
-    argument >> info.isOutgoing;
+    argument >> info.accountId;
     argument >> info.lastEvent;
+    argument >> info.flags;
     argument >> info.totalSize;
     argument >> info.bytesProgress;
+    argument >> info.peer;
     argument >> info.displayName;
     argument >> info.path;
-    argument >> info.accountId;
-    argument >> info.peer;
+    argument >> info.mimetype;
     argument.endStructure();
 
     return argument;
 }
 #endif
 
+#ifndef ENABLE_LIBWRAP
+static bool dbus_metaTypeInit = false;
+#endif
+inline void registerCommTypes() {
+#ifndef ENABLE_LIBWRAP
+   qDBusRegisterMetaType<MapStringString>               ();
+   qDBusRegisterMetaType<MapStringInt>                  ();
+   qDBusRegisterMetaType<VectorMapStringString>         ();
+   qDBusRegisterMetaType<MapStringMapStringVectorString>();
+   qDBusRegisterMetaType<VectorInt>                     ();
+   qDBusRegisterMetaType<VectorUInt>                    ();
+   qDBusRegisterMetaType<VectorULongLong>               ();
+   qDBusRegisterMetaType<VectorString>                  ();
+   qDBusRegisterMetaType<MapStringVectorString>         ();
+   qDBusRegisterMetaType<VectorVectorByte>              ();
+   qDBusRegisterMetaType<DataTransferInfo>              ();
+   dbus_metaTypeInit = true;
+#endif
+}
+
 #pragma GCC diagnostic pop
 
diff --git a/src/lrc.cpp b/src/lrc.cpp
index 2c88ad00..15a7a0d0 100644
--- a/src/lrc.cpp
+++ b/src/lrc.cpp
@@ -20,6 +20,7 @@
 
 // Models and database
 #include "api/newaccountmodel.h"
+#include "api/datatransfermodel.h"
 #include "api/behaviorcontroller.h"
 #include "database.h"
 #include "callbackshandler.h"
@@ -34,13 +35,14 @@ class LrcPimpl
 {
 
 public:
-    LrcPimpl(const Lrc& linked);
+    LrcPimpl(Lrc& linked);
 
     const Lrc& linked;
     std::unique_ptr<BehaviorController> behaviorController;
     std::unique_ptr<CallbacksHandler> callbackHandler;
     std::unique_ptr<Database> database;
     std::unique_ptr<NewAccountModel> accountModel;
+    std::unique_ptr<DataTransferModel> dataTransferModel;
 };
 
 Lrc::Lrc()
@@ -67,12 +69,19 @@ Lrc::getBehaviorController() const
     return *lrcPimpl_->behaviorController;
 }
 
-LrcPimpl::LrcPimpl(const Lrc& linked)
+DataTransferModel&
+Lrc::getDataTransferModel() const
+{
+    return *lrcPimpl_->dataTransferModel;
+}
+
+LrcPimpl::LrcPimpl(Lrc& linked)
 : linked(linked)
 , behaviorController(std::make_unique<BehaviorController>())
 , callbackHandler(std::make_unique<CallbacksHandler>(linked))
 , database(std::make_unique<Database>())
-, accountModel(std::make_unique<NewAccountModel>(*database, *callbackHandler, *behaviorController))
+, accountModel(std::make_unique<NewAccountModel>(linked, *database, *callbackHandler, *behaviorController))
+, dataTransferModel {std::make_unique<DataTransferModel>()}
 {
 }
 
diff --git a/src/newaccountmodel.cpp b/src/newaccountmodel.cpp
index e5ef18f0..50a620ee 100644
--- a/src/newaccountmodel.cpp
+++ b/src/newaccountmodel.cpp
@@ -20,6 +20,7 @@
 
 
 // LRC
+#include "api/lrc.h"
 #include "api/newcallmodel.h"
 #include "api/contactmodel.h"
 #include "api/conversationmodel.h"
@@ -46,12 +47,14 @@ class NewAccountModelPimpl: public QObject
     Q_OBJECT
 public:
     NewAccountModelPimpl(NewAccountModel& linked,
+                         Lrc& lrc,
                          Database& database,
                          const CallbacksHandler& callbackHandler,
                          const BehaviorController& behaviorController);
     ~NewAccountModelPimpl();
 
     NewAccountModel& linked;
+    Lrc& lrc;
     const CallbacksHandler& callbacksHandler;
     Database& database;
     NewAccountModel::AccountInfoMap accounts;
@@ -80,11 +83,12 @@ public Q_SLOTS:
     void slotProfileUpdated(const Profile* profile);
 };
 
-NewAccountModel::NewAccountModel(Database& database,
+NewAccountModel::NewAccountModel(Lrc& lrc,
+                                 Database& database,
                                  const CallbacksHandler& callbacksHandler,
                                  const BehaviorController& behaviorController)
 : QObject()
-, pimpl_(std::make_unique<NewAccountModelPimpl>(*this, database, callbacksHandler, behaviorController))
+, pimpl_(std::make_unique<NewAccountModelPimpl>(*this, lrc, database, callbacksHandler, behaviorController))
 {
 }
 
@@ -114,10 +118,12 @@ NewAccountModel::getAccountInfo(const std::string& accountId) const
 }
 
 NewAccountModelPimpl::NewAccountModelPimpl(NewAccountModel& linked,
+                                           Lrc& lrc,
                                            Database& database,
                                            const CallbacksHandler& callbacksHandler,
                                            const BehaviorController& behaviorController)
 : linked(linked)
+, lrc {lrc}
 , behaviorController(behaviorController)
 , callbacksHandler(callbacksHandler)
 , database(database)
@@ -151,13 +157,15 @@ NewAccountModelPimpl::slotAccountStatusChanged(const std::string& accountID, con
         addToAccounts(accountID);
         emit linked.accountAdded(accountID);
     } else if (accountInfo != accounts.end()) {
-        if (status == api::account::Status::REGISTERED) {
+        accountInfo->second.status = status;
+        if (status == api::account::Status::REGISTERED and not accounts[accountID].enabled) {
             accounts[accountID].enabled = true;
-        } else if (status == api::account::Status::UNREGISTERED) {
+            emit linked.accountStatusChanged(accountID);
+        } else if (status == api::account::Status::UNREGISTERED and accounts[accountID].enabled) {
             accounts[accountID].enabled = false;
-        }
-        accountInfo->second.status = status;
-        emit linked.accountStatusChanged(accountID);
+            emit linked.accountStatusChanged(accountID);
+        } else
+            emit linked.accountStatusChanged(accountID);
     }
 }
 
@@ -189,7 +197,7 @@ NewAccountModelPimpl::addToAccounts(const std::string& accountId)
     // Init models for this account
     owner.callModel = std::make_unique<NewCallModel>(owner, callbacksHandler);
     owner.contactModel = std::make_unique<ContactModel>(owner, database, callbacksHandler);
-    owner.conversationModel = std::make_unique<ConversationModel>(owner, database, callbacksHandler, behaviorController);
+    owner.conversationModel = std::make_unique<ConversationModel>(owner, lrc, database, callbacksHandler, behaviorController);
     owner.accountModel = &linked;
 }
 
diff --git a/src/qtwrapper/configurationmanager_wrap.h b/src/qtwrapper/configurationmanager_wrap.h
index 1171452f..ad4cac38 100644
--- a/src/qtwrapper/configurationmanager_wrap.h
+++ b/src/qtwrapper/configurationmanager_wrap.h
@@ -590,34 +590,45 @@ public Q_SLOTS: // METHODS
         return convertVectorULongLong(DRing::dataTransferList());
     }
 
-    uint64_t sendFile(const QString& account_id, const QString& peer_uri, const QString& file_path, const QString& display_name) {
-        return DRing::sendFile(account_id.toStdString(), peer_uri.toStdString(), file_path.toStdString(), display_name.toStdString());
-    }
-
-    DataTransferInfo dataTransferInfo(uint64_t transfer_id) {
-        auto dring_info = DRing::dataTransferInfo(transfer_id);
-        DataTransferInfo lrc_info;
-        lrc_info.isOutgoing = dring_info.isOutgoing;
-        lrc_info.lastEvent = uint(dring_info.lastEvent);
+    uint32_t sendFile(const DataTransferInfo& lrc_info, uint64_t& id) {
+        DRing::DataTransferInfo dring_info;
+        dring_info.accountId = lrc_info.accountId.toStdString();
+        dring_info.lastEvent = decltype(dring_info.lastEvent)(lrc_info.lastEvent);
+        dring_info.flags = lrc_info.flags;
+        dring_info.totalSize = lrc_info.totalSize;
+        dring_info.bytesProgress = lrc_info.bytesProgress;
+        dring_info.peer = lrc_info.peer.toStdString();
+        dring_info.displayName = lrc_info.displayName.toStdString();
+        dring_info.path = lrc_info.path.toStdString();
+        dring_info.mimetype = lrc_info.mimetype.toStdString();
+        return uint32_t(DRing::sendFile(dring_info, id));
+    }
+
+    uint32_t dataTransferInfo(uint64_t transfer_id, DataTransferInfo& lrc_info) {
+        DRing::DataTransferInfo dring_info;
+        auto error = uint32_t(DRing::dataTransferInfo(transfer_id, dring_info));
+        lrc_info.accountId = QString::fromStdString(dring_info.accountId);
+        lrc_info.lastEvent = quint32(dring_info.lastEvent);
+        lrc_info.flags = dring_info.flags;
         lrc_info.totalSize = dring_info.totalSize;
         lrc_info.bytesProgress = dring_info.bytesProgress;
+        lrc_info.peer = QString::fromStdString(dring_info.peer);
         lrc_info.displayName = QString::fromStdString(dring_info.displayName);
         lrc_info.path = QString::fromStdString(dring_info.path);
-        lrc_info.accountId = QString::fromStdString(dring_info.accountId);
-        lrc_info.peer = QString::fromStdString(dring_info.peer);
-        return lrc_info;
+        lrc_info.mimetype = QString::fromStdString(dring_info.mimetype);
+        return error;
     }
 
-    uint64_t dataTransferBytesProgress(uint64_t transfer_id) {
-        return DRing::dataTransferBytesProgress(transfer_id);
+    uint64_t dataTransferBytesProgress(uint64_t transfer_id, int64_t& total, int64_t& progress) {
+        return uint32_t(DRing::dataTransferBytesProgress(transfer_id, total, progress));
     }
 
-    void acceptFileTransfer(uint64_t transfer_id, const QString& file_path, uint64_t offset) {
-        DRing::acceptFileTransfer(transfer_id, file_path.toStdString(), offset);
+    uint32_t acceptFileTransfer(uint64_t transfer_id, const QString& file_path, int64_t offset) {
+        return uint32_t(DRing::acceptFileTransfer(transfer_id, file_path.toStdString(), offset));
     }
 
-    void cancelDataTransfer(uint64_t transfer_id) {
-        DRing::cancelDataTransfer(transfer_id);
+    uint32_t cancelDataTransfer(int64_t transfer_id) {
+        return uint32_t(DRing::cancelDataTransfer(transfer_id));
     }
 
 Q_SIGNALS: // SIGNALS
diff --git a/src/typedefs.h b/src/typedefs.h
index ecb6f6a0..476ecfb3 100644
--- a/src/typedefs.h
+++ b/src/typedefs.h
@@ -42,14 +42,15 @@ typedef QVector< QByteArray >                               VectorVectorByte
 // Adapted from libring DRing::DataTransferInfo
 struct DataTransferInfo
 {
-    bool isOutgoing;
-    uint lastEvent;
-    qulonglong totalSize;
-    qulonglong bytesProgress;
-    QString displayName;
-    QString path;
     QString accountId;
+    quint32 lastEvent;
+    quint32 flags;
+    qlonglong totalSize;
+    qlonglong bytesProgress;
     QString peer;
+    QString displayName;
+    QString path;
+    QString mimetype;
 };
 
 /**
diff --git a/test/mocks/configurationmanager_mock.h b/test/mocks/configurationmanager_mock.h
index 9e6f92d4..305f3d97 100644
--- a/test/mocks/configurationmanager_mock.h
+++ b/test/mocks/configurationmanager_mock.h
@@ -652,32 +652,35 @@ public Q_SLOTS: // METHODS
         return {};
     }
 
-    uint64_t sendFile(const QString& account_id, const QString& peer_uri, const QString& file_path, const QString& display_name) {
-        (void)account_id;
-        (void)peer_uri;
-        (void)file_path;
-        (void)display_name;
+    uint32_t sendFile(const DataTransferInfo& lrc_info, qulonglong& id) {
+        (void)lrc_info;
+        (void)id;
         return 0;
     }
 
-    DataTransferInfo dataTransferInfo(uint64_t transfer_id) {
+    uint32_t dataTransferInfo(uint64_t transfer_id, DataTransferInfo& lrc_info) {
         (void)transfer_id;
-        return {};
+        (void)lrc_info;
+        return 0;
     }
 
-    uint64_t dataTransferBytesProgress(uint64_t transfer_id) {
+    uint64_t dataTransferBytesProgress(uint64_t transfer_id, qlonglong& total, qlonglong& progress) {
         (void)transfer_id;
+        (void)total;
+        (void)progress;
         return 0;
     }
 
-    void acceptFileTransfer(uint64_t transfer_id, const QString& file_path, uint64_t offset) {
+    uint32_t acceptFileTransfer(uint64_t transfer_id, const QString& file_path, uint64_t offset) {
         (void)transfer_id;
         (void)file_path;
         (void)offset;
+        return 0;
     }
 
-    void cancelDataTransfer(uint64_t transfer_id) {
+    uint32_t cancelDataTransfer(uint64_t transfer_id) {
         (void)transfer_id;
+        return 0;
     }
 
 Q_SIGNALS: // SIGNALS
-- 
GitLab