about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command/cmd_ac.c97
-rw-r--r--src/command/cmd_defs.c42
-rw-r--r--src/command/cmd_funcs.c216
-rw-r--r--src/command/cmd_funcs.h3
-rw-r--r--src/config/files.c24
-rw-r--r--src/config/files.h7
-rw-r--r--src/config/preferences.c13
-rw-r--r--src/config/preferences.h3
-rw-r--r--src/config/theme.c1
-rw-r--r--src/database.c30
-rw-r--r--src/event/client_events.c187
-rw-r--r--src/event/server_events.c231
-rw-r--r--src/omemo/omemo.c35
-rw-r--r--src/otr/otr.c94
-rw-r--r--src/pgp/gpg.c361
-rw-r--r--src/pgp/gpg.h16
-rw-r--r--src/ui/chatwin.c5
-rw-r--r--src/ui/mucwin.c3
-rw-r--r--src/ui/titlebar.c14
-rw-r--r--src/ui/win_types.h1
-rw-r--r--src/ui/window.c3
-rw-r--r--src/xmpp/message.c177
-rw-r--r--src/xmpp/stanza.h4
-rw-r--r--src/xmpp/xmpp.h5
24 files changed, 1035 insertions, 537 deletions
diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c
index e64da7ce..0d9d4cc5 100644
--- a/src/command/cmd_ac.c
+++ b/src/command/cmd_ac.c
@@ -78,6 +78,7 @@ static char* _otr_autocomplete(ProfWin *window, const char *const input, gboolea
 #endif
 #ifdef HAVE_LIBGPGME
 static char* _pgp_autocomplete(ProfWin *window, const char *const input, gboolean previous);
+static char* _ox_autocomplete(ProfWin *window, const char *const input, gboolean previous);
 #endif
 #ifdef HAVE_OMEMO
 static char* _omemo_autocomplete(ProfWin *window, const char *const input, gboolean previous);
@@ -224,6 +225,9 @@ static Autocomplete receipts_ac;
 static Autocomplete pgp_ac;
 static Autocomplete pgp_log_ac;
 static Autocomplete pgp_sendfile_ac;
+static Autocomplete ox_ac;
+static Autocomplete ox_log_ac;
+static Autocomplete ox_sendfile_ac;
 #endif
 static Autocomplete tls_ac;
 static Autocomplete titlebar_ac;
@@ -262,6 +266,13 @@ static Autocomplete avatar_ac;
 static Autocomplete url_ac;
 static Autocomplete executable_ac;
 
+/*!
+ * \brief Initialization of auto completion for commands.
+ *
+ * This function implements the auto completion for profanity's commands.
+ *
+ */
+
 void
 cmd_ac_init(void)
 {
@@ -843,6 +854,29 @@ cmd_ac_init(void)
     pgp_sendfile_ac = autocomplete_new();
     autocomplete_add(pgp_sendfile_ac, "on");
     autocomplete_add(pgp_sendfile_ac, "off");
+
+    // XEP-0373: OX
+    ox_ac = autocomplete_new();
+    autocomplete_add(ox_ac, "keys");
+    autocomplete_add(ox_ac, "contacts");
+    autocomplete_add(ox_ac, "start");
+    autocomplete_add(ox_ac, "end");
+    autocomplete_add(ox_ac, "log");
+    autocomplete_add(ox_ac, "char");
+    autocomplete_add(ox_ac, "sendfile");
+    autocomplete_add(ox_ac, "announce");
+    autocomplete_add(ox_ac, "discover");
+    autocomplete_add(ox_ac, "request");
+
+    pgp_log_ac = autocomplete_new();
+    autocomplete_add(ox_log_ac, "on");
+    autocomplete_add(ox_log_ac, "off");
+    autocomplete_add(ox_log_ac, "redact");
+
+    pgp_sendfile_ac = autocomplete_new();
+    autocomplete_add(ox_sendfile_ac, "on");
+    autocomplete_add(ox_sendfile_ac, "off");
+
 #endif
 
     tls_ac = autocomplete_new();
@@ -1715,6 +1749,7 @@ _cmd_ac_complete_params(ProfWin *window, const char *const input, gboolean previ
 #endif
 #ifdef HAVE_LIBGPGME
     g_hash_table_insert(ac_funcs, "/pgp",           _pgp_autocomplete);
+    g_hash_table_insert(ac_funcs, "/ox",            _ox_autocomplete);
 #endif
 #ifdef HAVE_OMEMO
     g_hash_table_insert(ac_funcs, "/omemo",         _omemo_autocomplete);
@@ -2428,6 +2463,68 @@ _pgp_autocomplete(ProfWin *window, const char *const input, gboolean previous)
 
     return NULL;
 }
+
+/*!
+ * \brief Auto completion for XEP-0373: OpenPGP for XMPP command.
+ *
+ *
+ */
+static char*
+_ox_autocomplete(ProfWin *window, const char *const input, gboolean previous)
+{
+    char *found = NULL;
+
+    jabber_conn_status_t conn_status = connection_get_status();
+
+    if (conn_status == JABBER_CONNECTED) {
+        found = autocomplete_param_with_func(input, "/ox start", roster_contact_autocomplete, previous, NULL);
+        if (found) {
+            return found;
+        }
+    }
+
+    found = autocomplete_param_with_ac(input, "/ox log", ox_log_ac, TRUE, previous);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/ox sendfile", ox_sendfile_ac, TRUE, previous);
+    if (found) {
+        return found;
+    }
+
+    gboolean result;
+    gchar **args = parse_args(input, 2, 3, &result);
+    if ((strncmp(input, "/ox", 4) == 0) && (result == TRUE)) {
+        GString *beginning = g_string_new("/ox ");
+        g_string_append(beginning, args[0]);
+        if (args[1]) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[1]);
+        }
+        found = autocomplete_param_with_func(input, beginning->str, p_gpg_autocomplete_key, previous, NULL);
+        g_string_free(beginning, TRUE);
+        if (found) {
+            g_strfreev(args);
+            return found;
+        }
+    }
+    g_strfreev(args);
+
+    if (conn_status == JABBER_CONNECTED) {
+        found = autocomplete_param_with_func(input, "/ox setkey", roster_barejid_autocomplete, previous, NULL);
+        if (found) {
+            return found;
+        }
+    }
+
+    found = autocomplete_param_with_ac(input, "/ox", ox_ac, TRUE, previous);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
 #endif
 
 #ifdef HAVE_OMEMO
diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c
index c36ff712..0e91f803 100644
--- a/src/command/cmd_defs.c
+++ b/src/command/cmd_defs.c
@@ -1691,6 +1691,48 @@ static struct cmd_t command_defs[] =
             "/pgp char P")
     },
 
+// XEP-0373: OpenPGP for XMPP
+#ifdef HAVE_LIBGPGME
+    { "/ox",
+        parse_args, 1, 3, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_ox)
+        CMD_TAGS(
+            CMD_TAG_CHAT,
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/ox keys",
+            "/ox contacts",
+            "/ox start [<contact>]",
+            "/ox end",
+            "/ox log on|off|redact",
+            "/ox char <char>",
+            "/ox sendfile on|off",
+            "/ox announce <file>",
+            "/ox discover",
+            "/ox request <jid>")
+        CMD_DESC(
+            "OpenPGP (OX) commands to manage keys, and perform PGP encryption during chat sessions. ")
+        CMD_ARGS(
+            { "keys",                     "List all keys known to the system." },
+            { "contacts",                 "Show contacts with assigned public keys." },
+            { "start [<contact>]",        "Start PGP encrypted chat, current contact will be used if not specified." },
+            { "end",                      "End PGP encrypted chat with the current recipient." },
+            { "log on|off",               "Enable or disable plaintext logging of PGP encrypted messages." },
+            { "log redact",               "Log PGP encrypted messages, but replace the contents with [redacted]. This is the default." },
+            { "char <char>",              "Set the character to be displayed next to PGP encrypted messages." },
+            { "announce <file>",          "Announce a public key by pushing it on the XMPP Server"},
+            { "discover <jid>",           "Discover public keys of a jid "},
+            { "request <jid>",            "Request public keys"},
+            { "sendfile on|off",          "Allow /sendfile to send unencrypted files while otherwise using PGP."})
+        CMD_EXAMPLES(
+            "/ox log off",
+            "/ox start odin@valhalla.edda",
+            "/ox end",
+            "/ox char X")
+    },
+#endif // HAVE_LIBGPGME
+
     { "/otr",
         parse_args, 1, 3, NULL,
         CMD_SUBFUNCS(
diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c
index 2395c622..b60c9667 100644
--- a/src/command/cmd_funcs.c
+++ b/src/command/cmd_funcs.c
@@ -2190,66 +2190,32 @@ cmd_msg(ProfWin *window, const char *const command, gchar **args)
         ui_focus_win((ProfWin*)chatwin);
 
 #ifdef HAVE_OMEMO
-#ifndef HAVE_LIBOTR
-        if (omemo_automatic_start(barejid)) {
-            omemo_start_session(barejid);
-            chatwin->is_omemo = TRUE;
-        }
-
-        if (msg) {
-            cl_ev_send_msg(chatwin, msg, NULL);
-        }
-
-        return TRUE;
-#endif
-#endif
+        gboolean is_otr_secure = FALSE;
 
-#ifdef HAVE_OMEMO
 #ifdef HAVE_LIBOTR
-        if (omemo_automatic_start(barejid) && otr_is_secure(barejid)) {
+        is_otr_secure = otr_is_secure(barejid);
+#endif // HAVE_LIBOTR
+
+        if (omemo_automatic_start(barejid) && is_otr_secure) {
             win_println(window, THEME_DEFAULT, "!", "Chat could be either OMEMO or OTR encrypted. Use '/omemo start %s' or '/otr start %s' to start a session.", usr, usr);
             return TRUE;
         } else if (omemo_automatic_start(barejid)) {
             omemo_start_session(barejid);
             chatwin->is_omemo = TRUE;
         }
+#endif // HAVE_OMEMO
 
         if (msg) {
             cl_ev_send_msg(chatwin, msg, NULL);
         } else {
-            if (otr_is_secure(barejid)) {
-                chatwin_otr_secured(chatwin, otr_is_trusted(barejid));
-            }
-        }
-
-        return TRUE;
-#endif
-#endif
-
-#ifndef HAVE_OMEMO
 #ifdef HAVE_LIBOTR
-        if (msg) {
-            cl_ev_send_msg(chatwin, msg, NULL);
-        } else {
             if (otr_is_secure(barejid)) {
                 chatwin_otr_secured(chatwin, otr_is_trusted(barejid));
             }
+#endif // HAVE_LIBOTR
         }
 
         return TRUE;
-#endif
-#endif
-
-#ifndef HAVE_OMEMO
-#ifndef HAVE_LIBOTR
-        if (msg) {
-            cl_ev_send_msg(chatwin, msg, NULL);
-        }
-
-        return TRUE;
-#endif
-#endif
-
     }
 }
 
@@ -7393,6 +7359,11 @@ cmd_pgp(ProfWin *window, const char *const command, gchar **args)
             return TRUE;
         }
 
