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/command.c72
-rw-r--r--src/command/commands.c218
-rw-r--r--src/command/commands.h2
-rw-r--r--src/config/account.c9
-rw-r--r--src/config/account.h3
-rw-r--r--src/config/accounts.c26
-rw-r--r--src/config/accounts.h2
-rw-r--r--src/config/preferences.c35
-rw-r--r--src/config/preferences.h5
-rw-r--r--src/config/theme.c2
-rw-r--r--src/event/client_events.c78
-rw-r--r--src/event/server_events.c138
-rw-r--r--src/event/server_events.h8
-rw-r--r--src/log.c34
-rw-r--r--src/log.h2
-rw-r--r--src/main.c6
-rw-r--r--src/otr/otr.c57
-rw-r--r--src/otr/otr.h4
-rw-r--r--src/pgp/gpg.c380
-rw-r--r--src/pgp/gpg.h56
-rw-r--r--src/profanity.c9
-rw-r--r--src/ui/console.c21
-rw-r--r--src/ui/core.c27
-rw-r--r--src/ui/titlebar.c98
-rw-r--r--src/ui/ui.h3
-rw-r--r--src/ui/win_types.h3
-rw-r--r--src/xmpp/message.c124
-rw-r--r--src/xmpp/presence.c27
-rw-r--r--src/xmpp/stanza.h2
-rw-r--r--src/xmpp/xmpp.h6
30 files changed, 1267 insertions, 190 deletions
diff --git a/src/command/command.c b/src/command/command.c
index 43bca08b..1d99e56a 100644
--- a/src/command/command.c
+++ b/src/command/command.c
@@ -83,6 +83,7 @@ static char * _roster_autocomplete(ProfWin *window, const char * const input);
 static char * _group_autocomplete(ProfWin *window, const char * const input);
 static char * _bookmark_autocomplete(ProfWin *window, const char * const input);
 static char * _otr_autocomplete(ProfWin *window, const char * const input);
+static char * _pgp_autocomplete(ProfWin *window, const char * const input);
 static char * _connect_autocomplete(ProfWin *window, const char * const input);
 static char * _statuses_autocomplete(ProfWin *window, const char * const input);
 static char * _alias_autocomplete(ProfWin *window, const char * const input);
@@ -667,6 +668,14 @@ static struct cmd_t command_defs[] =
           "If the terminal does not support sounds, it may attempt to flash the screen instead.",
           NULL } } },
 
+    { "/encwarn",
+        cmd_encwarn, parse_args, 1, 1, &cons_encwarn_setting,
+        { "/encwarn on|off", "Titlebar encryption warning.",
+        { "/encwarn on|off",
+          "---------------",
+          "Enabled or disable the unencrypted warning message in the titlebar.",
+          NULL } } },
+
     { "/presence",
         cmd_presence, parse_args, 1, 1, &cons_presence_setting,
         { "/presence on|off", "Show the contacts presence in the titlebar.",
@@ -859,6 +868,21 @@ static struct cmd_t command_defs[] =
           "Send chat state notifications during chat sessions.",
           NULL } } },
 
