From 0aab9a2064fbc0eeebf592093965c8811b1dcfac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Blin?=
 <sebastien.blin@savoirfairelinux.com>
Date: Thu, 14 Jun 2018 15:33:47 -0400
Subject: [PATCH] newaccountmodel: add the ability to have a different avatar
 by account

In NewAccountModel: add a method to change the avatar of an account.

Note: We still use the old LRC (call.cpp) to send VCard during a call,
so sendProfile should be able to get the avatar from the database if
possible.

Change-Id: I74bc6d8aea64aa3bdfbf49d2f353f2f9b92325c1
Reviewed-by: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com>
---
 src/api/newaccountmodel.h        |  7 +++++++
 src/authority/databasehelper.cpp | 10 ++++++++++
 src/authority/databasehelper.h   |  7 +++++++
 src/call.cpp                     | 32 ++++++++++++++++++++++++++++++--
 src/database.cpp                 | 10 +---------
 src/database.h                   | 11 +++++++++++
 src/newaccountmodel.cpp          | 14 ++++++++++++++
 src/person.cpp                   |  9 +++++++--
 src/person.h                     |  2 +-
 src/private/vcardutils.cpp       | 13 +++++++------
 src/private/vcardutils.h         |  2 +-
 11 files changed, 96 insertions(+), 21 deletions(-)

diff --git a/src/api/newaccountmodel.h b/src/api/newaccountmodel.h
index 6b7246c5..ad3437f6 100644
--- a/src/api/newaccountmodel.h
+++ b/src/api/newaccountmodel.h
@@ -121,6 +121,13 @@ public:
      * @param enable
      */
     void enableAccount(const std::string& accountId, bool enabled);
+    /**
+     * Change the avatar of an account
+     * @param accountId
+     * @param avatar
+     * @throws out_of_range exception if account is not found
+     */
+    void setAvatar(const std::string& accountId, const std::string& avatar);
 
 Q_SIGNALS:
     /**
diff --git a/src/authority/databasehelper.cpp b/src/authority/databasehelper.cpp
index 412b15f7..7687aa8d 100644
--- a/src/authority/databasehelper.cpp
+++ b/src/authority/databasehelper.cpp
@@ -113,6 +113,16 @@ getAvatarForProfileId(Database& db, const std::string& profileId)
     return "";
 }
 
+void
+setAvatarForProfileId(Database& db, const std::string& profileId, const std::string& avatar)
+{
+    db.update("profiles",
+              "photo=:photo",
+              {{":photo", avatar}},
+              "id=:id",
+              {{":id", profileId}});
+}
+
 api::contact::Info
 buildContactFromProfileId(Database& db, const std::string& profileId)
 {
diff --git a/src/authority/databasehelper.h b/src/authority/databasehelper.h
index 31725a1f..61f8d77c 100644
--- a/src/authority/databasehelper.h
+++ b/src/authority/databasehelper.h
@@ -88,6 +88,13 @@ std::vector<std::string> getPeerParticipantsForConversation(Database& db,
  */
 std::string getAvatarForProfileId(Database& db, const std::string& profileId);
 
+/**
+ * @param  db
+ * @param  profileId
+ * @param  avatar
+ */
+void setAvatarForProfileId(Database& db, const std::string& profileId, const std::string& avatar);
+
 api::contact::Info buildContactFromProfileId(Database& db, const std::string& profileId);
 
 /**
diff --git a/src/call.cpp b/src/call.cpp
index 923f53f3..603cd6a2 100644
--- a/src/call.cpp
+++ b/src/call.cpp
@@ -21,13 +21,16 @@
 #include "call.h"
 
 //Std include
-#include <time.h>
+#include <fstream>
 #include <memory>
+#include <string>
+#include <time.h>
 
 //Qt
 #include <QtCore/QFile>
 #include <QtCore/QTimer>
 #include <QtCore/QDateTime>
+#include <QtCore/QStandardPaths>
 
 //DRing
 #include <account_const.h>
@@ -36,6 +39,7 @@
 
 //Ring library
 #include "dbus/callmanager.h"
+#include "dbus/configurationmanager.h"
 
 #include "collectioninterface.h"
 #include "person.h"
@@ -61,6 +65,9 @@
 #include "personmodel.h"
 #include "private/contactmethod_p.h"
 
+#include "database.h"
+#include "authority/databasehelper.h"
+
 #include "media/audio.h"
 #include "media/video.h"
 #include "media/text.h"
@@ -1684,7 +1691,28 @@ void CallPrivate::sendProfile()
      * like this is not. Therefore we use the proprietary PROFILE_VCF MIME.
      */
     auto t = mediaFactory<Media::Text>(Media::Media::Direction::OUT);
