about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--src/event/client_events.c38
-rw-r--r--src/event/server_events.c245
-rw-r--r--src/event/server_events.h17
-rw-r--r--src/log.c24
-rw-r--r--src/log.h10
-rw-r--r--src/omemo/store.c2
-rw-r--r--src/omemo/store.h2
-rw-r--r--src/otr/otr.c4
-rw-r--r--src/plugins/api.c10
-rw-r--r--src/ui/chatwin.c30
-rw-r--r--src/ui/mucwin.c35
-rw-r--r--src/ui/privwin.c8
-rw-r--r--src/ui/ui.h10
-rw-r--r--src/ui/window.c27
-rw-r--r--src/ui/window.h7
-rw-r--r--src/xmpp/message.c250
-rw-r--r--src/xmpp/message.h28
-rw-r--r--tests/unittests/log/stub_log.c8
-rw-r--r--tests/unittests/ui/stub_ui.c6
20 files changed, 418 insertions, 344 deletions
diff --git a/Makefile.am b/Makefile.am
index 7e3e7fdf..76351f22 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -101,6 +101,7 @@ unittest_sources = \
 	src/event/client_events.c src/event/client_events.h \
 	src/ui/tray.h src/ui/tray.c \
 	tests/unittests/xmpp/stub_xmpp.c \
+	tests/unittests/xmpp/stub_message.c \
 	tests/unittests/ui/stub_ui.c tests/unittests/ui/stub_ui.h \
 	tests/unittests/log/stub_log.c \
 	tests/unittests/config/stub_accounts.c \
diff --git a/src/event/client_events.c b/src/event/client_events.c
index 3a5d1e86..9368931b 100644
--- a/src/event/client_events.c
+++ b/src/event/client_events.c
@@ -143,14 +143,14 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
     if (chatwin->pgp_send) {
         char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt);
         chat_log_pgp_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt);
         free(id);
     } else {
         gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt);
         if (!handled) {
             char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
             chat_log_msg_out(chatwin->barejid, plugin_msg);
-            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
+            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt);
             free(id);
         }
     }
@@ -170,7 +170,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
     if (!handled) {
         char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
         chat_log_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt);
         free(id);
     }
 
@@ -188,12 +188,12 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
     if (chatwin->pgp_send) {
         char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt);
         chat_log_pgp_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt);
         free(id);
     } else {
         char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
         chat_log_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt);
         free(id);
     }
 
@@ -211,12 +211,12 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
     if (chatwin->is_omemo) {
         char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE);
         chat_log_omemo_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt);
         free(id);
     } else {
         char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
         chat_log_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt);
         free(id);
     }
 
@@ -234,14 +234,14 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
     if (chatwin->is_omemo) {
         char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE);
         chat_log_omemo_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt);
         free(id);
     } else {
         gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt);
         if (!handled) {
             char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
             chat_log_msg_out(chatwin->barejid, plugin_msg);
-            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
+            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt);
             free(id);
         }
     }
@@ -260,17 +260,17 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
     if (chatwin->is_omemo) {
         char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE);
         chat_log_omemo_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt);
         free(id);
     } else if (chatwin->pgp_send) {
         char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt);
         chat_log_pgp_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt);
         free(id);
     } else {
         char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
         chat_log_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt);
         free(id);
     }
 
@@ -288,19 +288,19 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
     if (chatwin->is_omemo) {
         char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE);
         chat_log_omemo_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OMEMO, request_receipt);
         free(id);
     } else if (chatwin->pgp_send) {
         char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt);
         chat_log_pgp_msg_out(chatwin->barejid, plugin_msg);
-        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP, request_receipt);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PGP, request_receipt);
         free(id);
     } else {
         gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt);
         if (!handled) {
             char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
             chat_log_msg_out(chatwin->barejid, plugin_msg);
-            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
+            chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt);
             free(id);
         }
     }
@@ -318,7 +318,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
 #ifndef HAVE_OMEMO
     char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
     chat_log_msg_out(chatwin->barejid, plugin_msg);
-    chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
+    chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_PLAIN, request_receipt);
     free(id);
 
     plugins_post_chat_message_send(chatwin->barejid, plugin_msg);
@@ -341,12 +341,12 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const
     if (mucwin->is_omemo) {
         char *id = omemo_on_message_send((ProfWin *)mucwin, plugin_msg, FALSE, TRUE);
         groupchat_log_omemo_msg_out(mucwin->roomjid, plugin_msg);
-        mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_OMEMO);
+        mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_OMEMO);
         free(id);
     } else {
         char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url);
         groupchat_log_msg_out(mucwin->roomjid, plugin_msg);
-        mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_PLAIN);
+        mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_PLAIN);
         free(id);
     }
 
@@ -358,7 +358,7 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const
 #ifndef HAVE_OMEMO
     char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url);
     groupchat_log_msg_out(mucwin->roomjid, plugin_msg);
-    mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_PLAIN);
+    mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_ENC_PLAIN);
     free(id);
 
     plugins_post_room_message_send(mucwin->roomjid, plugin_msg);
diff --git a/src/event/server_events.c b/src/event/server_events.c
index 9818a762..60d2dbf9 100644
--- a/src/event/server_events.c
+++ b/src/event/server_events.c
@@ -267,10 +267,9 @@ sv_ev_room_subject(const char *const room, const char *const nick, const char *c
 }
 
 void