+        if (chatwin->is_omemo) {
+            win_println(window, THEME_DEFAULT, "!", "You must disable OMEMO before starting an PGP encrypted session.");
+            return TRUE;
+        }
+
         ProfAccount *account = accounts_get_account(session_get_account_name());
         char *err_str = NULL;
         if (!p_gpg_valid_key(account->pgp_keyid, &err_str)) {
@@ -7450,6 +7421,169 @@ cmd_pgp(ProfWin *window, const char *const command, gchar **args)
 #endif
 }
 
+#ifdef HAVE_LIBGPGME
+
+/*!
+ * \brief Command for XEP-0373: OpenPGP for XMPP
+ *
+ */
+
+gboolean
+cmd_ox(ProfWin *window, const char *const command, gchar **args)
+{
+    if (args[0] == NULL) {
+        cons_bad_cmd_usage(command);
+        return TRUE;
+    }
+
+    // The '/ox keys' command - same like in pgp
+    // Should we move this to a common command
+    // e.g. '/openpgp keys'?.
+    else if (g_strcmp0(args[0], "keys") == 0) {
+        GHashTable *keys = p_gpg_list_keys();
+        if (!keys || g_hash_table_size(keys) == 0) {
+            cons_show("No keys found");
+            return TRUE;
+        }
+
+        cons_show("OpenPGP keys:");
+        GList *keylist = g_hash_table_get_keys(keys);
+        GList *curr = keylist;
+        while (curr) {
+            ProfPGPKey *key = g_hash_table_lookup(keys, curr->data);
+            cons_show("  %s", key->name);
+            cons_show("    ID          : %s", key->id);
+            char *format_fp = p_gpg_format_fp_str(key->fp);
+            cons_show("    Fingerprint : %s", format_fp);
+            free(format_fp);
+            if (key->secret) {
+                cons_show("    Type        : PUBLIC, PRIVATE");
+            } else {
+                cons_show("    Type        : PUBLIC");
+            }
+            curr = g_list_next(curr);
+        }
+        g_list_free(keylist);
+        p_gpg_free_keys(keys);
+        return TRUE;
+    }
+
+    else if (g_strcmp0(args[0], "contacts") == 0) {
+        GHashTable *keys = ox_gpg_public_keys();
+        cons_show("OpenPGP keys:");
+        GList *keylist = g_hash_table_get_keys(keys);
+        GList *curr = keylist;
+
+
+        GSList *roster_list = NULL;
+        jabber_conn_status_t conn_status = connection_get_status();
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+        } else {
+            roster_list = roster_get_contacts(ROSTER_ORD_NAME);
+        }
+
+        while (curr) {
+            ProfPGPKey *key = g_hash_table_lookup(keys, curr->data);
+            PContact contact = NULL;
+            if (roster_list) {
+                GSList *curr_c = roster_list;
+                while ( !contact && curr_c){
+                    contact = curr_c->data;
+                    const char *jid = p_contact_barejid(contact);
+                    GString* xmppuri = g_string_new("xmpp:");
+                    g_string_append(xmppuri, jid);
+                    if( g_strcmp0(key->name, xmppuri->str)) {
+                        contact = NULL;
+                    }
+                    curr_c = g_slist_next(curr_c);
+                }
+            }
+
+            if(contact) {
+                cons_show("%s - %s", key->fp, key->name);
+            } else {
+                cons_show("%s - %s (not in roster)", key->fp, key->name);
+            }
+            curr = g_list_next(curr);
+        }
+
+    } else if (g_strcmp0(args[0], "start") == 0) {
+        jabber_conn_status_t conn_status = connection_get_status();
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You must be connected to start OX encrpytion.");
+            return TRUE;
+        }
+
+        if (window->type != WIN_CHAT && args[1] == NULL) {
+            cons_show("You must be in a regular chat window to start OX encrpytion.");
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = NULL;
+
+        if (args[1]) {
+            char *contact = args[1];
+            char *barejid = roster_barejid_from_name(contact);
+            if (barejid == NULL) {
+                barejid = contact;
+            }
+
+            chatwin = wins_get_chat(barejid);
+            if (!chatwin) {
+                chatwin = chatwin_new(barejid);
+            }
+            ui_focus_win((ProfWin*)chatwin);
+        } else {
+            chatwin = (ProfChatWin*)window;
+            assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        }
+
+        if (chatwin->is_otr) {
+            win_println(window, THEME_DEFAULT, "!", "You must end the OTR session to start OX encryption.");
+            return TRUE;
+        }
+
+        if (chatwin->pgp_send) {
+            win_println(window, THEME_DEFAULT, "!", "You must end the PGP session to start OX encryption.");
+            return TRUE;
+        }
+
+        if (chatwin->is_ox) {
+            win_println(window, THEME_DEFAULT, "!", "You have already started OX encryption.");
+            return TRUE;
+        }
+
+        ProfAccount *account = accounts_get_account(session_get_account_name());
+
+        if ( !ox_is_private_key_available(account->jid) ) {
+            win_println(window, THEME_DEFAULT, "!", "No private OpenPGP found, cannot start OX encryption.");
+            account_free(account);
+            return TRUE;
+        }
+        account_free(account);
+
+        if (!ox_is_public_key_available(chatwin->barejid)) {
+            win_println(window, THEME_DEFAULT, "!", "No OX-OpenPGP key found for %s.", chatwin->barejid);
+            return TRUE;
+        }
+
+        chatwin->is_ox = TRUE;
+        win_println(window, THEME_DEFAULT, "!", "OX encryption enabled.");
+        return TRUE;
+    } else if (g_strcmp0(args[0], "push") == 0) {
+        if( args[1] ) {
+            cons_show("Push file...%s ", args[1] );
+        } else {
+            cons_show("Filename is required");
+        }
+    } else {
+        cons_show("OX not implemented");
+    }
+    return TRUE;
+}
+#endif // HAVE_LIBGPGME
+
 gboolean
 cmd_otr_char(ProfWin *window, const char *const command, gchar **args)
 {
diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h
index 4cb47113..955a2e39 100644
--- a/src/command/cmd_funcs.h
+++ b/src/command/cmd_funcs.h
@@ -107,6 +107,9 @@ gboolean cmd_msg(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_nick(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_notify(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_pgp(ProfWin *window, const char *const command, gchar **args);
+#ifdef HAVE_LIBGPGME
+gboolean cmd_ox(ProfWin *window, const char *const command, gchar **args);
+#endif // HAVE_LIBGPGME
 gboolean cmd_outtype(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_prefs(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_priority(ProfWin *window, const char *const command, gchar **args);
diff --git a/src/config/files.c b/src/config/files.c
index da2b66bb..576303fb 100644
--- a/src/config/files.c
+++ b/src/config/files.c
@@ -113,7 +113,7 @@ files_get_inputrc_file(void)
 }
 
 char*
-files_get_log_file(char *log_file)
+files_get_log_file(const char *const log_file)
 {
     gchar *xdg_data = _files_get_xdg_data_home();
     GString *logfile = g_string_new(xdg_data);
@@ -140,7 +140,7 @@ files_get_log_file(char *log_file)
 }
 
 gchar*
-files_get_config_path(char *config_base)
+files_get_config_path(const char *const config_base)
 {
     gchar *xdg_config = _files_get_xdg_config_home();
     GString *file_str = g_string_new(xdg_config);
@@ -154,7 +154,7 @@ files_get_config_path(char *config_base)
 }
 
 gchar*
-files_get_data_path(char *data_base)
+files_get_data_path(const char *const data_base)
 {
     gchar *xdg_data = _files_get_xdg_data_home();
     GString *file_str = g_string_new(xdg_data);
@@ -167,6 +167,24 @@ files_get_data_path(char *data_base)
     return result;
 }
 
+gchar*
+files_get_account_data_path(const char *const specific_dir, const char *const jid)
+{
+    gchar *data_dir = files_get_data_path(specific_dir);
+    GString *result_dir = g_string_new(data_dir);
+    g_free(data_dir);
+
+    gchar *account_dir = str_replace(jid, "@", "_at_");
+    g_string_append(result_dir, "/");
+    g_string_append(result_dir, account_dir);
+    g_free(account_dir);
+
+    gchar *result = g_strdup(result_dir->str);
+    g_string_free(result_dir, TRUE);
+
+    return result;
+}
+
 static char*
 _files_get_xdg_config_home(void)
 {
diff --git a/src/config/files.h b/src/config/files.h
index c87c4501..2a74793f 100644
--- a/src/config/files.h
+++ b/src/config/files.h
@@ -60,10 +60,11 @@
 
 void files_create_directories(void);
 
-gchar* files_get_config_path(char *config_base);
-gchar* files_get_data_path(char *data_base);
+gchar* files_get_config_path(const char *const config_base);
+gchar* files_get_data_path(const char *const data_base);
+gchar* files_get_account_data_path(const char *const specific_dir, const char *const jid);
 
-gchar* files_get_log_file(char *log_file);
+gchar* files_get_log_file(const char *const log_file);
 gchar* files_get_inputrc_file(void);
 
 #endif
diff --git a/src/config/preferences.c b/src/config/preferences.c
index 84f0302f..e46cbe45 100644
--- a/src/config/preferences.c
+++ b/src/config/preferences.c
@@ -62,6 +62,7 @@
 #define PREF_GROUP_OTR "otr"
 #define PREF_GROUP_PGP "pgp"
 #define PREF_GROUP_OMEMO "omemo"
+#define PREF_GROUP_OX "ox"
 #define PREF_GROUP_MUC "muc"
 #define PREF_GROUP_PLUGINS "plugins"
 #define PREF_GROUP_EXECUTABLES "executables"
@@ -1052,6 +1053,18 @@ prefs_set_pgp_char(char *ch)
 }
 
 char*
+prefs_get_ox_char(void)
+{
+    return _prefs_get_encryption_char("%", PREF_GROUP_OX, "ox.char");
+}
+
+gboolean
+prefs_set_ox_char(char *ch)
+{
+    return _prefs_set_encryption_char(ch, PREF_GROUP_OX, "ox.char");
+}
+
+char*
 prefs_get_omemo_char(void)
 {
     return _prefs_get_encryption_char("~", PREF_GROUP_OMEMO, "omemo.char");
diff --git a/src/config/preferences.h b/src/config/preferences.h
index 0bd10d44..e3110904 100644
--- a/src/config/preferences.h
+++ b/src/config/preferences.h
@@ -245,6 +245,9 @@ char* prefs_get_pgp_char(void);
 gboolean prefs_set_pgp_char(char *ch);
 char* prefs_get_omemo_char(void);
 gboolean prefs_set_omemo_char(char *ch);
+// XEP-0373: OpenPGP for XMPP
+char* prefs_get_ox_char(void);
+gboolean prefs_set_ox_char(char *ch);
 
 char prefs_get_roster_header_char(void);
 void prefs_set_roster_header_char(char ch);
diff --git a/src/config/theme.c b/src/config/theme.c
index e9726593..c4df93a0 100644
--- a/src/config/theme.c
+++ b/src/config/theme.c
@@ -102,6 +102,7 @@ theme_init(const char *const theme_name)
     g_hash_table_insert(defaults, strdup("titlebar.chat"),           strdup("white"));
     g_hash_table_insert(defaults, strdup("titlebar.dnd"),            strdup("white"));
     g_hash_table_insert(defaults, strdup("titlebar.xa"),             strdup("white"));
+    g_hash_table_insert(defaults, strdup("titlebar.scrolled"),       strdup("default"));
     g_hash_table_insert(defaults, strdup("statusbar.text"),          strdup("white"));
     g_hash_table_insert(defaults, strdup("statusbar.brackets"),      strdup("cyan"));
     g_hash_table_insert(defaults, strdup("statusbar.active"),        strdup("cyan"));
diff --git a/src/database.c b/src/database.c
index 3cc14d0d..3337e8df 100644
--- a/src/database.c
+++ b/src/database.c
@@ -56,31 +56,25 @@ static prof_msg_type_t _get_message_type_type(const char *const type);
 static char*
 _get_db_filename(ProfAccount *account)
 {
-    char *databasedir = files_get_data_path(DIR_DATABASE);
-    GString *basedir = g_string_new(databasedir);
-    free(databasedir);
+    gchar *database_dir = files_get_account_data_path(DIR_DATABASE, account->jid);
 
-    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);
+    int res = g_mkdir_with_parents(database_dir, S_IRWXU);
     if (res == -1) {
         char *errmsg = strerror(errno);
         if (errmsg) {
-            log_error("DATABASE: error creating directory: %s, %s", basedir->str, errmsg);
+            log_error("DATABASE: error creating directory: %s, %s", database_dir, errmsg);
         } else {
-            log_error("DATABASE: creating directory: %s", basedir->str);
+            log_error("DATABASE: creating directory: %s", database_dir);
         }
-        g_string_free(basedir, TRUE);
+        g_free(database_dir);
         return NULL;
     }
 
-    g_string_append(basedir, "/chatlog.db");
-    char *result = strdup(basedir->str);
-    g_string_free(basedir, TRUE);
+    GString *chatlog_filename = g_string_new(database_dir);
+    g_string_append(chatlog_filename, "/chatlog.db");
+    gchar *result = g_strdup(chatlog_filename->str);
+    g_string_free(chatlog_filename, TRUE);
+    g_free(database_dir);
 
     return result;
 }
@@ -288,6 +282,8 @@ static prof_msg_type_t _get_message_type_type(const char *const type) {
 
 static const char* _get_message_enc_str(prof_enc_t enc) {
     switch (enc) {
+    case PROF_MSG_ENC_OX:
+        return "ox";
     case PROF_MSG_ENC_PGP:
         return "pgp";
     case PROF_MSG_ENC_OTR:
@@ -298,7 +294,7 @@ static const char* _get_message_enc_str(prof_enc_t enc) {
         return "none";
     }
 
-    return "none";
+    return "none"; // do not return none - return NULL
 }
 
 static void
diff --git a/src/event/client_events.c b/src/event/client_events.c
index 96098ae0..15261760 100644
--- a/src/event/client_events.c
+++ b/src/event/client_events.c
@@ -131,181 +131,36 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
         replace_id = chatwin->last_msg_id;
     }
 
-// OTR suported, PGP supported, OMEMO unsupported
-#ifdef HAVE_LIBOTR
-#ifdef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    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 {
-        gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt, replace_id);
-        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);
-            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);
-    free(plugin_msg);
-    return;
-#endif
-#endif
-#endif
-
-// OTR supported, PGP unsupported, OMEMO unsupported
-#ifdef HAVE_LIBOTR
-#ifndef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt, replace_id);
-    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);
-        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);
-    }
-
-    plugins_post_chat_message_send(chatwin->barejid, plugin_msg);
-    free(plugin_msg);
-    return;
-#endif
-#endif
-#endif
-
-// OTR unsupported, PGP supported, OMEMO unsupported
-#ifndef HAVE_LIBOTR
-#ifdef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    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);
-        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);
-    free(plugin_msg);
-    return;
-#endif
-#endif
-#endif
-
-// OTR unsupported, PGP unsupported, OMEMO supported
-#ifndef HAVE_LIBOTR
-#ifndef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
     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);
