From e329a07e860f35c60cb575fe6fe7ee4e3a3b27e0 Mon Sep 17 00:00:00 2001
From: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com>
Date: Wed, 25 Apr 2018 15:03:16 -0400
Subject: [PATCH] Forbid sending messages/calling banned contacts

Also: In order to avoid crashes in the future, we check the contact
index variable before using it in conversationmodel.

Change-Id: I038c7049781a9bce951f09ed4a0f1d3100e6df3c
Reviewed-by: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
---
 src/api/contact.h          |  1 +
 src/bannedcontactmodel.cpp | 14 +++++++++++++-
 src/bannedcontactmodel.h   |  1 +
 src/contactmodel.cpp       |  9 +++++++++
 src/conversationmodel.cpp  | 26 ++++++++++++++++++++++++--
 5 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/src/api/contact.h b/src/api/contact.h
index 6a4e7535..07a6b76d 100644
--- a/src/api/contact.h
+++ b/src/api/contact.h
@@ -39,6 +39,7 @@ struct Info
     std::string registeredName;
     bool isTrusted = false;
     bool isPresent = false;
+    bool isBanned = false;
 };
 
 } // namespace contact
diff --git a/src/bannedcontactmodel.cpp b/src/bannedcontactmodel.cpp
index cbe3b8f4..e4b7e881 100644
--- a/src/bannedcontactmodel.cpp
+++ b/src/bannedcontactmodel.cpp
@@ -137,8 +137,9 @@ BannedContactModel::columnCount( const QModelIndex& parent ) const
 void
 BannedContactModel::add(ContactMethod* cm)
 {
-    if (d_ptr->m_lBanned.contains(cm))
+    if (isBanned(cm))
         return;
+
     beginInsertRows(QModelIndex(),d_ptr->m_lBanned.size(),d_ptr->m_lBanned.size());
     d_ptr->m_lBanned << cm;
     endInsertRows();
@@ -151,6 +152,7 @@ BannedContactModel::add(ContactMethod* cm)
 void
 BannedContactModel::remove(ContactMethod* cm)
 {
+    // Do not remove contact if contact isn't banned
     auto rowIndex = d_ptr->m_lBanned.indexOf(cm);
     if (rowIndex < 0)
         return;
@@ -166,3 +168,13 @@ BannedContactModel::remove(ContactMethod* cm)
 
     ConfigurationManager::instance().addContact(cm->account()->id(), cm->uri());
 }
+
+/**
+ * this function returns whether passed ContactMethod is in the banned list or not.
+ * @param cm, the ContactMethod whose presence in the banned list should be checked.
+ */
+bool
+BannedContactModel::isBanned(ContactMethod* cm)
+{
+    return d_ptr->m_lBanned.contains(cm);
+}
diff --git a/src/bannedcontactmodel.h b/src/bannedcontactmodel.h
index 84ec3736..98143df0 100644
--- a/src/bannedcontactmodel.h
+++ b/src/bannedcontactmodel.h
@@ -45,6 +45,7 @@ public:
     // Helper
     void add(ContactMethod* cm);
     void remove(ContactMethod* cm);
+    bool isBanned(ContactMethod* cm);
 
 private:
     virtual ~BannedContactModel();
diff --git a/src/contactmodel.cpp b/src/contactmodel.cpp
index a5d0486d..9dd339c7 100644
--- a/src/contactmodel.cpp
+++ b/src/contactmodel.cpp
@@ -37,6 +37,7 @@
 #include "contactmethod.h"
 #include "namedirectory.h"
 #include "phonedirectorymodel.h"
+#include "bannedcontactmodel.h"
 #include "private/vcardutils.h"
 
 #include "authority/daemon.h"
@@ -470,6 +471,7 @@ ContactModelPimpl::fillsWithRINGContacts() {
         contact::Info contactInfo;
         contactInfo.profileInfo = profileInfo;
         contactInfo.registeredName = cm->registeredName().toStdString();
+        contactInfo.isBanned = account->bannedContactModel()->isBanned(cm);
 
         {
             std::lock_guard<std::mutex> lk(contactsMtx_);
@@ -546,6 +548,13 @@ ContactModelPimpl::addToContacts(ContactMethod* cm, const profile::Type& type)
     auto contactInfo = database::buildContactFromProfileId(db, contactId);
     contactInfo.registeredName = cm->registeredName().toStdString();
 
+    auto* account = AccountModel::instance().getById(linked.owner.id.c_str());
+    if (not account) {
+        qDebug() << "ContactModel::addToContacts(), nullptr";
+        return;
+    }
+
+    contactInfo.isBanned = account->bannedContactModel()->isBanned(cm);
     contactInfo.isPresent = cm->isPresent();
     contactInfo.profileInfo.type = type; // Because PENDING should not be stored in the database
     auto iter = contacts.find(contactInfo.profileInfo.uri);
diff --git a/src/conversationmodel.cpp b/src/conversationmodel.cpp
index 20e898cd..21fc6429 100644
--- a/src/conversationmodel.cpp
+++ b/src/conversationmodel.cpp
@@ -503,6 +503,12 @@ ConversationModelPimpl::placeCall(const std::string& uid, bool isAudioOnly)
     if (url.empty())
         return; // Incorrect item
 
+    // Don't call banned contact
+    if (contactInfo.isBanned) {
+        qDebug() << "ContactModel::placeCall: denied, contact is banned";
+        return;
+    }
+
     sendContactRequest(participant);
 
     if (linked.owner.profileInfo.type != profile::Type::SIP) {
@@ -510,8 +516,13 @@ ConversationModelPimpl::placeCall(const std::string& uid, bool isAudioOnly)
     }
 
     // If call is with temporary contact, conversation has been removed and must be updated
+    int contactIndex;
+    if (isTemporary && (contactIndex = indexOfContact(convId)) < 0) {
+        qDebug() << "Can't place call: Other participant is not a contact";
+        return;
+    }
 
-    auto& newConv = isTemporary ? conversations.at(indexOfContact(convId)) : conversation;
+    auto& newConv = isTemporary ? conversations.at(contactIndex) : conversation;
     convId = newConv.uid;
 
     newConv.callId = linked.owner.callModel->createCall(url, isAudioOnly);
@@ -556,6 +567,12 @@ ConversationModel::sendMessage(const std::string& uid, const std::string& body)
     auto status = interaction::Status::SENDING;
     for (const auto& participant: conversation.participants) {
         auto contactInfo = owner.contactModel->getContact(participant);
+
+        if (contactInfo.isBanned) {
+            qDebug() << "ContactModel::sendMessage: denied, contact is banned";
+            return;
+        }
+
         pimpl_->sendContactRequest(participant);
 
         QStringList callLists = CallManager::instance().getCallList(); // no auto
@@ -576,8 +593,13 @@ ConversationModel::sendMessage(const std::string& uid, const std::string& body)
 
     // If first interaction with temporary contact, we have to update the conversations info
     // at this stage
+    int contactIndex;
+    if (isTemporary && (contactIndex = pimpl_->indexOfContact(convId)) < 0) {
+        qDebug() << "Can't send message: Other participant is not a contact";
+        return;
+    }
 
-    auto& newConv = isTemporary ? pimpl_->conversations.at(pimpl_->indexOfContact(convId)) : conversation;
+    auto& newConv = isTemporary ? pimpl_->conversations.at(contactIndex) : conversation;
     convId = newConv.uid;
 
     // Add interaction to database
-- 
GitLab