-sv_ev_room_history(const char *const room_jid, const char *const nick,
-    GDateTime *timestamp, const char *const message, int flags)
+sv_ev_room_history(prof_message_t *message)
 {
-    ProfMucWin *mucwin = wins_get_muc(room_jid);
+    ProfMucWin *mucwin = wins_get_muc(message->jid->barejid);
     if (mucwin) {
         // if this is the first successful connection
         if (_success_connections_counter == 1) {
@@ -282,34 +281,35 @@ sv_ev_room_history(const char *const room_jid, const char *const nick,
             mucwin->last_msg_timestamp  = g_date_time_new_now_local();
         }
 
-        gboolean younger = g_date_time_compare(mucwin->last_msg_timestamp, timestamp) < 0 ? TRUE : FALSE;
+        gboolean younger = g_date_time_compare(mucwin->last_msg_timestamp, message->timestamp) < 0 ? TRUE : FALSE;
         if (_success_connections_counter == 1 || younger ) {
-            mucwin_history(mucwin, nick, timestamp, message);
+            mucwin_history(mucwin, message->jid->resourcepart, message->timestamp, message->plain);
         }
     }
 }
 
 void
-sv_ev_room_message(const char *const room_jid, const char *const nick, const char *const message, const char *const id, int flags)
+sv_ev_room_message(prof_message_t *message)
 {
-    ProfMucWin *mucwin = wins_get_muc(room_jid);
+    ProfMucWin *mucwin = wins_get_muc(message->jid->barejid);
     if (!mucwin) {
         return;
     }
 
     char *mynick = muc_nick(mucwin->roomjid);
 
-    if (flags & MSG_ENC_OMEMO) {
-        groupchat_log_omemo_msg_in(room_jid, nick, message);
+    if (message->enc == PROF_MSG_ENC_OMEMO) {
+        groupchat_log_omemo_msg_in(message->jid->barejid, message->jid->resourcepart, message->plain);
     } else {
-        groupchat_log_msg_in(room_jid, nick, message);
+        groupchat_log_msg_in(message->jid->barejid, message->jid->resourcepart, message->plain);
     }
 
-    char *new_message = plugins_pre_room_message_display(room_jid, nick, message);
+    char *old_plain = message->plain;
+    message->plain = plugins_pre_room_message_display(message->jid->barejid, message->jid->resourcepart, message->plain);
 
     gboolean whole_word = prefs_get_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD);
     gboolean case_sensitive = prefs_get_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE);
-    char *message_search = case_sensitive ? strdup(new_message) : g_utf8_strdown(new_message, -1);
+    char *message_search = case_sensitive ? strdup(message->plain) : g_utf8_strdown(message->plain, -1);
     char *mynick_search = case_sensitive ? strdup(mynick) : g_utf8_strdown(mynick, -1);
 
     GSList *mentions = NULL;
@@ -318,13 +318,9 @@ sv_ev_room_message(const char *const room_jid, const char *const nick, const cha
     g_free(message_search);
     g_free(mynick_search);
 
-    GList *triggers = prefs_message_get_triggers(new_message);
+    GList *triggers = prefs_message_get_triggers(message->plain);
 
-    if (flags & MSG_ENC_OMEMO) {
-        mucwin_incoming_msg(mucwin, nick, new_message, id, mentions, triggers, flags);
-    } else {
-        mucwin_incoming_msg(mucwin, nick, new_message, id, mentions, triggers, PROF_MSG_PLAIN);
-    }
+    mucwin_incoming_msg(mucwin, message, mentions, triggers);
 
     g_slist_free(mentions);
 
@@ -337,7 +333,7 @@ sv_ev_room_message(const char *const room_jid, const char *const nick, const cha
         is_current = TRUE;
         status_bar_active(num, WIN_MUC, mucwin->roomjid);
 
-        if ((g_strcmp0(mynick, nick) != 0) && (prefs_get_boolean(PREF_BEEP))) {
+        if ((g_strcmp0(mynick, message->jid->resourcepart) != 0) && (prefs_get_boolean(PREF_BEEP))) {
             beep();
         }
 
@@ -345,11 +341,11 @@ sv_ev_room_message(const char *const room_jid, const char *const nick, const cha
     } else {
         status_bar_new(num, WIN_MUC, mucwin->roomjid);
 
-        if ((g_strcmp0(mynick, nick) != 0) && (prefs_get_boolean(PREF_FLASH))) {
+        if ((g_strcmp0(mynick, message->jid->resourcepart) != 0) && (prefs_get_boolean(PREF_FLASH))) {
             flash();
         }
 
-        cons_show_incoming_room_message(nick, mucwin->roomjid, num, mention, triggers, mucwin->unread);
+        cons_show_incoming_room_message(message->jid->resourcepart, mucwin->roomjid, num, mention, triggers, mucwin->unread);
 
         mucwin->unread++;
 
@@ -367,9 +363,9 @@ sv_ev_room_message(const char *const room_jid, const char *const nick, const cha
     }
     mucwin->last_msg_timestamp  = g_date_time_new_now_local();
 
-    if (prefs_do_room_notify(is_current, mucwin->roomjid, mynick, nick, new_message, mention, triggers != NULL)) {
+    if (prefs_do_room_notify(is_current, mucwin->roomjid, mynick, message->jid->resourcepart, message->plain, mention, triggers != NULL)) {
         Jid *jidp = jid_create(mucwin->roomjid);
-        notify_room_message(nick, jidp->localpart, num, new_message);
+        notify_room_message(message->jid->resourcepart, jidp->localpart, num, message->plain);
         jid_destroy(jidp);
     }
 
@@ -379,43 +375,48 @@ sv_ev_room_message(const char *const room_jid, const char *const nick, const cha
 
     rosterwin_roster();
 
-    plugins_post_room_message_display(room_jid, nick, new_message);
-    free(new_message);
+    plugins_post_room_message_display(message->jid->barejid, message->jid->resourcepart, message->plain);
+    free(message->plain);
+    message->plain = old_plain;
 }
 
 void
-sv_ev_incoming_private_message(const char *const fulljid, char *message)
+sv_ev_incoming_private_message(prof_message_t *message)
 {
-    char *plugin_message =  plugins_pre_priv_message_display(fulljid, message);
+    char *old_plain = message->plain;
+    message->plain = plugins_pre_priv_message_display(message->jid->fulljid, message->plain);
 
-    ProfPrivateWin *privatewin = wins_get_private(fulljid);
+    ProfPrivateWin *privatewin = wins_get_private(message->jid->fulljid);
     if (privatewin == NULL) {
-        ProfWin *window = wins_new_private(fulljid);
+        ProfWin *window = wins_new_private(message->jid->fulljid);
         privatewin = (ProfPrivateWin*)window;
     }
-    privwin_incoming_msg(privatewin, plugin_message, NULL);
+    privwin_incoming_msg(privatewin, message);
 
-    plugins_post_priv_message_display(fulljid, plugin_message);
+    plugins_post_priv_message_display(message->jid->fulljid, message->plain);
 
-    free(plugin_message);
+    free(message->plain);
+    message->plain = old_plain;
     rosterwin_roster();
 }
 
 void
-sv_ev_delayed_private_message(const char *const fulljid, char *message, GDateTime *timestamp)
+sv_ev_delayed_private_message(prof_message_t *message)
 {
-    char *new_message = plugins_pre_priv_message_display(fulljid, message);
+    char *old_plain = message->plain;
+    message->plain = plugins_pre_priv_message_display(message->jid->fulljid, message->plain);
 
-    ProfPrivateWin *privatewin = wins_get_private(fulljid);
+    ProfPrivateWin *privatewin = wins_get_private(message->jid->fulljid);
     if (privatewin == NULL) {
-        ProfWin *window = wins_new_private(fulljid);
+        ProfWin *window = wins_new_private(message->jid->fulljid);
         privatewin = (ProfPrivateWin*)window;
     }
-    privwin_incoming_msg(privatewin, new_message, timestamp);
+    privwin_incoming_msg(privatewin, message);
 
-    plugins_post_priv_message_display(fulljid, new_message);
+    plugins_post_priv_message_display(message->jid->fulljid, message->plain);
 
-    free(new_message);
+    free(message->plain);
+    message->plain = old_plain;
 }
 
 void
@@ -433,12 +434,12 @@ sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message, gboolean
     if (pgp_message) {
         char *decrypted = p_gpg_decrypt(pgp_message);
         if (decrypted) {
-            chatwin_outgoing_carbon(chatwin, decrypted, PROF_MSG_PGP);
+            chatwin_outgoing_carbon(chatwin, decrypted, PROF_MSG_ENC_PGP);
         } else {
-            chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
+            chatwin_outgoing_carbon(chatwin, message, PROF_MSG_ENC_PLAIN);
         }
     } else {
-        chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
+        chatwin_outgoing_carbon(chatwin, message, PROF_MSG_ENC_PLAIN);
     }
     return;
 #endif
@@ -447,9 +448,9 @@ sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message, gboolean
 #ifndef HAVE_LIBGPGME
 #ifdef HAVE_OMEMO
     if (omemo) {
-        chatwin_outgoing_carbon(chatwin, message, PROF_MSG_OMEMO);
+        chatwin_outgoing_carbon(chatwin, message, PROF_MSG_ENC_OMEMO);
     } else {
-        chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
+        chatwin_outgoing_carbon(chatwin, message, PROF_MSG_ENC_PLAIN);
     }
     return;
 #endif
@@ -458,16 +459,16 @@ sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message, gboolean
 #ifdef HAVE_LIBGPGME
 #ifdef HAVE_OMEMO
     if (omemo) {
-        chatwin_outgoing_carbon(chatwin, message, PROF_MSG_OMEMO);
+        chatwin_outgoing_carbon(chatwin, message, PROF_MSG_ENC_OMEMO);
     } else if (pgp_message) {
         char *decrypted = p_gpg_decrypt(pgp_message);
         if (decrypted) {
-            chatwin_outgoing_carbon(chatwin, decrypted, PROF_MSG_PGP);
+            chatwin_outgoing_carbon(chatwin, decrypted, PROF_MSG_ENC_PGP);
         } else {
-            chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
+            chatwin_outgoing_carbon(chatwin, message, PROF_MSG_ENC_PLAIN);
         }
     } else {
-        chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
+        chatwin_outgoing_carbon(chatwin, message, PROF_MSG_ENC_PLAIN);
     }
     return;
 #endif
@@ -475,24 +476,27 @@ sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message, gboolean
 
 #ifndef HAVE_LIBGPGME
 #ifndef HAVE_OMEMO
-    chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
+    chatwin_outgoing_carbon(chatwin, message, PROF_MSG_ENC_PLAIN);
 #endif
 #endif
 }
 
 #ifdef HAVE_LIBGPGME
 static void
-_sv_ev_incoming_pgp(ProfChatWin *chatwin, gboolean new_win, char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp)
+_sv_ev_incoming_pgp(ProfChatWin *chatwin, gboolean new_win, prof_message_t *message)
 {
-    char *decrypted = p_gpg_decrypt(pgp_message);
-    if (decrypted) {
-        chatwin_incoming_msg(chatwin, resource, decrypted, timestamp, new_win, PROF_MSG_PGP);
-        chat_log_pgp_msg_in(barejid, decrypted, timestamp);
+    message->plain = p_gpg_decrypt(message->encrypted);
+    if (message->plain) {
+        message->enc = PROF_MSG_ENC_PGP;
+        chatwin_incoming_msg(chatwin, message, new_win);
+        chat_log_pgp_msg_in(message);
         chatwin->pgp_recv = TRUE;
-        p_gpg_free_decrypted(decrypted);
+        p_gpg_free_decrypted(message->plain);
+        message->plain = NULL;
     } else {
-        chatwin_incoming_msg(chatwin, resource, message, timestamp, new_win, PROF_MSG_PLAIN);
-        chat_log_msg_in(barejid, message, timestamp);
+        message->enc = PROF_MSG_ENC_PLAIN;
+        chatwin_incoming_msg(chatwin, message, new_win);
+        chat_log_msg_in(message);
         chatwin->pgp_recv = FALSE;
     }
 }
@@ -500,19 +504,23 @@ _sv_ev_incoming_pgp(ProfChatWin *chatwin, gboolean new_win, char *barejid, char
 
 #ifdef HAVE_LIBOTR
 static void
-_sv_ev_incoming_otr(ProfChatWin *chatwin, gboolean new_win, char *barejid, char *resource, char *message, GDateTime *timestamp)
+_sv_ev_incoming_otr(ProfChatWin *chatwin, gboolean new_win, prof_message_t *message)
 {
     gboolean decrypted = FALSE;
-    char *otr_res = otr_on_message_recv(barejid, resource, message, &decrypted);
-    if (otr_res) {
+    message->plain = otr_on_message_recv(message->jid->barejid, message->jid->resourcepart, message->body, &decrypted);
+    if (message->plain) {
         if (decrypted) {
-            chatwin_incoming_msg(chatwin, resource, otr_res, timestamp, new_win, PROF_MSG_OTR);
+            message->enc = PROF_MSG_ENC_OTR;
             chatwin->pgp_send = FALSE;
         } else {
-            chatwin_incoming_msg(chatwin, resource, otr_res, timestamp, new_win, PROF_MSG_PLAIN);
+            message->enc = PROF_MSG_ENC_PLAIN;
         }
-        chat_log_otr_msg_in(barejid, otr_res, decrypted, timestamp);
-        otr_free_message(otr_res);
+
+        chatwin_incoming_msg(chatwin, message, new_win);
+
+        chat_log_otr_msg_in(message);
+        otr_free_message(message->plain);
+        message->plain = NULL;
         chatwin->pgp_recv = FALSE;
     }
 }
@@ -520,35 +528,36 @@ _sv_ev_incoming_otr(ProfChatWin *chatwin, gboolean new_win, char *barejid, char
 
 #ifdef HAVE_OMEMO
 static void
-_sv_ev_incoming_omemo(ProfChatWin *chatwin, gboolean new_win, char *barejid, char *resource, char *message, GDateTime *timestamp)
+_sv_ev_incoming_omemo(ProfChatWin *chatwin, gboolean new_win, prof_message_t *message)
 {
-    chatwin_incoming_msg(chatwin, resource, message, timestamp, new_win, PROF_MSG_OMEMO);
-    chat_log_omemo_msg_in(barejid, message, timestamp);
+    chatwin_incoming_msg(chatwin, message, new_win);
+    chat_log_omemo_msg_in(message);
     chatwin->pgp_recv = FALSE;
 }
 #endif
 
 static void
-_sv_ev_incoming_plain(ProfChatWin *chatwin, gboolean new_win, char *barejid, char *resource, char *message, GDateTime *timestamp)
+_sv_ev_incoming_plain(ProfChatWin *chatwin, gboolean new_win, prof_message_t *message)
 {
-    chatwin_incoming_msg(chatwin, resource, message, timestamp, new_win, PROF_MSG_PLAIN);
-    chat_log_msg_in(barejid, message, timestamp);
+    message->plain = strdup(message->body);
+    chatwin_incoming_msg(chatwin, message, new_win);
+    chat_log_msg_in(message);
     chatwin->pgp_recv = FALSE;
 }
 
 void
-sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp, gboolean omemo)
+sv_ev_incoming_message(prof_message_t *message)
 {
     gboolean new_win = FALSE;
-    ProfChatWin *chatwin = wins_get_chat(barejid);
+    ProfChatWin *chatwin = wins_get_chat(message->jid->barejid);
     if (!chatwin) {
-        ProfWin *window = wins_new_chat(barejid);
+        ProfWin *window = wins_new_chat(message->jid->barejid);
         chatwin = (ProfChatWin*)window;
         new_win = TRUE;
 
 #ifdef HAVE_OMEMO
-        if (omemo_automatic_start(barejid)) {
-            omemo_start_session(barejid);
+        if (omemo_automatic_start(message->jid->barejid)) {
+            omemo_start_session(message->jid->barejid);
             chatwin->is_omemo = TRUE;
         }
 #endif
@@ -558,14 +567,14 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
 #ifdef HAVE_LIBOTR
 #ifdef HAVE_LIBGPGME
 #ifndef HAVE_OMEMO
-    if (pgp_message) {
+    if (message->encrypted) {
         if (chatwin->is_otr) {
             win_println((ProfWin*)chatwin, THEME_DEFAULT, '-', "PGP encrypted message received whilst in OTR session.");
-        } else { // PROF_ENC_NONE, PROF_ENC_PGP
-            _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, timestamp);
+        } else {
+            _sv_ev_incoming_pgp(chatwin, new_win, message);
         }
     } else {
-        _sv_ev_incoming_otr(chatwin, new_win, barejid, resource, message, timestamp);
+        _sv_ev_incoming_otr(chatwin, new_win, message);
     }
     rosterwin_roster();
     return;
@@ -577,7 +586,7 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
 #ifdef HAVE_LIBOTR
 #ifndef HAVE_LIBGPGME
 #ifndef HAVE_OMEMO
-    _sv_ev_incoming_otr(chatwin, new_win, barejid, resource, message, timestamp);
+    _sv_ev_incoming_otr(chatwin, new_win, message);
     rosterwin_roster();
     return;
 #endif
@@ -588,10 +597,10 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
 #ifndef HAVE_LIBOTR
 #ifdef HAVE_LIBGPGME
 #ifndef HAVE_OMEMO
-    if (pgp_message) {
-        _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, timestamp);
+    if (message->encrypted) {
+        _sv_ev_incoming_pgp(chatwin, new_win, message);
     } else {
-        _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, timestamp);
+        _sv_ev_incoming_plain(chatwin, new_win, message);
     }
     rosterwin_roster();
     return;
@@ -603,16 +612,16 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
 #ifdef HAVE_LIBOTR
 #ifdef HAVE_LIBGPGME
 #ifdef HAVE_OMEMO
-    if (pgp_message) {
+    if (message->encrypted) {
         if (chatwin->is_otr) {
             win_println((ProfWin*)chatwin, THEME_DEFAULT, '-', "PGP encrypted message received whilst in OTR session.");
-        } else { // PROF_ENC_NONE, PROF_ENC_PGP
-            _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, timestamp);
+        } else {
+            _sv_ev_incoming_pgp(chatwin, new_win, message);
         }
-    } else if (omemo) {
-        _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp);
+    } else if (message->enc == PROF_MSG_ENC_OMEMO) {
+        _sv_ev_incoming_omemo(chatwin, new_win, message);
     } else {
-        _sv_ev_incoming_otr(chatwin, new_win, barejid, resource, message, timestamp);
+        _sv_ev_incoming_otr(chatwin, new_win, message);
     }
     rosterwin_roster();
     return;
@@ -624,10 +633,10 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
 #ifdef HAVE_LIBOTR
 #ifndef HAVE_LIBGPGME
 #ifdef HAVE_OMEMO
-    if (omemo) {
-        _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp);
+    if (message->enc == PROF_MSG_ENC_OMEMO) {
+        _sv_ev_incoming_omemo(chatwin, new_win, message);
     } else {
-        _sv_ev_incoming_otr(chatwin, new_win, barejid, resource, message, timestamp);
+        _sv_ev_incoming_otr(chatwin, new_win, message);
     }
     rosterwin_roster();
     return;
@@ -639,12 +648,12 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
 #ifndef HAVE_LIBOTR
 #ifdef HAVE_LIBGPGME
 #ifdef HAVE_OMEMO
-    if (pgp_message) {
-        _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, timestamp);
-    } else if (omemo) {
-        _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp);
+    if (message->encrypted) {
+        _sv_ev_incoming_pgp(chatwin, new_win, message);
+    } else if (message->enc == PROF_MSG_ENC_OMEMO) {
+        _sv_ev_incoming_omemo(chatwin, new_win, message);
     } else {
-        _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, timestamp);
+        _sv_ev_incoming_plain(chatwin, new_win, message);
     }
     rosterwin_roster();
     return;
@@ -656,10 +665,10 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
 #ifndef HAVE_LIBOTR
 #ifndef HAVE_LIBGPGME
 #ifdef HAVE_OMEMO
-    if (omemo) {
-        _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp);
+    if (message->enc == PROF_MSG_ENC_OMEMO) {
+        _sv_ev_incoming_omemo(chatwin, new_win, message);
     } else {
-        _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, timestamp);
+        _sv_ev_incoming_plain(chatwin, new_win, message);
     }
     rosterwin_roster();
     return;
@@ -671,7 +680,7 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
 #ifndef HAVE_LIBOTR
 #ifndef HAVE_LIBGPGME
 #ifndef HAVE_OMEMO
-    _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, timestamp);
+    _sv_ev_incoming_plain(chatwin, new_win, message);
     rosterwin_roster();
     return;
 #endif
@@ -680,18 +689,18 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
 }
 
 void
-sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message, gboolean omemo)
+sv_ev_incoming_carbon(prof_message_t *message)
 {
     gboolean new_win = FALSE;
-    ProfChatWin *chatwin = wins_get_chat(barejid);
+    ProfChatWin *chatwin = wins_get_chat(message->jid->barejid);
     if (!chatwin) {
-        ProfWin *window = wins_new_chat(barejid);
+        ProfWin *window = wins_new_chat(message->jid->barejid);
         chatwin = (ProfChatWin*)window;
         new_win = TRUE;
 
 #ifdef HAVE_OMEMO
-        if (omemo_automatic_start(barejid)) {
-            omemo_start_session(barejid);
+        if (omemo_automatic_start(message->jid->barejid)) {
+            omemo_start_session(message->jid->barejid);
             chatwin->is_omemo = TRUE;
         }
 #endif
@@ -699,10 +708,10 @@ sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_me
 
 #ifdef HAVE_LIBGPGME
 #ifndef HAVE_OMEMO
-    if (pgp_message) {
-        _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, NULL);
+    if (message->encrypted) {
+        _sv_ev_incoming_pgp(chatwin, new_win, message);
     } else {
-        _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL);
+        _sv_ev_incoming_plain(chatwin, new_win, message);
     }
     rosterwin_roster();
     return;
@@ -711,12 +720,12 @@ sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_me
 
 #ifdef HAVE_LIBGPGME
 #ifdef HAVE_OMEMO
-    if (pgp_message) {
-        _sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, NULL);
-    } else if (omemo) {
-        _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, NULL);
+    if (message->encrypted) {
+        _sv_ev_incoming_pgp(chatwin, new_win, message);
+    } else if (message->enc == PROF_MSG_ENC_OMEMO) {
+        _sv_ev_incoming_omemo(chatwin, new_win, message);
     } else {
-        _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL);
+        _sv_ev_incoming_plain(chatwin, new_win, message);
     }
     rosterwin_roster();
     return;
@@ -725,10 +734,10 @@ sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_me
 
 #ifndef HAVE_LIBGPGME
 #ifdef HAVE_OMEMO
-    if (omemo) {
-        _sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, NULL);
+    if (message->enc == PROF_MSG_ENC_OMEMO) {
+        _sv_ev_incoming_omemo(chatwin, new_win, message);
     } else {
-        _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL);
+        _sv_ev_incoming_plain(chatwin, new_win, message);
     }
     rosterwin_roster();
     return;
@@ -737,7 +746,7 @@ sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_me
 
 #ifndef HAVE_LIBGPGME
 #ifndef HAVE_OMEMO
-    _sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL);
+    _sv_ev_incoming_plain(chatwin, new_win, message);
     rosterwin_roster();
     return;
 #endif
diff --git a/src/event/server_events.h b/src/event/server_events.h
index 74016ceb..f0e24e54 100644
--- a/src/event/server_events.h
+++ b/src/event/server_events.h
@@ -36,6 +36,7 @@
 #define EVENT_SERVER_EVENTS_H
 
 #include "xmpp/xmpp.h"
+#include "xmpp/message.h"
 
 void sv_ev_login_account_success(char *account_name, gboolean secured);
 void sv_ev_lost_connection(void);
@@ -45,13 +46,11 @@ void sv_ev_room_invite(jabber_invite_t invite_type,
     const char *const reason, const char *const password);
 void sv_ev_room_broadcast(const char *const room_jid, const char *const message);
 void sv_ev_room_subject(const char *const room, const char *const nick, const char *const subject);
-void sv_ev_room_history(const char *const room_jid, const char *const nick,
-    GDateTime *timestamp, const char *const message, gboolean omemo);
-void sv_ev_room_message(const char *const room_jid, const char *const nick,
-    const char *const message, const char *const id, gboolean omemo);
-void sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp, gboolean omemo);
-void sv_ev_incoming_private_message(const char *const fulljid, char *message);
-void sv_ev_delayed_private_message(const char *const fulljid, char *message, GDateTime *timestamp);
+void sv_ev_room_history(prof_message_t *message);
+void sv_ev_room_message(prof_message_t *message);
+void sv_ev_incoming_message(prof_message_t *message);
+void sv_ev_incoming_private_message(prof_message_t *message);
+void sv_ev_delayed_private_message(prof_message_t *message);
 void sv_ev_typing(char *barejid, char *resource);
 void sv_ev_paused(char *barejid, char *resource);
 void sv_ev_inactive(char *barejid, char *resource);
@@ -73,8 +72,8 @@ void sv_ev_room_occupent_kicked(const char *const room, const char *const nick,
 void sv_ev_room_banned(const char *const room, const char *const actor, const char *const reason);
 void sv_ev_room_occupent_banned(const char *const room, const char *const nick, const char *const actor,
     const char *const reason);
-void sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message, gboolean omemo);
-void sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message, gboolean omemo);
+void sv_ev_outgoing_carbon(prof_message_t *message);
+void sv_ev_incoming_carbon(prof_message_t *message);
 void sv_ev_xmpp_stanza(const char *const msg);
 void sv_ev_muc_self_online(const char *const room, const char *const nick, gboolean config_required,
     const char *const role, const char *const affiliation, const char *const actor, const char *const reason,
diff --git a/src/log.c b/src/log.c
index 685e2835..732809bd 100644
--- a/src/log.c
+++ b/src/log.c
@@ -329,16 +329,16 @@ chat_log_omemo_msg_out(const char *const barejid, const char *const msg)
 }
 
 void