-    auto vCard = profile->person()->toVCard();
+
+    MapStringString details = ConfigurationManager::instance().getAccountDetails(m_Account->id());
+    using namespace DRing::Account;
+    std::string uri = "";
+    if (m_Account->protocol() == Account::Protocol::RING
+        && details[ConfProperties::USERNAME].contains("ring:")) {
+        uri = details[ConfProperties::USERNAME].toStdString().substr(std::string("ring:").size());
+    } else {
+        uri = details[ConfProperties::USERNAME].toStdString();
+    }
+
+    // NOTE: Some clients still use the old LRC. So, we should be able to use the database or old VCard files for now.
+    // TODO: migrate sendProfile to newcallmodel.cpp as soon as possible.
+    std::ifstream dbfile(lrc::DATABASE_PATH.toStdString());
+    std::string photo = "";
+    if (dbfile.good()) {
+        lrc::Database db;
+        auto accountProfileId = lrc::authority::database::getOrInsertProfile(db, uri);
+        // Retrieve avatar from database
+        photo = lrc::authority::database::getAvatarForProfileId(db, accountProfileId);
+    }
+    auto vCard = profile->person()->toVCard({}, photo);
 
     qsrand(time(nullptr));
     const auto& key = QString::number(qrand());
diff --git a/src/database.cpp b/src/database.cpp
index 34b00984..fba93d5c 100644
--- a/src/database.cpp
+++ b/src/database.cpp
@@ -30,7 +30,6 @@
 #include <QtSql/QSqlDatabase>
 #include <QtSql/QSqlError>
 #include <QtSql/QSqlRecord>
-#include <QtCore/QStandardPaths>
 #include <QtCore/QVariant>
 #include <QDir>
 
@@ -52,9 +51,6 @@ namespace lrc
 
 using namespace api;
 
