about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorJames Booth <boothj5@gmail.com>2015-06-23 23:29:10 +0100
committerJames Booth <boothj5@gmail.com>2015-06-23 23:29:10 +0100
commit358e9d6af0677354fa70fa089124de07d8423eb7 (patch)
tree71554c57d40f5267307e0b815bd50be6b397400c /src
parent2d3537515d9b0dbba51265ed7819dd80b31a33d8 (diff)
downloadprofani-tty-358e9d6af0677354fa70fa089124de07d8423eb7.tar.gz
Added /pgp setkey command
Diffstat (limited to 'src')
-rw-r--r--src/command/command.c21
-rw-r--r--src/command/commands.c37
-rw-r--r--src/event/server_events.c7
-rw-r--r--src/pgp/gpg.c147
-rw-r--r--src/pgp/gpg.h3
5 files changed, 206 insertions, 9 deletions
diff --git a/src/command/command.c b/src/command/command.c
index c43e968e..dd160adc 100644
--- a/src/command/command.c
+++ b/src/command/command.c
@@ -869,18 +869,19 @@ static struct cmd_t command_defs[] =
           NULL } } },
 
     { "/pgp",
-        cmd_pgp, parse_args, 1, 2, NULL,
+        cmd_pgp, parse_args, 1, 3, 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.",
+          "keys                 : List all keys.",
+          "libver               : Show which version of the libgpgme library is being used.",
+          "fps                  : Show known fingerprints.",
+          "setkey contact keyid : Manually associate a key ID with a JID.",
+          "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",
@@ -1611,6 +1612,7 @@ cmd_init(void)
     pgp_ac = autocomplete_new();
     autocomplete_add(pgp_ac, "keys");
     autocomplete_add(pgp_ac, "fps");
+    autocomplete_add(pgp_ac, "setkey");
     autocomplete_add(pgp_ac, "libver");
     autocomplete_add(pgp_ac, "start");
     autocomplete_add(pgp_ac, "end");
@@ -2503,6 +2505,11 @@ _pgp_autocomplete(ProfWin *window, const char * const input)
         return found;
     }
 
+    found = autocomplete_param_with_func(input, "/pgp setkey", roster_barejid_autocomplete);
+    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 f865b873..65fdeb98 100644
--- a/src/command/commands.c
+++ b/src/command/commands.c
@@ -680,6 +680,9 @@ cmd_disconnect(ProfWin *window, gchar **args, struct cmd_help_t help)
         muc_invites_clear();
         chat_sessions_clear();
         ui_disconnected();