-chat_log_otr_msg_in(const char *const barejid, const char *const msg, gboolean was_decrypted, GDateTime *timestamp)
+chat_log_otr_msg_in(prof_message_t *message)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         const char *jid = connection_get_fulljid();
         Jid *jidp = jid_create(jid);
         char *pref_otr_log = prefs_get_string(PREF_OTR_LOG);
-        if (!was_decrypted || (strcmp(pref_otr_log, "on") == 0)) {
-            _chat_log_chat(jidp->barejid, barejid, msg, PROF_IN_LOG, timestamp);
+        if (message->enc == PROF_MSG_ENC_PLAIN || (strcmp(pref_otr_log, "on") == 0)) {
+            _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp);
         } else if (strcmp(pref_otr_log, "redact") == 0) {
-            _chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_IN_LOG, timestamp);
+            _chat_log_chat(jidp->barejid, message->jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp);
         }
         prefs_free_string(pref_otr_log);
         jid_destroy(jidp);
@@ -346,16 +346,16 @@ chat_log_otr_msg_in(const char *const barejid, const char *const msg, gboolean w
 }
 
 void
-chat_log_pgp_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp)
+chat_log_pgp_msg_in(prof_message_t *message)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         const char *jid = connection_get_fulljid();
         Jid *jidp = jid_create(jid);
         char *pref_pgp_log = prefs_get_string(PREF_PGP_LOG);
         if (strcmp(pref_pgp_log, "on") == 0) {
-            _chat_log_chat(jidp->barejid, barejid, msg, PROF_IN_LOG, timestamp);
+            _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp);
         } else if (strcmp(pref_pgp_log, "redact") == 0) {
-            _chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_IN_LOG, timestamp);
+            _chat_log_chat(jidp->barejid, message->jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp);
         }
         prefs_free_string(pref_pgp_log);
         jid_destroy(jidp);