-        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);
-    free(plugin_msg);
-    return;
-#endif
-#endif
-#endif
-
-// OTR supported, PGP unsupported, OMEMO supported
-#ifdef HAVE_LIBOTR
-#ifndef HAVE_LIBGPGME
 #ifdef HAVE_OMEMO
-    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 {
-        gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt, replace_id);
-        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);
-            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);
-    free(plugin_msg);
-    return;
-#endif
-#endif
 #endif
-
-// OTR unsupported, PGP supported, OMEMO supported
-#ifndef HAVE_LIBOTR
+    } else if (chatwin->is_ox) {
 #ifdef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
-    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);
+        // XEP-0373: OpenPGP for XMPP
+        char *id = message_send_chat_ox(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);
-        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);
+        log_database_add_outgoing_chat(id, chatwin->barejid, plugin_msg, replace_id, PROF_MSG_ENC_OX);
+        chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_ENC_OX, request_receipt, replace_id);
         free(id);
-    }
-
-    plugins_post_chat_message_send(chatwin->barejid, plugin_msg);
-    free(plugin_msg);
-    return;
 #endif
-#endif
-#endif
-
-// OTR supported, PGP supported, OMEMO supported
-#ifdef HAVE_LIBOTR
-#ifdef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
-    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) {
+#ifdef HAVE_LIBGPGME
         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);
+#endif
     } else {
-        gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt, replace_id);
+        gboolean handled = FALSE;
+#ifdef HAVE_LIBOTR
+         handled = otr_on_message_send(chatwin, plugin_msg, request_receipt, replace_id);
+#endif
         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);
@@ -318,26 +173,6 @@ cl_ev_send_msg_correct(ProfChatWin *chatwin, const char *const msg, const char *
     plugins_post_chat_message_send(chatwin->barejid, plugin_msg);
     free(plugin_msg);
     return;
-#endif
-#endif
-#endif
-
-// OTR unsupported, PGP unsupported, OMEMO unsupported
-#ifndef HAVE_LIBOTR
-#ifndef HAVE_LIBGPGME
-#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);
-    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);
-    free(plugin_msg);
-    return;
-#endif
-#endif
-#endif
 }
 
 void
diff --git a/src/event/server_events.c b/src/event/server_events.c
index 44fef42c..137c6cd9 100644
--- a/src/event/server_events.c
+++ b/src/event/server_events.c
@@ -457,49 +457,10 @@ sv_ev_outgoing_carbon(ProfMessage *message)
         log_database_add_incoming(message);
     }
 
-#ifdef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    if (message->encrypted) {
-        message->plain = p_gpg_decrypt(message->encrypted);
-        if (message->plain) {
-            message->enc = PROF_MSG_ENC_PGP;
-            chatwin_outgoing_carbon(chatwin, message);
-        } else {
-            if (!message->body) {
-                log_error("Couldn't decrypt GPG message and body was empty");
-                return;
-            }
-            message->enc = PROF_MSG_ENC_NONE;
-            message->plain = strdup(message->body);
-            chatwin_outgoing_carbon(chatwin, message);
-        }
-    } else {
-        message->enc = PROF_MSG_ENC_NONE;
-        message->plain = strdup(message->body);
-        chatwin_outgoing_carbon(chatwin, message);
-    }
-    return;
-#endif
-#endif
-
-#ifndef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
-    if (message->enc == PROF_MSG_ENC_OMEMO) {
-        chatwin_outgoing_carbon(chatwin, message);
-    } else {
-        message->enc = PROF_MSG_ENC_NONE;
-        message->plain = strdup(message->body);
-        chatwin_outgoing_carbon(chatwin, message);
-    }
-    return;
-#endif
-#endif
-
-#ifdef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
     if (message->enc == PROF_MSG_ENC_OMEMO) {
         chatwin_outgoing_carbon(chatwin, message);
     } else if (message->encrypted) {
+#ifdef HAVE_LIBGPGME
         message->plain = p_gpg_decrypt(message->encrypted);
         if (message->plain) {
             message->enc = PROF_MSG_ENC_PGP;
@@ -513,30 +474,20 @@ sv_ev_outgoing_carbon(ProfMessage *message)
             message->plain = strdup(message->body);
             chatwin_outgoing_carbon(chatwin, message);
         }
+#endif
     } else {
         message->enc = PROF_MSG_ENC_NONE;
         message->plain = strdup(message->body);
         chatwin_outgoing_carbon(chatwin, message);
     }
     return;