-static constexpr auto VERSION = "1";
-static constexpr auto NAME = "ring.db";
-
 Database::Database()
 : QObject()
 {
@@ -70,11 +66,7 @@ Database::Database()
 
     // initalize the database.
     db_ = QSqlDatabase::addDatabase("QSQLITE");
-#ifdef ENABLE_TEST
-    db_.setDatabaseName(QString("/tmp/") + QString(NAME));
-#else
-    db_.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + NAME);
-#endif
+    db_.setDatabaseName(DATABASE_PATH);
 
     // open the database.
     if (not db_.open()) {
diff --git a/src/database.h b/src/database.h
index 3a14a769..7163e29c 100644
--- a/src/database.h
+++ b/src/database.h
@@ -26,11 +26,22 @@
 
 // Qt
 #include <qobject.h>
+#include <QtCore/QDir>
 #include <QtSql/QSqlQuery>
+#include <QtCore/QStandardPaths>
 
 namespace lrc
 {
 
+static constexpr auto VERSION = "1";
+static constexpr auto NAME = "ring.db";
+
+#ifdef ENABLE_TEST
+    static const QString DATABASE_PATH = QDir("/tmp/").filePath(NAME);
+#else
+    static const QString DATABASE_PATH = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)).filePath(NAME);
+#endif
+
 /**
   *  @brief Class that communicates with the database.
   *  @note not thread safe.
diff --git a/src/newaccountmodel.cpp b/src/newaccountmodel.cpp
index da7bf6b1..ca39e3cc 100644
--- a/src/newaccountmodel.cpp
+++ b/src/newaccountmodel.cpp
@@ -170,6 +170,20 @@ NewAccountModel::enableAccount(const std::string& accountId, bool enabled)
     accountInfo->second.enabled = enabled;
 }
 
+void
+NewAccountModel::setAvatar(const std::string& accountId, const std::string& avatar)
+{
+    auto accountInfo = pimpl_->accounts.find(accountId);
+    if (accountInfo == pimpl_->accounts.end()) {
+        throw std::out_of_range("NewAccountModel::setAvatar, can't find " + accountId);
+    }
+    accountInfo->second.profileInfo.avatar = avatar;
+    auto accountProfileId = authority::database::getOrInsertProfile(pimpl_->database, accountInfo->second.profileInfo.uri);
+    if (!accountProfileId.empty()) {
+        authority::database::setAvatarForProfileId(pimpl_->database, accountProfileId, avatar);
+    }
+}
+
 bool
 NewAccountModel::exportToFile(const std::string& accountId, const std::string& path) const
 {
diff --git a/src/person.cpp b/src/person.cpp
index 2703cf7f..286a3044 100644
--- a/src/person.cpp
+++ b/src/person.cpp
@@ -724,7 +724,7 @@ void Person::addCustomField(const QString& key, const QString& value)
    d_ptr->m_lCustomAttributes.insert(key, value);
 }
 
-const QByteArray Person::toVCard(QList<Account*> accounts) const
+const QByteArray Person::toVCard(QList<Account*> accounts, const std::string& avatar) const
 {
    //serializing here
    VCardUtils maker;
@@ -759,7 +759,12 @@ const QByteArray Person::toVCard(QList<Account*> accounts) const
       maker.addProperty(VCardUtils::Property::X_RINGACCOUNT, acc->id());
    }
 
-   maker.addPhoto(GlobalInstances::pixmapManipulator().toByteArray(photo()));
+   if (avatar.empty()) {
+      maker.addPhoto(GlobalInstances::pixmapManipulator().toByteArray(photo()));
+   } else {
+       maker.addPhoto(QByteArray(avatar.c_str()), false);
+   }
+
    return maker.endVCard();
 }
 
diff --git a/src/person.h b/src/person.h
index 4907837f..7e502f46 100644
--- a/src/person.h
+++ b/src/person.h
@@ -113,7 +113,7 @@ public:
    //Mutator
    Q_INVOKABLE void addAddress(const Address& addr);
    Q_INVOKABLE void addCustomField(const QString& key, const QString& value);
-   Q_INVOKABLE const QByteArray toVCard(QList<Account*> accounts = {}) const;
+   Q_INVOKABLE const QByteArray toVCard(QList<Account*> accounts = {}, const std::string& avatar = "") const;
 
 protected:
    //The D-Pointer can be shared if a PlaceHolderPerson is merged with a real one
diff --git a/src/private/vcardutils.cpp b/src/private/vcardutils.cpp
index af73a2c3..28c957b3 100644
--- a/src/private/vcardutils.cpp
+++ b/src/private/vcardutils.cpp
@@ -279,13 +279,14 @@ void VCardUtils::addContactMethod(const QString& type, const QString& num)
    addProperty(prop, num);
 }
 
-void VCardUtils::addPhoto(const QByteArray img)
+void VCardUtils::addPhoto(const QByteArray img, bool convertToBase64)
 {
-   m_vCard << (QString::fromUtf8(Property::PHOTO) +
-               QString::fromUtf8(Delimiter::SEPARATOR_TOKEN) +
-               "ENCODING=BASE64" +
-               QString::fromUtf8(Delimiter::SEPARATOR_TOKEN) +
-               "TYPE=PNG:" + img.toBase64().trimmed());
+    auto b64Img = convertToBase64 ? img.toBase64().trimmed() : img.trimmed();
+    m_vCard << (QString::fromUtf8(Property::PHOTO) +
+                QString::fromUtf8(Delimiter::SEPARATOR_TOKEN) +
+                "ENCODING=BASE64" +
+                QString::fromUtf8(Delimiter::SEPARATOR_TOKEN) +
+                "TYPE=PNG:" + b64Img);
 }
 
 const QByteArray VCardUtils::endVCard()
diff --git a/src/private/vcardutils.h b/src/private/vcardutils.h
index 57e60a3a..028e1a2b 100644
--- a/src/private/vcardutils.h
+++ b/src/private/vcardutils.h
@@ -73,7 +73,7 @@ public:
    void addEmail(const QString& type, const QString& num);
    void addAddress(const Person::Address& addr);
    void addContactMethod(const QString& type, const QString& num);
-   void addPhoto(const QByteArray img);
+   void addPhoto(const QByteArray img, bool convertToBase64 = true);
    const QByteArray endVCard();
 
    //Loading
-- 
GitLab