From 59e3cd180313c81b6607c52a67546c22cea61d9f Mon Sep 17 00:00:00 2001
From: Nicolas Jager <nicolas.jager@savoirfairelinux.com>
Date: Thu, 27 Apr 2017 09:53:08 -0400
Subject: [PATCH] add vcard from contact request

- mapToPersonFromIncomingContactRequest : this function creates a
Person from a vCard stored by the daemon; we make sure to ignore any
ContactMethods which might be contained in the vCard and only use
the CM from which we receive it.

- PendingContactRequestModel::data now can return :
  * PEER_ID : get the username if possible or ring Id if not.
  * FORMATTED_NAME : get the profile name stored in the vCard.
  * COUNT__ : used to get the number of elements in the enum.

- ContactRequest now store a person. This person matches the vCard.

- PeerProfileCollection updates the CM(s) of the vCards it stores
with the last modified date of the vCard in to get the date at which
the contact request was accepted.

[SS: update commit message]
[SS: change how we restore the ContactRequest accept date]

Change-Id: I3bafda5b38d5e2332e095cd5f1f1d0b101847f86
Signed-off-by: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
Reviewed-by: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
---
 src/account.cpp                    | 14 ++++++++++----
 src/accountmodel.cpp               |  9 +++++++--
 src/contactrequest.cpp             | 24 +++++++++++++++++++++++-
 src/contactrequest.h               |  7 ++++++-
 src/peerprofilecollection.cpp      | 15 ++++++++++++++-
 src/pendingcontactrequestmodel.cpp | 27 ++++++++++++++++++---------
 src/pendingcontactrequestmodel.h   |  7 +++++++
 src/private/vcardutils.cpp         | 29 +++++++++++++++++++++++++++++
 src/private/vcardutils.h           |  1 +
 src/recentmodel.cpp                | 18 ------------------
 10 files changed, 115 insertions(+), 36 deletions(-)

diff --git a/src/account.cpp b/src/account.cpp
index 500abdcd..081e5298 100644
--- a/src/account.cpp
+++ b/src/account.cpp
@@ -143,9 +143,14 @@ Account* Account::buildExistingAccountFromId(const QByteArray& _accountId)
 
    //Load the pending trust requests
    if (a->protocol() == Account::Protocol::RING) {
-       const VectorMapStringString& pending_tr {ConfigurationManager::instance().getTrustRequests(a->id())};
-       for (const auto& tr_info : pending_tr) {
-         a->pendingContactRequestModel()->d_ptr->addRequest(new ContactRequest(a, tr_info["from"], 0));
+      const VectorMapStringString& pending_tr {ConfigurationManager::instance().getTrustRequests(a->id())};
+      for (const auto& tr_info : pending_tr) {
+         auto payload = tr_info["payload"];
+         auto peer = VCardUtils::mapToPersonFromIncomingContactRequest(VCardUtils::toHashMap(payload.toUtf8()),
+                                                                       tr_info["from"]);
+
+         a->pendingContactRequestModel()->d_ptr->addRequest(new ContactRequest(a, peer, tr_info["from"],
+                                                                                        tr_info["received"].toInt()));
       }
    }
 
@@ -155,7 +160,8 @@ Account* Account::buildExistingAccountFromId(const QByteArray& _accountId)
    });
 
    // Load the contacts associated from the daemon and create the cms.