-#endif
-#endif
 
-#ifndef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    if (message->body) {
-        message->enc = PROF_MSG_ENC_NONE;
-        message->plain = strdup(message->body);
-        chatwin_outgoing_carbon(chatwin, message);
-    }
-#endif
-#endif
 }
 
-#ifdef HAVE_LIBGPGME
 static void
 _sv_ev_incoming_pgp(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message, gboolean logit)
 {
+#ifdef HAVE_LIBGPGME
     message->plain = p_gpg_decrypt(message->encrypted);
     if (message->plain) {
         message->enc = PROF_MSG_ENC_PGP;
@@ -562,13 +513,29 @@ _sv_ev_incoming_pgp(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message
         chat_log_msg_in(message);
         chatwin->pgp_recv = FALSE;
     }
+#endif
 }
+
+static void
+_sv_ev_incoming_ox(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message, gboolean logit)
+{
+#ifdef HAVE_LIBGPGME
+        //_clean_incoming_message(message);
+        chatwin_incoming_msg(chatwin, message, new_win);
+        log_database_add_incoming(message);
+        if (logit) {
+            chat_log_pgp_msg_in(message);
+        }
+        chatwin->pgp_recv = TRUE;
+        //p_gpg_free_decrypted(message->plain);
+        message->plain = NULL;
 #endif
+}
 
-#ifdef HAVE_LIBOTR
 static void
 _sv_ev_incoming_otr(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message)
 {
+#ifdef HAVE_LIBOTR
     gboolean decrypted = FALSE;
     message->plain = otr_on_message_recv(message->from_jid->barejid, message->from_jid->resourcepart, message->body, &decrypted);
     if (message->plain) {
@@ -588,13 +555,13 @@ _sv_ev_incoming_otr(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message
         message->plain = NULL;
         chatwin->pgp_recv = FALSE;
     }
-}
 #endif
+}
 
-#ifdef HAVE_OMEMO
 static void
 _sv_ev_incoming_omemo(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message, gboolean logit)
 {
+#ifdef HAVE_OMEMO
     _clean_incoming_message(message);
     chatwin_incoming_msg(chatwin, message, new_win);
     log_database_add_incoming(message);
@@ -602,8 +569,8 @@ _sv_ev_incoming_omemo(ProfChatWin *chatwin, gboolean new_win, ProfMessage *messa
         chat_log_omemo_msg_in(message);
     }
     chatwin->pgp_recv = FALSE;
-}
 #endif
+}
 
 static void
 _sv_ev_incoming_plain(ProfChatWin *chatwin, gboolean new_win, ProfMessage *message, gboolean logit)
@@ -653,56 +620,9 @@ sv_ev_incoming_message(ProfMessage *message)
 #endif
     }
 
-// OTR suported, PGP supported, OMEMO unsupported
-#ifdef HAVE_LIBOTR
-#ifdef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    if (message->encrypted) {
-        if (chatwin->is_otr) {
-            win_println((ProfWin*)chatwin, THEME_DEFAULT, "-", "PGP encrypted message received whilst in OTR session.");
-        } else {
-            _sv_ev_incoming_pgp(chatwin, new_win, message, TRUE);
-        }
-    } else {
-        _sv_ev_incoming_otr(chatwin, new_win, message);
-    }
-    rosterwin_roster();
-    return;
-#endif
-#endif
-#endif
-
-// OTR supported, PGP unsupported, OMEMO unsupported
-#ifdef HAVE_LIBOTR
-#ifndef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    _sv_ev_incoming_otr(chatwin, new_win, message);
-    rosterwin_roster();
-    return;
-#endif
-#endif
-#endif
-
-// OTR unsupported, PGP supported, OMEMO unsupported
-#ifndef HAVE_LIBOTR
-#ifdef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    if (message->encrypted) {
-        _sv_ev_incoming_pgp(chatwin, new_win, message, TRUE);
-    } else {
-        _sv_ev_incoming_plain(chatwin, new_win, message, TRUE);
-    }
-    rosterwin_roster();
-    return;
-#endif
-#endif
-#endif
-
-// OTR suported, PGP supported, OMEMO supported
-#ifdef HAVE_LIBOTR
-#ifdef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
-    if (message->encrypted) {
+    if( message->enc == PROF_MSG_ENC_OX) {
+	 _sv_ev_incoming_ox(chatwin, new_win, message, TRUE);
+    } else if (message->encrypted) {
         if (chatwin->is_otr) {
             win_println((ProfWin*)chatwin, THEME_DEFAULT, "-", "PGP encrypted message received whilst in OTR session.");
         } else {
@@ -715,67 +635,7 @@ sv_ev_incoming_message(ProfMessage *message)
     }
     rosterwin_roster();
     return;
-#endif
-#endif
-#endif
-
-// OTR supported, PGP unsupported, OMEMO supported
-#ifdef HAVE_LIBOTR
-#ifndef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
-    if (message->enc == PROF_MSG_ENC_OMEMO) {
-        _sv_ev_incoming_omemo(chatwin, new_win, message, TRUE);
-    } else {
-        _sv_ev_incoming_otr(chatwin, new_win, message);
-    }
-    rosterwin_roster();
-    return;
-#endif
-#endif
-#endif
-
-// OTR unsupported, PGP supported, OMEMO supported
-#ifndef HAVE_LIBOTR
-#ifdef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
-    if (message->encrypted) {
-        _sv_ev_incoming_pgp(chatwin, new_win, message, TRUE);
-    } else if (message->enc == PROF_MSG_ENC_OMEMO) {
-        _sv_ev_incoming_omemo(chatwin, new_win, message, TRUE);
-    } else {
-        _sv_ev_incoming_plain(chatwin, new_win, message, TRUE);
-    }
-    rosterwin_roster();
-    return;
-#endif
-#endif
-#endif
-
-// OTR unsupported, PGP unsupported, OMEMO supported
-#ifndef HAVE_LIBOTR
-#ifndef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
-    if (message->enc == PROF_MSG_ENC_OMEMO) {
-        _sv_ev_incoming_omemo(chatwin, new_win, message, TRUE);
-    } else {
-        _sv_ev_incoming_plain(chatwin, new_win, message, TRUE);
-    }
-    rosterwin_roster();
-    return;
-#endif
-#endif
-#endif
 
-// OTR unsupported, PGP unsupported, OMEMO unsupported
-#ifndef HAVE_LIBOTR
-#ifndef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    _sv_ev_incoming_plain(chatwin, new_win, message, TRUE);
-    rosterwin_roster();
-    return;
-#endif
-#endif
-#endif
 }
 
 void
@@ -796,21 +656,9 @@ sv_ev_incoming_carbon(ProfMessage *message)
 #endif
     }
 
-#ifdef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    if (message->encrypted) {
-        _sv_ev_incoming_pgp(chatwin, new_win, message, FALSE);
-    } else {
-        _sv_ev_incoming_plain(chatwin, new_win, message, FALSE);
-    }
-    rosterwin_roster();
-    return;
-#endif
-#endif
-
-#ifdef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
-    if (message->encrypted) {
+    if (message->enc == PROF_MSG_ENC_OX) {
+        _sv_ev_incoming_ox(chatwin, new_win, message, FALSE);
+    } else if (message->encrypted) {
         _sv_ev_incoming_pgp(chatwin, new_win, message, FALSE);
     } else if (message->enc == PROF_MSG_ENC_OMEMO) {
         _sv_ev_incoming_omemo(chatwin, new_win, message, FALSE);
@@ -819,28 +667,7 @@ sv_ev_incoming_carbon(ProfMessage *message)
     }
     rosterwin_roster();
     return;
-#endif
-#endif
-
-#ifndef HAVE_LIBGPGME
-#ifdef HAVE_OMEMO
-    if (message->enc == PROF_MSG_ENC_OMEMO) {
-        _sv_ev_incoming_omemo(chatwin, new_win, message, FALSE);
-    } else {
-        _sv_ev_incoming_plain(chatwin, new_win, message, FALSE);
-    }
-    rosterwin_roster();
-    return;
-#endif
-#endif
 
-#ifndef HAVE_LIBGPGME
-#ifndef HAVE_OMEMO
-    _sv_ev_incoming_plain(chatwin, new_win, message, FALSE);
-    rosterwin_roster();
-    return;
-#endif
-#endif
 }
 
 void
diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c
index 0718a287..343f2f60 100644
--- a/src/omemo/omemo.c
+++ b/src/omemo/omemo.c
@@ -227,37 +227,30 @@ omemo_on_connect(ProfAccount *account)
     omemo_ctx.device_list_handler = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
     omemo_ctx.known_devices = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_g_hash_table_free);
 
-    char *omemodir = files_get_data_path(DIR_OMEMO);
-    GString *basedir = g_string_new(omemodir);
-    free(omemodir);
-    gchar *account_dir = str_replace(account->jid, "@", "_at_");
-    g_string_append(basedir, "/");
-    g_string_append(basedir, account_dir);
-    g_string_append(basedir, "/");
-    free(account_dir);
-
-    omemo_ctx.identity_filename = g_string_new(basedir->str);
-    g_string_append(omemo_ctx.identity_filename, "identity.txt");
-    omemo_ctx.trust_filename = g_string_new(basedir->str);
-    g_string_append(omemo_ctx.trust_filename, "trust.txt");
-    omemo_ctx.sessions_filename = g_string_new(basedir->str);
-    g_string_append(omemo_ctx.sessions_filename, "sessions.txt");
-    omemo_ctx.known_devices_filename = g_string_new(basedir->str);
-    g_string_append(omemo_ctx.known_devices_filename, "known_devices.txt");
+    gchar *omemo_dir = files_get_account_data_path(DIR_OMEMO, account->jid);
+
+    omemo_ctx.identity_filename = g_string_new(omemo_dir);
+    g_string_append(omemo_ctx.identity_filename, "/identity.txt");
+    omemo_ctx.trust_filename = g_string_new(omemo_dir);
+    g_string_append(omemo_ctx.trust_filename, "/trust.txt");
+    omemo_ctx.sessions_filename = g_string_new(omemo_dir);
+    g_string_append(omemo_ctx.sessions_filename, "/sessions.txt");
+    omemo_ctx.known_devices_filename = g_string_new(omemo_dir);
+    g_string_append(omemo_ctx.known_devices_filename, "/known_devices.txt");
 
 
     errno = 0;
