From 2d3537515d9b0dbba51265ed7819dd80b31a33d8 Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 23 Jun 2015 20:37:42 +0100 Subject: [PATCH 1/5] Added AC_SYS_LARGEFILE to configure.ac --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index e945155a..139d8520 100644 --- a/configure.ac +++ b/configure.ac @@ -11,6 +11,9 @@ AM_INIT_AUTOMAKE([foreign subdir-objects]) ### Checks for programs. AC_PROG_CC +## Check for LFS +AC_SYS_LARGEFILE + ### Get canonical host AC_CANONICAL_HOST PLATFORM="unknown" From 02cd8d2330abf589679d089bfcf20ac76825f658 Mon Sep 17 00:00:00 2001 From: Dmitry Podgorny Date: Tue, 23 Jun 2015 22:03:09 +0000 Subject: [PATCH 2/5] ui: decreased log level of the message on resizing Resizing of terminal window can produce many logs "Resizing UI". Also they are not so informative to be shown without -l DEBUG. --- src/ui/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/core.c b/src/ui/core.c index ec5ae9af..a7e46437 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -254,7 +254,7 @@ ui_resize(void) resizeterm(w.ws_row, w.ws_col); refresh(); - log_info("Resizing UI"); + log_debug("Resizing UI"); title_bar_resize(); wins_resize_all(); status_bar_resize(); From 6a73ca1b5281c5a64cb7bc6e70775c5022ab276a Mon Sep 17 00:00:00 2001 From: Dmitry Podgorny Date: Tue, 23 Jun 2015 22:13:50 +0000 Subject: [PATCH 3/5] pgp: Fixed compilation error when libotr is not supported --- src/event/server_events.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/event/server_events.c b/src/event/server_events.c index 004e743b..c1aa7c01 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -254,9 +254,8 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *enc_m // 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); + char *decrypted = p_gpg_decrypt(barejid, enc_message); if (decrypted) { ui_incoming_msg(chatwin, resource, decrypted, NULL, new_win); chat_log_pgp_msg_in(barejid, decrypted); From 358e9d6af0677354fa70fa089124de07d8423eb7 Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 23 Jun 2015 23:29:10 +0100 Subject: [PATCH 4/5] Added /pgp setkey command --- src/command/command.c | 21 +++-- src/command/commands.c | 37 ++++++++- src/event/server_events.c | 7 ++ src/pgp/gpg.c | 147 +++++++++++++++++++++++++++++++++ src/pgp/gpg.h | 3 + tests/unittests/pgp/stub_gpg.c | 9 ++ 6 files changed, 215 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 #include #include +#include +#include #include +#include #include #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); diff --git a/tests/unittests/pgp/stub_gpg.c b/tests/unittests/pgp/stub_gpg.c index 03b58b55..671b2092 100644 --- a/tests/unittests/pgp/stub_gpg.c +++ b/tests/unittests/pgp/stub_gpg.c @@ -38,3 +38,12 @@ char * p_gpg_decrypt(const char * const barejid, const char * const cipher) { return NULL; } + +void p_gpg_on_connect(const char * const barejid) {} +void p_gpg_on_disconnect(void) {} + +gboolean p_gpg_addkey(const char * const jid, const char * const keyid) +{ + return TRUE; +} + From c47688f37c2048221594f9424d918ff552bee69f Mon Sep 17 00:00:00 2001 From: Dmitry Podgorny Date: Tue, 23 Jun 2015 22:33:51 +0000 Subject: [PATCH 5/5] autotools: Don't link profanity with libstabber --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 139d8520..1bf05a92 100644 --- a/configure.ac +++ b/configure.ac @@ -241,7 +241,7 @@ AM_CONDITIONAL([THEMES_INSTALL], "$THEMES_INSTALL") PKG_CHECK_MODULES([cmocka], [cmocka], [], [AC_MSG_NOTICE([cmocka is not found, will not be able to run tests])]) -AC_CHECK_LIB([stabber], [stbbr_start], [], +AC_CHECK_LIB([stabber], [stbbr_start], [LIBS="$LIBS"], [AC_MSG_NOTICE([stabber not found, will not be able to run functional tests])]) ### Check for ncursesw/ncurses.h first, Arch linux uses ncurses.h for ncursesw