+#ifdef HAVE_LIBGPGME
+        p_gpg_on_disconnect();
+#endif
         free(jid);
     } else {
         cons_show("You are not currently connected.");
@@ -4192,6 +4195,35 @@ cmd_pgp(ProfWin *window, gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
+    if (g_strcmp0(args[0], "setkey") == 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;
+        }
+
+        char *jid = args[1];
+        if (!args[1]) {
+            cons_show("Usage: %s", help.usage);
+            return TRUE;
+        }
+
+        char *keyid = args[2];
+        if (!args[2]) {
+            cons_show("Usage: %s", help.usage);
+            return TRUE;
+        }
+
+        gboolean res = p_gpg_addkey(jid, keyid);
+        if (!res) {
+            cons_show("Key ID not found.");
+        } else {
+            cons_show("Key %s set for %s.", keyid, jid);
+        }
+
+        return TRUE;
+    }
+
     if (g_strcmp0(args[0], "fps") == 0) {
         jabber_conn_status_t conn_status = jabber_get_connection_status();
         if (conn_status != JABBER_CONNECTED) {
@@ -4201,11 +4233,11 @@ cmd_pgp(ProfWin *window, gchar **args, struct cmd_help_t help)
         GHashTable *fingerprints = p_gpg_fingerprints();
         GList *jids = g_hash_table_get_keys(fingerprints);
         if (!jids) {
-            cons_show("No PGP fingerprints received.");
+            cons_show("No PGP fingerprints available.");
             return TRUE;
         }
 
-        cons_show("Received PGP fingerprints:");
+        cons_show("Known PGP fingerprints:");
         GList *curr = jids;
         while (curr) {
             char *jid = curr->data;
@@ -4314,6 +4346,7 @@ cmd_pgp(ProfWin *window, gchar **args, struct cmd_help_t help)
         return TRUE;
     }
 
+    cons_show("Usage: %s", help.usage);
     return TRUE;
 #else
     cons_show("This version of Profanity has not been built with PGP support enabled");
diff --git a/src/event/server_events.c b/src/event/server_events.c
index 004e743b..ff0263cb 100644
--- a/src/event/server_events.c
+++ b/src/event/server_events.c
@@ -63,6 +63,10 @@ sv_ev_login_account_success(char *account_name)
     otr_on_connect(account);
 #endif
 
+#ifdef HAVE_LIBGPGME
+    p_gpg_on_connect(account->jid);
+#endif
+
     ui_handle_login_account_success(account);
 
     // attempt to rejoin rooms with passwords
@@ -97,6 +101,9 @@ sv_ev_lost_connection(void)
     muc_invites_clear();
     chat_sessions_clear();
     ui_disconnected();
+#ifdef HAVE_LIBGPGME
+    p_gpg_on_disconnect();
+#endif
 }
 
 void
diff --git a/src/pgp/gpg.c b/src/pgp/gpg.c
index ce40690f..e02a3595 100644
--- a/src/pgp/gpg.c
+++ b/src/pgp/gpg.c
@@ -35,12 +35,16 @@
 #include <locale.h>
 #include <string.h>
 #include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
 
 #include <glib.h>
+#include <glib/gstdio.h>
 #include <gpgme.h>
 
 #include "pgp/gpg.h"
 #include "log.h"
+#include "common.h"
 
 #define PGP_SIGNATURE_HEADER "-----BEGIN PGP SIGNATURE-----"
 #define PGP_SIGNATURE_FOOTER "-----END PGP SIGNATURE-----"
@@ -50,8 +54,12 @@
 static const char *libversion;
 static GHashTable *fingerprints;
 
+static gchar *fpsloc;
+static GKeyFile *fpskeyfile;
+
 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_fps(void);
 
 void
 p_gpg_init(void)
@@ -67,6 +75,135 @@ void
 p_gpg_close(void)
 {
     g_hash_table_destroy(fingerprints);
+    fingerprints = NULL;
+
+    g_key_file_free(fpskeyfile);
+    fpskeyfile = NULL;
+
+    free(fpsloc);
+    fpsloc = NULL;
+}
+
+void
+p_gpg_on_connect(const char * const barejid)
+{
+    gchar *data_home = xdg_get_data_home();
+    GString *fpsfile = g_string_new(data_home);
+    free(data_home);
+
+    gchar *account_dir = str_replace(barejid, "@", "_at_");
+    g_string_append(fpsfile, "/profanity/pgp/");
+    g_string_append(fpsfile, account_dir);
+    free(account_dir);
+
+    // mkdir if doesn't exist for account
+    errno = 0;
+    int res = g_mkdir_with_parents(fpsfile->str, S_IRWXU);
+    if (res == -1) {
+        char *errmsg = strerror(errno);
+        if (errmsg) {
+            log_error("Error creating directory: %s, %s", fpsfile->str, errmsg);
+        } else {
+            log_error("Error creating directory: %s", fpsfile->str);
+        }
+    }
+
+    // create or read fingerprints keyfile
+    g_string_append(fpsfile, "/fingerprints");
+    fpsloc = fpsfile->str;
+    g_string_free(fpsfile, FALSE);
+
+    if (g_file_test(fpsloc, G_FILE_TEST_EXISTS)) {
+        g_chmod(fpsloc, S_IRUSR | S_IWUSR);
+    }
+
+    fpskeyfile = g_key_file_new();
+    g_key_file_load_from_file(fpskeyfile, fpsloc, G_KEY_FILE_KEEP_COMMENTS, NULL);
+
+    // load each keyid
+    gsize len = 0;
+    gchar **jids = g_key_file_get_groups(fpskeyfile, &len);
+
+    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));
+        g_strfreev(jids);
+        return;
+    }
+
+    int i = 0;
+    for (i = 0; i < len; i++) {
+        GError *gerr = NULL;
+        gchar *jid = jids[i];
+        gchar *keyid = g_key_file_get_string(fpskeyfile, jid, "keyid", &gerr);
+        if (gerr) {
+            log_error("Error loading PGP key id for %s", jid);
+            g_error_free(gerr);
+        } else {
+            gpgme_key_t key = NULL;
+            error = gpgme_get_key(ctx, keyid, &key, 1);
+            if (error || key == NULL) {
+                log_error("GPG: Failed to get key. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+                continue;
+            }
+
+            g_hash_table_replace(fingerprints, strdup(jid), strdup(key->subkeys->fpr));
+            gpgme_key_release(key);
+        }
+    }
+
+    gpgme_release(ctx);
+    g_strfreev(jids);
+
+    _save_fps();
+}
+
+void
+p_gpg_on_disconnect(void)
+{
+    if (fingerprints) {
+        g_hash_table_destroy(fingerprints);
+        fingerprints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+    }
+
+    if (fpskeyfile) {
+        g_key_file_free(fpskeyfile);
+        fpskeyfile = NULL;
+    }
+
+    if (fpsloc) {
+        free(fpsloc);
+        fpsloc = NULL;
+    }
+}
+
+gboolean
+p_gpg_addkey(const char * const jid, const char * const keyid)
+{
+    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 FALSE;
+    }
+
+    gpgme_key_t key = NULL;
+    error = gpgme_get_key(ctx, keyid, &key, 1);
+    if (error || key == NULL) {
+        log_error("GPG: Failed to get key. %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return FALSE;
+    }
+
+    // save to ID keyfile
+    g_key_file_set_string(fpskeyfile, jid, "keyid", keyid);
+    _save_fps();
+
+    // update in memory fingerprint list
+    g_hash_table_replace(fingerprints, strdup(jid), strdup(key->subkeys->fpr));
+    gpgme_key_release(key);
+
+    return TRUE;
 }
 
 GSList *
@@ -378,3 +515,13 @@ _add_header_footer(const char * const str, const char * const header, const char
 
     return result;
 }
+
+static void
+_save_fps(void)
+{
+    gsize g_data_size;
+    gchar *g_fps_data = g_key_file_to_data(fpskeyfile, &g_data_size, NULL);
+    g_file_set_contents(fpsloc, g_fps_data, g_data_size, NULL);
+    g_chmod(fpsloc, S_IRUSR | S_IWUSR);
+    g_free(g_fps_data);
+}
diff --git a/src/pgp/gpg.h b/src/pgp/gpg.h
index 74d86568..07c99465 100644
--- a/src/pgp/gpg.h
+++ b/src/pgp/gpg.h
@@ -43,7 +43,10 @@ typedef struct pgp_key_t {
 
 void p_gpg_init(void);
 void p_gpg_close(void);
+void p_gpg_on_connect(const char * const barejid);
+void p_gpg_on_disconnect(void);
 GSList* p_gpg_list_keys(void);
+gboolean p_gpg_addkey(const char * const jid, const char * const keyid);
 GHashTable* p_gpg_fingerprints(void);
 gboolean p_gpg_available(const char * const barejid);
 const char* p_gpg_libver(void);