-    int res = g_mkdir_with_parents(basedir->str, S_IRWXU);
+    int res = g_mkdir_with_parents(omemo_dir, S_IRWXU);
     if (res == -1) {
         char *errmsg = strerror(errno);
         if (errmsg) {
-            log_error("OMEMO: error creating directory: %s, %s", basedir->str, errmsg);
+            log_error("OMEMO: error creating directory: %s, %s", omemo_dir, errmsg);
         } else {
-            log_error("OMEMO: creating directory: %s", basedir->str);
+            log_error("OMEMO: creating directory: %s", omemo_dir);
         }
     }
 
-    g_string_free(basedir, TRUE);
+    g_free(omemo_dir);
 
     omemo_devicelist_subscribe();
 
diff --git a/src/otr/otr.c b/src/otr/otr.c
index 3c3ed618..21bb222e 100644
--- a/src/otr/otr.c
+++ b/src/otr/otr.c
@@ -127,24 +127,18 @@ static void
 cb_write_fingerprints(void *opdata)
 {
     gcry_error_t err = 0;
+    gchar *otr_dir = files_get_account_data_path(DIR_OTR, jid);
+
+    GString *fpsfilename = g_string_new(otr_dir);
+    g_string_append(fpsfilename, "/fingerprints.txt");
 
-    char *otrdir = files_get_data_path(DIR_OTR);
-    GString *basedir = g_string_new(otrdir);
-    free(otrdir);
-    gchar *account_dir = str_replace(jid, "@", "_at_");
-    g_string_append(basedir, "/");
-    g_string_append(basedir, account_dir);
-    g_string_append(basedir, "/");
-    free(account_dir);
-
-    GString *fpsfilename = g_string_new(basedir->str);
-    g_string_append(fpsfilename, "fingerprints.txt");
     err = otrl_privkey_write_fingerprints(user_state, fpsfilename->str);
     if (err != GPG_ERR_NO_ERROR) {
         log_error("Failed to write fingerprints file");
         cons_show_error("Failed to create fingerprints file");
     }
-    g_string_free(basedir, TRUE);
+
+    g_free(otr_dir);
     g_string_free(fpsfilename, TRUE);
 }
 
@@ -216,19 +210,12 @@ otr_on_connect(ProfAccount *account)
     jid = strdup(account->jid);
     log_info("Loading OTR key for %s", jid);
 
