conversationsview: various bugs, modern C++, leaks

Fix a bunch of newly discovered memory leaks, port some code parts
using C-style fixed-length buffers to modern C++ and check call
status before displaying it as last interaction.

Change-Id: I6114585e80411717e89990653eff26bf7fc49de5
Reviewed-by: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
diff --git a/src/conversationsview.cpp b/src/conversationsview.cpp
index e00dae3..9efa4ce 100644
--- a/src/conversationsview.cpp
+++ b/src/conversationsview.cpp
@@ -2,6 +2,7 @@
  *   Copyright (C) 2017-2018 Savoir-faire Linux                             *
  *   Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>             *
  *   Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>           *
+ *   Author: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com>             *
  *                                                                          *
  *   This library is free software; you can redistribute it and/or          *
  *   modify it under the terms of the GNU Lesser General Public             *
@@ -21,6 +22,10 @@
 
 // std
 #include <algorithm>
+#include <chrono>
+#include <iomanip> // for std::put_time
+#include <string>
+#include <sstream>
 
 // GTK+ related
 #include <QSize>
@@ -104,11 +109,7 @@
         g_object_set(G_OBJECT(cell), "pixbuf", image.get(), NULL);
 
         // Banned contacts should be displayed with grey bg
-        if (contactInfo.isBanned) {
-            g_object_set(G_OBJECT(cell), "cell-background", "#BDBDBD", NULL);
-        } else {
-            g_object_set(G_OBJECT(cell), "cell-background", NULL, NULL);
-        }
+        g_object_set(G_OBJECT(cell), "cell-background", contactInfo.isBanned ? "#BDBDBD" : NULL, NULL);
     }
     catch (const std::exception&)
     {
@@ -182,13 +183,10 @@
     }
 
     // Banned contacts should be displayed with grey bg
-    if (contactInfo.isBanned) {
-        g_object_set(G_OBJECT(cell), "cell-background", "#BDBDBD", NULL);
-    } else {
-        g_object_set(G_OBJECT(cell), "cell-background", NULL, NULL);
-    }
+    g_object_set(G_OBJECT(cell), "cell-background", contactInfo.isBanned ? "#BDBDBD" : NULL, NULL);
 
     g_object_set(G_OBJECT(cell), "markup", text, NULL);
+    g_free(text);
     g_free(uid);
     g_free(uri);
     g_free(alias);
@@ -202,64 +200,54 @@
             GtkTreeIter *iter,
             G_GNUC_UNUSED GtkTreeView *treeview)
 {
+    g_return_if_fail(IS_CONVERSATIONS_VIEW(treeview));
+    auto priv = CONVERSATIONS_VIEW_GET_PRIVATE(treeview);
+    g_return_if_fail(priv);
 
     // Get active conversation
     auto path = gtk_tree_model_get_path(model, iter);
     auto row = std::atoi(gtk_tree_path_to_string(path));
-    if (row == -1) return;
-    auto priv = CONVERSATIONS_VIEW_GET_PRIVATE(treeview);
-    if (!priv) return;
+    g_return_if_fail(row != -1);
 
-    char empty[] = {'\0'};
-    gchar *text = empty;
-
-    try
-    {
+    try {
         auto conversation = (*priv->accountInfo_)->conversationModel->filteredConversation(row);
         auto contactUri = conversation.participants.front();
-        auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
+        auto& contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
 
         // Banned contacts should be displayed with grey bg
-        if (contactInfo.isBanned) {
-            g_object_set(G_OBJECT(cell), "cell-background", "#BDBDBD", NULL);
-        } else {
-            g_object_set(G_OBJECT(cell), "cell-background", NULL, NULL);
-        }
+        g_object_set(G_OBJECT(cell), "cell-background", contactInfo.isBanned ? "#BDBDBD" : NULL, NULL);
 
         auto callId = conversation.confId.empty() ? conversation.callId : conversation.confId;
         if (!callId.empty()) {
             auto call = (*priv->accountInfo_)->callModel->getCall(callId);
-            text = g_markup_printf_escaped("%s",
-                lrc::api::call::to_string(call.status).c_str()
-            );
-        } else if (conversation.interactions.empty()) {
-        } else {
-            auto lastUid = conversation.lastMessageUid;
-            if (conversation.interactions.find(lastUid) == conversation.interactions.end()) {
-            } else {
-                std::time_t lastInteractionTimestamp = conversation.interactions[lastUid].timestamp;
-                std::time_t now = std::time(nullptr);
-                char interactionDay[100];
-                char nowDay[100];
-                std::strftime(interactionDay, sizeof(interactionDay), "%x", std::localtime(&lastInteractionTimestamp));
-                std::strftime(nowDay, sizeof(nowDay), "%x", std::localtime(&now));
-
-                if (std::string(interactionDay) == std::string(nowDay)) {
-                    char interactionTime[100];
-                    std::strftime(interactionTime, sizeof(interactionTime), "%R", std::localtime(&lastInteractionTimestamp));
-                    text = g_markup_printf_escaped("<span size=\"smaller\" color=\"#666\">%s</span>", &interactionTime[0]);
-                } else {
-                    text = g_markup_printf_escaped("<span size=\"smaller\" color=\"#666\">%s</span>", &interactionDay[0]);
-                }
+            if (call.status != lrc::api::call::Status::ENDED) {
+                g_object_set(G_OBJECT(cell), "markup", lrc::api::call::to_string(call.status).c_str(), NULL);
+                return;
             }
         }
-    }
-    catch (const std::exception&)
-    {
+
+        auto& interactions = conversation.interactions;
+        auto lastUid = conversation.lastMessageUid;
+
+        if (!interactions.empty() && interactions.find(lastUid) != interactions.end()) {
+            std::time_t lastTimestamp = interactions[lastUid].timestamp;
+            std::chrono::time_point<std::chrono::system_clock> lastTs = std::chrono::system_clock::from_time_t(lastTimestamp);
+            std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
+            std::chrono::hours diff = std::chrono::duration_cast<std::chrono::hours>(now - lastTs);
+
+            std::stringstream timestamp;
+            timestamp << std::put_time(std::localtime(&lastTimestamp), diff.count() < 24 ? "%R" : "%x");
+            gchar* text = g_markup_printf_escaped("<span size=\"smaller\" color=\"#666\">%s</span>", timestamp.str().c_str());
+            g_object_set(G_OBJECT(cell), "markup", text, NULL);
+
+            g_free(text);
+            return;
+        }
+    } catch (const std::exception&) {
         g_warning("Can't get conversation at row %i", row);
     }
 
-    g_object_set(G_OBJECT(cell), "markup", text, NULL);
+    g_object_set(G_OBJECT(cell), "markup", "", NULL);
 }
 
 void
@@ -525,7 +513,7 @@
         g_debug("source path: %s", path_str_source);
 
         /* get the destination path */
-        GtkTreePath *dest_path = NULL;
+        GtkTreePath *dest_path = nullptr;
         if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(treeview), x, y, &dest_path, NULL)) {
             auto model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
 
@@ -549,6 +537,10 @@
             );
 
             gtk_tree_path_free(dest_path);
+            g_free(conversationUidSrc);
+            g_free(conversationUidDest);
+
+            success = TRUE;
         }
     }
 
@@ -637,7 +629,7 @@
     priv->callChangedConnection_ = QObject::connect(
     &*(*priv->accountInfo_)->callModel,
     &lrc::api::NewCallModel::callStatusChanged,
-    [self, priv] (const std::string& callId) {
+    [self, priv] (const std::string&) {
         // retrieve currently selected conversation
         GtkTreeIter iter;
         GtkTreeModel *model = nullptr;