-   const auto account_contacts = static_cast<QVector<QMap<QString, QString>>>(ConfigurationManager::instance().getContacts(a->id().data()));
+   const auto account_contacts = static_cast<QVector<QMap<QString, QString>>>(ConfigurationManager::instance()
+                                                                                          .getContacts(a->id().data()));
 
    if (a->protocol() == Account::Protocol::RING) {
       for (auto contact_info : account_contacts) {
diff --git a/src/accountmodel.cpp b/src/accountmodel.cpp
index 62cd2df3..604a5aaa 100644
--- a/src/accountmodel.cpp
+++ b/src/accountmodel.cpp
@@ -50,6 +50,8 @@
 #include "dbus/instancemanager.h"
 #include "codecmodel.h"
 #include "private/pendingcontactrequestmodel_p.h"
+#include "person.h"
+#include "private/vcardutils.h"
 
 QHash<QByteArray,AccountPlaceHolder*> AccountModelPrivate::m_hsPlaceHolder;
 
@@ -459,7 +461,7 @@ void AccountModelPrivate::slotVolatileAccountDetailsChange(const QString& accoun
 void AccountModelPrivate::slotIncomingContactRequest(const QString& accountId, const QString& hash, const QByteArray& payload, time_t time)
 {
    Q_UNUSED(payload);
-   qDebug() << "INCOMING REQUEST" << accountId << hash << time;
+
    Account* a = q_ptr->getById(accountId.toLatin1());
 
    if (!a) {
@@ -467,7 +469,10 @@ void AccountModelPrivate::slotIncomingContactRequest(const QString& accountId, c
       return;
    }
 
-   ContactRequest* r = new ContactRequest(a, hash, time);
+   auto peer = VCardUtils::mapToPersonFromIncomingContactRequest(VCardUtils::toHashMap(payload), hash);
+
+   ContactRequest* r = new ContactRequest(a, peer, hash, time);
+
    a->pendingContactRequestModel()->d_ptr->addRequest(r);
 }
 
diff --git a/src/contactrequest.cpp b/src/contactrequest.cpp
index 3d5941af..448b05ad 100644
--- a/src/contactrequest.cpp
+++ b/src/contactrequest.cpp
@@ -25,6 +25,7 @@
 #include <certificate.h>
 #include <certificatemodel.h>
 #include "itembase.h"
+#include "personmodel.h"
 
 //DRing
 #include "dbus/configurationmanager.h"
@@ -36,11 +37,13 @@ public:
    QDateTime    m_Time        ;
    Certificate* m_pCertificate;
    Account*     m_pAccount    ;
+   Person*      m_pPeer       ;
 };
 
-ContactRequest::ContactRequest(Account* a, const QString& id, time_t time) : QObject(a), d_ptr(new ContactRequestPrivate)
+ContactRequest::ContactRequest(Account* a, Person* p, const QString& id, time_t time) : QObject(a), d_ptr(new ContactRequestPrivate)
 {
    d_ptr->m_pAccount     = a;
+   d_ptr->m_pPeer        = p;
    d_ptr->m_Time         = QDateTime::fromTime_t(time);
    d_ptr->m_pCertificate = CertificateModel::instance().getCertificateFromId(id, a);
 }
@@ -65,9 +68,28 @@ Account* ContactRequest::account() const
    return d_ptr->m_pAccount;
 }
 
+/**
+ * get the person associated to the contact request.
+ */
+Person*
+ContactRequest::peer() const
+{
+   return d_ptr->m_pPeer;
+}
+
+/**
+ * set the person associated to the contact request.
+ */
+void
+ContactRequest::setPeer(Person* person)
+{
+   d_ptr->m_pPeer = person;
+}
+
 bool ContactRequest::accept()
 {
    if (ConfigurationManager::instance().acceptTrustRequest(d_ptr->m_pAccount->id(), d_ptr->m_pCertificate->remoteId())) {
+      PersonModel::instance().addPeerProfile(peer());
       emit requestAccepted();
       return true;
    }
diff --git a/src/contactrequest.h b/src/contactrequest.h
index f2950b68..f9e1427d 100644
--- a/src/contactrequest.h
+++ b/src/contactrequest.h
@@ -26,6 +26,7 @@ class AccountModelPrivate;
 class Certificate;
 class Account;
 class AccountPrivate;
+class Person;
 
 class LIB_EXPORT ContactRequest : public QObject
 {
@@ -42,6 +43,10 @@ public:
    QDateTime    date       () const;
    Account*     account    () const;
    Q_INVOKABLE QVariant roleData (int role) const;
+   Person* peer() const;
+
+   // Setter
+   void setPeer(Person* person);
 
    //Mutator
    Q_INVOKABLE bool accept ();
@@ -52,7 +57,7 @@ public:
    bool operator==(const ContactRequest& another) const;
 
 private:
-   explicit ContactRequest(Account* a, const QString& id, time_t time);
+   explicit ContactRequest(Account* a, Person* p, const QString& id = QString(), time_t time = -1);
    virtual ~ContactRequest();
 
    ContactRequestPrivate* d_ptr;
diff --git a/src/peerprofilecollection.cpp b/src/peerprofilecollection.cpp
index 2b74ef78..e09b5a82 100644
--- a/src/peerprofilecollection.cpp
+++ b/src/peerprofilecollection.cpp
@@ -25,12 +25,14 @@
 #include <QtCore/QStandardPaths>
 #include <QtCore/QUrl>
 #include <QtCore/QVector>
+#include <QtCore/QDateTime>
 
 //Ring
 #include "private/vcardutils.h"
 #include "account.h"
 #include "accountmodel.h"
 #include "person.h"
+#include "contactmethod.h"
 
 class PeerProfileEditor final : public CollectionEditor<Person>
 {
@@ -161,10 +163,21 @@ bool PeerProfileCollection::load()
     const QStringList entries = profilesDir.entryList({QStringLiteral("*.vcf")}, QDir::Files);
 
     foreach (const QString& item , entries) {
+        auto filePath = profilesDir.path() + '/' + item;
+
+        // create Person
         auto personProfile = new Person(this);
         QList<Account*> accs;
-        VCardUtils::mapToPerson(personProfile,QUrl(profilesDir.path()+'/'+item),&accs);
+        VCardUtils::mapToPerson(personProfile, QUrl(filePath), &accs);
         editor<Person>()->addExisting(personProfile);
+
+        // set last used time based on last modified of vCard file; this is in case there has been
+        // no other interactions with these CMs
+        auto lastUsed = QFileInfo(filePath).lastModified();
+        for (auto cm : personProfile->phoneNumbers()) {
+            qDebug() << "ppc setting last used" << personProfile << cm << lastUsed;
+            cm->setLastUsed(lastUsed.toTime_t());
+        }
     }
 
     return true;
diff --git a/src/pendingcontactrequestmodel.cpp b/src/pendingcontactrequestmodel.cpp
index ef3a689e..c8f5603c 100644
--- a/src/pendingcontactrequestmodel.cpp
+++ b/src/pendingcontactrequestmodel.cpp
@@ -25,11 +25,8 @@
 #include <certificate.h>
 #include <account.h>
 #include "private/pendingcontactrequestmodel_p.h"
-
-enum Columns {
-   HASH,
-   TIME
-};
+#include "person.h"
+#include "contactmethod.h"
 
 PendingContactRequestModelPrivate::PendingContactRequestModelPrivate(PendingContactRequestModel* p) : q_ptr(p)
 {}
@@ -51,10 +48,10 @@ QVariant PendingContactRequestModel::data( const QModelIndex& index, int role )
       return QVariant();
 
    switch(index.column()) {
-      case Columns::HASH:
+      case Columns::PEER_ID:
          switch(role) {
             case Qt::DisplayRole:
-               return d_ptr->m_lRequests[index.row()]->certificate()->remoteId();
+            return d_ptr->m_lRequests[index.row()]->peer()->phoneNumbers()[0]->getBestId();
          }
          break;
       case Columns::TIME:
@@ -63,6 +60,18 @@ QVariant PendingContactRequestModel::data( const QModelIndex& index, int role )
                return d_ptr->m_lRequests[index.row()]->date();
          }
          break;
+      case Columns::FORMATTED_NAME:
+         switch(role) {
+            case Qt::DisplayRole:
+               return d_ptr->m_lRequests[index.row()]->peer()->formattedName();
+         }
+         break;
+      case Columns::COUNT__:
+         switch(role) {
+             case Qt::DisplayRole:
+                return static_cast<int>(PendingContactRequestModel::Columns::COUNT__);
+         }
+         break;
    }
 
    return QVariant::fromValue(d_ptr->m_lRequests[index.row()]->roleData(role));
@@ -75,7 +84,7 @@ int PendingContactRequestModel::rowCount( const QModelIndex& parent ) const
 
 int PendingContactRequestModel::columnCount( const QModelIndex& parent ) const
 {
-   return parent.isValid()? 0 : 2;
+   return parent.isValid()? 0 : static_cast<int>(PendingContactRequestModel::Columns::COUNT__);
 }
 
 Qt::ItemFlags PendingContactRequestModel::flags( const QModelIndex& index ) const
@@ -138,7 +147,7 @@ void PendingContactRequestModelPrivate::addRequest(ContactRequest* r)
         removeRequest(r);
     });
 
-   emit q_ptr->requestAdded(r);
+    emit q_ptr->requestAdded(r);
 }
 
 void PendingContactRequestModelPrivate::removeRequest(ContactRequest* r)
diff --git a/src/pendingcontactrequestmodel.h b/src/pendingcontactrequestmodel.h
index 8e811e75..2e90173f 100644
--- a/src/pendingcontactrequestmodel.h
+++ b/src/pendingcontactrequestmodel.h
@@ -36,6 +36,13 @@ class LIB_EXPORT PendingContactRequestModel : public QAbstractTableModel
    friend class AccountModelPrivate;
 public:
 
+   enum Columns {
+      PEER_ID,
+      TIME,
+      FORMATTED_NAME,
+      COUNT__
+   };
+
    //Model functions
    virtual QVariant      data        ( const QModelIndex& index, int role = Qt::DisplayRole     ) const override;
    virtual int           rowCount    ( const QModelIndex& parent = QModelIndex()                ) const override;
diff --git a/src/private/vcardutils.cpp b/src/private/vcardutils.cpp
index 07fd2bba..c73fbf5e 100644
--- a/src/private/vcardutils.cpp
+++ b/src/private/vcardutils.cpp
@@ -400,6 +400,35 @@ Person* VCardUtils::mapToPerson(const QHash<QByteArray, QByteArray>& vCard, QLis
     return personMapped;
 }
 
+/**
+ * create a Person object from a vcard and a ringId, to use when loading and receiving a contact request.
+ *
+ * @param vCard, the vcard. Ring ids inside a vcard should be discarded.
+ * @param ringId, this ring id should always used to identify the peer.
+ */
+Person* VCardUtils::mapToPersonFromIncomingContactRequest(const QHash<QByteArray, QByteArray>& vCard, const QString& ringId)
+{
+    auto personMapped = new Person();
+
+    QHashIterator<QByteArray, QByteArray> it(vCard);
+    while (it.hasNext()) {
+        it.next();
+
+        if (it.key() == VCardUtils::Property::TELEPHONE) {
+            // Do not trust a ringid from an incoming vcard, it could have been falsified.
+            continue;
+        }
+
+        vc_mapper->metacall(personMapped, it.key(), it.value().trimmed());
+    }
+
+    // Add the ringid
+    vc_mapper->metacall(personMapped, VCardUtils::Property::TELEPHONE, ringId.toLatin1());
+    vc_mapper->apply();
+
+    return personMapped;
+}
+
 QHash<QByteArray, QByteArray> VCardUtils::toHashMap(const QByteArray& content)
 {
     QHash<QByteArray, QByteArray> vCard;
diff --git a/src/private/vcardutils.h b/src/private/vcardutils.h
index bd15d1ce..604dbc4e 100644
--- a/src/private/vcardutils.h
+++ b/src/private/vcardutils.h
@@ -83,6 +83,7 @@ public:
    static bool mapToPerson(Person* p, const QUrl& url, QList<Account*>* accounts = nullptr);
    static bool mapToPerson(Person* p, const QByteArray& content, QList<Account*>* accounts = nullptr);
    static Person* mapToPerson(const QHash<QByteArray, QByteArray>& vCard, QList<Account*>* accounts = nullptr);
+   static Person* mapToPersonFromIncomingContactRequest(const QHash<QByteArray, QByteArray>& vCard, const QString& hash);
    static QHash<QByteArray, QByteArray> toHashMap(const QByteArray& content);
 
    //Serialization
diff --git a/src/recentmodel.cpp b/src/recentmodel.cpp
index b6c658f2..9899e4f7 100644
--- a/src/recentmodel.cpp
+++ b/src/recentmodel.cpp
@@ -216,24 +216,6 @@ RecentModel::RecentModel(QObject* parent) : QAbstractItemModel(parent), d_ptr(ne
     connect(&CallModel::instance()          , &CallModel::conferenceChanged        , d_ptr, &RecentModelPrivate::slotConferenceChanged  );
     connect(CallModel::instance().selectionModel(), &QItemSelectionModel::currentChanged, d_ptr, &RecentModelPrivate::slotCurrentCallChanged);
 
-    // Fill contacts from daemon source
-    for (int i = 0; i < AccountModel::instance().rowCount(); i++) {
-        auto account = qvariant_cast<Account*>(AccountModel::instance().data(AccountModel::instance().index(i,0),
-                                                                             static_cast<int>(Account::Role::Object)));
-
-        for (auto cm : account->getContacts())
-            d_ptr->slotLastUsedChanged(cm, cm->lastUsed());
-    }
-
-    // Called when a contact was added to the daemon list (e.g when the user as accepted a request)
-    connect(&AccountModel::instance(), &AccountModel::accountContactAdded, [this] (Account* a, const ContactRequest* r) {
-       auto cm = r->certificate()->contactMethod();
-       if (!cm)
-          cm = PhoneDirectoryModel::instance().getNumber(r->certificate()->remoteId(), a);
-
-       d_ptr->slotLastUsedChanged(cm, cm->lastUsed());
-    });
-
     //Fill the contacts
     for (int i=0; i < PersonModel::instance().rowCount(); i++) {
         auto person = qvariant_cast<Person*>(PersonModel::instance().data(
-- 
GitLab