-    char *otrdir = files_get_data_path(DIR_OTR);
-    GString *basedir = g_string_new(otrdir);
-    free(otrdir);
-    gchar *account_dir = str_replace(jid, "@", "_at_");
-    g_string_append(basedir, "/");
-    g_string_append(basedir, account_dir);
-    g_string_append(basedir, "/");
-    free(account_dir);
-
-    if (!mkdir_recursive(basedir->str)) {
-        log_error("Could not create %s for account %s.", basedir->str, jid);
-        cons_show_error("Could not create %s for account %s.", basedir->str, jid);
-        g_string_free(basedir, TRUE);
+    gchar *otr_dir = files_get_account_data_path(DIR_OTR, jid);
+
+    if (!mkdir_recursive(otr_dir)) {
+        log_error("Could not create %s for account %s.", otr_dir, jid);
+        cons_show_error("Could not create %s for account %s.", otr_dir, jid);
+        g_free(otr_dir);
         return;
     }
 
@@ -239,8 +226,8 @@ otr_on_connect(ProfAccount *account)
 
     gcry_error_t err = 0;
 
-    GString *keysfilename = g_string_new(basedir->str);
-    g_string_append(keysfilename, "keys.txt");
+    GString *keysfilename = g_string_new(otr_dir);
+    g_string_append(keysfilename, "/keys.txt");
     if (!g_file_test(keysfilename->str, G_FILE_TEST_IS_REGULAR)) {
         log_info("No OTR private key file found %s", keysfilename->str);
         data_loaded = FALSE;
@@ -250,7 +237,7 @@ otr_on_connect(ProfAccount *account)
         if (err != GPG_ERR_NO_ERROR) {
             log_warning("Failed to read OTR private key file: %s", keysfilename->str);
             cons_show_error("Failed to read OTR private key file: %s", keysfilename->str);
-            g_string_free(basedir, TRUE);
+            g_free(otr_dir);
             g_string_free(keysfilename, TRUE);
             return;
         }
@@ -259,7 +246,7 @@ otr_on_connect(ProfAccount *account)
         if (!privkey) {
             log_warning("No OTR private key found for account \"%s\", protocol \"xmpp\" in file: %s", jid, keysfilename->str);
             cons_show_error("No OTR private key found for account \"%s\", protocol \"xmpp\" in file: %s", jid, keysfilename->str);
-            g_string_free(basedir, TRUE);
+            g_free(otr_dir);
             g_string_free(keysfilename, TRUE);
             return;
         }
@@ -267,8 +254,8 @@ otr_on_connect(ProfAccount *account)
         data_loaded = TRUE;
     }
 
-    GString *fpsfilename = g_string_new(basedir->str);
-    g_string_append(fpsfilename, "fingerprints.txt");
+    GString *fpsfilename = g_string_new(otr_dir);
+    g_string_append(fpsfilename, "/fingerprints.txt");
     if (!g_file_test(fpsfilename->str, G_FILE_TEST_IS_REGULAR)) {
         log_info("No OTR fingerprints file found %s", fpsfilename->str);
         data_loaded = FALSE;
@@ -277,7 +264,7 @@ otr_on_connect(ProfAccount *account)
         err = otrl_privkey_read_fingerprints(user_state, fpsfilename->str, NULL, NULL);
         if (err != GPG_ERR_NO_ERROR) {
             log_error("Failed to load OTR fingerprints file: %s", fpsfilename->str);
-            g_string_free(basedir, TRUE);
+            g_free(otr_dir);
             g_string_free(keysfilename, TRUE);
             g_string_free(fpsfilename, TRUE);
             return;
@@ -291,7 +278,7 @@ otr_on_connect(ProfAccount *account)
         cons_show("Loaded OTR private key for %s", jid);
     }
 
-    g_string_free(basedir, TRUE);
+    g_free(otr_dir);
     g_string_free(keysfilename, TRUE);
     g_string_free(fpsfilename, TRUE);
     return;
@@ -388,39 +375,30 @@ otr_keygen(ProfAccount *account)
         return;
     }
 
-    if (jid) {
-        free(jid);
-    }
+    free(jid);
     jid = strdup(account->jid);
     log_info("Generating OTR key for %s", jid);
 
-    char *otrdir = files_get_data_path(DIR_OTR);
-    GString *basedir = g_string_new(otrdir);
-    free(otrdir);
-    gchar *account_dir = str_replace(jid, "@", "_at_");
-    g_string_append(basedir, "/");
-    g_string_append(basedir, account_dir);
-    g_string_append(basedir, "/");
-    free(account_dir);
-
-    if (!mkdir_recursive(basedir->str)) {
-        log_error("Could not create %s for account %s.", basedir->str, jid);
-        cons_show_error("Could not create %s for account %s.", basedir->str, jid);
-        g_string_free(basedir, TRUE);
+    gchar *otr_dir = files_get_account_data_path(DIR_OTR, jid);
+
+    if (!mkdir_recursive(otr_dir)) {
+        log_error("Could not create %s for account %s.", otr_dir, jid);
+        cons_show_error("Could not create %s for account %s.", otr_dir, jid);
+        g_free(otr_dir);
         return;
     }
 
     gcry_error_t err = 0;
 
-    GString *keysfilename = g_string_new(basedir->str);
-    g_string_append(keysfilename, "keys.txt");
+    GString *keysfilename = g_string_new(otr_dir);
+    g_string_append(keysfilename, "/keys.txt");
     log_debug("Generating private key file %s for %s", keysfilename->str, jid);
     cons_show("Generating private key, this may take some time.");
     cons_show("Moving the mouse randomly around the screen may speed up the process!");
     ui_update();
     err = otrl_privkey_generate(user_state, keysfilename->str, account->jid, "xmpp");
     if (err != GPG_ERR_NO_ERROR) {
-        g_string_free(basedir, TRUE);
+        g_free(otr_dir);
         g_string_free(keysfilename, TRUE);
         log_error("Failed to generate private key");
         cons_show_error("Failed to generate private key");
@@ -430,12 +408,12 @@ otr_keygen(ProfAccount *account)
     cons_show("");
     cons_show("Private key generation complete.");
 
-    GString *fpsfilename = g_string_new(basedir->str);
-    g_string_append(fpsfilename, "fingerprints.txt");
+    GString *fpsfilename = g_string_new(otr_dir);
+    g_string_append(fpsfilename, "/fingerprints.txt");
     log_debug("Generating fingerprints file %s for %s", fpsfilename->str, jid);
     err = otrl_privkey_write_fingerprints(user_state, fpsfilename->str);
     if (err != GPG_ERR_NO_ERROR) {
-        g_string_free(basedir, TRUE);
+        g_free(otr_dir);
         g_string_free(keysfilename, TRUE);
         log_error("Failed to create fingerprints file");
         cons_show_error("Failed to create fingerprints file");
@@ -445,7 +423,7 @@ otr_keygen(ProfAccount *account)
 
     err = otrl_privkey_read(user_state, keysfilename->str);
     if (err != GPG_ERR_NO_ERROR) {
-        g_string_free(basedir, TRUE);
+        g_free(otr_dir);
         g_string_free(keysfilename, TRUE);
         log_error("Failed to load private key");
         data_loaded = FALSE;
@@ -454,7 +432,7 @@ otr_keygen(ProfAccount *account)
 
     err = otrl_privkey_read_fingerprints(user_state, fpsfilename->str, NULL, NULL);
     if (err != GPG_ERR_NO_ERROR) {
-        g_string_free(basedir, TRUE);
+        g_free(otr_dir);
         g_string_free(keysfilename, TRUE);
         log_error("Failed to load fingerprints");
         data_loaded = FALSE;
@@ -463,7 +441,7 @@ otr_keygen(ProfAccount *account)
 
     data_loaded = TRUE;
 
-    g_string_free(basedir, TRUE);
+    g_free(otr_dir);
     g_string_free(keysfilename, TRUE);
     g_string_free(fpsfilename, TRUE);
     return;
diff --git a/src/pgp/gpg.c b/src/pgp/gpg.c
index 528b772e..d0834923 100644
--- a/src/pgp/gpg.c
+++ b/src/pgp/gpg.c
@@ -72,6 +72,9 @@ static char* _remove_header_footer(char *str, const char *const footer);
 static char* _add_header_footer(const char *const str, const char *const header, const char *const footer);
 static void _save_pubkeys(void);
 
+static gpgme_key_t _ox_key_lookup(const char *const barejid, gboolean secret_only);
+static gboolean _ox_key_is_usable(gpgme_key_t key, const char *const barejid, gboolean secret);
+
 void
 _p_gpg_free_pubkeyid(ProfPGPPubKeyId *pubkeyid)
 {
@@ -158,30 +161,26 @@ p_gpg_close(void)
 void
 p_gpg_on_connect(const char *const barejid)
 {
-    char *pgpdir = files_get_data_path(DIR_PGP);
-    GString *pubsfile = g_string_new(pgpdir);
-    free(pgpdir);
-    gchar *account_dir = str_replace(barejid, "@", "_at_");
-    g_string_append(pubsfile, "/");
-    g_string_append(pubsfile, account_dir);
-    free(account_dir);
+    gchar* pubsfile = files_get_account_data_path(DIR_PGP, barejid);
 
     // mkdir if doesn't exist for account
     errno = 0;
-    int res = g_mkdir_with_parents(pubsfile->str, S_IRWXU);
+    int res = g_mkdir_with_parents(pubsfile, S_IRWXU);
     if (res == -1) {
         char *errmsg = strerror(errno);
         if (errmsg) {
-            log_error("Error creating directory: %s, %s", pubsfile->str, errmsg);
+            log_error("Error creating directory: %s, %s", pubsfile, errmsg);
         } else {
-            log_error("Error creating directory: %s", pubsfile->str);
+            log_error("Error creating directory: %s", pubsfile);
         }
     }
 
     // create or read publickeys
-    g_string_append(pubsfile, "/pubkeys");
-    pubsloc = pubsfile->str;
-    g_string_free(pubsfile, FALSE);
+    GString *pubtmp = g_string_new(pubsfile);
+    g_string_append(pubtmp, "/pubkeys");
+    pubsloc = pubtmp->str;
+    g_string_free(pubtmp, FALSE);
+    g_free(pubsfile);
 
     if (g_file_test(pubsloc, G_FILE_TEST_EXISTS)) {
         g_chmod(pubsloc, S_IRUSR | S_IWUSR);
@@ -787,6 +786,219 @@ p_gpg_format_fp_str(char *fp)
     return result;
 }
 
+/*!
+ * \brief Public keys with XMPP-URI.
+ *
+ * This function will look for all public key with a XMPP-URI as UID.
+ *
+ */
+GHashTable* ox_gpg_public_keys(void){
+    gpgme_error_t error;
+    GHashTable *result = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_p_gpg_free_key);
+
+    gpgme_ctx_t ctx;
+    error = gpgme_new(&ctx);
+
+    if (error) {
+        log_error("OX - gpgme_new failed: %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    error = gpgme_op_keylist_start(ctx, NULL, 0); // all public keys
+    if (error == GPG_ERR_NO_ERROR) {
+        gpgme_key_t key;
+        error = gpgme_op_keylist_next(ctx, &key);
+        if ( error != GPG_ERR_EOF && error != GPG_ERR_NO_ERROR)  {
+            log_error("OX: gpgme_op_keylist_next %s %s", gpgme_strsource(error), gpgme_strerror(error));
+            g_hash_table_destroy(result);
+            return NULL;
+        }
+        while (!error) {
+            // Looking for XMPP URI UID
+            gpgme_user_id_t uid = key->uids;
+            gpgme_user_id_t xmppid = NULL;
+            while (!xmppid && uid) {
+                if( uid->name && strlen(uid->name) >= 10 ) {
+                    if( strstr(uid->name, "xmpp:") == uid->name ) {
+                        xmppid = uid;
+                    }
+                }
+                uid = uid->next;
+            }
+
+            if(xmppid) {
+                // Build Key information about all subkey
+                gpgme_subkey_t sub = key->subkeys;
+
+                ProfPGPKey *p_pgpkey = _p_gpg_key_new();
+                p_pgpkey->id = strdup(sub->keyid);
+                p_pgpkey->name = strdup(xmppid->uid);
+                p_pgpkey->fp = strdup(sub->fpr);
+                if (sub->can_encrypt) p_pgpkey->encrypt = TRUE;
+                if (sub->can_authenticate) p_pgpkey->authenticate = TRUE;
+                if (sub->can_certify) p_pgpkey->certify = TRUE;
+                if (sub->can_sign) p_pgpkey->sign = TRUE;
+
+                sub = sub->next;
+                while (sub) {
+                    if (sub->can_encrypt) p_pgpkey->encrypt = TRUE;
+                    if (sub->can_authenticate) p_pgpkey->authenticate = TRUE;
+                    if (sub->can_certify) p_pgpkey->certify = TRUE;
+                    if (sub->can_sign) p_pgpkey->sign = TRUE;
+
+                    sub = sub->next;
+                }
+
+                g_hash_table_insert(result, strdup(p_pgpkey->name), p_pgpkey);
+
+            }
+            gpgme_key_unref(key);
+            error = gpgme_op_keylist_next(ctx, &key);
+        }
+    }
+    gpgme_release(ctx);
+
+    //autocomplete_clear(key_ac);
+    //    GList *ids = g_hash_table_get_keys(result);
+    //    GList *curr = ids;
+    //    while (curr) {
+    //        ProfPGPKey *key = g_hash_table_lookup(result, curr->data);
+    //        autocomplete_add(key_ac, key->id);
+    //        curr = curr->next;
+    //    }
+    //    g_list_free(ids);
+
+    return result;
+
+}
+
+char* p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const recipient_barejid , const char* const message) {
+  setlocale (LC_ALL, "");
+  gpgme_check_version (NULL);
+  gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
+  gpgme_ctx_t ctx;
+  gpgme_error_t error = gpgme_new (&ctx);
+  if(GPG_ERR_NO_ERROR != error ) {
+    printf("gpgme_new: %d\n", error);
+    return NULL;
+  }
+  error = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+  }
+  gpgme_set_armor(ctx,0);
+  gpgme_set_textmode(ctx,0);
+  gpgme_set_offline(ctx,1);
+  gpgme_set_keylist_mode(ctx, GPGME_KEYLIST_MODE_LOCAL);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+  }
+
+  gpgme_key_t recp[3];
+  recp[0] = NULL,
+  recp[1] = NULL;
+
+  char* xmpp_jid_me = alloca( (strlen(sender_barejid)+6) * sizeof(char) );
+  char* xmpp_jid_recipient =  alloca( (strlen(recipient_barejid)+6) * sizeof(char) );
+
+  strcpy(xmpp_jid_me, "xmpp:");
+  strcpy(xmpp_jid_recipient, "xmpp:");
+  strcat(xmpp_jid_me, sender_barejid);
+  strcat(xmpp_jid_recipient,recipient_barejid);
+
+  gpgme_signers_clear(ctx);
+
+  // lookup own key
+  recp[0] =  _ox_key_lookup(sender_barejid, TRUE);
+  if(error != 0) {
+    log_error("Key not found for %s. GpgME Error: %s", xmpp_jid_me, gpgme_strerror(error));
+    return NULL;
+  }
+
+  error = gpgme_signers_add(ctx,recp[0]);
+  if(error != 0) {
+    log_error("gpgme_signers_add %s. GpgME Error: %s", xmpp_jid_me, gpgme_strerror(error));
+    return NULL;
+  }
+
+
+  // lookup key of recipient
+  recp[1] =  _ox_key_lookup(recipient_barejid, FALSE);
+  if(error != 0) {
+    log_error("Key not found for %s. GpgME Error: %s", xmpp_jid_recipient, gpgme_strerror(error));
+    return NULL;
+  }
+  recp[2] = NULL;
+  log_debug("%s <%s>", recp[0]->uids->name, recp[0]->uids->email);
+  log_debug("%s <%s>", recp[1]->uids->name, recp[1]->uids->email);
+
+  gpgme_encrypt_flags_t flags = 0;
+
+  gpgme_data_t plain;
+  gpgme_data_t cipher;
+
+  error = gpgme_data_new (&plain);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+    return NULL;
+  }
+
+  error = gpgme_data_new_from_mem(&plain, message, strlen(message),0);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+    return NULL;
+  }
+  error = gpgme_data_new (&cipher);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+    return NULL;
+  }
+
+  error = gpgme_op_encrypt_sign ( ctx, recp, flags, plain, cipher);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+    return NULL;
+  }
+
+  size_t len;
+  char *cipher_str = gpgme_data_release_and_get_mem(cipher, &len);
+  char* result = g_base64_encode( (unsigned char*) cipher_str,len);
+  gpgme_key_release (recp[0]);
+  gpgme_key_release (recp[1]);
+  gpgme_release (ctx);
+  return result;
+
+}
+
+gboolean ox_is_private_key_available(const char *const barejid) {
+    g_assert(barejid);
+    gboolean result = FALSE;
+
+    gpgme_key_t key = _ox_key_lookup(barejid, TRUE);
+    if(key) {
+        if (_ox_key_is_usable(key, barejid, TRUE) ) {
+            result = TRUE;
+        }
+    gpgme_key_unref(key);
+    }
+
+    return result;
+}
+
+gboolean ox_is_public_key_available(const char *const barejid) {
+    g_assert(barejid);
+    gboolean result = FALSE;
+    gpgme_key_t key = _ox_key_lookup(barejid, FALSE);
+    if(key) {
+        if (_ox_key_is_usable(key, barejid, FALSE) ) {
+            result = TRUE;
+        }
+    gpgme_key_unref(key);
+    }
+    return result;
+}
+
+
 static char*
 _remove_header_footer(char *str, const char *const footer)
 {
@@ -837,3 +1049,126 @@ _save_pubkeys(void)
     g_chmod(pubsloc, S_IRUSR | S_IWUSR);
     g_free(g_pubkeys_data);
 }
+
+static gpgme_key_t _ox_key_lookup(const char *const barejid, gboolean secret_only) {
+    g_assert(barejid);
+    log_debug("Looking for %s key: %s", secret_only == TRUE ? "Private" : "Public",   barejid);
+    gpgme_key_t key = NULL;
+    gpgme_error_t error;
+
+    gpgme_ctx_t ctx;
+    error = gpgme_new(&ctx);
+
+    if (error) {
+        log_error("OX - gpgme_new failed: %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    error = gpgme_op_keylist_start(ctx, NULL, secret_only);
+    if (error == GPG_ERR_NO_ERROR) {
+        error = gpgme_op_keylist_next(ctx, &key);
+        if ( error != GPG_ERR_EOF && error != GPG_ERR_NO_ERROR)  {
+            log_error("OX: gpgme_op_keylist_next %s %s", gpgme_strsource(error), gpgme_strerror(error));
+            return NULL;
+        }
+
+        GString* xmppuri = g_string_new("xmpp:");
+        g_string_append(xmppuri,barejid);
+
+        while (!error) {
+            // Looking for XMPP URI UID
+            gpgme_user_id_t uid = key->uids;
+
+            while ( uid ) {
+                if( uid->name && strlen(uid->name) >= 10 ) {
+                    if( g_strcmp0(uid->name, xmppuri->str) == 0 ) {
+                        gpgme_release(ctx);
+                        return key;
+                    }
+                }
+                uid = uid->next;
+            }
+            gpgme_key_unref(key);
+            error = gpgme_op_keylist_next(ctx, &key);
+        }
+    }
+    gpgme_release(ctx);
+
+    return key;
+}
+
+static gboolean _ox_key_is_usable(gpgme_key_t key, const char *const barejid, gboolean secret) {
+    gboolean result = TRUE;
+
+    if(key->revoked) result = FALSE;
+    if(key->expired) result = FALSE;
+    if(key->disabled) result = FALSE;
+
+    return result;
+}
+
+/*!
+ * @brief XMPP-OX: Decrypt OX Message.
+ *
+ *
+ *
+ * @param base64  base64_encode OpenPGP message.
+ *
+ * @result decrypt XMPP OX Message NULL terminated C-String
+ */
+char* p_ox_gpg_decrypt(char* base64) {
+  setlocale (LC_ALL, "");
+  gpgme_check_version (NULL);
+  gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
+  gpgme_ctx_t ctx;
+  gpgme_error_t error = gpgme_new (&ctx);
+  if(GPG_ERR_NO_ERROR != error ) {
+    printf("gpgme_new: %d\n", error);
+    return NULL;
+  }
+  error = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+  }
+  gpgme_set_armor(ctx,0);
+  gpgme_set_textmode(ctx,0);
+  gpgme_set_offline(ctx,1);
+  gpgme_set_keylist_mode(ctx, GPGME_KEYLIST_MODE_LOCAL);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+  }
+
+  gpgme_data_t plain = NULL;
+  gpgme_data_t cipher = NULL;
+
+  gsize s;
+  guchar* encypted = g_base64_decode(base64, &s);
+  error = gpgme_data_new_from_mem(&cipher, (char*)encypted, s,0);
+  if(error != 0) {
+    log_error("GpgME Error gpgme_data_new_from_mem: %s", gpgme_strerror(error));
+    return NULL;
+  }
+
+  error = gpgme_data_new (&plain);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+    return NULL;
+  }
+
+  error = gpgme_op_decrypt_verify(ctx, cipher, plain);
+  if(error != 0) {
+    log_error("GpgME Error gpgme_op_decrypt: %s", gpgme_strerror(error));
+    error = gpgme_op_decrypt(ctx, cipher, plain);
+    if ( error  != 0 ) {
+        return NULL;
+    }
+  }
+  size_t len;
+  char *plain_str = gpgme_data_release_and_get_mem(plain, &len);
+  char* result = malloc(len+1);
+  strcpy(result, plain_str);
+  result[len] = '\0';
+  return result;
+}
+
+
diff --git a/src/pgp/gpg.h b/src/pgp/gpg.h
index 0417c8a5..3eae6032 100644
--- a/src/pgp/gpg.h
+++ b/src/pgp/gpg.h
@@ -72,4 +72,20 @@ char* p_gpg_autocomplete_key(const char *const search_str, gboolean previous, vo
 void p_gpg_autocomplete_key_reset(void);
 char* p_gpg_format_fp_str(char *fp);
 
+char* p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const recipient_barejid , const char* const message);
+
+char* p_ox_gpg_decrypt(char* base64);
+
+/*!
+ * \brief List of public keys with xmpp-URI.
+ *
+ * @returns GHashTable* with GString* xmpp-uri and ProfPGPKey* value. Empty
+ * hash, if there is no key. NULL in case of error.
+ *
+ */
+GHashTable* ox_gpg_public_keys(void);
+
+gboolean ox_is_private_key_available(const char *const barejid);
+gboolean ox_is_public_key_available(const char *const barejid);
+
 #endif
diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c
index 5c17b0d4..35ad803f 100644
--- a/src/ui/chatwin.c
+++ b/src/ui/chatwin.c
@@ -90,6 +90,9 @@ chatwin_new(const char *const barejid)
     if (prefs_get_boolean(PREF_MAM)) {
         iq_mam_request(chatwin);
     }
+    // XEP-0373: OpenPGP for XMPP
+    chatwin->is_ox = FALSE;
+
     return chatwin;
 }
 
