about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorMichael Vetter <jubalh@iodoru.org>2020-04-06 22:25:40 +0200
committerGitHub <noreply@github.com>2020-04-06 22:25:40 +0200
commit6c158a9af86c0211a2e32aaabcb9a374214d53dd (patch)
tree65a57e346b56424b57ebd3e22a0ec102d3939e21 /src
parent49057077b9396a16c600ddd61357a1816a94e791 (diff)
parent6218a537276ca9c5e6ca3f6956759421c9425c23 (diff)
downloadprofani-tty-6c158a9af86c0211a2e32aaabcb9a374214d53dd.tar.gz
Merge pull request #1282 from profanity-im/feature/sqlite-log
SQLite backend
Diffstat (limited to 'src')
-rw-r--r--src/config/files.c1
-rw-r--r--src/config/files.h1
-rw-r--r--src/database.c325
-rw-r--r--src/database.h52
-rw-r--r--src/event/client_events.c44
-rw-r--r--src/event/common.c2
-rw-r--r--src/event/server_events.c30
-rw-r--r--src/log.c64
-rw-r--r--src/log.h1
-rw-r--r--src/otr/otr.c4
-rw-r--r--src/ui/chatwin.c31
-rw-r--r--src/ui/mucwin.c17
-rw-r--r--src/ui/window.c36
-rw-r--r--src/ui/window.h2
-rw-r--r--src/xmpp/message.c26
-rw-r--r--src/xmpp/xmpp.h18
16 files changed, 518 insertions, 136 deletions
diff --git a/src/config/files.c b/src/config/files.c
index 133a270e..bdaf6a93 100644
--- a/src/config/files.c
+++ b/src/config/files.c
@@ -3,6 +3,7 @@
  * vim: expandtab:ts=4:sts=4:sw=4
  *
  * Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2020 Michael Vetter <jubalh@idoru.org>
  *
  * This file is part of Profanity.
  *
diff --git a/src/config/files.h b/src/config/files.h
index 12c3f03a..1e16802a 100644
--- a/src/config/files.h
+++ b/src/config/files.h
@@ -55,6 +55,7 @@
 #define DIR_PGP "pgp"
 #define DIR_OMEMO "omemo"
 #define DIR_PLUGINS "plugins"
+#define DIR_DATABASE "database"
 
 void files_create_directories(void);
 