@@ -363,16 +363,16 @@ chat_log_pgp_msg_in(const char *const barejid, const char *const msg, GDateTime
 }
 
 void
-chat_log_omemo_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp)
+chat_log_omemo_msg_in(prof_message_t *message)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         const char *jid = connection_get_fulljid();
         Jid *jidp = jid_create(jid);
         char *pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
         if (strcmp(pref_omemo_log, "on") == 0) {
-            _chat_log_chat(jidp->barejid, barejid, msg, PROF_IN_LOG, timestamp);
+            _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp);
         } else if (strcmp(pref_omemo_log, "redact") == 0) {
-            _chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_IN_LOG, timestamp);
+            _chat_log_chat(jidp->barejid, message->jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp);
         }
         prefs_free_string(pref_omemo_log);
         jid_destroy(jidp);
@@ -380,12 +380,12 @@ chat_log_omemo_msg_in(const char *const barejid, const char *const msg, GDateTim
 }
 
 void
-chat_log_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp)
+chat_log_msg_in(prof_message_t *message)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         const char *jid = connection_get_fulljid();
         Jid *jidp = jid_create(jid);
-        _chat_log_chat(jidp->barejid, barejid, msg, PROF_IN_LOG, timestamp);
+        _chat_log_chat(jidp->barejid, message->jid->barejid, message->plain, PROF_IN_LOG, message->timestamp);
         jid_destroy(jidp);
     }
 }
diff --git a/src/log.h b/src/log.h
index 1f45545c..e6bea8dc 100644
--- a/src/log.h
+++ b/src/log.h
@@ -37,6 +37,8 @@
 
 #include <glib.h>
 
+#include "xmpp/message.h"
+
 // log levels
 typedef enum {
     PROF_LEVEL_DEBUG,
@@ -73,10 +75,10 @@ void chat_log_otr_msg_out(const char *const barejid, const char *const msg);
 void chat_log_pgp_msg_out(const char *const barejid, const char *const msg);
 void chat_log_omemo_msg_out(const char *const barejid, const char *const msg);
 
-void chat_log_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp);
-void chat_log_otr_msg_in(const char *const barejid, const char *const msg, gboolean was_decrypted, GDateTime *timestamp);
-void chat_log_pgp_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp);
-void chat_log_omemo_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp);
+void chat_log_msg_in(prof_message_t *message);
+void chat_log_otr_msg_in(prof_message_t *message);
+void chat_log_pgp_msg_in(prof_message_t *message);
+void chat_log_omemo_msg_in(prof_message_t *message);
 
 void chat_log_close(void);
 GSList* chat_log_get_previous(const gchar *const login, const gchar *const recipient);
diff --git a/src/omemo/store.c b/src/omemo/store.c
index 51d5e67a..61f39c8b 100644
--- a/src/omemo/store.c
+++ b/src/omemo/store.c
@@ -414,7 +414,7 @@ is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data,
 
 
     if (identity_key_store->recv) {
-        identity_key_store->trusted = ret;
+        identity_key_store->trusted_msg = ret;
         return 1;
     } else {
         return ret;
diff --git a/src/omemo/store.h b/src/omemo/store.h
index 628b1df9..a1524c62 100644
--- a/src/omemo/store.h
+++ b/src/omemo/store.h
@@ -49,7 +49,7 @@ typedef struct {
    uint32_t registration_id;
    GHashTable *trusted;
    bool recv;
-   bool trusted;
+   bool trusted_msg;
 } identity_key_store_t;
 
 GHashTable * session_store_new(void);
diff --git a/src/otr/otr.c b/src/otr/otr.c
index 47d5adf6..32109cf2 100644
--- a/src/otr/otr.c
+++ b/src/otr/otr.c
@@ -347,7 +347,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);
             chat_log_otr_msg_out(chatwin->barejid, message);
-            chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_OTR, request_receipt);
+            chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_ENC_OTR, request_receipt);
             otr_free_message(encrypted);
             free(id);
             return TRUE;