@@ -344,6 +347,8 @@ chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id,
         enc_char = prefs_get_pgp_char();
     } else if (enc_mode == PROF_MSG_ENC_OMEMO) {
         enc_char = prefs_get_omemo_char();
+    } else if (enc_mode == PROF_MSG_ENC_OX) {
+        enc_char = prefs_get_ox_char();
     } else {
         enc_char = strdup("-");
     }
diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c
index cb0167d0..66f33a4b 100644
--- a/src/ui/mucwin.c
+++ b/src/ui/mucwin.c
@@ -503,6 +503,7 @@ mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *c
     ProfWin *window = (ProfWin*)mucwin;
     char *mynick = muc_nick(mucwin->roomjid);
 
+    // displayed message char
     char *ch;
     if (mucwin->message_char) {
         ch = strdup(mucwin->message_char);
@@ -512,6 +513,8 @@ mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *c
         ch = prefs_get_pgp_char();
     } else if (enc_mode == PROF_MSG_ENC_OMEMO) {
         ch = prefs_get_omemo_char();
+    } else if (enc_mode == PROF_MSG_ENC_OX) {
+        ch = prefs_get_omemo_char();
     } else {
         ch = strdup("-");
     }
diff --git a/src/ui/titlebar.c b/src/ui/titlebar.c
index b557b59a..0a688e10 100644
--- a/src/ui/titlebar.c
+++ b/src/ui/titlebar.c
@@ -439,6 +439,20 @@ _show_privacy(ProfChatWin *chatwin)
         return;
     }
 
+    // XEP-0373: OpenPGP for XMPP
+    if (chatwin->is_ox) {
+        wprintw(win, " ");
+        wattron(win, bracket_attrs);
+        wprintw(win, "[");
+        wattroff(win, bracket_attrs);
+        wattron(win, encrypted_attrs);
+        wprintw(win, "OX");
+        wattroff(win, encrypted_attrs);
+        wattron(win, bracket_attrs);
+        wprintw(win, "]");
+        wattroff(win, bracket_attrs);
+    }
+
     if (chatwin->is_otr) {
         wprintw(win, " ");
         wattron(win, bracket_attrs);
diff --git a/src/ui/win_types.h b/src/ui/win_types.h
index 5da1765a..cb6834c6 100644
--- a/src/ui/win_types.h
+++ b/src/ui/win_types.h
@@ -155,6 +155,7 @@ typedef struct prof_chat_win_t {
     gboolean pgp_send;
     gboolean pgp_recv;
     gboolean is_omemo;
+    gboolean is_ox;     // XEP-0373: OpenPGP for XMPP
     char *resource_override;
     gboolean history_shown;
     unsigned long memcheck;
diff --git a/src/ui/window.c b/src/ui/window.c
index 763f389d..81230801 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -147,6 +147,7 @@ win_create_chat(const char *const barejid)
     new_win->pgp_recv = FALSE;
     new_win->pgp_send = FALSE;
     new_win->is_omemo = FALSE;
+    new_win->is_ox = FALSE;
     new_win->history_shown = FALSE;
     new_win->unread = 0;
     new_win->state = chat_state_new();
@@ -1144,6 +1145,8 @@ win_print_incoming(ProfWin *window, const char *const display_name_from, ProfMes
                 enc_char = prefs_get_otr_char();
             } else if (message->enc == PROF_MSG_ENC_PGP) {
                 enc_char = prefs_get_pgp_char();
+            } else if (message->enc == PROF_MSG_ENC_OX) { // XEP-0373: OpenPGP for XMPP
+                enc_char = prefs_get_ox_char();
             } else if (message->enc == PROF_MSG_ENC_OMEMO) {
                 enc_char = prefs_get_omemo_char();
             } else {
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index d578786e..74b6d2ea 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -76,7 +76,6 @@ 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 _handle_error(xmpp_stanza_t *const stanza);
 static void _handle_groupchat(xmpp_stanza_t *const stanza);
 static void _handle_muc_user(xmpp_stanza_t *const stanza);
@@ -85,9 +84,13 @@ static void _handle_conference(xmpp_stanza_t *const stanza);
 static void _handle_captcha(xmpp_stanza_t *const stanza);
 static void _handle_receipt_received(xmpp_stanza_t *const stanza);
 static void _handle_chat(xmpp_stanza_t *const stanza, gboolean is_mam);
+static void _handle_ox_chat(xmpp_stanza_t *const stanza, ProfMessage *message, gboolean is_mam);
+static void _send_message_stanza(xmpp_stanza_t *const stanza);
 static gboolean _handle_mam(xmpp_stanza_t *const stanza);
 
-static void _send_message_stanza(xmpp_stanza_t *const stanza);
+#ifdef HAVE_LIBGPGME
+static xmpp_stanza_t* _openpgp_signcrypt(xmpp_ctx_t* ctx, const char* const to, const char* const text);
+#endif // HAVE_LIBGPGME
 
 static GHashTable *pubsub_event_handlers;
 
@@ -353,6 +356,7 @@ message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean
     }
     account_free(account);
 #else
+    // ?
     message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id);
     xmpp_message_set_body(message, msg);
 #endif
@@ -376,6 +380,71 @@ message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean
     return id;
 }
 