diff --git a/src/database.c b/src/database.c
new file mode 100644
index 00000000..714c4b1a
--- /dev/null
+++ b/src/database.c
@@ -0,0 +1,325 @@
+/*
+ * database.c
+ * vim: expandtab:ts=4:sts=4:sw=4
+ *
+ * Copyright (C) 2020 Michael Vetter <jubalh@idoru.org>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#define _GNU_SOURCE 1
+
+#include <sys/stat.h>
+#include <sqlite3.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "log.h"
+#include "config/files.h"
+
+static sqlite3 *g_chatlog_database;
+
+static void _add_to_db(ProfMessage *message, char *type, const Jid * const from_jid, const Jid * const to_jid);
+static char* _get_db_filename(ProfAccount *account);
+
+static char*
+_get_db_filename(ProfAccount *account)
+{
+    char *databasedir = files_get_data_path(DIR_DATABASE);
+    GString *basedir = g_string_new(databasedir);
+    free(databasedir);
+
+    g_string_append(basedir, "/");
+
+    gchar *account_dir = str_replace(account->jid, "@", "_at_");
+    g_string_append(basedir, account_dir);
+    free(account_dir);
+
+    int res = g_mkdir_with_parents(basedir->str, S_IRWXU);
+    if (res == -1) {
+        char *errmsg = strerror(errno);
+        if (errmsg) {
+            log_error("DATABASE: error creating directory: %s, %s", basedir->str, errmsg);
+        } else {
+            log_error("DATABASE: creating directory: %s", basedir->str);
+        }
+        g_string_free(basedir, TRUE);
+        return NULL;
+    }
+
+    g_string_append(basedir, "/chatlog.db");
+    char *result = strdup(basedir->str);
+    g_string_free(basedir, TRUE);
+
+    return result;
+}
+
+gboolean
+log_database_init(ProfAccount *account)
+{
+    int ret = sqlite3_initialize();
+    if (ret != SQLITE_OK) {
+        log_error("Error initializing SQLite database: %d", ret);
+        return FALSE;
+    }
+
+    char *filename = _get_db_filename(account);
+    if (!filename) {
+        return FALSE;
+    }
+
+    ret = sqlite3_open(filename, &g_chatlog_database);
+    if (ret != SQLITE_OK) {
+        const char *err_msg = sqlite3_errmsg(g_chatlog_database);
+        log_error("Error opening SQLite database: %s", err_msg);
+        free(filename);
+        return FALSE;
+    }
+
+    char *err_msg;
+    // id is the ID of DB the entry
+    // from_jid is the senders jid
+    // to_jid is the receivers jid
+    // from_resource is the senders resource
+    // to_jid is the receivers resource
+    // message is the message text
+    // timestamp the timestamp like "2020/03/24 11:12:14"
+    // type is there to distinguish: message (chat), MUC message (muc), muc pm (mucpm)
+    // stanza_id is the ID from XEP-0359: Unique and Stable Stanza IDs
+    // archive_id is the ID from XEP-0313: Message Archive Management
+    // replace_id is the ID from XEP-0308: Last Message Correction
+    // encryption is to distinguish: none, omemo, otr, pgp
+    // marked_read is 0/1 whether a message has been marked as read via XEP-0333: Chat Markers
+    char *query = "CREATE TABLE IF NOT EXISTS `ChatLogs` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `from_jid` TEXT NOT NULL, `to_jid` TEXT NOT NULL, `from_resource` TEXT, `to_resource` TEXT, `message` TEXT, `timestamp` TEXT, `type` TEXT, `stanza_id` TEXT, `archive_id` TEXT, `replace_id` TEXT, `encryption` TEXT, `marked_read` INTEGER)";
+    if( SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
+        goto out;
+    }
+
+    query = "CREATE TABLE IF NOT EXISTS `DbVersion` ( `dv_id` INTEGER PRIMARY KEY, `version` INTEGER UNIQUE)";
+    if( SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
+        goto out;
+    }
+
+    query = "INSERT OR IGNORE INTO `DbVersion` (`version`) VALUES('1')";
+    if( SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
+        goto out;
+    }
+
+    log_debug("Initialized SQLite database: %s", filename);
+    free(filename);
+    return TRUE;
+
+out:
+    if (err_msg) {
+        log_error("SQLite error: %s", err_msg);
+        sqlite3_free(err_msg);
+    } else {
+        log_error("Unknown SQLite error");
+    }
+    free(filename);
+    return FALSE;
+}
+
+void
+log_database_close(void)
+{
+    if (g_chatlog_database) {
+        sqlite3_close(g_chatlog_database);
+        sqlite3_shutdown();
+        g_chatlog_database = NULL;
+    }
+}
+
+void
+log_database_add_incoming(ProfMessage *message)
+{
+    const char *jid = connection_get_fulljid();
+    Jid *myjid = jid_create(jid);
+
+    _add_to_db(message, NULL, message->jid, myjid);
+
+    jid_destroy(myjid);
+}
+
+static void
+_log_database_add_outgoing(char *type, const char * const id, const char * const barejid, const char * const message, const char *const replace_id, prof_enc_t enc)
+{
+    ProfMessage *msg = message_init();
+
+    msg->id = id ? strdup(id) : NULL;
+    msg->jid = jid_create(barejid);
+    msg->plain = message ? strdup(message) : NULL;
+    msg->replace_id = replace_id ? strdup(replace_id) : NULL;
+    msg->timestamp = g_date_time_new_now_local(); //TODO: get from outside. best to have whole ProfMessage from outside
+    msg->enc = enc;
+
+    const char *jid = connection_get_fulljid();
+    Jid *myjid = jid_create(jid);
+
+    _add_to_db(msg, type, myjid, msg->jid);
+
+    jid_destroy(myjid);
+    message_free(msg);
+}
+
+void
+log_database_add_outgoing_chat(const char * const id, const char * const barejid, const char * const message, const char *const replace_id, prof_enc_t enc)
+{
+    _log_database_add_outgoing("chat", id, barejid, message, replace_id, enc);
+}
+
+void
+log_database_add_outgoing_muc(const char * const id, const char * const barejid, const char * const message, const char *const replace_id, prof_enc_t enc)
+{
+    _log_database_add_outgoing("muc", id, barejid, message, replace_id, enc);
+}
+
+void
+log_database_add_outgoing_muc_pm(const char * const id, const char * const barejid, const char * const message, const char *const replace_id, prof_enc_t enc)
+{
+    _log_database_add_outgoing("mucpm", id, barejid, message, replace_id, enc);
+}
+
+GSList*
+log_database_get_previous_chat(const gchar *const contact_barejid)
+{
+	sqlite3_stmt *stmt = NULL;
+    char *query;
+
+    if (asprintf(&query, "SELECT * FROM (SELECT `message`, `timestamp`, `from_jid` from `ChatLogs` WHERE `from_jid` = '%s' OR `to_jid` = '%s' ORDER BY `timestamp` DESC LIMIT 10) ORDER BY `timestamp` ASC;", contact_barejid, contact_barejid) == -1) {
+        log_error("log_database_get_previous_chat(): SQL query. could not allocate memory");
+        return NULL;
+    }
+
+	int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL);
+	if( rc!=SQLITE_OK ) {
+        log_error("log_database_get_previous_chat(): unknown SQLite error");
+        return NULL;
+    }
+
+    GSList *history = NULL;
+
+	while( sqlite3_step(stmt) == SQLITE_ROW ) {
+		char *message = (char*)sqlite3_column_text(stmt, 0);
+		char *date = (char*)sqlite3_column_text(stmt, 1);
+		char *from = (char*)sqlite3_column_text(stmt, 2);
+
+        ProfMessage *msg = message_init();
+        msg->jid = jid_create(from);
+        msg->plain = strdup(message);
+        msg->timestamp = g_date_time_new_from_iso8601(date, NULL);
+        // TODO: later we can get more fields like 'enc'. then we can display the history like regular chats with all info the user enabled.
+
+        history = g_slist_append(history, msg);
+	}
+	sqlite3_finalize(stmt);
+    free(query);
+
+    return history;
+}
+
+static const char* _get_message_type_str(prof_msg_type_t type) {
+    switch (type) {
+    case PROF_MSG_TYPE_CHAT:
+        return "chat";
+    case PROF_MSG_TYPE_MUC:
+        return "muc";
+    case PROF_MSG_TYPE_MUCPM:
+        return "mucpm";
+    case PROF_MSG_TYPE_UNINITIALIZED:
+        return NULL;
+    }
+    return NULL;
+}
+
+static const char* _get_message_enc_str(prof_enc_t enc) {
+    switch (enc) {
+    case PROF_MSG_ENC_PGP:
+        return "pgp";
+    case PROF_MSG_ENC_OTR:
+        return "otr";
+    case PROF_MSG_ENC_OMEMO:
+        return "omemo";
+    case PROF_MSG_ENC_NONE:
+        return "none";
+    }
+
+    return "none";
+}
+
+static void
+_add_to_db(ProfMessage *message, char *type, const Jid * const from_jid, const Jid * const to_jid)
+{
+    if (!g_chatlog_database) {
+        log_debug("log_database_add() called but db is not initialized");
+        return;
+    }
+
+    char *err_msg;
+    char *query;
+    gchar *date_fmt;
+
+    if (message->timestamp) {
+        date_fmt = g_date_time_format_iso8601(message->timestamp);
+    } else {
+        date_fmt = g_date_time_format_iso8601(g_date_time_new_now_local());
+    }
+
+    const char *enc = _get_message_enc_str(message->enc);
+
+    if (!type) {
+        type = (char*)_get_message_type_str(message->type);
+    }
+
+    if (asprintf(&query, "INSERT INTO `ChatLogs` (`from_jid`, `from_resource`, `to_jid`, `to_resource`, `message`, `timestamp`, `stanza_id`, `replace_id`, `type`, `encryption`) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')",
+                from_jid->barejid,
+                from_jid->resourcepart ? from_jid->resourcepart : "",
+                to_jid->barejid,
+                to_jid->resourcepart ? to_jid->resourcepart : "",
+                message->plain,
+                date_fmt,
+                message->id ? message->id : "",
+                message->replace_id ? message->replace_id : "",
+                type,
+                enc) == -1) {
+        log_error("log_database_add(): SQL query. could not allocate memory");
+        return;
+    }
+    g_free(date_fmt);
+
+    if( SQLITE_OK != sqlite3_exec(g_chatlog_database, query, NULL, 0, &err_msg)) {
+        if (err_msg) {
+            log_error("SQLite error: %s", err_msg);
+            sqlite3_free(err_msg);
+        } else {
+            log_error("Unknown SQLite error");
+        }
+    }
+    free(query);
+}
diff --git a/src/database.h b/src/database.h
new file mode 100644
index 00000000..4ad3fc9c
--- /dev/null
+++ b/src/database.h
@@ -0,0 +1,52 @@
+/*
+ * database.h
+ * vim: expandtab:ts=4:sts=4:sw=4
+ *
+ * Copyright (C) 2020 Michael Vetter <jubalh@idoru.org>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#ifndef DATABASE_H
+#define DATABASE_H
+
+#include <glib.h>
+#include "config/account.h"
+#include "xmpp/xmpp.h"
+
+gboolean log_database_init(ProfAccount *account);
+void log_database_add_incoming(ProfMessage *message);
+void log_database_add_outgoing_chat(const char * const id, const char * const barejid, const char * const message, const char *const replace_id, prof_enc_t enc);
+void log_database_add_outgoing_muc(const char * const id, const char * const barejid, const char * const message, const char *const replace_id, prof_enc_t enc);
+void log_database_add_outgoing_muc_pm(const char * const id, const char * const barejid, const char * const message, const char *const replace_id, prof_enc_t enc);
+GSList* log_database_get_previous_chat(const gchar *const contact_barejid);
+void log_database_close(void);
+
+#endif // DATABASE_H
+
diff --git a/src/event/client_events.c b/src/event/client_events.c
index e0ea85d7..e57f3875 100644
--- a/src/event/client_events.c
+++ b/src/event/client_events.c
@@ -40,6 +40,7 @@
 #include <glib.h>
 
 #include "log.h"
+#include "database.h"
 #include "config/preferences.h"
 #include "event/common.h"
 #include "plugins/plugins.h"
@@ -138,6 +139,7 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
     if (chatwin->pgp_send) {
         char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt, replace_id);
         chat_log_pgp_msg_out(chatwin->barejid, plugin_msg, NULL);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_PGP);
         chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt, replace_id);
         free(id);
     } else {
@@ -145,7 +147,8 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
         if (!handled) {
             char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id);
             chat_log_msg_out(chatwin->barejid, plugin_msg, NULL);
-            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id);
+            log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_NONE);
+            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_NONE, request_receipt, replace_id);
             free(id);
         }
     }
@@ -165,7 +168,8 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
     if (!handled) {
         char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id);
         chat_log_msg_out(chatwin->barejid, plugin_msg, NULL);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_NONE);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_NONE, request_receipt);
         free(id);
     }
 
@@ -183,12 +187,14 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
     if (chatwin->pgp_send) {
         char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt, replace_id);
         chat_log_pgp_msg_out(chatwin->barejid, plugin_msg, NULL);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_PGP);
         chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt, replace_id);
         free(id);
     } else {
         char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id);
         chat_log_msg_out(chatwin->barejid, plugin_msg, NULL);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_NONE);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_NONE, request_receipt, replace_id);
         free(id);
     }
 
@@ -206,12 +212,14 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
     if (chatwin->is_omemo) {
         char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE, replace_id);
         chat_log_omemo_msg_out(chatwin->barejid, plugin_msg, NULL);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_OMEMO);
         chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt, replace_id);
         free(id);
     } else {
         char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id);
         chat_log_msg_out(chatwin->barejid, plugin_msg, NULL);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_NONE);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_NONE, request_receipt, replace_id);
         free(id);
     }
 
@@ -229,6 +237,7 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
     if (chatwin->is_omemo) {
         char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE, replace_id);
         chat_log_omemo_msg_out(chatwin->barejid, plugin_msg, NULL);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_OMEMO);
         chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt, replace_id);
         free(id);
     } else {
@@ -236,7 +245,8 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
         if (!handled) {
             char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id);
             chat_log_msg_out(chatwin->barejid, plugin_msg, NULL);
-            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id);
+            log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_NONE);
+            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_NONE, request_receipt, replace_id);
             free(id);
         }
     }
@@ -255,17 +265,20 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
     if (chatwin->is_omemo) {
         char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE, replace_id);
         chat_log_omemo_msg_out(chatwin->barejid, plugin_msg, NULL);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_OMEMO);
         chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt, replace_id);
         free(id);
     } else if (chatwin->pgp_send) {
         char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt, replace_id);
         chat_log_pgp_msg_out(chatwin->barejid, plugin_msg, NULL);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_PGP);
         chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt, replace_id);
         free(id);
     } else {
         char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id);
         chat_log_msg_out(chatwin->barejid, plugin_msg, NULL);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_NONE);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_NONE, request_receipt, replace_id);
         free(id);
     }
 
@@ -283,11 +296,13 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
     if (chatwin->is_omemo) {
         char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE, replace_id);
         chat_log_omemo_msg_out(chatwin->barejid, plugin_msg, NULL);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_OMEMO);
         chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt, replace_id);
         free(id);
     } else if (chatwin->pgp_send) {
         char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt, replace_id);
         chat_log_pgp_msg_out(chatwin->barejid, plugin_msg, NULL);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_PGP);
         chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt, replace_id);
         free(id);
     } else {
@@ -295,7 +310,8 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
         if (!handled) {
             char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id);
             chat_log_msg_out(chatwin->barejid, plugin_msg, NULL);
-            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id);
+            log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_NONE);
+            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_NONE, request_receipt, replace_id);
             free(id);
         }
     }
@@ -313,7 +329,8 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
 #ifndef HAVE_OMEMO
     char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt, replace_id);
     chat_log_msg_out(chatwin->barejid, plugin_msg, NULL);
-    chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id);
+    log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_NONE);
+    chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_NONE, request_receipt, replace_id);
     free(id);
 
     plugins_post_chat_message_send(chatwin->barejid, plugin_msg);
@@ -347,12 +364,14 @@ cl_ev_send_muc_msg_corrected(ProfMucWin *mucwin, const char *const msg, const ch
     if (mucwin->is_omemo) {
         char *id = omemo_on_message_send((ProfWin *)mucwin, plugin_msg, FALSE, TRUE, replace_id);
         groupchat_log_omemo_msg_out(mucwin->roomjid, plugin_msg);
+        log_database_add_outgoing_muc(id, mucwin->roomjid, plugin_msg, replace_id, PROF_MSG_ENC_OMEMO);
         mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, replace_id);
         free(id);
     } else {
         char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url, replace_id);
         groupchat_log_msg_out(mucwin->roomjid, plugin_msg);
-        mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, replace_id);
+        log_database_add_outgoing_muc(id, mucwin->roomjid, plugin_msg, replace_id, PROF_MSG_ENC_NONE);
+        mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_NONE, replace_id);
         free(id);
     }
 
@@ -364,7 +383,8 @@ cl_ev_send_muc_msg_corrected(ProfMucWin *mucwin, const char *const msg, const ch
 #ifndef HAVE_OMEMO
     char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url, replace_id);
     groupchat_log_msg_out(mucwin->roomjid, plugin_msg);
-    mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, replace_id);
+    log_database_add_outgoing_muc(id, mucwin->roomjid, plugin_msg, replace_id, PROF_MSG_ENC_NONE);
+    mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_NONE, replace_id);
     free(id);
 
     plugins_post_room_message_send(mucwin->roomjid, plugin_msg);
@@ -390,9 +410,11 @@ cl_ev_send_priv_msg(ProfPrivateWin *privwin, const char *const msg, const char *
         char *plugin_msg = plugins_pre_priv_message_send(privwin->fulljid, msg);
         Jid *jidp = jid_create(privwin->fulljid);
 
-        message_send_private(privwin->fulljid, plugin_msg, oob_url);
+        char *id = message_send_private(privwin->fulljid, plugin_msg, oob_url);
         chat_log_msg_out(jidp->barejid, plugin_msg, jidp->resourcepart);
+        log_database_add_outgoing_muc_pm(id, privwin->fulljid, plugin_msg, NULL, PROF_MSG_ENC_NONE);
         privwin_outgoing_msg(privwin, plugin_msg);
+        free(id);
 
         plugins_post_priv_message_send(privwin->fulljid, plugin_msg);
 
diff --git a/src/event/common.c b/src/event/common.c
index 05c3f30b..2d829e5c 100644
--- a/src/event/common.c
+++ b/src/event/common.c
@@ -39,6 +39,7 @@
 #include "xmpp/roster_list.h"
 #include "xmpp/muc.h"
 #include "xmpp/xmpp.h"
+#include "database.h"
 
 #ifdef HAVE_LIBGPGME
 #include "pgp/gpg.h"
@@ -67,6 +68,7 @@ ev_disconnect_cleanup(void)
 #ifdef HAVE_OMEMO
     omemo_on_disconnect();
 #endif
+    log_database_close();
 }
 
 gboolean
diff --git a/src/event/server_events.c b/src/event/server_events.c
index ea4277b2..b63af3b6 100644
--- a/src/event/server_events.c
+++ b/src/event/server_events.c
@@ -42,6 +42,7 @@
 
 #include "profanity.h"
 #include "log.h"
+#include "database.h"
 #include "config/preferences.h"
 #include "config/tlscerts.h"
 #include "config/account.h"
@@ -91,6 +92,8 @@ sv_ev_login_account_success(char *account_name, gboolean secured)
     omemo_on_connect(account);
 #endif
 
+    log_database_init(account);
+
     avatar_pep_subscribe();
 
     ui_handle_login_account_success(account, secured);
@@ -298,6 +301,7 @@ static void _log_muc(ProfMessage *message)
     } else {
         groupchat_log_msg_in(message->jid->barejid, message->jid->resourcepart, message->plain);
     }
+    log_database_add_incoming(message);
 }
 
 void
@@ -397,6 +401,7 @@ sv_ev_incoming_private_message(ProfMessage *message)
     }
 
     _clean_incoming_message(message);
+    log_database_add_incoming(message);
     privwin_incoming_msg(privatewin, message);
     chat_log_msg_in(message);
 
@@ -440,7 +445,7 @@ sv_ev_outgoing_carbon(ProfMessage *message)
     chat_state_active(chatwin->state);
 
     if (message->plain) {
-        if (message->mucuser) {
+        if (message->type == PROF_MSG_TYPE_MUCPM) {
             // MUC PM, should have resource (nick) in filename
             chat_log_msg_out(message->jid->barejid, message->plain, message->jid->resourcepart);
         } else {
@@ -460,12 +465,12 @@ sv_ev_outgoing_carbon(ProfMessage *message)
                 log_error("Couldn't decrypt GPG message and body was empty");
                 return;
             }
-            message->enc = PROF_MSG_ENC_PLAIN;
+            message->enc = PROF_MSG_ENC_NONE;
             message->plain = strdup(message->body);
             chatwin_outgoing_carbon(chatwin, message);
         }
     } else {
-        message->enc = PROF_MSG_ENC_PLAIN;
+        message->enc = PROF_MSG_ENC_NONE;
         message->plain = strdup(message->body);
         chatwin_outgoing_carbon(chatwin, message);
     }
@@ -478,7 +483,7 @@ sv_ev_outgoing_carbon(ProfMessage *message)
     if (message->enc == PROF_MSG_ENC_OMEMO) {
         chatwin_outgoing_carbon(chatwin, message);
     } else {
-        message->enc = PROF_MSG_ENC_PLAIN;
+        message->enc = PROF_MSG_ENC_NONE;
         message->plain = strdup(message->body);
         chatwin_outgoing_carbon(chatwin, message);
     }
@@ -500,12 +505,12 @@ sv_ev_outgoing_carbon(ProfMessage *message)
                 log_error("Couldn't decrypt GPG message and body was empty");
                 return;
             }
-            message->enc = PROF_MSG_ENC_PLAIN;
+            message->enc = PROF_MSG_ENC_NONE;
             message->plain = strdup(message->body);
             chatwin_outgoing_carbon(chatwin, message);
         }
     } else {
-        message->enc = PROF_MSG_ENC_PLAIN;
+        message->enc = PROF_MSG_ENC_NONE;
         message->plain = strdup(message->body);
         chatwin_outgoing_carbon(chatwin, message);
     }
@@ -516,7 +521,7 @@ sv_ev_outgoing_carbon(ProfMessage *message)
 #ifndef HAVE_LIBGPGME
 #ifndef HAVE_OMEMO
     if (message->body) {
-        message->enc = PROF_MSG_ENC_PLAIN;
+        message->enc = PROF_MSG_ENC_NONE;
         message->plain = strdup(message->body);
         chatwin_outgoing_carbon(chatwin, message);
     }
@@ -532,6 +537,7 @@ _sv_ev_incoming_pgp(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message
     if (message->plain) {
         message->enc = PROF_MSG_ENC_PGP;
         _clean_incoming_message(message);
+        log_database_add_incoming(message);
         chatwin_incoming_msg(chatwin, message, new_win);
         if (logit) {
             chat_log_pgp_msg_in(message);
@@ -544,9 +550,10 @@ _sv_ev_incoming_pgp(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message
             log_error("Couldn't decrypt GPG message and body was empty");
             return;
         }
-        message->enc = PROF_MSG_ENC_PLAIN;
+        message->enc = PROF_MSG_ENC_NONE;
         message->plain = strdup(message->body);
         _clean_incoming_message(message);
+        log_database_add_incoming(message);
         chatwin_incoming_msg(chatwin, message, new_win);
         chat_log_msg_in(message);
         chatwin->pgp_recv = FALSE;
@@ -565,10 +572,11 @@ _sv_ev_incoming_otr(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message
             message->enc = PROF_MSG_ENC_OTR;
             chatwin->pgp_send = FALSE;
         } else {
-            message->enc = PROF_MSG_ENC_PLAIN;
+            message->enc = PROF_MSG_ENC_NONE;
         }
 
         _clean_incoming_message(message);
+        log_database_add_incoming(message);
         chatwin_incoming_msg(chatwin, message, new_win);
 
         chat_log_otr_msg_in(message);
@@ -584,6 +592,7 @@ static void
 _sv_ev_incoming_omemo(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message, gboolean logit)
 {
     _clean_incoming_message(message);
+    log_database_add_incoming(message);
     chatwin_incoming_msg(chatwin, message, new_win);
     if (logit) {
         chat_log_omemo_msg_in(message);
@@ -596,9 +605,10 @@ static void
 _sv_ev_incoming_plain(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message, gboolean logit)
 {
     if (message->body) {
-        message->enc = PROF_MSG_ENC_PLAIN;
+        message->enc = PROF_MSG_ENC_NONE;
         message->plain = strdup(message->body);
         _clean_incoming_message(message);
+        log_database_add_incoming(message);
         chatwin_incoming_msg(chatwin, message, new_win);
         if (logit) {
             chat_log_msg_in(message);
diff --git a/src/log.c b/src/log.c
index 7304ed03..ae8f1542 100644
--- a/src/log.c
+++ b/src/log.c
@@ -355,14 +355,14 @@ chat_log_otr_msg_in(ProfMessage *message)
         const char *jid = connection_get_fulljid();
         Jid *jidp = jid_create(jid);
         char *pref_otr_log = prefs_get_string(PREF_OTR_LOG);
-        if (message->enc == PROF_MSG_ENC_PLAIN || (strcmp(pref_otr_log, "on") == 0)) {
-            if (message->mucuser) {
+        if (message->enc == PROF_MSG_ENC_NONE || (strcmp(pref_otr_log, "on") == 0)) {
+            if (message->type == PROF_MSG_TYPE_MUCPM) {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, message->jid->resourcepart);
             } else {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, NULL);
             }
         } else if (strcmp(pref_otr_log, "redact") == 0) {
-            if (message->mucuser) {
+            if (message->type == PROF_MSG_TYPE_MUCPM) {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp, message->jid->resourcepart);
             } else {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp, NULL);
@@ -381,13 +381,13 @@ chat_log_pgp_msg_in(ProfMessage *message)
         Jid *jidp = jid_create(jid);
         char *pref_pgp_log = prefs_get_string(PREF_PGP_LOG);
         if (strcmp(pref_pgp_log, "on") == 0) {
-            if (message->mucuser) {
+            if (message->type == PROF_MSG_TYPE_MUCPM) {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, message->jid->resourcepart);
             } else {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, NULL);
             }
         } else if (strcmp(pref_pgp_log, "redact") == 0) {
-            if (message->mucuser) {
+            if (message->type == PROF_MSG_TYPE_MUCPM) {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp, message->jid->resourcepart);
             } else {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp, NULL);
@@ -406,13 +406,13 @@ chat_log_omemo_msg_in(ProfMessage *message)
         Jid *jidp = jid_create(jid);
         char *pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
         if (strcmp(pref_omemo_log, "on") == 0) {
-            if (message->mucuser) {
+            if (message->type == PROF_MSG_TYPE_MUCPM) {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, message->jid->resourcepart);
             } else {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, NULL);
             }
         } else if (strcmp(pref_omemo_log, "redact") == 0) {
-            if (message->mucuser) {
+            if (message->type == PROF_MSG_TYPE_MUCPM) {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp, message->jid->resourcepart);
             } else {
                 _chat_log_chat(jidp->barejid, message->jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp, message->jid->resourcepart);
@@ -430,7 +430,7 @@ chat_log_msg_in(ProfMessage *message)
         const char *jid = connection_get_fulljid();
         Jid *jidp = jid_create(jid);
 
-        if (message->mucuser) {
+        if (message->type == PROF_MSG_TYPE_MUCPM) {
             _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, message->jid->resourcepart);
         } else {
             _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, NULL);
@@ -619,54 +619,6 @@ _groupchat_log_chat(const gchar *const login, const gchar *const room, const gch
     g_date_time_unref(dt);
 }
 
-GSList*
-chat_log_get_previous(const gchar *const login, const gchar *const recipient)
-{
-    GSList *history = NULL;
-    GDateTime *now = g_date_time_new_now_local();
-    GDateTime *log_date = g_date_time_new(tz,
-        g_date_time_get_year(session_started),
-        g_date_time_get_month(session_started),
-        g_date_time_get_day_of_month(session_started),
-        g_date_time_get_hour(session_started),
-        g_date_time_get_minute(session_started),
-        g_date_time_get_second(session_started));
-
-    // get data from all logs from the day the session was started to today
-    while (g_date_time_compare(log_date, now) != 1) {
-        char *filename = _get_log_filename(recipient, login, log_date, FALSE);
-
-        FILE *logp = fopen(filename, "r");
-        if (logp) {
-            GString *header = g_string_new("");
-            g_string_append_printf(header, "%d/%d/%d:",
-                g_date_time_get_day_of_month(log_date),
-                g_date_time_get_month(log_date),
-                g_date_time_get_year(log_date));
-            history = g_slist_append(history, header->str);
-            g_string_free(header, FALSE);
-
-            char *line;
-            while ((line = file_getline(logp)) != NULL) {
-                history = g_slist_append(history, line);
-            }
-
-            fclose(logp);
-        }
-
-        free(filename);
-
-        GDateTime *next = g_date_time_add_days(log_date, 1);
-        g_date_time_unref(log_date);
-        log_date = next;
-    }
-
-    g_date_time_unref(log_date);
-    g_date_time_unref(now);
-
-    return history;
-}
-
 void
 chat_log_close(void)
 {
diff --git a/src/log.h b/src/log.h
index 592c4039..3e29e8ca 100644
--- a/src/log.h
+++ b/src/log.h
@@ -82,7 +82,6 @@ void chat_log_pgp_msg_in(ProfMessage *message);
 void chat_log_omemo_msg_in(ProfMessage *message);
 
 void chat_log_close(void);
-GSList* chat_log_get_previous(const gchar *const login, const gchar *const recipient);
 
 void groupchat_log_init(void);
 
diff --git a/src/otr/otr.c b/src/otr/otr.c
index c32e708e..3c3ed618 100644
--- a/src/otr/otr.c
+++ b/src/otr/otr.c
@@ -40,6 +40,7 @@
 #include <glib.h>
 
 #include "log.h"
+#include "database.h"
 #include "config/preferences.h"
 #include "config/files.h"
 #include "otr/otr.h"
@@ -348,6 +349,7 @@ otr_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean re
         if (encrypted) {
             id = message_send_chat_otr(chatwin->barejid, encrypted, request_receipt, replace_id);
             chat_log_otr_msg_out(chatwin->barejid, message, NULL);
+            log_database_add_outgoing_chat(id, chatwin->barejid, message, replace_id, PROF_MSG_ENC_OTR);
             chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_ENC_OTR, request_receipt, replace_id);
             otr_free_message(encrypted);
             free(id);
@@ -368,7 +370,7 @@ otr_on_message_send(ProfChatWin *chatwin, const char *const message, gboolean re
     if (policy == PROF_OTRPOLICY_OPPORTUNISTIC) {
         char *otr_tagged_msg = otr_tag_message(message);
         id = message_send_chat_otr(chatwin->barejid, otr_tagged_msg, request_receipt, replace_id);
-        chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_ENC_PLAIN, request_receipt, replace_id);
+        chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_ENC_NONE, request_receipt, replace_id);
         chat_log_msg_out(chatwin->barejid, message, NULL);
         free(otr_tagged_msg);
         free(id);
diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c
index 9ebc8d76..5e2ac8c2 100644
--- a/src/ui/chatwin.c
+++ b/src/ui/chatwin.c
@@ -44,6 +44,7 @@
 #include "window_list.h"
 #include "xmpp/roster_list.h"
 #include "log.h"
+#include "database.h"
 #include "config/preferences.h"
 #include "ui/ui.h"
 #include "ui/window.h"
@@ -56,7 +57,7 @@
 #include "omemo/omemo.h"
 #endif
 
-static void _chatwin_history(ProfChatWin *chatwin, const char *const contact);
+static void _chatwin_history(ProfChatWin *chatwin, const char *const contact_barejid);
 static void _chatwin_set_last_message(ProfChatWin *chatwin, const char *const id, const char *const message);
 
 ProfChatWin*
@@ -477,38 +478,20 @@ chatwin_unset_outgoing_char(ProfChatWin *chatwin)
 }
 
 static void
-_chatwin_history(ProfChatWin *chatwin, const char *const contact)
+_chatwin_history(ProfChatWin *chatwin, const char *const contact_barejid)
 {
     if (!chatwin->history_shown) {
-        Jid *jid = jid_create(connection_get_fulljid());
-        GSList *history = chat_log_get_previous(jid->barejid, contact);
-        jid_destroy(jid);
+        GSList *history = log_database_get_previous_chat(contact_barejid);
         GSList *curr = history;
-        int idd = 0;
-        int imo = 0;
-        int iyy = 0;
 
         while (curr) {
-            char *line = curr->data;
-            // entry, containing the actual entries with date followed by text
-            if (line[2] == ':') {
-                char hh[3]; memcpy(hh, &line[0], 2); hh[2] = '\0'; int ihh = atoi(hh);
-                char mm[3]; memcpy(mm, &line[3], 2); mm[2] = '\0'; int imm = atoi(mm);
-                char ss[3]; memcpy(ss, &line[6], 2); ss[2] = '\0'; int iss = atoi(ss);
-                GDateTime *timestamp = g_date_time_new_local(iyy, imo, idd, ihh, imm, iss);
-                win_print_history((ProfWin*)chatwin, timestamp, curr->data+11);
-                g_date_time_unref(timestamp);
-            // header, containing the date from filename "21/10/2019:"
-            } else {
-                char dd[3]; memcpy(dd, &line[0], 2); dd[2] = '\0'; idd = atoi(dd);
-                char mm[3]; memcpy(mm, &line[3], 2); mm[2] = '\0'; imo = atoi(mm);
-                char yy[5]; memcpy(yy, &line[6], 4); yy[4] = '\0'; iyy = atoi(yy);
-            }
+            ProfMessage *msg = curr->data;
+            win_print_history((ProfWin*)chatwin, msg, FALSE);
             curr = g_slist_next(curr);
         }
         chatwin->history_shown = TRUE;
 
-        g_slist_free_full(history, free);
+        g_slist_free_full(history, (GDestroyNotify)message_free);
     }
 }
 
diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c
index 79a82a61..620ac704 100644
--- a/src/ui/mucwin.c
+++ b/src/ui/mucwin.c
@@ -373,22 +373,7 @@ mucwin_history(ProfMucWin *mucwin, const ProfMessage *const message)
     char *muc_history_color = prefs_get_string(PREF_HISTORY_COLOR_MUC);
 
     if (g_strcmp0(muc_history_color, "unanimous") == 0) {
-        GString *line = g_string_new("");
-
-        if (strncmp(message->plain, "/me ", 4) == 0) {
-            g_string_append(line, "*");
-            g_string_append(line, nick);
-            g_string_append(line, " ");
-            g_string_append(line, message->plain + 4);
-        } else {
-            g_string_append(line, nick);
-            g_string_append(line, ": ");
-            g_string_append(line, message->plain);
-        }
-
-        win_print_history(window, message->timestamp, line->str);
-
-        g_string_free(line, TRUE);
+        win_print_history(window, message, TRUE);
     } else {
         char *mynick = muc_nick(mucwin->roomjid);
         GSList *mentions = get_mentions(prefs_get_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD), prefs_get_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE), message->plain, mynick);
diff --git a/src/ui/window.c b/src/ui/window.c
index fdf5d3f9..5b0aec8b 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -1218,15 +1218,41 @@ win_print_outgoing(ProfWin *window, const char *show_char, const char *const id,
 }
 
 void
-win_print_history(ProfWin *window, GDateTime *timestamp, const char *const message)
+win_print_history(ProfWin *window, const ProfMessage *const message, gboolean is_muc)
 {
-    g_date_time_ref(timestamp);
+    g_date_time_ref(message->timestamp);
 
-    buffer_append(window->layout->buffer, "-", 0, timestamp, 0, THEME_TEXT_HISTORY, "", NULL, message, NULL, NULL);
-    _win_print_internal(window, "-", 0, timestamp, 0, THEME_TEXT_HISTORY, "", message, NULL);
+    int flags = 0;
+
+    // TODO: ProfMessage needs a 'type' field like we have in sql db. then we can know whether each message is a chat, muc, mucpm
+    char *display_name;
+    if (is_muc) {
+        display_name = strdup(message->jid->resourcepart);
+
+        char *muc_history_color = prefs_get_string(PREF_HISTORY_COLOR_MUC);
+        if (g_strcmp0(muc_history_color, "unanimous") == 0) {
+            flags = NO_COLOUR_FROM;
+        }
+        g_free(muc_history_color);
+    } else {
+        const char *jid = connection_get_fulljid();
+        Jid *jidp = jid_create(jid);
+
+        if (g_strcmp0(jidp->barejid, message->jid->barejid) == 0) {
+            display_name = strdup("me");
+        } else {
+            display_name = roster_get_msg_display_name(message->jid->barejid, message->jid->resourcepart);
+        }
+        jid_destroy(jidp);
+    }
+
+    buffer_append(window->layout->buffer, "-", 0, message->timestamp, flags, THEME_TEXT_HISTORY, display_name, NULL, message->plain, NULL, NULL);
+    _win_print_internal(window, "-", 0, message->timestamp, flags, THEME_TEXT_HISTORY, display_name, message->plain, NULL);
+
+    free(display_name);
 
     inp_nonblocking(TRUE);
-    g_date_time_unref(timestamp);
+    g_date_time_unref(message->timestamp);
 }
 
 void
diff --git a/src/ui/window.h b/src/ui/window.h
index d17560bc..378dae24 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -68,7 +68,7 @@ void win_print_outgoing(ProfWin *window, const char *show_char, const char *cons
 void win_print_outgoing_with_receipt(ProfWin *window, const char *show_char, const char *const from, const char *const message, char *id, const char *const replace_id);
 void win_println_incoming_muc_msg(ProfWin *window, char *show_char, int flags, const ProfMessage *const message);
 void win_print_outgoing_muc_msg(ProfWin *window, char *show_char, const char *const me, const char *const id, const char *const replace_id, const char *const message);
-void win_print_history(ProfWin *window, GDateTime *timestamp, const char *const message);
+void win_print_history(ProfWin *window, const ProfMessage *const message, gboolean is_muc);
 
 void win_print_http_upload(ProfWin *window, const char *const message, char *url);
 
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index d9159e81..81928d90 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -110,6 +110,7 @@ _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *con
         _handle_error(stanza);
     }
 
+    // if muc
     if (g_strcmp0(type, STANZA_TYPE_GROUPCHAT) == 0) {
         _handle_groupchat(stanza);
     }
@@ -192,10 +193,10 @@ message_init(void)
     message->body = NULL;
     message->encrypted = NULL;
     message->plain = NULL;
-    message->enc = PROF_MSG_ENC_PLAIN;
+    message->enc = PROF_MSG_ENC_NONE;
     message->timestamp = NULL;
     message->trusted = true;
-    message->mucuser = false;
+    message->type = PROF_MSG_TYPE_UNINITIALIZED;
 
     return message;
 }
@@ -508,7 +509,7 @@ message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys,
 }
 #endif
 
-void
+char*
 message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url)
 {
     xmpp_ctx_t * const ctx = connection_get_ctx();
@@ -517,14 +518,14 @@ message_send_private(const char *const fulljid, const char *const msg, const cha
     xmpp_stanza_t *message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, fulljid, id);
     xmpp_message_set_body(message, msg);
 
-    free(id);
-
     if (oob_url) {
         stanza_attach_x_oob_url(ctx, message, oob_url);
     }
 
     _send_message_stanza(message);
     xmpp_stanza_release(message);
+
+    return id;
 }
 
 char*
@@ -831,6 +832,7 @@ _handle_groupchat(xmpp_stanza_t *const stanza)
 
     ProfMessage *message = message_init();
     message->jid = jid;
+    message->type = PROF_MSG_TYPE_MUC;
 
     if (id) {
         message->id = strdup(id);
@@ -977,7 +979,7 @@ _handle_muc_private_message(xmpp_stanza_t *const stanza)
 {
     // standard chat message, use jid without resource
     ProfMessage *message = message_init();
-    message->mucuser = TRUE;
+    message->type = PROF_MSG_TYPE_MUCPM;
 
     const gchar *from = xmpp_stanza_get_from(stanza);
     message->jid = jid_create(from);
@@ -1009,6 +1011,8 @@ _handle_muc_private_message(xmpp_stanza_t *const stanza)
     if (message->timestamp) {
         sv_ev_delayed_private_message(message);
     } else {
+        message->timestamp = g_date_time_new_now_local();
+
         sv_ev_incoming_private_message(message);
     }
 
@@ -1060,11 +1064,12 @@ _handle_carbons(xmpp_stanza_t *const stanza)
     }
 
     ProfMessage *message = message_init();
+    message->type = PROF_MSG_TYPE_CHAT;
 
     // check whether message was a MUC PM
     xmpp_stanza_t *mucuser = xmpp_stanza_get_child_by_ns(message_stanza, STANZA_NS_MUC_USER);
     if (mucuser) {
-        message->mucuser = TRUE;
+        message->type = PROF_MSG_TYPE_MUCPM;
     }
 
     // id
@@ -1179,6 +1184,7 @@ _handle_chat(xmpp_stanza_t *const stanza)
     // standard chat message, use jid without resource
     ProfMessage *message = message_init();
     message->jid = jid;
+    message->type = PROF_MSG_TYPE_CHAT;
 
     // message stanza id
     const char *id = xmpp_stanza_get_id(stanza);
@@ -1195,10 +1201,14 @@ _handle_chat(xmpp_stanza_t *const stanza)
     }
 
     if (mucuser) {
-        message->mucuser = TRUE;
+        message->type = PROF_MSG_TYPE_MUCPM;
     }
 
     message->timestamp = stanza_get_delay(stanza);
+    if (!message->timestamp) {
+        message->timestamp = g_date_time_new_now_local();
+    }
+
     if (body) {
         message->body = xmpp_stanza_get_text(body);
     }
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index 85c49e03..c83869a5 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -120,19 +120,31 @@ typedef struct disco_item_t {
 } DiscoItem;
 
 typedef enum {
-    PROF_MSG_ENC_PLAIN,
+    PROF_MSG_ENC_NONE,
     PROF_MSG_ENC_OTR,
     PROF_MSG_ENC_PGP,
     PROF_MSG_ENC_OMEMO
 } prof_enc_t;
 
+typedef enum {
+    PROF_MSG_TYPE_UNINITIALIZED,
+    // regular 1:1 chat
+    PROF_MSG_TYPE_CHAT,
+    // groupchats to whole group
+    PROF_MSG_TYPE_MUC,
+    // groupchat private message
+    PROF_MSG_TYPE_MUCPM
+} prof_msg_type_t;
+
 typedef struct prof_message_t {
    Jid *jid;
+   /* regular <message id=""> */
    char *id;
    /* </origin-id> XEP-0359 */
    char *originid;
    /* <replace id> XEP-0308 LMC */
    char *replace_id;
+   /* for MAM we will need archive_id (also XEP-0359) (see database.c)
    /* The raw body from xmpp message, either plaintext or OTR encrypted text */
    char *body;
    /* The encrypted message as for PGP */
@@ -142,7 +154,7 @@ typedef struct prof_message_t {
    GDateTime *timestamp;
    prof_enc_t enc;
    gboolean trusted;
-   gboolean mucuser;
+   prof_msg_type_t type;
 } ProfMessage;
 
 void session_init(void);
@@ -175,7 +187,7 @@ char* message_send_chat(const char *const barejid, const char *const msg, const
 char* message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt, const char *const replace_id);
 char* message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt, const char *const replace_id);
 char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt, gboolean muc, const char *const replace_id);
-void message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url);
+char* message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url);
 char* message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url, const char *const replace_id);
 void message_send_groupchat_subject(const char *const roomjid, const char *const subject);
 void message_send_inactive(const char *const jid);