@@ -367,7 +367,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);
-        chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_PLAIN, request_receipt);
+        chatwin_outgoing_msg(chatwin, message, id, PROF_MSG_ENC_PLAIN, request_receipt);
         chat_log_msg_out(chatwin->barejid, message);
         free(otr_tagged_msg);
         free(id);
diff --git a/src/plugins/api.c b/src/plugins/api.c
index fc47f193..2761b254 100644
--- a/src/plugins/api.c
+++ b/src/plugins/api.c
@@ -471,12 +471,18 @@ api_settings_int_set(const char *const group, const char *const key, int value)
 }
 
 void
-api_incoming_message(const char *const barejid, const char *const resource, const char *const message)
+api_incoming_message(const char *const barejid, const char *const resource, const char *const plain)
 {
-    sv_ev_incoming_message((char*)barejid, (char*)resource, (char*)message, NULL, NULL, FALSE);
+    prof_message_t *message = message_init();
+    message->jid = jid_create_from_bare_and_resource(barejid, resource);
+    message->plain = strdup(plain);
+
+    sv_ev_incoming_message(message);
 
     // TODO handle all states
     sv_ev_activity((char*)barejid, (char*)resource, FALSE);
+
+    message_free(message);
 }
 
 void
diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c
index 8e17df8e..f6c1097e 100644
--- a/src/ui/chatwin.c
+++ b/src/ui/chatwin.c
@@ -241,23 +241,24 @@ chatwin_recipient_gone(ProfChatWin *chatwin)
 }
 
 void
-chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, const char *const message, GDateTime *timestamp, gboolean win_created, prof_enc_t enc_mode)
+chatwin_incoming_msg(ProfChatWin *chatwin, prof_message_t *message, gboolean win_created)
 {
     assert(chatwin != NULL);
+    char *old_plain = message->plain;
 
-    char *plugin_message = plugins_pre_chat_message_display(chatwin->barejid, resource, message);
+    message->plain = plugins_pre_chat_message_display(chatwin->barejid, message->jid->resourcepart, message->plain);
 
     ProfWin *window = (ProfWin*)chatwin;
     int num = wins_get_num(window);
 
-    char *display_name = roster_get_msg_display_name(chatwin->barejid, resource);
+    char *display_name = roster_get_msg_display_name(chatwin->barejid, message->jid->resourcepart);
 
     gboolean is_current = wins_is_current(window);
     gboolean notify = prefs_do_chat_notify(is_current);
 
     // currently viewing chat window with sender
     if (wins_is_current(window)) {
-        win_print_incoming(window, timestamp, display_name, plugin_message, enc_mode);
+        win_print_incoming(window, display_name, message);
         title_bar_set_typing(FALSE);
         status_bar_active(num, WIN_CHAT, chatwin->barejid);
 
@@ -277,14 +278,14 @@ chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, const cha
         }
 
         // show users status first, when receiving message via delayed delivery
-        if (timestamp && win_created) {
+        if (message->timestamp && win_created) {
             PContact pcontact = roster_get_contact(chatwin->barejid);
             if (pcontact) {
                 win_show_contact(window, pcontact);
             }
         }
 
-        win_print_incoming(window, timestamp, display_name, plugin_message, enc_mode);
+        win_print_incoming(window, display_name, message);
     }
 
     if (prefs_get_boolean(PREF_BEEP)) {
@@ -292,14 +293,15 @@ chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, const cha
     }
 
     if (notify) {
-        notify_message(display_name, num, plugin_message);
+        notify_message(display_name, num, message->plain);
     }
 
     free(display_name);
 
-    plugins_post_chat_message_display(chatwin->barejid, resource, plugin_message);
+    plugins_post_chat_message_display(chatwin->barejid, message->jid->resourcepart, message->plain);
 
-    free(plugin_message);
+    free(message->plain);
+    message->plain = old_plain;
 }
 
 void
@@ -311,11 +313,11 @@ chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id,
     char enc_char = '-';
     if (chatwin->outgoing_char) {
         enc_char = chatwin->outgoing_char[0];
-    } else if (enc_mode == PROF_MSG_OTR) {
+    } else if (enc_mode == PROF_MSG_ENC_OTR) {
         enc_char = prefs_get_otr_char();
-    } else if (enc_mode == PROF_MSG_PGP) {
+    } else if (enc_mode == PROF_MSG_ENC_PGP) {
         enc_char = prefs_get_pgp_char();
-    } else if (enc_mode == PROF_MSG_OMEMO) {
+    } else if (enc_mode == PROF_MSG_ENC_OMEMO) {
         enc_char = prefs_get_omemo_char();
     }
 