+    { "/pgp",
+        cmd_pgp, parse_args, 1, 2, NULL,
+        { "/pgp command [args..]", "Open PGP commands.",
+        { "/pgp command [args..]",
+          "---------------------",
+          "Open PGP commands.",
+          "",
+          "keys              : List private keys.",
+          "libver            : Show which version of the libgpgme library is being used.",
+          "fps               : Show received fingerprints.",
+          "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|redact : PGP message logging, default: redact.",
+          NULL } } },
+
     { "/otr",
         cmd_otr, parse_args, 1, 3, NULL,
         { "/otr command [args..]", "Off The Record encryption commands.",
@@ -1214,6 +1238,8 @@ static Autocomplete time_statusbar_ac;
 static Autocomplete resource_ac;
 static Autocomplete inpblock_ac;
 static Autocomplete receipts_ac;
+static Autocomplete pgp_ac;
+static Autocomplete pgp_log_ac;
 
 /*
  * Initialise command autocompleter and history
@@ -1370,6 +1396,7 @@ cmd_init(void)
     autocomplete_add(account_set_ac, "muc");
     autocomplete_add(account_set_ac, "nick");
     autocomplete_add(account_set_ac, "otr");
+    autocomplete_add(account_set_ac, "pgpkeyid");
 
     account_clear_ac = autocomplete_new();
     autocomplete_add(account_clear_ac, "password");
@@ -1377,6 +1404,7 @@ cmd_init(void)
     autocomplete_add(account_clear_ac, "server");
     autocomplete_add(account_clear_ac, "port");
     autocomplete_add(account_clear_ac, "otr");
+    autocomplete_add(account_clear_ac, "pgpkeyid");
 
     account_default_ac = autocomplete_new();
     autocomplete_add(account_default_ac, "set");
@@ -1467,7 +1495,6 @@ cmd_init(void)
     autocomplete_add(otr_ac, "untrust");
     autocomplete_add(otr_ac, "secret");
     autocomplete_add(otr_ac, "log");
-    autocomplete_add(otr_ac, "warn");
     autocomplete_add(otr_ac, "libver");
     autocomplete_add(otr_ac, "policy");
     autocomplete_add(otr_ac, "question");
@@ -1579,6 +1606,19 @@ cmd_init(void)
     receipts_ac = autocomplete_new();
     autocomplete_add(receipts_ac, "send");
     autocomplete_add(receipts_ac, "request");
+
+    pgp_ac = autocomplete_new();
+    autocomplete_add(pgp_ac, "keys");
+    autocomplete_add(pgp_ac, "fps");
+    autocomplete_add(pgp_ac, "libver");
+    autocomplete_add(pgp_ac, "start");
+    autocomplete_add(pgp_ac, "end");
+    autocomplete_add(pgp_ac, "log");
+
+    pgp_log_ac = autocomplete_new();
+    autocomplete_add(pgp_log_ac, "on");
+    autocomplete_add(pgp_log_ac, "off");
+    autocomplete_add(pgp_log_ac, "redact");
 }
 
 void
@@ -1638,6 +1678,8 @@ cmd_uninit(void)
     autocomplete_free(resource_ac);
     autocomplete_free(inpblock_ac);
     autocomplete_free(receipts_ac);
+    autocomplete_free(pgp_ac);
+    autocomplete_free(pgp_log_ac);
 }
 
 gboolean
@@ -1810,6 +1852,8 @@ cmd_reset_autocomplete(ProfWin *window)
     autocomplete_reset(resource_ac);
     autocomplete_reset(inpblock_ac);
     autocomplete_reset(receipts_ac);
+    autocomplete_reset(pgp_ac);
+    autocomplete_reset(pgp_log_ac);
 
     if (window->type == WIN_CHAT) {
         ProfChatWin *chatwin = (ProfChatWin*)window;
@@ -1932,7 +1976,7 @@ _cmd_complete_parameters(ProfWin *window, const char * const input)
     // autocomplete boolean settings
     gchar *boolean_choices[] = { "/beep", "/intype", "/states", "/outtype",
         "/flash", "/splash", "/chlog", "/grlog", "/mouse", "/history",
-        "/vercheck", "/privileges", "/presence", "/wrap", "/winstidy", "/carbons" };
+        "/vercheck", "/privileges", "/presence", "/wrap", "/winstidy", "/carbons", "/encwarn" };
 
     for (i = 0; i < ARRAY_SIZE(boolean_choices); i++) {
         result = autocomplete_param_with_func(input, boolean_choices[i], prefs_autocomplete_boolean_choice);
@@ -2020,6 +2064,7 @@ _cmd_complete_parameters(ProfWin *window, const char * const input)
     g_hash_table_insert(ac_funcs, "/bookmark",      _bookmark_autocomplete);
     g_hash_table_insert(ac_funcs, "/autoconnect",   _autoconnect_autocomplete);
     g_hash_table_insert(ac_funcs, "/otr",           _otr_autocomplete);
+    g_hash_table_insert(ac_funcs, "/pgp",           _pgp_autocomplete);
     g_hash_table_insert(ac_funcs, "/connect",       _connect_autocomplete);
     g_hash_table_insert(ac_funcs, "/statuses",      _statuses_autocomplete);
     g_hash_table_insert(ac_funcs, "/alias",         _alias_autocomplete);
@@ -2434,13 +2479,30 @@ _otr_autocomplete(ProfWin *window, const char * const input)
         return found;
     }
 
-    found = autocomplete_param_with_func(input, "/otr warn",
-        prefs_autocomplete_boolean_choice);
+    found = autocomplete_param_with_ac(input, "/otr", otr_ac, TRUE);
     if (found) {
         return found;
     }
 
-    found = autocomplete_param_with_ac(input, "/otr", otr_ac, TRUE);
+    return NULL;
+}
+
+static char *
+_pgp_autocomplete(ProfWin *window, const char * const input)
+{
+    char *found = NULL;
+
+    found = autocomplete_param_with_func(input, "/pgp start", roster_contact_autocomplete);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/pgp log", pgp_log_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/pgp", pgp_ac, TRUE);
     if (found) {
         return found;
     }
diff --git a/src/command/commands.c b/src/command/commands.c
index 73dc4d47..38f15a74 100644
--- a/src/command/commands.c
+++ b/src/command/commands.c
@@ -57,6 +57,9 @@
 #ifdef HAVE_LIBOTR
 #include "otr/otr.h"
 #endif
+#ifdef HAVE_LIBGPGME
+#include "pgp/gpg.h"
+#endif
 #include "profanity.h"
 #include "tools/autocomplete.h"
 #include "tools/parser.h"
@@ -477,6 +480,10 @@ cmd_account(ProfWin *window, gchar **args, struct cmd_help_t help)
                         cons_show("Updated login status for account %s: %s", account_name, value);
                     }
                     cons_show("");
+                } else if (strcmp(property, "pgpkeyid") == 0) {
+                    accounts_set_pgp_keyid(account_name, value);
+                    cons_show("Updated PGP key ID for account %s: %s", account_name, value);
+                    cons_show("");
                 } else if (valid_resource_presence_string(property)) {
                     int intval;
                     char *err_msg = NULL;
@@ -555,6 +562,10 @@ cmd_account(ProfWin *window, gchar **args, struct cmd_help_t help)
                     accounts_clear_otr(account_name);
                     cons_show("OTR policy removed for account %s", account_name);
                     cons_show("");
+                } else if (strcmp(property, "pgpkeyid") == 0) {
+                    accounts_clear_pgp_keyid(account_name);
+                    cons_show("Removed PGP key ID for account %s", account_name);
+                    cons_show("");
                 } else {
                     cons_show("Invalid property: %s", property);
                     cons_show("");
@@ -4126,6 +4137,188 @@ cmd_xa(ProfWin *window, gchar **args, struct cmd_help_t help)
 }
 
 gboolean
+cmd_pgp(ProfWin *window, gchar **args, struct cmd_help_t help)
+{
+#ifdef HAVE_LIBGPGME
+    if (args[0] == NULL) {
+        cons_show("Usage: %s", help.usage);
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "log") == 0) {
+        char *choice = args[1];
+        if (g_strcmp0(choice, "on") == 0) {
+            prefs_set_string(PREF_PGP_LOG, "on");
+            cons_show("PGP messages will be logged as plaintext.");
+            if (!prefs_get_boolean(PREF_CHLOG)) {
+                cons_show("Chat logging is currently disabled, use '/chlog on' to enable.");
+            }
+        } else if (g_strcmp0(choice, "off") == 0) {
+            prefs_set_string(PREF_PGP_LOG, "off");
+            cons_show("PGP message logging disabled.");
+        } else if (g_strcmp0(choice, "redact") == 0) {
+            prefs_set_string(PREF_PGP_LOG, "redact");
+            cons_show("PGP messages will be logged as '[redacted]'.");
+            if (!prefs_get_boolean(PREF_CHLOG)) {
+                cons_show("Chat logging is currently disabled, use '/chlog on' to enable.");
+            }
+        } else {
+            cons_show("Usage: %s", help.usage);
+        }
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "keys") == 0) {
+        GSList *keys = p_gpg_list_keys();
+        if (!keys) {
+            cons_show("No keys found");
+            return TRUE;
+        }
+
+        cons_show("PGP keys:");
+        GSList *curr = keys;
+        while (curr) {
+            ProfPGPKey *key = curr->data;
+            cons_show("  %s", key->name);
+            cons_show("    ID          : %s", key->id);
+            cons_show("    Fingerprint : %s", key->fp);
+            curr = g_slist_next(curr);
+        }
+        g_slist_free_full(keys, (GDestroyNotify)p_gpg_free_key);
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "fps") == 0) {
+        jabber_conn_status_t conn_status = jabber_get_connection_status();
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
+        GHashTable *fingerprints = p_gpg_fingerprints();
+        GList *jids = g_hash_table_get_keys(fingerprints);
+        if (!jids) {
+            cons_show("No PGP fingerprints received.");
+            return TRUE;
+        }
+
+        cons_show("Received PGP fingerprints:");
+        GList *curr = jids;
+        while (curr) {
+            char *jid = curr->data;
+            char *fingerprint = g_hash_table_lookup(fingerprints, jid);
+            cons_show("  %s: %s", jid, fingerprint);
+            curr = g_list_next(curr);
+        }
+        g_list_free(jids);
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "libver") == 0) {
+        const char *libver = p_gpg_libver();
+        if (!libver) {
+            cons_show("Could not get libgpgme version");
+            return TRUE;
+        }
+
+        GString *fullstr = g_string_new("Using libgpgme version ");
+        g_string_append(fullstr, libver);
+        cons_show("%s", fullstr->str);
+        g_string_free(fullstr, TRUE);
+
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "start") == 0) {
+        jabber_conn_status_t conn_status = jabber_get_connection_status();
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You must be connected to start PGP encrpytion.");
+            return TRUE;
+        }
+
+        if (window->type != WIN_CHAT && args[1] == NULL) {
+            cons_show("You must be in a regular chat window to start PGP 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 = ui_ev_new_chat_win(barejid);
+            }
+            ui_ev_focus_win((ProfWin*)chatwin);
+        } else {
+            chatwin = (ProfChatWin*)window;
+            assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        }
+
+        if (chatwin->enc_mode == PROF_ENC_OTR) {
+            ui_current_print_formatted_line('!', 0, "You must end the OTR session to start PGP encryption.");
+            return TRUE;
+        }
+
+        if (chatwin->enc_mode == PROF_ENC_PGP) {
+            ui_current_print_formatted_line('!', 0, "You have already started PGP encryption.");
+            return TRUE;
+        }
+
+        ProfAccount *account = accounts_get_account(jabber_get_account_name());
+        if (!account->pgp_keyid) {
+            ui_current_print_formatted_line('!', 0, "You must specify a PGP key ID for this account to start PGP encryption.");
+            account_free(account);
+            return TRUE;
+        }
+        account_free(account);
+
+        if (!p_gpg_available(chatwin->barejid)) {
+            ui_current_print_formatted_line('!', 0, "No PGP key found for %s.", chatwin->barejid);
+            return TRUE;
+        }
+
+        chatwin->enc_mode = PROF_ENC_PGP;
+        ui_current_print_formatted_line('!', 0, "PGP encyption enabled.");
+        return TRUE;
+    }
+
+    if (g_strcmp0(args[0], "end") == 0) {
+        jabber_conn_status_t conn_status = jabber_get_connection_status();
+        if (conn_status != JABBER_CONNECTED) {
+            cons_show("You are not currently connected.");
+            return TRUE;
+        }
+
+        if (window->type != WIN_CHAT) {
+            cons_show("You must be in a regular chat window to end PGP encrpytion.");
+            return TRUE;
+        }
+
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        if (chatwin->enc_mode != PROF_ENC_PGP) {
+            ui_current_print_formatted_line('!', 0, "PGP encryption is not currently enabled.");
+            return TRUE;
+        }
+
+        chatwin->enc_mode = PROF_ENC_NONE;
+        ui_current_print_formatted_line('!', 0, "PGP encyption disabled.");
+        return TRUE;
+    }
+
+    return TRUE;
+#else
+    cons_show("This version of Profanity has not been built with PGP support enabled");
+    return TRUE;
+#endif
+
+}
+
+gboolean
 cmd_otr(ProfWin *window, gchar **args, struct cmd_help_t help)
 {
 #ifdef HAVE_LIBOTR
@@ -4156,11 +4349,6 @@ cmd_otr(ProfWin *window, gchar **args, struct cmd_help_t help)
         }
         return TRUE;
 
-    } else if (strcmp(args[0], "warn") == 0) {
-        gboolean result =  _cmd_set_boolean_preference(args[1], help,
-            "OTR warning message", PREF_OTR_WARN);
-        return result;
-
     } else if (strcmp(args[0], "libver") == 0) {
         char *version = otr_libotr_version();
         cons_show("Using libotr version %s", version);
@@ -4257,6 +4445,11 @@ cmd_otr(ProfWin *window, gchar **args, struct cmd_help_t help)
             }
             ui_ev_focus_win((ProfWin*)chatwin);
 
+            if (chatwin->enc_mode == PROF_ENC_PGP) {
+                ui_current_print_formatted_line('!', 0, "You must disable PGP encryption before starting an OTR session.");
+                return TRUE;
+            }
+
             if (chatwin->enc_mode == PROF_ENC_OTR) {
                 ui_current_print_formatted_line('!', 0, "You are already in an OTR session.");
                 return TRUE;
@@ -4269,7 +4462,7 @@ cmd_otr(ProfWin *window, gchar **args, struct cmd_help_t help)
 
             if (!otr_is_secure(barejid)) {
                 char *otr_query_message = otr_start_query();
-                message_send_chat_encrypted(barejid, otr_query_message);
+                message_send_chat_otr(barejid, otr_query_message);
                 return TRUE;
             }
 
@@ -4285,6 +4478,11 @@ cmd_otr(ProfWin *window, gchar **args, struct cmd_help_t help)
 
             ProfChatWin *chatwin = (ProfChatWin*)window;
             assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+            if (chatwin->enc_mode == PROF_ENC_PGP) {
+                ui_current_print_formatted_line('!', 0, "You must disable PGP encryption before starting an OTR session.");
+                return TRUE;
+            }
+
             if (chatwin->enc_mode == PROF_ENC_OTR) {
                 ui_current_print_formatted_line('!', 0, "You are already in an OTR session.");
                 return TRUE;
@@ -4296,7 +4494,7 @@ cmd_otr(ProfWin *window, gchar **args, struct cmd_help_t help)
             }
 
             char *otr_query_message = otr_start_query();
-            message_send_chat_encrypted(chatwin->barejid, otr_query_message);
+            message_send_chat_otr(chatwin->barejid, otr_query_message);
             return TRUE;
         }
 
@@ -4428,6 +4626,12 @@ cmd_otr(ProfWin *window, gchar **args, struct cmd_help_t help)
 #endif
 }
 
+gboolean
+cmd_encwarn(ProfWin *window, gchar **args, struct cmd_help_t help)
+{
+    return _cmd_set_boolean_preference(args[0], help, "Encryption warning message", PREF_ENC_WARN);
+}
+
 // helper function for status change commands
 static void
 _update_presence(const resource_presence_t resource_presence,
diff --git a/src/command/commands.h b/src/command/commands.h
index 2af24180..6f2bada2 100644
--- a/src/command/commands.h
+++ b/src/command/commands.h
@@ -105,6 +105,7 @@ gboolean cmd_nick(ProfWin *window, gchar **args, struct cmd_help_t help);
 gboolean cmd_notify(ProfWin *window, gchar **args, struct cmd_help_t help);
 gboolean cmd_online(ProfWin *window, gchar **args, struct cmd_help_t help);
 gboolean cmd_otr(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_pgp(ProfWin *window, gchar **args, struct cmd_help_t help);
 gboolean cmd_outtype(ProfWin *window, gchar **args, struct cmd_help_t help);
 gboolean cmd_prefs(ProfWin *window, gchar **args, struct cmd_help_t help);
 gboolean cmd_priority(ProfWin *window, gchar **args, struct cmd_help_t help);
@@ -145,6 +146,7 @@ gboolean cmd_wrap(ProfWin *window, gchar **args, struct cmd_help_t help);
 gboolean cmd_time(ProfWin *window, gchar **args, struct cmd_help_t help);
 gboolean cmd_resource(ProfWin *window, gchar **args, struct cmd_help_t help);
 gboolean cmd_inpblock(ProfWin *window, gchar **args, struct cmd_help_t help);
+gboolean cmd_encwarn(ProfWin *window, gchar **args, struct cmd_help_t help);
 
 gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args);
 
diff --git a/src/config/account.c b/src/config/account.c
index 857d049b..de48ba02 100644
--- a/src/config/account.c
+++ b/src/config/account.c
@@ -51,7 +51,7 @@ account_new(const gchar * const name, const gchar * const jid,
     int priority_away, int priority_xa, int priority_dnd,
     const gchar * const muc_service, const gchar * const muc_nick,
     const gchar * const otr_policy, GList *otr_manual, GList *otr_opportunistic,
-    GList *otr_always)
+    GList *otr_always, const gchar * const pgp_keyid)
 {
     ProfAccount *new_account = malloc(sizeof(ProfAccount));
 
@@ -144,6 +144,12 @@ account_new(const gchar * const name, const gchar * const jid,
     new_account->otr_opportunistic = otr_opportunistic;
     new_account->otr_always = otr_always;
 
+    if (pgp_keyid != NULL) {
+        new_account->pgp_keyid = strdup(pgp_keyid);
+    } else {
+        new_account->pgp_keyid = NULL;
+    }
+
     return new_account;
 }
 
@@ -210,6 +216,7 @@ account_free(ProfAccount *account)
         free(account->muc_service);
         free(account->muc_nick);
         free(account->otr_policy);
+        free(account->pgp_keyid);
         g_list_free_full(account->otr_manual, g_free);
         g_list_free_full(account->otr_opportunistic, g_free);
         g_list_free_full(account->otr_always, g_free);
diff --git a/src/config/account.h b/src/config/account.h
index 218f8ce7..22c29161 100644
--- a/src/config/account.h
+++ b/src/config/account.h
@@ -59,6 +59,7 @@ typedef struct prof_account_t {
     GList *otr_manual;
     GList *otr_opportunistic;
     GList *otr_always;
+    gchar *pgp_keyid;
 } ProfAccount;
 
 ProfAccount* account_new(const gchar * const name, const gchar * const jid,
@@ -68,7 +69,7 @@ ProfAccount* account_new(const gchar * const name, const gchar * const jid,
     int priority_away, int priority_xa, int priority_dnd,
     const gchar * const muc_service, const gchar * const muc_nick,
     const gchar * const otr_policy, GList *otr_manual, GList *otr_opportunistic,
-    GList *otr_always);
+    GList *otr_always, const gchar * const pgp_keyid);
 char* account_create_full_jid(ProfAccount *account);
 gboolean account_eval_password(ProfAccount *account);
 void account_free(ProfAccount *account);
diff --git a/src/config/accounts.c b/src/config/accounts.c
index d68f3a55..218e9d30 100644
--- a/src/config/accounts.c
+++ b/src/config/accounts.c
@@ -280,11 +280,16 @@ accounts_get_account(const char * const name)
             g_strfreev(always);
         }
 
+        gchar *pgp_keyid = NULL;
+        if (g_key_file_has_key(accounts, name, "pgp.keyid", NULL)) {
+            pgp_keyid = g_key_file_get_string(accounts, name, "pgp.keyid", NULL);
+        }
+
         ProfAccount *new_account = account_new(name, jid, password, eval_password, enabled,
             server, port, resource, last_presence, login_presence,
             priority_online, priority_chat, priority_away, priority_xa,
             priority_dnd, muc_service, muc_nick, otr_policy, otr_manual,
-            otr_opportunistic, otr_always);
+            otr_opportunistic, otr_always, pgp_keyid);
 
         g_free(jid);
         g_free(password);
@@ -296,6 +301,7 @@ accounts_get_account(const char * const name)
         g_free(muc_service);
         g_free(muc_nick);
         g_free(otr_policy);
+        g_free(pgp_keyid);
 
         return new_account;
     }
@@ -454,6 +460,15 @@ accounts_set_eval_password(const char * const account_name, const char * const v
 }
 
 void
+accounts_set_pgp_keyid(const char * const account_name, const char * const value)
+{
+    if (accounts_account_exists(account_name)) {
+        g_key_file_set_string(accounts, account_name, "pgp.keyid", value);
+        _save_accounts();
+    }
+}
+
+void
 accounts_clear_password(const char * const account_name)
 {
     if (accounts_account_exists(account_name)) {
@@ -490,6 +505,15 @@ accounts_clear_port(const char * const account_name)
 }
 
 void
+accounts_clear_pgp_keyid(const char * const account_name)
+{
+    if (accounts_account_exists(account_name)) {
+        g_key_file_remove_key(accounts, account_name, "pgp.keyid", NULL);
+        _save_accounts();
+    }
+}
+
+void
 accounts_clear_otr(const char * const account_name)
 {
     if (accounts_account_exists(account_name)) {
diff --git a/src/config/accounts.h b/src/config/accounts.h
index 50307b5b..eb981cb8 100644
--- a/src/config/accounts.h
+++ b/src/config/accounts.h
@@ -77,11 +77,13 @@ void accounts_set_priority_dnd(const char * const account_name, const gint value
 void accounts_set_priority_all(const char * const account_name, const gint value);
 gint accounts_get_priority_for_presence_type(const char * const account_name,
     resource_presence_t presence_type);
+void accounts_set_pgp_keyid(const char * const account_name, const char * const value);
 void accounts_clear_password(const char * const account_name);
 void accounts_clear_eval_password(const char * const account_name);
 void accounts_clear_server(const char * const account_name);
 void accounts_clear_port(const char * const account_name);
 void accounts_clear_otr(const char * const account_name);
+void accounts_clear_pgp_keyid(const char * const account_name);
 void accounts_add_otr_policy(const char * const account_name, const char * const contact_jid, const char * const policy);
 
 #endif
diff --git a/src/config/preferences.c b/src/config/preferences.c
index 62e2522b..fef0db0b 100644
--- a/src/config/preferences.c
+++ b/src/config/preferences.c
@@ -55,6 +55,7 @@
 #define PREF_GROUP_CONNECTION "connection"
 #define PREF_GROUP_ALIAS "alias"
 #define PREF_GROUP_OTR "otr"
+#define PREF_GROUP_PGP "pgp"
 
 #define INPBLOCK_DEFAULT 1000
 
@@ -75,8 +76,6 @@ void
 prefs_load(void)
 {
     GError *err;
-
-//    log_info("Loading preferences");
     prefs_loc = _get_preferences_file();
 
     if (g_file_test(prefs_loc, G_FILE_TEST_EXISTS)) {
@@ -94,22 +93,12 @@ prefs_load(void)
         g_error_free(err);
     }
 
-    // move pre 0.4.6 OTR warn preferences to [ui] group
+    // move pre 0.4.7 otr.warn to enc.warn
     err = NULL;
-    gboolean otr_warn = g_key_file_get_boolean(prefs, PREF_GROUP_OTR, "warn", &err);
+    gboolean otr_warn = g_key_file_get_boolean(prefs, PREF_GROUP_UI, "otr.warn", &err);
     if (err == NULL) {
-        g_key_file_set_boolean(prefs, PREF_GROUP_UI, _get_key(PREF_OTR_WARN), otr_warn);
-        g_key_file_remove_key(prefs, PREF_GROUP_OTR, "warn", NULL);
-    } else {
-        g_error_free(err);
-    }
-
-    // move pre 0.4.6 titlebar preference
-    err = NULL;
-    gchar *old_titlebar = g_key_file_get_string(prefs, PREF_GROUP_UI, "titlebar", &err);
-    if (err == NULL) {
-        g_key_file_set_string(prefs, PREF_GROUP_UI, _get_key(PREF_TITLEBAR_SHOW), old_titlebar);
-        g_key_file_remove_key(prefs, PREF_GROUP_UI, "titlebar", NULL);
+        g_key_file_set_boolean(prefs, PREF_GROUP_UI, _get_key(PREF_ENC_WARN), otr_warn);
+        g_key_file_remove_key(prefs, PREF_GROUP_UI, "otr.warn", NULL);
     } else {
         g_error_free(err);
     }
@@ -510,7 +499,7 @@ _get_group(preference_t pref)
         case PREF_ROSTER_BY:
         case PREF_RESOURCE_TITLE:
         case PREF_RESOURCE_MESSAGE:
-        case PREF_OTR_WARN:
+        case PREF_ENC_WARN:
         case PREF_INPBLOCK_DYNAMIC:
             return PREF_GROUP_UI;
         case PREF_STATES:
@@ -545,6 +534,8 @@ _get_group(preference_t pref)
         case PREF_OTR_LOG:
         case PREF_OTR_POLICY:
             return PREF_GROUP_OTR;
+        case PREF_PGP_LOG:
+            return PREF_GROUP_PGP;
         default:
             return NULL;
     }
@@ -637,8 +628,6 @@ _get_key(preference_t pref)
             return "defaccount";
         case PREF_OTR_LOG:
             return "log";
-        case PREF_OTR_WARN:
-            return "otr.warn";
         case PREF_OTR_POLICY:
             return "policy";
         case PREF_LOG_ROTATE:
@@ -669,6 +658,10 @@ _get_key(preference_t pref)
             return "resource.message";
         case PREF_INPBLOCK_DYNAMIC:
             return "inpblock.dynamic";
+        case PREF_ENC_WARN:
+            return "enc.warn";
+        case PREF_PGP_LOG:
+            return "log";
         default:
             return NULL;
     }
@@ -681,7 +674,7 @@ _get_default_boolean(preference_t pref)
 {
     switch (pref)
     {
-        case PREF_OTR_WARN:
+        case PREF_ENC_WARN:
         case PREF_AUTOAWAY_CHECK:
         case PREF_LOG_ROTATE:
         case PREF_LOG_SHARED:
@@ -735,6 +728,8 @@ _get_default_string(preference_t pref)
             return "seconds";
         case PREF_TIME_STATUSBAR:
             return "minutes";
+        case PREF_PGP_LOG:
+            return "redact";
         default:
             return NULL;
     }
diff --git a/src/config/preferences.h b/src/config/preferences.h
index 273ce6bb..9e8d2898 100644
--- a/src/config/preferences.h
+++ b/src/config/preferences.h
@@ -98,11 +98,12 @@ typedef enum {
     PREF_LOG_ROTATE,
     PREF_LOG_SHARED,
     PREF_OTR_LOG,
-    PREF_OTR_WARN,
     PREF_OTR_POLICY,
     PREF_RESOURCE_TITLE,
     PREF_RESOURCE_MESSAGE,
-    PREF_INPBLOCK_DYNAMIC
+    PREF_INPBLOCK_DYNAMIC,
+    PREF_ENC_WARN,
+    PREF_PGP_LOG
 } preference_t;
 
 typedef struct prof_alias_t {
diff --git a/src/config/theme.c b/src/config/theme.c
index 640b7298..d870b371 100644
--- a/src/config/theme.c
+++ b/src/config/theme.c
@@ -463,7 +463,7 @@ _load_preferences(void)
     _set_boolean_preference("presence", PREF_PRESENCE);
     _set_boolean_preference("intype", PREF_INTYPE);
 
-    _set_boolean_preference("otr.warn", PREF_OTR_WARN);
+    _set_boolean_preference("enc.warn", PREF_ENC_WARN);
 }
 
 static gchar *
diff --git a/src/event/client_events.c b/src/event/client_events.c
index 54cc0247..1ddb3d22 100644
--- a/src/event/client_events.c
+++ b/src/event/client_events.c
@@ -43,6 +43,9 @@
 #ifdef HAVE_LIBOTR
 #include "otr/otr.h"
 #endif
+#ifdef HAVE_LIBGPGME
+#include "pgp/gpg.h"
+#endif
 
 jabber_conn_status_t
 cl_ev_connect_jid(const char * const jid, const char * const passwd, const char * const altdomain, const int port)
@@ -64,7 +67,19 @@ cl_ev_connect_account(ProfAccount *account)
 void
 cl_ev_presence_send(const resource_presence_t presence_type, const char * const msg, const int idle)
 {
-    presence_send(presence_type, msg, idle);
+    char *signed_status = NULL;
+
+#ifdef HAVE_LIBGPGME
+    char *account_name = jabber_get_account_name();
+    ProfAccount *account = accounts_get_account(account_name);
+    if (account->pgp_keyid) {
+        signed_status = p_gpg_sign(msg, account->pgp_keyid);
+    }
+#endif
+
+    presence_send(presence_type, msg, idle, signed_status);
+
+    free(signed_status);
 }
 
 void
@@ -72,13 +87,70 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char * const msg)
 {
     chat_state_active(chatwin->state);
 
+// OTR suported, PGP supported
+#ifdef HAVE_LIBOTR
+#ifdef HAVE_LIBGPGME
+    prof_enc_t enc_mode = chatwin->enc_mode;
+    if (enc_mode == PROF_ENC_NONE || enc_mode == PROF_ENC_OTR) {
+        gboolean handled = otr_on_message_send(chatwin, msg);
+        if (!handled) {
+            char *id = message_send_chat(chatwin->barejid, msg);
+            chat_log_msg_out(chatwin->barejid, msg);
+            ui_outgoing_chat_msg(chatwin, msg, id);
+            free(id);
+        }
+    } else { // enc_mode = PROF_ENC_PGP
+        char *id = message_send_chat_pgp(chatwin->barejid, msg);
+        chat_log_pgp_msg_out(chatwin->barejid, msg);
+        ui_outgoing_chat_msg(chatwin, msg, id);
+        free(id);
+    }
+    return;
+#endif
+#endif
+
+// OTR supported, PGP unsupported
 #ifdef HAVE_LIBOTR
-    otr_on_message_send(chatwin, msg);
-#else
+#ifndef HAVE_LIBGPGME
+    gboolean handled = otr_on_message_send(chatwin, msg);
+    if (!handled) {
+        char *id = message_send_chat(chatwin->barejid, msg);
+        chat_log_msg_out(chatwin->barejid, msg);
+        ui_outgoing_chat_msg(chatwin, msg, id);
+        free(id);
+    }
+    return;
+#endif
+#endif
+
+// OTR unsupported, PGP supported
+#ifndef HAVE_LIBOTR
+#ifdef HAVE_LIBGPGME
+    prof_enc_t enc_mode = chatwin->enc_mode;
+    if (enc_mode == PROF_ENC_NONE) {
+        char *id = message_send_chat(chatwin->barejid, msg);
+        chat_log_msg_out(chatwin->barejid, msg);
+        ui_outgoing_chat_msg(chatwin, msg, id);
+        free(id);
+    } else if (enc_mode == PROF_ENC_PGP) {
+        char *id = message_send_chat_pgp(chatwin->barejid, msg);
+        chat_log_pgp_msg_out(chatwin->barejid, msg);
+        ui_outgoing_chat_msg(chatwin, msg, id);
+        free(id);
+    }
+    return;
+#endif
+#endif
+
+// OTR unsupported, PGP unsupported
+#ifndef HAVE_LIBOTR
+#ifndef HAVE_LIBGPGME
     char *id = message_send_chat(chatwin->barejid, msg);
     chat_log_msg_out(chatwin->barejid, msg);
     ui_outgoing_chat_msg(chatwin, msg, id);
     free(id);
+    return;
+#endif
 #endif
 }
 
diff --git a/src/event/server_events.c b/src/event/server_events.c
index e2e910a3..004e743b 100644
--- a/src/event/server_events.c
+++ b/src/event/server_events.c
@@ -43,10 +43,14 @@
 #include "config/preferences.h"
 #include "config/account.h"
 #include "roster_list.h"
+#include "window_list.h"
 
 #ifdef HAVE_LIBOTR
 #include "otr/otr.h"
 #endif
+#ifdef HAVE_LIBGPGME
+#include "pgp/gpg.h"
+#endif
 
 #include "ui/ui.h"
 
@@ -160,19 +164,125 @@ sv_ev_incoming_private_message(const char * const fulljid, char *message)
 }
 
 void
-sv_ev_carbon(char *barejid, char *message)
+sv_ev_outgoing_carbon(char *barejid, char *message)
 {
     ui_outgoing_chat_msg_carbon(barejid, message);
 }
 
 void
-sv_ev_incoming_message(char *barejid, char *resource, char *message)
+sv_ev_incoming_carbon(char *barejid, char *resource, char *message)
+{
+    gboolean new_win = FALSE;
+    ProfChatWin *chatwin = wins_get_chat(barejid);
+    if (!chatwin) {
+        ProfWin *window = wins_new_chat(barejid);
+        chatwin = (ProfChatWin*)window;
+        new_win = TRUE;
+    }
+
+    ui_incoming_msg(chatwin, resource, message, NULL, new_win);
+    chat_log_msg_in(barejid, message);
+}
+
+void
+sv_ev_incoming_message(char *barejid, char *resource, char *message, char *enc_message)
 {
+    gboolean new_win = FALSE;
+    ProfChatWin *chatwin = wins_get_chat(barejid);
+    if (!chatwin) {
+        ProfWin *window = wins_new_chat(barejid);
+        chatwin = (ProfChatWin*)window;
+        new_win = TRUE;
+    }
+
+// OTR suported, PGP supported
+#ifdef HAVE_LIBOTR
+#ifdef HAVE_LIBGPGME
+    prof_enc_t enc_mode = chatwin->enc_mode;
+    if (enc_message) {
+        if (enc_mode == PROF_ENC_OTR) {
+            win_println((ProfWin*)chatwin, "PGP encrypted message received whilst in OTR session.");
+        } else { // PROF_ENC_NONE, PROF_ENC_PGP
+            char *decrypted = p_gpg_decrypt(barejid, enc_message);
+            if (decrypted) {
+                if (enc_mode == PROF_ENC_NONE) {
+                    win_println((ProfWin*)chatwin, "PGP encryption enabled.");
+                }
+                ui_incoming_msg(chatwin, resource, decrypted, NULL, new_win);
+                chat_log_pgp_msg_in(barejid, decrypted);
+                chatwin->enc_mode = PROF_ENC_PGP;
+            } else {
+                ui_incoming_msg(chatwin, resource, message, NULL, new_win);
+                chat_log_msg_in(barejid, message);
+                chatwin->enc_mode = PROF_ENC_NONE;
+            }
+        }
+    } else {
+        if (enc_mode == PROF_ENC_PGP) {
+            win_println((ProfWin*)chatwin, "PGP encryption disabled.");
+            ui_incoming_msg(chatwin, resource, message, NULL, new_win);
+            chat_log_msg_in(barejid, message);
+            chatwin->enc_mode = PROF_ENC_NONE;
+        } else {
+            gboolean decrypted = FALSE;
+            char *otr_res = otr_on_message_recv(barejid, resource, message, &decrypted);
+            if (otr_res) {
+                ui_incoming_msg(chatwin, resource, otr_res, NULL, new_win);
+                chat_log_otr_msg_in(barejid, otr_res, decrypted);
+                otr_free_message(otr_res);
+            }
+        }
+    }
+    return;
+#endif
+#endif
+
+// OTR supported, PGP unsupported
 #ifdef HAVE_LIBOTR
-    otr_on_message_recv(barejid, resource, message);
-#else
-    ui_incoming_msg(barejid, resource, message, NULL);
+#ifndef HAVE_LIBGPGME
+    gboolean decrypted = FALSE;
+    char *otr_res = otr_on_message_recv(barejid, resource, message, &decrypted);
+    if (otr_res) {
+        ui_incoming_msg(chatwin, resource, otr_res, NULL, new_win);
+        chat_log_otr_msg_in(barejid, otr_res, decrypted);
+        otr_free_message(otr_res);
+    }
+    return;
+#endif
+#endif
+
+// OTR unsupported, PGP supported
+#ifndef HAVE_LIBOTR
+#ifdef HAVE_LIBGPGME
+    prof_enc_t enc_mode = chatwin->enc_mode;
+    if (enc_message) {
+        char *decrypted = p_gpg_decrypt(jid->barejid, enc_message);
+        if (decrypted) {
+            ui_incoming_msg(chatwin, resource, decrypted, NULL, new_win);
+            chat_log_pgp_msg_in(barejid, decrypted);
+            chatwin->enc_mode = PROF_ENC_PGP;
+        } else {
+            ui_incoming_msg(chatwin, resource, message, NULL, new_win);
+            chat_log_msg_in(barejid, message);
+            chatwin->enc_mode = PROF_ENC_NONE;
+        }
+    } else {
+        ui_incoming_msg(chatwin, resource, message, NULL, new_win);
+        chat_log_msg_in(barejid, message);
+        chatwin->enc_mode = PROF_ENC_NONE;
+    }
+    return;
+#endif
+#endif
+
+// OTR unsupported, PGP unsupported
+#ifndef HAVE_LIBOTR
+#ifndef HAVE_LIBGPGME
+    ui_incoming_msg(chatwin, resource, message, NULL, new_win);
     chat_log_msg_in(barejid, message);
+    chatwin->enc_mode = PROF_ENC_NONE;
+    return;
+#endif
 #endif
 }
 
@@ -185,7 +295,15 @@ sv_ev_delayed_private_message(const char * const fulljid, char *message, GTimeVa
 void
 sv_ev_delayed_message(char *barejid, char *message, GTimeVal tv_stamp)
 {
-    ui_incoming_msg(barejid, NULL, message, &tv_stamp);
+    gboolean new_win = FALSE;
+    ProfChatWin *chatwin = wins_get_chat(barejid);
+    if (!chatwin) {
+        ProfWin *window = wins_new_chat(barejid);
+        chatwin = (ProfChatWin*)window;
+        new_win = TRUE;
+    }
+
+    ui_incoming_msg(chatwin, NULL, message, &tv_stamp, new_win);
     chat_log_msg_in_delayed(barejid, message, &tv_stamp);
 }
 
@@ -280,7 +398,7 @@ sv_ev_contact_offline(char *barejid, char *resource, char *status)
 }
 
 void
-sv_ev_contact_online(char *barejid, Resource *resource, GDateTime *last_activity)
+sv_ev_contact_online(char *barejid, Resource *resource, GDateTime *last_activity, char *pgpsig)
 {
     gboolean updated = roster_update_presence(barejid, resource, last_activity);
 
@@ -288,6 +406,12 @@ sv_ev_contact_online(char *barejid, Resource *resource, GDateTime *last_activity
         ui_contact_online(barejid, resource, last_activity);
     }
 
+#ifdef HAVE_LIBGPGME
+    if (pgpsig) {
+        p_gpg_verify(barejid, pgpsig);
+    }
+#endif
+
     rosterwin_roster();
     chat_session_remove(barejid);
 }
diff --git a/src/event/server_events.h b/src/event/server_events.h
index 46d485da..51564d5c 100644
--- a/src/event/server_events.h
+++ b/src/event/server_events.h
@@ -50,7 +50,7 @@ void sv_ev_room_history(const char * const room_jid, const char * const nick,
     GTimeVal tv_stamp, const char * const message);
 void sv_ev_room_message(const char * const room_jid, const char * const nick,
     const char * const message);
-void sv_ev_incoming_message(char *barejid, char *resource, char *message);
+void sv_ev_incoming_message(char *barejid, char *resource, char *message, char *enc_message);
 void sv_ev_incoming_private_message(const char * const fulljid, char *message);
 void sv_ev_delayed_message(char *fulljid, char *message, GTimeVal tv_stamp);
 void sv_ev_delayed_private_message(const char * const fulljid, char *message, GTimeVal tv_stamp);
@@ -62,8 +62,7 @@ void sv_ev_gone(const char * const barejid, const char * const resource);
 void sv_ev_subscription(const char *from, jabber_subscr_t type);
 void sv_ev_message_receipt(char *barejid, char *id);
 void sv_ev_contact_offline(char *contact, char *resource, char *status);
-void sv_ev_contact_online(char *contact, Resource *resource,
-    GDateTime *last_activity);
+void sv_ev_contact_online(char *contact, Resource *resource, GDateTime *last_activity, char *pgpkey);
 void sv_ev_leave_room(const char * const room);
 void sv_ev_room_destroy(const char * const room);
 void sv_ev_room_occupant_offline(const char * const room, const char * const nick,
@@ -76,7 +75,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_carbon(char *barejid, char *message);
+void sv_ev_outgoing_carbon(char *barejid, char *message);
+void sv_ev_incoming_carbon(char *barejid, char *resource, char *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 a7727e8b..941bea6f 100644
--- a/src/log.c
+++ b/src/log.c
@@ -280,6 +280,23 @@ 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)
+{
+    if (prefs_get_boolean(PREF_CHLOG)) {
+        const char *jid = jabber_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_OUT_LOG, NULL);
+        } else if (strcmp(pref_pgp_log, "redact") == 0) {
+            _chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_OUT_LOG, NULL);
+        }
+        prefs_free_string(pref_pgp_log);
+        jid_destroy(jidp);
+    }
+}
+
+void
 chat_log_otr_msg_in(const char * const barejid, const char * const msg, gboolean was_decrypted)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
@@ -297,6 +314,23 @@ chat_log_otr_msg_in(const char * const barejid, const char * const msg, gboolean
 }
 
 void
+chat_log_pgp_msg_in(const char * const barejid, const char * const msg)
+{
+    if (prefs_get_boolean(PREF_CHLOG)) {
+        const char *jid = jabber_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, NULL);
+        } else if (strcmp(pref_pgp_log, "redact") == 0) {
+            _chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_IN_LOG, NULL);
+        }
+        prefs_free_string(pref_pgp_log);
+        jid_destroy(jidp);
+    }
+}
+
+void
 chat_log_msg_in(const char * const barejid, const char * const msg)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
diff --git a/src/log.h b/src/log.h
index 0689e2e6..3c98c3bd 100644
--- a/src/log.h
+++ b/src/log.h
@@ -67,10 +67,12 @@ void chat_log_init(void);
 
 void chat_log_msg_out(const char * const barejid, const char * const msg);
 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_msg_in(const char * const barejid, const char * const msg);
 void chat_log_msg_in_delayed(const char * const barejid, const char * msg, GTimeVal *tv_stamp);
 void chat_log_otr_msg_in(const char * const barejid, const char * const msg, gboolean was_decrypted);
+void chat_log_pgp_msg_in(const char * const barejid, const char * const msg);
 
 void chat_log_close(void);
 GSList * chat_log_get_previous(const gchar * const login,
diff --git a/src/main.c b/src/main.c
index 3bb7eeb6..ea8f0cea 100644
--- a/src/main.c
+++ b/src/main.c
@@ -121,6 +121,12 @@ main(int argc, char **argv)
         g_print("OTR support: Disabled\n");
 #endif
 
+#ifdef HAVE_LIBGPGME
+        g_print("PGP support: Enabled\n");
+#else
+        g_print("PGP support: Disabled\n");
+#endif
+
         return 0;
     }
 
diff --git a/src/otr/otr.c b/src/otr/otr.c
index 1f63c8f9..e61a0e47 100644
--- a/src/otr/otr.c
+++ b/src/otr/otr.c
@@ -110,7 +110,7 @@ static void
 cb_inject_message(void *opdata, const char *accountname,
     const char *protocol, const char *recipient, const char *message)
 {
-    message_send_chat_encrypted(recipient, message);
+    message_send_chat_otr(recipient, message);
 }
 
 static void
@@ -272,12 +272,9 @@ otr_on_connect(ProfAccount *account)
     return;
 }
 
-void
-otr_on_message_recv(const char * const barejid, const char * const resource, const char * const message)
+char*
+otr_on_message_recv(const char * const barejid, const char * const resource, const char * const message, gboolean *was_decrypted)
 {
-    gboolean was_decrypted = FALSE;
-    char *decrypted;
-
     prof_otrpolicy_t policy = otr_get_policy(barejid);
     char *whitespace_base = strstr(message, OTRL_MESSAGE_TAG_BASE);
 
@@ -294,65 +291,65 @@ otr_on_message_recv(const char * const barejid, const char * const resource, con
                 memmove(whitespace_base, whitespace_base+tag_length, tag_length);
                 char *otr_query_message = otr_start_query();
                 cons_show("OTR Whitespace pattern detected. Attempting to start OTR session...");
-                message_send_chat_encrypted(barejid, otr_query_message);
+                message_send_chat_otr(barejid, otr_query_message);
             }
         }
     }
-    decrypted = otr_decrypt_message(barejid, message, &was_decrypted);
 
-    // internal OTR message
-    if (decrypted == NULL) {
-        return;
+    char *decrypted = otr_decrypt_message(barejid, message, was_decrypted);
+    if (!decrypted) { // internal OTR message
+        return NULL;
     }
 
-    if (policy == PROF_OTRPOLICY_ALWAYS && !was_decrypted && !whitespace_base) {
+    if (policy == PROF_OTRPOLICY_ALWAYS && *was_decrypted == FALSE && !whitespace_base) {
         char *otr_query_message = otr_start_query();
         cons_show("Attempting to start OTR session...");
-        message_send_chat_encrypted(barejid, otr_query_message);
+        message_send_chat_otr(barejid, otr_query_message);
     }
 
-    ui_incoming_msg(barejid, resource, decrypted, NULL);
-    chat_log_otr_msg_in(barejid, decrypted, was_decrypted);
-    otr_free_message(decrypted);
+    return decrypted;
 }
 
-void
+gboolean
 otr_on_message_send(ProfChatWin *chatwin, const char * const message)
 {
     char *id = NULL;
-
     prof_otrpolicy_t policy = otr_get_policy(chatwin->barejid);
 
+    // Send encrypted message
     if (otr_is_secure(chatwin->barejid)) {
         char *encrypted = otr_encrypt_message(chatwin->barejid, message);
         if (encrypted) {
-            id = message_send_chat_encrypted(chatwin->barejid, encrypted);
+            id = message_send_chat_otr(chatwin->barejid, encrypted);
             chat_log_otr_msg_out(chatwin->barejid, message);
             ui_outgoing_chat_msg(chatwin, message, id);
             otr_free_message(encrypted);
+            free(id);
+            return TRUE;
         } else {
             ui_win_error_line((ProfWin*)chatwin, "Failed to encrypt and send message.");
-            return;
+            return TRUE;
         }
+    }
 
-    } else if (policy == PROF_OTRPOLICY_ALWAYS) {
+    // show error if not secure and policy always
+    if (policy == PROF_OTRPOLICY_ALWAYS) {
         ui_win_error_line((ProfWin*)chatwin, "Failed to send message. OTR policy set to: always");
-        return;
+        return TRUE;
+    }
 
-    } else if (policy == PROF_OTRPOLICY_OPPORTUNISTIC) {
+    // tag and send for policy opportunistic
+    if (policy == PROF_OTRPOLICY_OPPORTUNISTIC) {
         char *otr_tagged_msg = otr_tag_message(message);
-        id = message_send_chat_encrypted(chatwin->barejid, otr_tagged_msg);
+        id = message_send_chat_otr(chatwin->barejid, otr_tagged_msg);
         ui_outgoing_chat_msg(chatwin, message, id);
         chat_log_msg_out(chatwin->barejid, message);
         free(otr_tagged_msg);
-
-    } else {
-        id = message_send_chat(chatwin->barejid, message);
-        ui_outgoing_chat_msg(chatwin, message, id);
-        chat_log_msg_out(chatwin->barejid, message);
+        free(id);
+        return TRUE;
     }
 
-    free(id);
+    return FALSE;
 }
 
 void
diff --git a/src/otr/otr.h b/src/otr/otr.h
index 3c46ac3d..45abdc20 100644
--- a/src/otr/otr.h
+++ b/src/otr/otr.h
@@ -58,8 +58,8 @@ char* otr_start_query(void);
 void otr_poll(void);
 void otr_on_connect(ProfAccount *account);
 
-void otr_on_message_recv(const char * const barejid, const char * const resource, const char * const message);
-void otr_on_message_send(ProfChatWin *chatwin, const char * const message);
+char* otr_on_message_recv(const char * const barejid, const char * const resource, const char * const message, gboolean *was_decrypted);
+gboolean otr_on_message_send(ProfChatWin *chatwin, const char * const message);
 
 void otr_keygen(ProfAccount *account);
 
diff --git a/src/pgp/gpg.c b/src/pgp/gpg.c
new file mode 100644
index 00000000..ce40690f
--- /dev/null
+++ b/src/pgp/gpg.c
@@ -0,0 +1,380 @@
+/*
+ * gpg.c
+ *
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#include <locale.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gpgme.h>
+
+#include "pgp/gpg.h"
+#include "log.h"
+
+#define PGP_SIGNATURE_HEADER "-----BEGIN PGP SIGNATURE-----"
+#define PGP_SIGNATURE_FOOTER "-----END PGP SIGNATURE-----"
+#define PGP_MESSAGE_HEADER "-----BEGIN PGP MESSAGE-----"
+#define PGP_MESSAGE_FOOTER "-----END PGP MESSAGE-----"
+
+static const char *libversion;
+static GHashTable *fingerprints;
+
+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);
+
+void
+p_gpg_init(void)
+{
+    libversion = gpgme_check_version(NULL);
+    log_debug("GPG: Found gpgme version: %s", libversion);
+    gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
+
+    fingerprints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+}
+
+void
+p_gpg_close(void)
+{
+    g_hash_table_destroy(fingerprints);
+}
+
+GSList *
+p_gpg_list_keys(void)
+{
+    gpgme_error_t error;
+    gpgme_ctx_t ctx;
+    gpgme_key_t key;
+    GSList *result = NULL;
+
+    error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Could not list keys. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    error = gpgme_op_keylist_start(ctx, NULL, 1);
+    if (error == GPG_ERR_NO_ERROR) {
+        while (!error) {
+            error = gpgme_op_keylist_next(ctx, &key);
+            if (error) {
+                break;
+            }
+
+            ProfPGPKey *p_pgpkey = malloc(sizeof(ProfPGPKey));
+            p_pgpkey->id = strdup(key->subkeys->keyid);
+            p_pgpkey->name = strdup(key->uids->uid);
+            p_pgpkey->fp = strdup(key->subkeys->fpr);
+
+            result = g_slist_append(result, p_pgpkey);
+
+            gpgme_key_release(key);
+        }
+    } else {
+        log_error("GPG: Could not list keys. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+    }
+
+    gpgme_release(ctx);
+
+    return result;
+}
+
+GHashTable *
+p_gpg_fingerprints(void)
+{
+    return fingerprints;
+}
+
+const char*
+p_gpg_libver(void)
+{
+    return libversion;
+}
+
+void
+p_gpg_free_key(ProfPGPKey *key)
+{
+    if (key) {
+        free(key->id);
+        free(key->name);
+        free(key->fp);
+        free(key);
+    }
+}
+
+gboolean
+p_gpg_available(const char * const barejid)
+{
+    char *fp = g_hash_table_lookup(fingerprints, barejid);
+    return (fp != NULL);
+}
+
+void
+p_gpg_verify(const char * const barejid, const char *const sign)
+{
+    if (!sign) {
+        return;
+    }
+
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return;
+    }
+
+    gpgme_data_t sign_data;
+    gpgme_data_t plain_data;
+    char *sign_with_header_footer = _add_header_footer(sign, PGP_SIGNATURE_HEADER, PGP_SIGNATURE_FOOTER);
+    gpgme_data_new_from_mem(&sign_data, sign_with_header_footer, strlen(sign_with_header_footer), 1);
+    gpgme_data_new(&plain_data);
+
+    error = gpgme_op_verify(ctx, sign_data, NULL, plain_data);
+    if (error) {
+        log_error("GPG: Failed to verify. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return;
+    }
+
+    gpgme_verify_result_t result = gpgme_op_verify_result(ctx);
+    if (result) {
+        if (result->signatures) {
+            log_debug("Fingerprint found for %s: %s ", barejid, result->signatures->fpr);
+            g_hash_table_replace(fingerprints, strdup(barejid), strdup(result->signatures->fpr));
+        }
+    }
+
+    gpgme_data_release(sign_data);
+    gpgme_data_release(plain_data);
+}
+
+char*
+p_gpg_sign(const char * const str, const char * const fp)
+{
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    gpgme_key_t key = NULL;
+    error = gpgme_get_key(ctx, fp, &key, 1);
+    if (error || key == NULL) {
+        log_error("GPG: Failed to get key. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release (ctx);
+        return NULL;
+    }
+
+    gpgme_signers_clear(ctx);
+    error = gpgme_signers_add(ctx, key);
+    if (error) {
+        log_error("GPG: Failed to load signer. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return NULL;
+    }
+
+    gpgme_data_t str_data;
+    gpgme_data_t signed_data;
+    char *str_or_empty = NULL;
+    if (str) {
+        str_or_empty = strdup(str);
+    } else {
+        str_or_empty = strdup("");
+    }
+    gpgme_data_new_from_mem(&str_data, str_or_empty, strlen(str_or_empty), 1);
+    gpgme_data_new(&signed_data);
+
+    gpgme_set_armor(ctx,1);
+    error = gpgme_op_sign(ctx,str_data,signed_data,GPGME_SIG_MODE_DETACH);
+    if (error) {
+        log_error("GPG: Failed to sign string. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return NULL;
+    }
+
+    char *result = NULL;
+    gpgme_data_release(str_data);
+
+    size_t len = 0;
+    char *signed_str = gpgme_data_release_and_get_mem(signed_data, &len);
+    if (signed_str) {
+        signed_str[len] = 0;
+        result = _remove_header_footer(signed_str, PGP_SIGNATURE_FOOTER);
+    }
+    gpgme_free(signed_str);
+    gpgme_release(ctx);
+    free(str_or_empty);
+
+    return result;
+}
+
+char *
+p_gpg_encrypt(const char * const barejid, const char * const message)
+{
+    char *fp = g_hash_table_lookup(fingerprints, barejid);
+
+    if (!fp) {
+        return NULL;
+    }
+
+    gpgme_key_t keys[2];
+
+    keys[0] = NULL;
+    keys[1] = NULL;
+
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    gpgme_key_t key;
+    error = gpgme_get_key(ctx, fp, &key, 0);
+    if (error || key == NULL) {
+        log_error("GPG: Failed to get key. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return NULL;
+    }
+
+    keys[0] = key;
+
+    gpgme_data_t plain;
+    gpgme_data_t cipher;
+    gpgme_data_new_from_mem(&plain, message, strlen(message), 1);
+    gpgme_data_new(&cipher);
+
+    gpgme_set_armor(ctx, 1);
+    error = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, plain, cipher);
+    if (error) {
+        log_error("GPG: Failed to encrypt message. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return NULL;
+    }
+    gpgme_data_release(plain);
+
+    char *cipher_str = NULL;
+    char *result = NULL;
+    size_t len;
+    cipher_str = gpgme_data_release_and_get_mem(cipher, &len);
+    if (cipher_str) {
+        result = _remove_header_footer(cipher_str, PGP_MESSAGE_FOOTER);
+    }
+
+    gpgme_free(cipher_str);
+    gpgme_release(ctx);
+
+    return result;
+}
+
+char *
+p_gpg_decrypt(const char * const barejid, const char * const cipher)
+{
+    char *cipher_with_headers = _add_header_footer(cipher, PGP_MESSAGE_HEADER, PGP_MESSAGE_FOOTER);
+
+    gpgme_ctx_t ctx;
+    gpgme_error_t error = gpgme_new(&ctx);
+    if (error) {
+        log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    gpgme_data_t plain_data;
+    gpgme_data_t cipher_data;
+    gpgme_data_new_from_mem (&cipher_data, cipher_with_headers, strlen(cipher_with_headers), 1);
+    gpgme_data_new(&plain_data);
+
+    error = gpgme_op_decrypt(ctx, cipher_data, plain_data);
+    if (error) {
+        log_error("GPG: Failed to encrypt message. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        gpgme_release(ctx);
+        return NULL;
+    }
+
+    gpgme_data_release(cipher_data);
+
+    size_t len = 0;
+    char *plain_str = gpgme_data_release_and_get_mem(plain_data, &len);
+    char *result = NULL;
+    if (plain_str) {
+        plain_str[len] = 0;
+        result = g_strdup(plain_str);
+    }
+    gpgme_free(plain_str);
+
+    gpgme_release(ctx);
+
+    return result;
+}
+
+static char*
+_remove_header_footer(char *str, const char * const footer)
+{
+    int pos = 0;
+    int newlines = 0;
+
+    while (newlines < 3) {
+        if (str[pos] == '\n') {
+            newlines++;
+        }
+        pos++;
+
+        if (str[pos] == '\0') {
+            return NULL;
+        }
+    }
+
+    char *stripped = strdup(&str[pos]);
+    char *footer_start = g_strrstr(stripped, footer);
+    footer_start[0] = '\0';
+
+    return stripped;
+}
+
+static char*
+_add_header_footer(const char * const str, const char * const header, const char * const footer)
+{
+    GString *result_str = g_string_new("");
+
+    g_string_append(result_str, header);
+    g_string_append(result_str, "\n\n");
+    g_string_append(result_str, str);
+    g_string_append(result_str, "\n");
+    g_string_append(result_str, footer);
+
+    char *result = result_str->str;
+    g_string_free(result_str, FALSE);
+
+    return result;
+}
diff --git a/src/pgp/gpg.h b/src/pgp/gpg.h
new file mode 100644
index 00000000..74d86568
--- /dev/null
+++ b/src/pgp/gpg.h
@@ -0,0 +1,56 @@
+/*
+ * gpg.h
+ *
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#ifndef GPG_H
+#define GPG_H
+
+typedef struct pgp_key_t {
+    char *id;
+    char *name;
+    char *fp;
+} ProfPGPKey;
+
+void p_gpg_init(void);
+void p_gpg_close(void);
+GSList* p_gpg_list_keys(void);
+GHashTable* p_gpg_fingerprints(void);
+gboolean p_gpg_available(const char * const barejid);
+const char* p_gpg_libver(void);
+void p_gpg_free_key(ProfPGPKey *key);
+char* p_gpg_sign(const char * const str, const char * const fp);
+void p_gpg_verify(const char * const barejid, const char *const sign);
+char* p_gpg_encrypt(const char * const barejid, const char * const message);
+char* p_gpg_decrypt(const char * const barejid, const char * const cipher);
+
+#endif
diff --git a/src/profanity.c b/src/profanity.c
index 0e11a933..569c875e 100644
--- a/src/profanity.c
+++ b/src/profanity.c
@@ -60,6 +60,9 @@
 #ifdef HAVE_LIBOTR
 #include "otr/otr.h"
 #endif
+#ifdef HAVE_LIBGPGME
+#include "pgp/gpg.h"
+#endif
 #include "resource.h"
 #include "xmpp/xmpp.h"
 #include "ui/ui.h"
@@ -246,6 +249,9 @@ _init(const int disable_tls, char *log_level)
 #ifdef HAVE_LIBOTR
     otr_init();
 #endif
+#ifdef HAVE_LIBGPGME
+    p_gpg_init();
+#endif
     atexit(_shutdown);
     ui_input_nonblocking(TRUE);
 }
@@ -270,6 +276,9 @@ _shutdown(void)
 #ifdef HAVE_LIBOTR
     otr_shutdown();
 #endif
+#ifdef HAVE_LIBGPGME
+    p_gpg_close();
+#endif
     chat_log_close();
     prefs_close();
     theme_close();
diff --git a/src/ui/console.c b/src/ui/console.c
index 7e071bd9..ac18c7da 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -710,6 +710,10 @@ cons_show_account(ProfAccount *account)
         g_string_free(always, TRUE);
     }
 
+    if (account->pgp_keyid) {
+        cons_show   ("PGP Key ID        : %s", account->pgp_keyid);
+    }
+
     cons_show       ("Priority          : chat:%d, online:%d, away:%d, xa:%d, dnd:%d",
         account->priority_chat, account->priority_online, account->priority_away,
         account->priority_xa, account->priority_dnd);
@@ -881,6 +885,16 @@ cons_winstidy_setting(void)
 }
 
 void
+cons_encwarn_setting(void)
+{
+    if (prefs_get_boolean(PREF_ENC_WARN)) {
+        cons_show("Warn unencrypted (/encwarn)   : ON");
+    } else {
+        cons_show("Warn unencrypted (/encwarn)   : OFF");
+    }
+}
+
+void
 cons_presence_setting(void)
 {
     if (prefs_get_boolean(PREF_PRESENCE))
@@ -1054,6 +1068,7 @@ cons_show_ui_prefs(void)
     cons_roster_setting();
     cons_privileges_setting();
     cons_titlebar_setting();
+    cons_encwarn_setting();
     cons_presence_setting();
     cons_inpblock_setting();
 
@@ -1394,12 +1409,6 @@ cons_show_otr_prefs(void)
     cons_show("OTR policy (/otr policy) : %s", policy_value);
     prefs_free_string(policy_value);
 
-    if (prefs_get_boolean(PREF_OTR_WARN)) {
-        cons_show("Warn non-OTR (/otr warn) : ON");
-    } else {
-        cons_show("Warn non-OTR (/otr warn) : OFF");
-    }
-
     char *log_value = prefs_get_string(PREF_OTR_LOG);
     if (strcmp(log_value, "on") == 0) {
         cons_show("OTR logging (/otr log)   : ON");
diff --git a/src/ui/core.c b/src/ui/core.c
index 10c5d347..ec5ae9af 100644
--- a/src/ui/core.c
+++ b/src/ui/core.c
@@ -416,21 +416,12 @@ ui_message_receipt(const char * const barejid, const char * const id)
 }
 
 void
-ui_incoming_msg(const char * const barejid, const char * const resource, const char * const message, GTimeVal *tv_stamp)
+ui_incoming_msg(ProfChatWin *chatwin, const char * const resource, const char * const message, GTimeVal *tv_stamp, gboolean win_created)
 {
-    gboolean win_created = FALSE;
-
-    ProfChatWin *chatwin = wins_get_chat(barejid);
-    if (chatwin == NULL) {
-        ProfWin *window = wins_new_chat(barejid);
-        chatwin = (ProfChatWin*)window;
-        win_created = TRUE;
-    }
-
-    ProfWin *window = (ProfWin*) chatwin;
+    ProfWin *window = (ProfWin*)chatwin;
     int num = wins_get_num(window);
 
-    char *display_name = roster_get_msg_display_name(barejid, resource);
+    char *display_name = roster_get_msg_display_name(chatwin->barejid, resource);
 
     // currently viewing chat window with sender
     if (wins_is_current(window)) {
@@ -449,12 +440,12 @@ ui_incoming_msg(const char * const barejid, const char * const resource, const c
 
         chatwin->unread++;
         if (prefs_get_boolean(PREF_CHLOG) && prefs_get_boolean(PREF_HISTORY)) {
-            _win_show_history(chatwin, barejid);
+            _win_show_history(chatwin, chatwin->barejid);
         }
 
         // show users status first, when receiving message via delayed delivery
-        if (tv_stamp && (win_created)) {
-            PContact pcontact = roster_get_contact(barejid);
+        if (tv_stamp && win_created) {
+            PContact pcontact = roster_get_contact(chatwin->barejid);
             if (pcontact) {
                 win_show_contact(window, pcontact);
             }
@@ -1277,12 +1268,6 @@ ui_new_chat_win(const char * const barejid)
     ProfWin *window = wins_new_chat(barejid);
     ProfChatWin *chatwin = (ProfChatWin *)window;
 
-#ifdef HAVE_LIBOTR
-    if (otr_is_secure(barejid)) {
-        chatwin->enc_mode = PROF_ENC_OTR;
-    }
-#endif
-
     if (prefs_get_boolean(PREF_CHLOG) && prefs_get_boolean(PREF_HISTORY)) {
         _win_show_history(chatwin, barejid);
     }
diff --git a/src/ui/titlebar.c b/src/ui/titlebar.c
index 746d2782..16a9eaff 100644
--- a/src/ui/titlebar.c
+++ b/src/ui/titlebar.c
@@ -58,9 +58,7 @@ static GTimer *typing_elapsed;
 static void _title_bar_draw(void);
 static void _show_self_presence(void);
 static void _show_contact_presence(ProfChatWin *chatwin);
-#ifdef HAVE_LIBOTR
 static void _show_privacy(ProfChatWin *chatwin);
-#endif
 
 void
 create_title_bar(void)
@@ -174,10 +172,7 @@ _title_bar_draw(void)
         ProfChatWin *chatwin = (ProfChatWin*) current;
         assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
         _show_contact_presence(chatwin);
-
-#ifdef HAVE_LIBOTR
         _show_privacy(chatwin);
-#endif
 
         if (typing) {
             wprintw(win, " (typing...)");
@@ -246,66 +241,81 @@ _show_self_presence(void)
     wattroff(win, bracket_attrs);
 }
 
-#ifdef HAVE_LIBOTR
 static void
 _show_privacy(ProfChatWin *chatwin)
 {
     int bracket_attrs = theme_attrs(THEME_TITLE_BRACKET);
+    int encrypted_attrs = theme_attrs(THEME_TITLE_ENCRYPTED);
+    int unencrypted_attrs = theme_attrs(THEME_TITLE_UNENCRYPTED);
+    int trusted_attrs = theme_attrs(THEME_TITLE_TRUSTED);
+    int untrusted_attrs = theme_attrs(THEME_TITLE_UNTRUSTED);
+
+    switch (chatwin->enc_mode) {
+        case PROF_ENC_NONE:
+            if (prefs_get_boolean(PREF_ENC_WARN)) {
+                wprintw(win, " ");
+                wattron(win, bracket_attrs);
+                wprintw(win, "[");
+                wattroff(win, bracket_attrs);
+                wattron(win, unencrypted_attrs);
+                wprintw(win, "unencrypted");
+                wattroff(win, unencrypted_attrs);
+                wattron(win, bracket_attrs);
+                wprintw(win, "]");
+                wattroff(win, bracket_attrs);
+            }
+            break;
 
-    if (chatwin->enc_mode == PROF_ENC_NONE) {
-        if (prefs_get_boolean(PREF_OTR_WARN)) {
-            int unencrypted_attrs = theme_attrs(THEME_TITLE_UNENCRYPTED);
+        case PROF_ENC_OTR:
             wprintw(win, " ");
             wattron(win, bracket_attrs);
             wprintw(win, "[");
             wattroff(win, bracket_attrs);
-            wattron(win, unencrypted_attrs);
-            wprintw(win, "unencrypted");
-            wattroff(win, unencrypted_attrs);
+            wattron(win, encrypted_attrs);
+            wprintw(win, "OTR");
+            wattroff(win, encrypted_attrs);
             wattron(win, bracket_attrs);
             wprintw(win, "]");
             wattroff(win, bracket_attrs);
-        }
-    } else {
-        int encrypted_attrs = theme_attrs(THEME_TITLE_ENCRYPTED);
-        wprintw(win, " ");
-        wattron(win, bracket_attrs);
-        wprintw(win, "[");
-        wattroff(win, bracket_attrs);
-        wattron(win, encrypted_attrs);
-        wprintw(win, "OTR");
-        wattroff(win, encrypted_attrs);
-        wattron(win, bracket_attrs);
-        wprintw(win, "]");
-        wattroff(win, bracket_attrs);
-        if (chatwin->otr_is_trusted) {
-            int trusted_attrs = theme_attrs(THEME_TITLE_TRUSTED);
-            wprintw(win, " ");
-            wattron(win, bracket_attrs);
-            wprintw(win, "[");
-            wattroff(win, bracket_attrs);
-            wattron(win, trusted_attrs);
-            wprintw(win, "trusted");
-            wattroff(win, trusted_attrs);
-            wattron(win, bracket_attrs);
-            wprintw(win, "]");
-            wattroff(win, bracket_attrs);
-        } else {
-            int untrusted_attrs = theme_attrs(THEME_TITLE_UNTRUSTED);
+            if (chatwin->otr_is_trusted) {
+                wprintw(win, " ");
+                wattron(win, bracket_attrs);
+                wprintw(win, "[");
+                wattroff(win, bracket_attrs);
+                wattron(win, trusted_attrs);
+                wprintw(win, "trusted");
+                wattroff(win, trusted_attrs);
+                wattron(win, bracket_attrs);
+                wprintw(win, "]");
+                wattroff(win, bracket_attrs);
+            } else {
+                wprintw(win, " ");
+                wattron(win, bracket_attrs);
+                wprintw(win, "[");
+                wattroff(win, bracket_attrs);
+                wattron(win, untrusted_attrs);
+                wprintw(win, "untrusted");
+                wattroff(win, untrusted_attrs);
+                wattron(win, bracket_attrs);
+                wprintw(win, "]");
+                wattroff(win, bracket_attrs);
+            }
+            break;
+
+        case PROF_ENC_PGP:
             wprintw(win, " ");
             wattron(win, bracket_attrs);
             wprintw(win, "[");
             wattroff(win, bracket_attrs);
-            wattron(win, untrusted_attrs);
-            wprintw(win, "untrusted");
-            wattroff(win, untrusted_attrs);
+            wattron(win, encrypted_attrs);
+            wprintw(win, "PGP");
+            wattroff(win, encrypted_attrs);
             wattron(win, bracket_attrs);
             wprintw(win, "]");
             wattroff(win, bracket_attrs);
-        }
+            break;
     }
 }
-#endif
 
 static void
 _show_contact_presence(ProfChatWin *chatwin)
diff --git a/src/ui/ui.h b/src/ui/ui.h
index 396bae1c..b5bfb482 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -101,7 +101,7 @@ void ui_handle_stanza(const char * const msg);
 // ui events
 void ui_contact_online(char *barejid, Resource *resource, GDateTime *last_activity);
 void ui_contact_typing(const char * const barejid, const char * const resource);
-void ui_incoming_msg(const char * const from, const char * const resource,  const char * const message, GTimeVal *tv_stamp);
+void ui_incoming_msg(ProfChatWin *chatwin, const char * const resource,  const char * const message, GTimeVal *tv_stamp, gboolean win_created);
 void ui_incoming_private_msg(const char * const fulljid, const char * const message, GTimeVal *tv_stamp);
 void ui_message_receipt(const char * const barejid, const char * const id);
 
@@ -288,6 +288,7 @@ void cons_privileges_setting(void);
 void cons_beep_setting(void);
 void cons_flash_setting(void);
 void cons_splash_setting(void);
+void cons_encwarn_setting(void);
 void cons_vercheck_setting(void);
 void cons_occupants_setting(void);
 void cons_roster_setting(void);
diff --git a/src/ui/win_types.h b/src/ui/win_types.h
index 7e757b88..3214fa94 100644
--- a/src/ui/win_types.h
+++ b/src/ui/win_types.h
@@ -91,7 +91,8 @@ typedef enum {
 
 typedef enum {
     PROF_ENC_NONE,
-    PROF_ENC_OTR
+    PROF_ENC_OTR,
+    PROF_ENC_PGP
 } prof_enc_t;
 
 typedef struct prof_win_t {
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index bc702199..4cc440ea 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -50,6 +50,7 @@
 #include "roster_list.h"
 #include "xmpp/stanza.h"
 #include "xmpp/xmpp.h"
+#include "pgp/gpg.h"
 
 #define HANDLE(ns, type, func) xmpp_handler_add(conn, func, ns, STANZA_NAME_MESSAGE, type, ctx)
 
@@ -76,37 +77,57 @@ message_add_handlers(void)
     HANDLE(STANZA_NS_RECEIPTS,   NULL,                   _receipt_received_handler);
 }
 
-char *
-message_send_chat(const char * const barejid, const char * const msg)
+static char*
+_session_jid(const char * const barejid)
 {
-    xmpp_conn_t * const conn = connection_get_conn();
-    xmpp_ctx_t * const ctx = connection_get_ctx();
-
     ChatSession *session = chat_session_get(barejid);
-    char *state = NULL;
     char *jid = NULL;
     if (session) {
-        if (prefs_get_boolean(PREF_STATES) && session->send_states) {
-            state = STANZA_NAME_ACTIVE;
-        }
         Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource);
         jid = strdup(jidp->fulljid);
         jid_destroy(jidp);
+    } else {
+        jid = strdup(barejid);
+    }
 
+    return jid;
+}
+
+static char*
+_session_state(const char * const barejid)
+{
+    ChatSession *session = chat_session_get(barejid);
+    char *state = NULL;
+    if (session) {
+        if (prefs_get_boolean(PREF_STATES) && session->send_states) {
+            state = STANZA_NAME_ACTIVE;
+        }
     } else {
         if (prefs_get_boolean(PREF_STATES)) {
             state = STANZA_NAME_ACTIVE;
         }
-        jid = strdup(barejid);
     }
 
+    return state;
+}
+
+char *
+message_send_chat(const char * const barejid, const char * const msg)
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+
+    char *state = _session_state(barejid);
+    char *jid = _session_jid(barejid);
     char *id = create_unique_id("msg");
+
     xmpp_stanza_t *message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, msg);
     free(jid);
 
     if (state) {
         stanza_attach_state(ctx, message, state);
     }
+
     if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) {
         stanza_attach_receipt_request(ctx, message);
     }
@@ -118,36 +139,80 @@ message_send_chat(const char * const barejid, const char * const msg)
 }
 
 char *
-message_send_chat_encrypted(const char * const barejid, const char * const msg)
+message_send_chat_pgp(const char * const barejid, const char * const msg)
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
 
-    ChatSession *session = chat_session_get(barejid);
-    char *state = NULL;
-    char *jid = NULL;
-    if (session) {
-        if (prefs_get_boolean(PREF_STATES) && session->send_states) {
-            state = STANZA_NAME_ACTIVE;
+    char *state = _session_state(barejid);
+    char *jid = _session_jid(barejid);
+    char *id = create_unique_id("msg");
+
+    xmpp_stanza_t *message = NULL;
+#ifdef HAVE_LIBGPGME
+    char *account_name = jabber_get_account_name();
+    ProfAccount *account = accounts_get_account(account_name);
+    if (account->pgp_keyid) {
+        Jid *jidp = jid_create(jid);
+        char *encrypted = p_gpg_encrypt(jidp->barejid, msg);
+        if (encrypted) {
+            message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, "This message is encrypted.");
+            xmpp_stanza_t *x = xmpp_stanza_new(ctx);
+            xmpp_stanza_set_name(x, STANZA_NAME_X);
+            xmpp_stanza_set_ns(x, STANZA_NS_ENCRYPTED);
+            xmpp_stanza_t *enc_st = xmpp_stanza_new(ctx);
+            xmpp_stanza_set_text(enc_st, encrypted);
+            xmpp_stanza_add_child(x, enc_st);
+            xmpp_stanza_release(enc_st);
+            xmpp_stanza_add_child(message, x);
+            xmpp_stanza_release(x);
+            free(encrypted);
+        } else {
+            message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, msg);
         }
-        Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource);
-        jid = strdup(jidp->fulljid);
-        jid_destroy(jidp);
     } else {
-        if (prefs_get_boolean(PREF_STATES)) {
-            state = STANZA_NAME_ACTIVE;
-        }
-        jid = strdup(barejid);
+        message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, msg);
     }
+#else
+    message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, msg);
+#endif
+    free(jid);
 
+    if (state) {
+        stanza_attach_state(ctx, message, state);
+    }
+
+    stanza_attach_carbons_private(ctx, message);
+
+    if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) {
+        stanza_attach_receipt_request(ctx, message);
+    }
+
+    xmpp_send(conn, message);
+    xmpp_stanza_release(message);
+
+    return id;
+}
+
+char *
+message_send_chat_otr(const char * const barejid, const char * const msg)
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+
+    char *state = _session_state(barejid);
+    char *jid = _session_jid(barejid);
     char *id = create_unique_id("msg");
+
     xmpp_stanza_t *message = stanza_create_message(ctx, id, barejid, STANZA_TYPE_CHAT, msg);
     free(jid);
 
     if (state) {
         stanza_attach_state(ctx, message, state);
     }
+
     stanza_attach_carbons_private(ctx, message);
+
     if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) {
         stanza_attach_receipt_request(ctx, message);
     }
@@ -640,11 +705,11 @@ _handle_carbons(xmpp_stanza_t * const stanza)
             if (message) {
                 // if we are the recipient, treat as standard incoming message
                 if(g_strcmp0(my_jid->barejid, jid_to->barejid) == 0){
-                    sv_ev_incoming_message(jid_from->barejid, jid_from->resourcepart, message);
+                    sv_ev_incoming_carbon(jid_from->barejid, jid_from->resourcepart, message);
                 }
                 // else treat as a sent message
                 else{
-                    sv_ev_carbon(jid_to->barejid, message);
+                    sv_ev_outgoing_carbon(jid_to->barejid, message);
                 }
                 xmpp_free(ctx, message);
             }
@@ -703,7 +768,12 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * con
             if (delayed) {
                 sv_ev_delayed_message(jid->barejid, message, tv_stamp);
             } else {
-                sv_ev_incoming_message(jid->barejid, jid->resourcepart, 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);
             }
 
             _receipt_request_handler(stanza);
diff --git a/src/xmpp/presence.c b/src/xmpp/presence.c
index e46730e3..4cf648dc 100644
--- a/src/xmpp/presence.c
+++ b/src/xmpp/presence.c
@@ -193,7 +193,7 @@ presence_reset_sub_request_search(void)
 }
 
 void
-presence_send(const resource_presence_t presence_type, const char * const msg, const int idle)
+presence_send(const resource_presence_t presence_type, const char * const msg, const int idle, char *signed_status)
 {
     if (jabber_get_connection_status() != JABBER_CONNECTED) {
         log_warning("Error setting presence, not connected.");
@@ -218,7 +218,21 @@ presence_send(const resource_presence_t presence_type, const char * const msg, c
     char *id = create_unique_id("presence");
     xmpp_stanza_set_id(presence, id);
     stanza_attach_show(ctx, presence, show);
+
     stanza_attach_status(ctx, presence, msg);
+
+    if (signed_status) {
+        xmpp_stanza_t *x = xmpp_stanza_new(ctx);
+        xmpp_stanza_set_name(x, STANZA_NAME_X);
+        xmpp_stanza_set_ns(x, STANZA_NS_SIGNED);
+        xmpp_stanza_t *signed_text = xmpp_stanza_new(ctx);
+        xmpp_stanza_set_text(signed_text, signed_status);
+        xmpp_stanza_add_child(x, signed_text);
+        xmpp_stanza_release(signed_text);
+        xmpp_stanza_add_child(presence, x);
+        xmpp_stanza_release(x);
+    }
+
     stanza_attach_priority(ctx, presence, pri);
     stanza_attach_last_activity(ctx, presence, idle);
     stanza_attach_caps(ctx, presence);
@@ -603,7 +617,14 @@ _available_handler(xmpp_conn_t * const conn,
     if (g_strcmp0(xmpp_presence->jid->barejid, my_jid->barejid) == 0) {
         connection_add_available_resource(resource);
     } else {
-        sv_ev_contact_online(xmpp_presence->jid->barejid, resource, xmpp_presence->last_activity);
+        char *pgpsig = NULL;
+        xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_SIGNED);
+        if (x) {
+            pgpsig = xmpp_stanza_get_text(x);
+        }
+        sv_ev_contact_online(xmpp_presence->jid->barejid, resource, xmpp_presence->last_activity, pgpsig);
+        xmpp_ctx_t *ctx = connection_get_ctx();
+        xmpp_free(ctx, pgpsig);
     }
 
     jid_destroy(my_jid);
@@ -783,4 +804,4 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void *
     jid_destroy(from_jid);
 
     return 1;
-}
\ No newline at end of file
+}
diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h
index 89dbda57..042b6aea 100644
--- a/src/xmpp/stanza.h
+++ b/src/xmpp/stanza.h
@@ -160,6 +160,8 @@
 #define STANZA_NS_CARBONS "urn:xmpp:carbons:2"
 #define STANZA_NS_FORWARD "urn:xmpp:forward:0"
 #define STANZA_NS_RECEIPTS "urn:xmpp:receipts"
+#define STANZA_NS_SIGNED "jabber:x:signed"
+#define STANZA_NS_ENCRYPTED "jabber:x:encrypted"
 
 #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo"
 
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index 6b985b08..575f9ae1 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -146,7 +146,8 @@ GList * jabber_get_available_resources(void);
 
 // message functions
 char* message_send_chat(const char * const barejid, const char * const msg);
-char* message_send_chat_encrypted(const char * const barejid, const char * const msg);
+char* message_send_chat_otr(const char * const barejid, const char * const msg);
+char* message_send_chat_pgp(const char * const barejid, const char * const msg);
 void message_send_private(const char * const fulljid, const char * const msg);
 void message_send_groupchat(const char * const roomjid, const char * const msg);
 void message_send_groupchat_subject(const char * const roomjid, const char * const subject);
@@ -168,8 +169,7 @@ char * presence_sub_request_find(const char * const search_str);
 void presence_join_room(char *room, char *nick, char * passwd);
 void presence_change_room_nick(const char * const room, const char * const nick);
 void presence_leave_chat_room(const char * const room_jid);
-void presence_send(resource_presence_t status, const char * const msg,
-    int idle);
+void presence_send(resource_presence_t status, const char * const msg, int idle, char *signed_status);
 gboolean presence_sub_request_exists(const char * const bare_jid);
 
 // iq functions