+// XEP-0373: OpenPGP for XMPP
+char*
+message_send_chat_ox(const char *const barejid, const char *const msg, gboolean request_receipt, const char *const replace_id)
+{
+#ifdef HAVE_LIBGPGME
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+
+    char *state = chat_session_get_state(barejid);
+    char *jid = chat_session_get_jid(barejid);
+    char *id = connection_create_stanza_id();
+
+    xmpp_stanza_t *message = NULL;
+    Jid *jidp = jid_create(jid);
+
+    char *account_name = session_get_account_name();
+    ProfAccount *account = accounts_get_account(account_name);
+
+    message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id);
+    xmpp_message_set_body(message, "This message is encrypted (XEP-0373: OpenPGP for XMPP).");
+
+    xmpp_stanza_t *openpgp = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(openpgp, STANZA_NAME_OPENPGP);
+    xmpp_stanza_set_ns(openpgp, STANZA_NS_OPENPGP_0);
+
+    xmpp_stanza_t * signcrypt = _openpgp_signcrypt(ctx, barejid, msg);
+    char* c;
+    size_t s;
+    xmpp_stanza_to_text(signcrypt, &c,&s);
+    char* signcrypt_e = p_ox_gpg_signcrypt(account->jid, barejid, c);
+    if( signcrypt_e == NULL ) {
+      log_error("Message not signcrypted.");
+      return NULL;
+    }
+    // BASE64_OPENPGP_MESSAGE
+    xmpp_stanza_t* base64_openpgp_message = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_text(base64_openpgp_message,signcrypt_e);
+    xmpp_stanza_add_child(openpgp, base64_openpgp_message);
+    xmpp_stanza_add_child(message, openpgp);
+
+    xmpp_stanza_to_text(message, &c,&s);
+
+    account_free(account);
+    jid_destroy(jidp);
+    free(jid);
+
+    if (state) {
+        stanza_attach_state(ctx, message, state);
+    }
+
+    if (request_receipt) {
+        stanza_attach_receipt_request(ctx, message);
+    }
+
+    if (replace_id) {
+        stanza_attach_correction(ctx, message, replace_id);
+    }
+
+    _send_message_stanza(message);
+    xmpp_stanza_release(message);
+
+    return id;
+#endif // HAVE_LIBGPGME
+    return NULL;
+}
+
 char*
 message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt, const char *const replace_id)
 {
@@ -1140,6 +1209,12 @@ _handle_carbons(xmpp_stanza_t *const stanza)
     if (x) {
         message->encrypted = xmpp_stanza_get_text(x);
     }
+    // OX
+    xmpp_stanza_t *ox = xmpp_stanza_get_child_by_ns(message_stanza, STANZA_NS_OPENPGP_0);
+    if( ox ) {
+       message->enc=PROF_MSG_ENC_OX;
+      _handle_ox_chat(message_stanza,message, FALSE);
+    }
 
     //TODO: maybe also add is_carbon maybe even an enum with outgoing/incoming
     //could be that then we can have sv_ev_carbon no incoming/outgoing
@@ -1260,6 +1335,12 @@ _handle_chat(xmpp_stanza_t *const stanza, gboolean is_mam)
         message->encrypted = xmpp_stanza_get_text(encrypted);
     }
 
+    xmpp_stanza_t *ox = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OPENPGP_0);
+    if( ox ) {
+      message->enc=PROF_MSG_ENC_OX;
+      _handle_ox_chat(stanza,message, FALSE);
+    }
+
     if (message->plain || message->body || message->encrypted) {
         sv_ev_incoming_message(message);
 
@@ -1290,6 +1371,36 @@ _handle_chat(xmpp_stanza_t *const stanza, gboolean is_mam)
     message_free(message);
 }
 
+
+/*!
+ * @brief Handle incoming XMMP-OX chat message.
+ *
+ *
+ */
+static void _handle_ox_chat(xmpp_stanza_t *const stanza, ProfMessage *message, gboolean is_mam) {
+#ifdef HAVE_LIBGPGME
+	xmpp_stanza_t *ox = stanza_get_child_by_name_and_ns(stanza, "openpgp", STANZA_NS_OPENPGP_0);
+	message->plain = p_ox_gpg_decrypt(xmpp_stanza_get_text(ox));
+
+    // Implementation for libstrophe 0.10.
+    /*
+    xmpp_stanza_t *x =  xmpp_stanza_new_from_string(connection_get_ctx(), message->plain);
+    xmpp_stanza_t *p =  xmpp_stanza_get_child_by_name(x, "payload");
+    xmpp_stanza_t *b =  xmpp_stanza_get_child_by_name(p, "body");
+    message->plain = xmpp_stanza_get_text(b);
+    if(message->plain == NULL ) {
+        message->plain = xmpp_stanza_get_text(stanza);
+    }
+	message->encrypted = xmpp_stanza_get_text(ox);
+    */
+
+    if(message->plain == NULL ) {
+        message->plain = xmpp_stanza_get_text(stanza);
+    }
+	message->encrypted = xmpp_stanza_get_text(ox);
+#endif // HAVE_LIBGPGME
+}
+
 static gboolean
 _handle_mam(xmpp_stanza_t *const stanza)
 {
@@ -1372,3 +1483,65 @@ message_is_sent_by_us(const ProfMessage *const message, bool checkOID) {
 
     return  ret;
 }
+
+#ifdef HAVE_LIBGPGME
+xmpp_stanza_t* _openpgp_signcrypt(xmpp_ctx_t* ctx, const char* const to, const char* const text) {
+
+  time_t now = time(NULL);
+  struct tm* tm = localtime(&now);
+  char buf[255];
+  strftime(buf, sizeof(buf), "%FT%T%z", tm);
+    int randnr = rand() % 5;
+    char rpad_data[randnr];
+    for(int i = 0; i < randnr-1; i++) {
+      rpad_data[i] = 'c';
+    }
+    rpad_data[randnr-1] = '\0';
+
+    // signcrypt
+    xmpp_stanza_t *signcrypt = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(signcrypt, "signcrypt");
+    xmpp_stanza_set_ns(signcrypt, "urn:xmpp:openpgp:0");
+    // to
+    xmpp_stanza_t *s_to = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(s_to, "to");
+    xmpp_stanza_set_attribute(s_to, "jid", to);
+    // time
+    xmpp_stanza_t *time = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(time, "time");
+    xmpp_stanza_set_attribute(time, "stamp", buf);
+    xmpp_stanza_set_name(time, "time");
+    // rpad
+    xmpp_stanza_t *rpad = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(rpad, "rpad");
+    xmpp_stanza_t *rpad_text = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_text(rpad_text, rpad_data);
+    // payload
+    xmpp_stanza_t *payload= xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(payload, "payload");
+    // body
+    xmpp_stanza_t *body = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(body, "body");
+    xmpp_stanza_set_ns(body, "jabber:client");
+    // text
+    xmpp_stanza_t *body_text = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_text(body_text, text);
+    xmpp_stanza_add_child(signcrypt,s_to);
+    xmpp_stanza_add_child(signcrypt,time);
+    xmpp_stanza_add_child(signcrypt,rpad);
+    xmpp_stanza_add_child(rpad,rpad_text);
+    xmpp_stanza_add_child(signcrypt,payload);
+    xmpp_stanza_add_child(payload, body);
+    xmpp_stanza_add_child(body, body_text);
+
+    xmpp_stanza_release(body_text);
+    xmpp_stanza_release(body);
+    xmpp_stanza_release(rpad_text);
+    xmpp_stanza_release(rpad);
+    xmpp_stanza_release(time);
+    xmpp_stanza_release(s_to);
+
+    return signcrypt;
+}
+#endif // HAVE_LIBGPGME
+
diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h
index b20af1cd..9d1f6d38 100644
--- a/src/xmpp/stanza.h
+++ b/src/xmpp/stanza.h
@@ -62,6 +62,8 @@
 #define STANZA_NAME_PRESENCE "presence"
 #define STANZA_NAME_PRIORITY "priority"
 #define STANZA_NAME_X "x"
+// XEP-0373: OpenPGP for XMPP
+#define STANZA_NAME_OPENPGP "openpgp"
 #define STANZA_NAME_SHOW "show"
 #define STANZA_NAME_STATUS "status"
 #define STANZA_NAME_IQ "iq"
@@ -192,6 +194,8 @@
 #define STANZA_NS_RECEIPTS "urn:xmpp:receipts"
 #define STANZA_NS_SIGNED "jabber:x:signed"
 #define STANZA_NS_ENCRYPTED "jabber:x:encrypted"
+// XEP-0373: OpenPGP for XMPP
+#define STANZA_NS_OPENPGP_0 "urn:xmpp:openpgp:0"
 #define STANZA_NS_HTTP_UPLOAD "urn:xmpp:http:upload"
 #define STANZA_NS_X_OOB "jabber:x:oob"
 #define STANZA_NS_BLOCKING "urn:xmpp:blocking"
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index 1444cffe..c097387b 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -126,7 +126,8 @@ typedef enum {
     PROF_MSG_ENC_NONE,
     PROF_MSG_ENC_OTR,
     PROF_MSG_ENC_PGP,
-    PROF_MSG_ENC_OMEMO
+    PROF_MSG_ENC_OMEMO,
+    PROF_MSG_ENC_OX
 } prof_enc_t;
 
 typedef enum {
@@ -192,6 +193,8 @@ const char* connection_get_profanity_identifier(void);
 char* message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url, gboolean request_receipt, const char *const replace_id);
 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);
+// XEP-0373: OpenPGP for XMPP
+char* message_send_chat_ox(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);
 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);