@@ -332,9 +334,9 @@ chatwin_outgoing_carbon(ProfChatWin *chatwin, const char *const message, prof_en
     assert(chatwin != NULL);
 
     char enc_char = '-';
-    if (enc_mode == PROF_MSG_PGP) {
+    if (enc_mode == PROF_MSG_ENC_PGP) {
         enc_char = prefs_get_pgp_char();
-    } else if (enc_mode == PROF_MSG_OMEMO) {
+    } else if (enc_mode == PROF_MSG_ENC_OMEMO) {
         enc_char = prefs_get_omemo_char();
     }
 
diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c
index c110a2af..e2c9abdc 100644
--- a/src/ui/mucwin.c
+++ b/src/ui/mucwin.c
@@ -383,7 +383,7 @@ mucwin_history(ProfMucWin *mucwin, const char *const nick, GDateTime *timestamp,
 }
 
 static void
-_mucwin_print_mention(ProfWin *window, const char *const message, const char *const from, const char *const mynick, GSList *mentions, const char *const ch)
+_mucwin_print_mention(ProfWin *window, const char *const message, const char *const from, const char *const mynick, GSList *mentions, const char *const ch, int flags)
 {
     int last_pos = 0;
     int pos = 0;
@@ -393,11 +393,11 @@ _mucwin_print_mention(ProfWin *window, const char *const message, const char *co
 
         char *before_str = g_strndup(message + last_pos, pos - last_pos);
         if (strncmp(before_str, "/me ", 4) == 0) {
-            win_print_them(window, THEME_ROOMMENTION, *ch, "");
+            win_print_them(window, THEME_ROOMMENTION, *ch, flags, "");
             win_append_highlight(window, THEME_ROOMMENTION, "*%s ", from);
             win_append_highlight(window, THEME_ROOMMENTION, "%s", before_str + 4);
         } else {
-            win_print_them(window, THEME_ROOMMENTION, *ch, from);
+            win_print_them(window, THEME_ROOMMENTION, *ch, flags, from);
             win_append_highlight(window, THEME_ROOMMENTION, "%s", before_str);
         }
         g_free(before_str);
@@ -512,11 +512,11 @@ mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *c
     char ch = '-';
     if (mucwin->message_char) {
         ch = mucwin->message_char[0];
-    } else if (enc_mode == PROF_MSG_OTR) {
+    } else if (enc_mode == PROF_MSG_ENC_OTR) {
         ch = prefs_get_otr_char();
-    } else if (enc_mode == PROF_MSG_PGP) {
+    } else if (enc_mode == PROF_MSG_ENC_PGP) {
         ch = prefs_get_pgp_char();
-    } else if (enc_mode == PROF_MSG_OMEMO) {
+    } else if (enc_mode == PROF_MSG_ENC_OMEMO) {
         ch = prefs_get_omemo_char();
     }
 
@@ -524,36 +524,41 @@ mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *c
 }
 
 void
-mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, const char *const id, GSList *mentions, GList *triggers, prof_enc_t enc_mode)
+mucwin_incoming_msg(ProfMucWin *mucwin, prof_message_t *message, GSList *mentions, GList *triggers)
 {
     assert(mucwin != NULL);
+    int flags = 0;
 
-    if (id && g_hash_table_remove(mucwin->sent_messages, id)) {
+    if (message->id && g_hash_table_remove(mucwin->sent_messages, message->id)) {
         /* Ignore reflection messages */
         return;
     }
 
+    if (!message->trusted) {
+        flags |= NO_TRUST;
+    }
+
     ProfWin *window = (ProfWin*)mucwin;
     char *mynick = muc_nick(mucwin->roomjid);
 
     char ch = '-';
     if (mucwin->message_char) {
         ch = mucwin->message_char[0];
-    } else if (enc_mode == PROF_MSG_OTR) {
+    } else if (message->enc == PROF_MSG_ENC_OTR) {
         ch = prefs_get_otr_char();
-    } else if (enc_mode == PROF_MSG_PGP) {
+    } else if (message->enc == PROF_MSG_ENC_PGP) {
         ch = prefs_get_pgp_char();
-    } else if (enc_mode == PROF_MSG_OMEMO) {
+    } else if (message->enc == PROF_MSG_ENC_OMEMO) {
         ch = prefs_get_omemo_char();
     }
 
     if (g_slist_length(mentions) > 0) {
-        _mucwin_print_mention(window, message, nick, mynick, mentions, &ch);
+        _mucwin_print_mention(window, message->plain, message->jid->resourcepart, mynick, mentions, &ch, flags);
     } else if (triggers) {
-        win_print_them(window, THEME_ROOMTRIGGER, ch, nick);
-        _mucwin_print_triggers(window, message, triggers);
+        win_print_them(window, THEME_ROOMTRIGGER, ch, flags, message->jid->resourcepart);
+        _mucwin_print_triggers(window, message->plain, triggers);
     } else {
-        win_println_them_message(window, ch, nick, "%s", message);
+        win_println_them_message(window, ch, flags, message->jid->resourcepart, "%s", message->plain);
     }
 }
 
diff --git a/src/ui/privwin.c b/src/ui/privwin.c
index 459c3e80..8fc8400a 100644
--- a/src/ui/privwin.c
+++ b/src/ui/privwin.c
@@ -43,7 +43,7 @@
 #include "ui/window_list.h"
 
 void
-privwin_incoming_msg(ProfPrivateWin *privatewin, const char *const message, GDateTime *timestamp)
+privwin_incoming_msg(ProfPrivateWin *privatewin, prof_message_t *message)
 {
     assert(privatewin != NULL);
 
@@ -60,7 +60,7 @@ privwin_incoming_msg(ProfPrivateWin *privatewin, const char *const message, GDat
 
     // currently viewing chat window with sender
     if (wins_is_current(window)) {
-        win_print_incoming(window, timestamp, jidp->resourcepart, message, PROF_MSG_PLAIN);
+        win_print_incoming(window, jidp->resourcepart, message);
         title_bar_set_typing(FALSE);
         status_bar_active(num, WIN_PRIVATE, privatewin->fulljid);
 
@@ -68,7 +68,7 @@ privwin_incoming_msg(ProfPrivateWin *privatewin, const char *const message, GDat
     } else {
         status_bar_new(num, WIN_PRIVATE, privatewin->fulljid);
         cons_show_incoming_private_message(jidp->resourcepart, jidp->barejid, num, privatewin->unread);
-        win_print_incoming(window, timestamp, jidp->resourcepart, message, PROF_MSG_PLAIN);
+        win_print_incoming(window, jidp->resourcepart, message);
 
         privatewin->unread++;
 
@@ -82,7 +82,7 @@ privwin_incoming_msg(ProfPrivateWin *privatewin, const char *const message, GDat
     }
 
     if (notify) {
-        notify_message(jidp->resourcepart, num, message);
+        notify_message(jidp->resourcepart, num, message->plain);
     }
 
     jid_destroy(jidp);
diff --git a/src/ui/ui.h b/src/ui/ui.h
index 51d80b34..edce5924 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -41,6 +41,7 @@
 #include "config/account.h"
 #include "command/cmd_funcs.h"
 #include "ui/win_types.h"
+#include "xmpp/message.h"
 #include "xmpp/muc.h"
 
 #ifdef HAVE_LIBOTR
@@ -52,6 +53,8 @@
 #define NO_EOL          4
 #define NO_COLOUR_FROM  8
 #define NO_COLOUR_DATE  16
+#define NO_TRUST        32
+
 
 // core UI
 void ui_init(void);
@@ -117,8 +120,7 @@ gboolean ui_win_has_unsaved_form(int num);
 
 // Chat window
 ProfChatWin* chatwin_new(const char *const barejid);
-void chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, const char *const message,
-    GDateTime *timestamp, gboolean win_created, prof_enc_t enc_mode);
+void chatwin_incoming_msg(ProfChatWin *chatwin, prof_message_t *message, gboolean win_created);
 void chatwin_receipt_received(ProfChatWin *chatwin, const char *const id);
 void chatwin_recipient_gone(ProfChatWin *chatwin);
 void chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id, prof_enc_t enc_mode,
@@ -157,7 +159,7 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char
 void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char *const presence);
 void mucwin_history(ProfMucWin *mucwin, const char *const nick, GDateTime *timestamp, const char *const message);
 void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode);
-void mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, const char *const id, GSList *mentions, GList *triggers, prof_enc_t enc_mode);
+void mucwin_incoming_msg(ProfMucWin *mucwin, prof_message_t *message, GSList *mentions, GList *triggers);
 void mucwin_subject(ProfMucWin *mucwin, const char *const nick, const char *const subject);
 void mucwin_requires_config(ProfMucWin *mucwin);
 void mucwin_info(ProfMucWin *mucwin);
@@ -195,7 +197,7 @@ void mucwin_set_message_char(ProfMucWin *mucwin, const char *const ch);
 void mucwin_unset_message_char(ProfMucWin *mucwin);
 
 // MUC private chat window
-void privwin_incoming_msg(ProfPrivateWin *privatewin, const char *const message, GDateTime *timestamp);
+void privwin_incoming_msg(ProfPrivateWin *privatewin, prof_message_t *message);
 void privwin_outgoing_msg(ProfPrivateWin *privwin, const char *const message);
 void privwin_message_occupant_offline(ProfPrivateWin *privwin);
 
diff --git a/src/ui/window.c b/src/ui/window.c
index 90508917..d4bd3241 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -1044,14 +1044,13 @@ win_show_status_string(ProfWin *window, const char *const from,
 }
 
 void
-win_print_incoming(ProfWin *window, GDateTime *timestamp,
-    const char *const from, const char *const message, prof_enc_t enc_mode)
+win_print_incoming(ProfWin *window, const char *const from, prof_message_t *message)
 {
     char enc_char = '-';
     int flags = NO_ME;
 
-    if (!trusted) {
-        flags != NO_TRUST;
+    if (!message->trusted) {
+        flags |= NO_TRUST;
     }
 
     switch (window->type)
@@ -1061,18 +1060,18 @@ win_print_incoming(ProfWin *window, GDateTime *timestamp,
             ProfChatWin *chatwin = (ProfChatWin*)window;
             if (chatwin->incoming_char) {
                 enc_char = chatwin->incoming_char[0];
-            } else if (enc_mode == PROF_MSG_OTR) {
+            } else if (message->enc == PROF_MSG_ENC_OTR) {
                 enc_char = prefs_get_otr_char();
-            } else if (enc_mode == PROF_MSG_PGP) {
+            } else if (message->enc == PROF_MSG_ENC_PGP) {
                 enc_char = prefs_get_pgp_char();
-            } else if (enc_mode == PROF_MSG_OMEMO) {
+            } else if (message->enc == PROF_MSG_ENC_OMEMO) {
                 enc_char = prefs_get_omemo_char();
             }
-            _win_printf(window, enc_char, 0, timestamp, flags, THEME_TEXT_THEM, from, "%s", message);
+            _win_printf(window, enc_char, 0, message->timestamp, flags, THEME_TEXT_THEM, from, "%s", message->plain);
             break;
         }
         case WIN_PRIVATE:
-            _win_printf(window, '-', 0, timestamp, flags, THEME_TEXT_THEM, from, "%s", message);
+            _win_printf(window, '-', 0, message->timestamp, flags, THEME_TEXT_THEM, from, "%s", message->plain);
             break;
         default:
             assert(FALSE);
@@ -1081,13 +1080,13 @@ win_print_incoming(ProfWin *window, GDateTime *timestamp,
 }
 
 void
-win_print_them(ProfWin *window, theme_item_t theme_item, char ch, const char *const them)
+win_print_them(ProfWin *window, theme_item_t theme_item, char ch, int flags, const char *const them)
 {
-    _win_printf(window, ch, 0, NULL, NO_ME | NO_EOL, theme_item, them, "");
+    _win_printf(window, ch, 0, NULL, flags | NO_ME | NO_EOL, theme_item, them, "");
 }
 
 void
-win_println_them_message(ProfWin *window, char ch, const char *const them, const char *const message, ...)
+win_println_them_message(ProfWin *window, char ch, int flags, const char *const them, const char *const message, ...)
 {
     GDateTime *timestamp = g_date_time_new_now_local();
 
@@ -1096,9 +1095,9 @@ win_println_them_message(ProfWin *window, char ch, const char *const them, const
     GString *fmt_msg = g_string_new(NULL);
     g_string_vprintf(fmt_msg, message, arg);
 
-    buffer_append(window->layout->buffer, ch, 0, timestamp, NO_ME, THEME_TEXT_THEM, them, fmt_msg->str, NULL);
+    buffer_append(window->layout->buffer, ch, 0, timestamp, flags | NO_ME, THEME_TEXT_THEM, them, fmt_msg->str, NULL);
 
-    _win_print(window, ch, 0, timestamp, NO_ME, THEME_TEXT_THEM, them, fmt_msg->str, NULL);
+    _win_print(window, ch, 0, timestamp, flags | NO_ME, THEME_TEXT_THEM, them, fmt_msg->str, NULL);
     inp_nonblocking(TRUE);
     g_date_time_unref(timestamp);
 
diff --git a/src/ui/window.h b/src/ui/window.h
index 17ebd226..09dd06bb 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -60,13 +60,12 @@ void win_show_status_string(ProfWin *window, const char *const from,
     GDateTime *last_activity, const char *const pre,
     const char *const default_show);
 
-void win_print_them(ProfWin *window, theme_item_t theme_item, char ch, const char *const them);
-void win_println_them_message(ProfWin *window, char ch, const char *const them, const char *const message, ...);
+void win_print_them(ProfWin *window, theme_item_t theme_item, char ch, int flags, const char *const them);
+void win_println_them_message(ProfWin *window, char ch, int flags, const char *const them, const char *const message, ...);
 void win_println_me_message(ProfWin *window, char ch, const char *const me, const char *const message, ...);
 
 void win_print_outgoing(ProfWin *window, const char ch, const char *const message, ...);
-void win_print_incoming(ProfWin *window, GDateTime *timestamp,
-    const char *const from, const char *const message, prof_enc_t enc_mode);
+void win_print_incoming(ProfWin *window, const char *const from, prof_message_t *message);
 void win_print_history(ProfWin *window, GDateTime *timestamp, const char *const message, ...);
 
 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 424c59ca..19657da0 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -75,6 +75,7 @@ typedef struct p_message_handle_t {
 } ProfMessageHandler;
 
 static int _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata);
+static void _private_chat_handler(xmpp_stanza_t *const stanza);
 
 static void _handle_error(xmpp_stanza_t *const stanza);
 static void _handle_groupchat(xmpp_stanza_t *const stanza);
@@ -178,6 +179,53 @@ message_handlers_init(void)
     pubsub_event_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
 }
 
+prof_message_t *
+message_init(void)
+{
+    prof_message_t *message = malloc(sizeof(prof_message_t));
+
+    message->jid = NULL;
+    message->id = NULL;
+    message->body = NULL;
+    message->encrypted = NULL;
+    message->plain = NULL;
+    message->enc = PROF_MSG_ENC_PLAIN;
+    message->timestamp = NULL;
+
+    return message;
+}
+
+void
+message_free(prof_message_t *message)
+{
+    xmpp_ctx_t *ctx = connection_get_ctx();
+    if (message->jid) {
+        jid_destroy(message->jid);
+    }
+
+    if (message->id) {
+        xmpp_free(ctx, message->id);
+    }
+
+    if (message->body) {
+        xmpp_free(ctx, message->body);
+    }
+
+    if (message->encrypted) {
+        xmpp_free(ctx, message->encrypted);
+    }
+
+    if (message->plain) {
+        free(message->plain);
+    }
+
+    if (message->timestamp) {
+        g_date_time_unref(message->timestamp);
+    }
+
+    free(message);
+}
+
 void
 message_handlers_clear(void)
 {
@@ -696,8 +744,6 @@ static void
 _handle_groupchat(xmpp_stanza_t *const stanza)
 {
     xmpp_ctx_t *ctx = connection_get_ctx();
-    int flags = 0;
-    char *message = NULL;
 
     const char *id = xmpp_stanza_get_id(stanza);
 
@@ -712,9 +758,10 @@ _handle_groupchat(xmpp_stanza_t *const stanza)
     // handle room subject
     xmpp_stanza_t *subject = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_SUBJECT);
     if (subject) {
-        message = xmpp_stanza_get_text(subject);
-        sv_ev_room_subject(jid->barejid, jid->resourcepart, message);
-        xmpp_free(ctx, message);
+        char *subject_text;
+        subject_text = xmpp_stanza_get_text(subject);
+        sv_ev_room_subject(jid->barejid, jid->resourcepart, subject_text);
+        xmpp_free(ctx, subject_text);
 
         jid_destroy(jid);
         return;
@@ -722,14 +769,15 @@ _handle_groupchat(xmpp_stanza_t *const stanza)
 
     // handle room broadcasts
     if (!jid->resourcepart) {
-        message = xmpp_message_get_body(stanza);
-        if (!message) {
+        char *broadcast;
+        broadcast = xmpp_message_get_body(stanza);
+        if (!broadcast) {
             jid_destroy(jid);
             return;
         }
 
-        sv_ev_room_broadcast(room_jid, message);
-        xmpp_free(ctx, message);
+        sv_ev_room_broadcast(room_jid, broadcast);
+        xmpp_free(ctx, broadcast);
 
         jid_destroy(jid);
         return;
@@ -748,37 +796,29 @@ _handle_groupchat(xmpp_stanza_t *const stanza)
         return;
     }
 
+    prof_message_t *message = message_init();
+    message->jid = jid;
+    message->id = strdup(id);
+
     // check omemo encryption
 #ifdef HAVE_OMEMO
-    gboolean trusted = FALSE;
-    message = omemo_receive_message(stanza, &trusted);
-    if (message != NULL) {
-        flags |= MSG_ENC_OMEMO;
-        if (trusted) {
-            flags |= MSG_TRUSTED;
-        }
+    message->plain = omemo_receive_message(stanza, &message->trusted);
+    if (message->plain != NULL) {
+        message->enc = PROF_MSG_ENC_OMEMO;
     }
 #endif
 
-    if (!message) {
-        message = xmpp_message_get_body(stanza);
-        if (!message) {
-            jid_destroy(jid);
-            return;
-        }
-    }
+    message->body = xmpp_message_get_body(stanza);
 
     // determine if the notifications happened whilst offline
-    GDateTime *timestamp = stanza_get_delay(stanza);
-    if (timestamp) {
-        sv_ev_room_history(jid->barejid, jid->resourcepart, timestamp, message, flags);
-        g_date_time_unref(timestamp);
+    message->timestamp = stanza_get_delay(stanza);
+    if (message->timestamp) {
+        sv_ev_room_history(message);
     } else {
-        sv_ev_room_message(jid->barejid, jid->resourcepart, message, id, flags);
+        sv_ev_room_message(message);
     }
 
-    xmpp_free(ctx, message);
-    jid_destroy(jid);
+    message_free(message);
 }
 
 void
@@ -854,31 +894,38 @@ _receipt_request_handler(xmpp_stanza_t *const stanza)
     jid_destroy(jid);
 }
 
-void
-_private_chat_handler(xmpp_stanza_t *const stanza, const char *const fulljid)
+static void
+_private_chat_handler(xmpp_stanza_t *const stanza)
 {
-    char *message = xmpp_message_get_body(stanza);
-    if (!message) {
-        return;
+    // standard chat message, use jid without resource
+    prof_message_t *message = message_init();
+
+    const gchar *from = xmpp_stanza_get_from(stanza);
+    message->jid = jid_create(from);
+
+    // check omemo encryption
+#ifdef HAVE_OMEMO
+    message->plain = omemo_receive_message(stanza, &message->trusted);
+    if (message->plain != NULL) {
+        message->enc = PROF_MSG_ENC_OMEMO;
     }
+#endif
+
+    message->timestamp = stanza_get_delay(stanza);
+    message->body = xmpp_message_get_body(stanza);
 
-    GDateTime *timestamp = stanza_get_delay(stanza);
-    if (timestamp) {
-        sv_ev_delayed_private_message(fulljid, message, timestamp);
-        g_date_time_unref(timestamp);
+    if (message->timestamp) {
+        sv_ev_delayed_private_message(message);
     } else {
-        sv_ev_incoming_private_message(fulljid, message);
+        sv_ev_incoming_private_message(message);
     }
 
-    xmpp_ctx_t *ctx = connection_get_ctx();
-    xmpp_free(ctx, message);
+    message_free(message);
 }
 
 static gboolean
 _handle_carbons(xmpp_stanza_t *const stanza)
 {
-    char *message_txt = NULL;
-    int flags = 0;
     xmpp_stanza_t *carbons = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CARBONS);
     if (!carbons) {
         return FALSE;
@@ -906,32 +953,12 @@ _handle_carbons(xmpp_stanza_t *const stanza)
         return TRUE;
     }
 
-    xmpp_stanza_t *message = xmpp_stanza_get_child_by_name(forwarded, STANZA_NAME_MESSAGE);
-    if (!message) {
+    xmpp_stanza_t *message_stanza = xmpp_stanza_get_child_by_name(forwarded, STANZA_NAME_MESSAGE);
+    if (!message_stanza) {
         log_warning("Carbon received with no message element");
         return TRUE;
     }
 
-    // check omemo encryption
-#ifdef HAVE_OMEMO
-    gboolean trusted = FALSE;
-    message_txt = omemo_receive_message(message, &trusted);
-    if (message_txt != NULL) {
-        flags != MSG_ENC_OMEMO;
-        if (trusted) {
-            flags |= MSG_TRUSTED;
-        }
-    }
-#endif
-
-    if (!message_txt) {
-        message_txt = xmpp_message_get_body(message);
-        if (!message_txt) {
-            log_warning("Carbon received with no message.");
-            return TRUE;
-        }
-    }
-
     Jid *my_jid = jid_create(connection_get_fulljid());
     const char *const stanza_from = xmpp_stanza_get_from(stanza);
     if (g_strcmp0(my_jid->barejid, stanza_from) != 0) {
@@ -939,8 +966,20 @@ _handle_carbons(xmpp_stanza_t *const stanza)
         return TRUE;
     }
 
-    const gchar *to = xmpp_stanza_get_to(message);
-    const gchar *from = xmpp_stanza_get_from(message);
+    prof_message_t *message = message_init();
+
+    // check omemo encryption
+#ifdef HAVE_OMEMO
+    message->plain = omemo_receive_message(message_stanza, &message->trusted);
+    if (message->plain != NULL) {
+        message->enc = PROF_MSG_ENC_OMEMO;
+    }
+#endif
+
+    message->body = xmpp_message_get_body(message_stanza);
+
+    const gchar *to = xmpp_stanza_get_to(message_stanza);
+    const gchar *from = xmpp_stanza_get_from(message_stanza);
 
     // happens when receive a carbon of a self sent message
     if (!to) to = from;
@@ -949,27 +988,25 @@ _handle_carbons(xmpp_stanza_t *const stanza)
     Jid *jid_to = jid_create(to);
 
     // check for pgp encrypted message
-    char *enc_message = NULL;
-    xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(message, STANZA_NS_ENCRYPTED);
+    xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(message_stanza, STANZA_NS_ENCRYPTED);
     if (x) {
-        enc_message = xmpp_stanza_get_text(x);
+        message->encrypted = xmpp_stanza_get_text(x);
     }
 
     // if we are the recipient, treat as standard incoming message
     if (g_strcmp0(my_jid->barejid, jid_to->barejid) == 0) {
-        sv_ev_incoming_carbon(jid_from->barejid, jid_from->resourcepart, message_txt, enc_message, flags);
+        jid_destroy(jid_to);
+        message->jid = jid_from;
+        sv_ev_incoming_carbon(message);
 
     // else treat as a sent message
     } else {
-        sv_ev_outgoing_carbon(jid_to->barejid, message_txt, enc_message, flags);
+        jid_destroy(jid_from);
+        message->jid = jid_to;
+        sv_ev_outgoing_carbon(message);
     }
 
-    xmpp_ctx_t *ctx = connection_get_ctx();
-    xmpp_free(ctx, message_txt);
-    xmpp_free(ctx, enc_message);
-
-    jid_destroy(jid_from);
-    jid_destroy(jid_to);
+    message_free(message);
     jid_destroy(my_jid);
 
     return TRUE;
@@ -978,7 +1015,6 @@ _handle_carbons(xmpp_stanza_t *const stanza)
 static void
 _handle_chat(xmpp_stanza_t *const stanza)
 {
-    char *message = NULL;
     // ignore if type not chat or absent
     const char *type = xmpp_stanza_get_type(stanza);
     if (!(g_strcmp0(type, "chat") == 0 || type == NULL)) {
@@ -991,14 +1027,6 @@ _handle_chat(xmpp_stanza_t *const stanza)
         return;
     }
 
-    // check omemo encryption
-    gboolean omemo = FALSE;
-    gboolean trusted = FALSE;
-#ifdef HAVE_OMEMO
-    message = omemo_receive_message(stanza, &trusted);
-    omemo = message != NULL;
-#endif
-
     // ignore handled namespaces
     xmpp_stanza_t *conf = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CONFERENCE);
     xmpp_stanza_t *captcha = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CAPTCHA);
@@ -1020,38 +1048,41 @@ _handle_chat(xmpp_stanza_t *const stanza)
 
     // private message from chat room use full jid (room/nick)
     if (muc_active(jid->barejid)) {
-        _private_chat_handler(stanza, jid->fulljid);
+        _private_chat_handler(stanza);
         jid_destroy(jid);
         return;
     }
 
     // standard chat message, use jid without resource
-    xmpp_ctx_t *ctx = connection_get_ctx();
-    GDateTime *timestamp = stanza_get_delay(stanza);
-    if (!message && body) {
-        message = xmpp_stanza_get_text(body);
+    prof_message_t *message = message_init();
+    message->jid = jid;
+
+    message->timestamp = stanza_get_delay(stanza);
+    if (body) {
+        message->body = xmpp_stanza_get_text(body);
     }
 
-    if (message) {
-        char *enc_message = NULL;
-        xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_ENCRYPTED);
-        if (x) {
-            enc_message = xmpp_stanza_get_text(x);
-        }
-        sv_ev_incoming_message(jid->barejid, jid->resourcepart, message, enc_message, timestamp, omemo, trusted);
-        xmpp_free(ctx, enc_message);
+    // check omemo encryption
+#ifdef HAVE_OMEMO
+    message->plain = omemo_receive_message(stanza, &message->trusted);
+    if (message->plain != NULL) {
+        message->enc = PROF_MSG_ENC_OMEMO;
+    }
+#endif
 
-        _receipt_request_handler(stanza);
+    xmpp_stanza_t *encrypted = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_ENCRYPTED);
+    if (encrypted) {
+        message->encrypted = xmpp_stanza_get_text(encrypted);
+    }
 
-        if (omemo) {
-            free(message);
-        } else {
-            xmpp_free(ctx, message);
-        }
+    if (message->plain || message->body || message->encrypted) {
+        sv_ev_incoming_message(message);
+
+        _receipt_request_handler(stanza);
     }
 
     // handle chat sessions and states
-    if (!timestamp && jid->resourcepart) {
+    if (!message->timestamp && jid->resourcepart) {
         gboolean gone = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL;
         gboolean typing = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_COMPOSING) != NULL;
         gboolean paused = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PAUSED) != NULL;
@@ -1071,8 +1102,7 @@ _handle_chat(xmpp_stanza_t *const stanza)
         }
     }
 
-    if (timestamp) g_date_time_unref(timestamp);
-    jid_destroy(jid);
+    message_free(message);
 }
 
 static void
diff --git a/src/xmpp/message.h b/src/xmpp/message.h
index b73406a0..f4861378 100644
--- a/src/xmpp/message.h
+++ b/src/xmpp/message.h
@@ -35,14 +35,34 @@
 #ifndef XMPP_MESSAGE_H
 #define XMPP_MESSAGE_H
 
-#define PROF_MSG_ENC_OTR     1
-#define PROF_MSG_ENC_PGP     2
-#define PROF_MSG_ENC_OMEMO   4
-#define PROF_MSG_TRUSTED     8
+#include "xmpp/xmpp.h"
+
+typedef enum {
+    PROF_MSG_ENC_PLAIN,
+    PROF_MSG_ENC_OTR,
+    PROF_MSG_ENC_PGP,
+    PROF_MSG_ENC_OMEMO
+} prof_enc_t;
+
+typedef struct {
+   Jid *jid;
+   char *id;
+   /* The raw body from xmpp message, either plaintext or OTR encrypted text */
+   char *body;
+   /* The encrypted message as for PGP */
+   char *encrypted;
+   /* The message that will be printed on screen and logs */
+   char *plain;
+   GDateTime *timestamp;
+   prof_enc_t enc;
+   gboolean trusted;
+} prof_message_t;
 
 typedef int(*ProfMessageCallback)(xmpp_stanza_t *const stanza, void *const userdata);
 typedef void(*ProfMessageFreeCallback)(void *userdata);
 
+prof_message_t *message_init(void);
+void message_free(prof_message_t *message);
 void message_handlers_init(void);
 void message_handlers_clear(void);
 void message_pubsub_event_handler_add(const char *const node, ProfMessageCallback func, ProfMessageFreeCallback free_func, void *userdata);
diff --git a/tests/unittests/log/stub_log.c b/tests/unittests/log/stub_log.c
index 2eb9faa5..fdbd4f31 100644
--- a/tests/unittests/log/stub_log.c
+++ b/tests/unittests/log/stub_log.c
@@ -60,10 +60,10 @@ void chat_log_otr_msg_out(const char * const barejid, const char * const msg) {}
 void chat_log_pgp_msg_out(const char * const barejid, const char * const msg) {}
 void chat_log_omemo_msg_out(const char *const barejid, const char *const msg) {}
 
-void chat_log_msg_in(const char * const barejid, const char * const msg, GDateTime *timestamp) {}
-void chat_log_otr_msg_in(const char * const barejid, const char * const msg, gboolean was_decrypted, GDateTime *timestamp) {}
-void chat_log_pgp_msg_in(const char * const barejid, const char * const msg, GDateTime *timestamp) {}
-void chat_log_omemo_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp) {}
+void chat_log_msg_in(prof_message_t *message) {}
+void chat_log_otr_msg_in(prof_message_t *message) {}
+void chat_log_pgp_msg_in(prof_message_t *message) {}
+void chat_log_omemo_msg_in(prof_message_t *message) {}
 
 void chat_log_close(void) {}
 GSList * chat_log_get_previous(const gchar * const login,
diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c
index 41d64d60..ecf1735c 100644
--- a/tests/unittests/ui/stub_ui.c
+++ b/tests/unittests/ui/stub_ui.c
@@ -152,10 +152,10 @@ void ui_contact_online(char *barejid, Resource *resource, GDateTime *last_activi
 }
 
 void ui_contact_typing(const char * const barejid, const char * const resource) {}
-void chatwin_incoming_msg(ProfChatWin *chatwin, const char * const resource, const char * const message, GDateTime *timestamp, gboolean win_created, prof_enc_t enc_mode) {}
+void chatwin_incoming_msg(ProfChatWin *chatwin, prof_message_t *message, gboolean win_created) {}
 void chatwin_receipt_received(ProfChatWin *chatwin, const char * const id) {}
 
-void privwin_incoming_msg(ProfPrivateWin *privatewin, const char * const message, GDateTime *timestamp) {}
+void privwin_incoming_msg(ProfPrivateWin *privatewin, prof_message_t *message) {}
 
 void ui_disconnected(void) {}
 void chatwin_recipient_gone(ProfChatWin *chatwin) {}
@@ -190,7 +190,7 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char
     const char * const affiliation, const char * const actor, const char * const reason) {}
 void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char * const presence) {}
 void mucwin_history(ProfMucWin *mucwin, const char * const nick, GDateTime *timestamp, const char * const message) {}
-void mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, const char *const id, GSList *mentions, GList *triggers, prof_enc_t enc_mode) {}
+void mucwin_incoming_msg(ProfMucWin *mucwin, prof_message_t *message, GSList *mentions, GList *triggers) {}
 void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode) {}
 void mucwin_subject(ProfMucWin *mucwin, const char * const nick, const char * const subject) {}
 void mucwin_requires_config(ProfMucWin *mucwin) {}