diff --git a/src/command/command.c b/src/command/command.c index f6205169..cac4d488 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -59,6 +59,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" @@ -1142,38 +1145,43 @@ static struct cmd_t command_defs[] = { "/pgp", cmd_pgp, parse_args, 1, 3, NULL, CMD_TAGS( - CMD_TAG_CHAT) + CMD_TAG_CHAT, + CMD_TAG_UI) CMD_SYN( "/pgp libver", "/pgp keys", - "/pgp fps", + "/pgp contacts", "/pgp setkey ", "/pgp start []", "/pgp end", - "/pgp log on|off|redact") + "/pgp log on|off|redact", + "/pgp char ") CMD_DESC( "Open PGP commands to manage keys, and perform PGP encryption during chat sessions. " "See the /account command to set your own PGP key.") CMD_ARGS( { "libver", "Show which version of the libgpgme library is being used." }, - { "keys", "List all keys." }, - { "fps", "Show known fingerprints." }, - { "setkey ", "Manually associate a key ID with a JID." }, + { "keys", "List all keys known to the system." }, + { "contacts", "Show contacts with assigned public keys." }, + { "setkey ", "Manually associate a contact with a public key." }, { "start []", "Start PGP encrypted chat, current contact will be used if not specified." }, { "end", "End PGP encrypted chat with the current recipient." }, { "log on|off", "Enable or disable plaintext logging of PGP encrypted messages." }, - { "log redact", "Log PGP encrypted messages, but replace the contents with [redacted]. This is the default." }) + { "log redact", "Log PGP encrypted messages, but replace the contents with [redacted]. This is the default." }, + { "char ", "Set the character to be displayed next to PGP encrypted messages." }) CMD_EXAMPLES( "/pgp log off", "/pgp setkey buddy@buddychat.org BA19CACE5A9592C5", "/pgp start buddy@buddychat.org", - "/pgp end") + "/pgp end", + "/pgp char P") }, { "/otr", cmd_otr, parse_args, 1, 3, NULL, CMD_TAGS( - CMD_TAG_CHAT) + CMD_TAG_CHAT, + CMD_TAG_UI) CMD_SYN( "/otr libver", "/otr gen", @@ -1185,7 +1193,8 @@ static struct cmd_t command_defs[] = "/otr question ", "/otr answer ", "/otr policy manual|opportunistic|always", - "/otr log on|off|redact") + "/otr log on|off|redact", + "/otr char ") CMD_DESC( "Off The Record (OTR) commands to manage keys, and perform OTR encryption during chat sessions.") CMD_ARGS( @@ -1203,7 +1212,8 @@ static struct cmd_t command_defs[] = { "policy opportunistic", "Set the global OTR policy to opportunistic, and OTR sessions will be attempted upon starting a conversation." }, { "policy always", "Set the global OTR policy to always, an error will be displayed if an OTR session cannot be initiated upon starting a conversation." }, { "log on|off", "Enable or disable plaintext logging of OTR encrypted messages." }, - { "log redact", "Log OTR encrypted messages, but replace the contents with [redacted]. This is the default." }) + { "log redact", "Log OTR encrypted messages, but replace the contents with [redacted]. This is the default." }, + { "char ", "Set the character to be displayed next to OTR encrypted messages." }) CMD_EXAMPLES( "/otr log off", "/otr policy manual", @@ -1212,7 +1222,8 @@ static struct cmd_t command_defs[] = "/otr myfp", "/otr theirfp", "/otr question \"What is the name of my rabbit?\" fiffi", - "/otr end") + "/otr end", + "/otr char *") }, { "/outtype", @@ -1946,6 +1957,7 @@ cmd_init(void) autocomplete_add(otr_ac, "policy"); autocomplete_add(otr_ac, "question"); autocomplete_add(otr_ac, "answer"); + autocomplete_add(otr_ac, "char"); otr_log_ac = autocomplete_new(); autocomplete_add(otr_log_ac, "on"); @@ -2053,12 +2065,13 @@ cmd_init(void) pgp_ac = autocomplete_new(); autocomplete_add(pgp_ac, "keys"); - autocomplete_add(pgp_ac, "fps"); + autocomplete_add(pgp_ac, "contacts"); autocomplete_add(pgp_ac, "setkey"); autocomplete_add(pgp_ac, "libver"); autocomplete_add(pgp_ac, "start"); autocomplete_add(pgp_ac, "end"); autocomplete_add(pgp_ac, "log"); + autocomplete_add(pgp_ac, "char"); pgp_log_ac = autocomplete_new(); autocomplete_add(pgp_log_ac, "on"); @@ -2242,6 +2255,9 @@ cmd_reset_autocomplete(ProfWin *window) accounts_reset_enabled_search(); prefs_reset_boolean_choice(); presence_reset_sub_request_search(); +#ifdef HAVE_LIBGPGME + p_gpg_autocomplete_key_reset(); +#endif autocomplete_reset(help_ac); autocomplete_reset(help_commands_ac); autocomplete_reset(notify_ac); @@ -2983,6 +2999,26 @@ _pgp_autocomplete(ProfWin *window, const char * const input) return found; } +#ifdef HAVE_LIBGPGME + gboolean result; + gchar **args = parse_args(input, 2, 3, &result); + if ((strncmp(input, "/pgp", 4) == 0) && (result == TRUE)) { + GString *beginning = g_string_new("/pgp "); + g_string_append(beginning, args[0]); + if (args[1]) { + g_string_append(beginning, " "); + g_string_append(beginning, args[1]); + } + found = autocomplete_param_with_func(input, beginning->str, p_gpg_autocomplete_key); + g_string_free(beginning, TRUE); + if (found) { + g_strfreev(args); + return found; + } + } + g_strfreev(args); +#endif + found = autocomplete_param_with_func(input, "/pgp setkey", roster_barejid_autocomplete); if (found) { return found; @@ -3583,6 +3619,17 @@ _account_autocomplete(ProfWin *window, const char * const input) g_strfreev(args); return found; } +#ifdef HAVE_LIBGPGME + } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "pgpkeyid")) == 0) { + g_string_append(beginning, " "); + g_string_append(beginning, args[2]); + found = autocomplete_param_with_func(input, beginning->str, p_gpg_autocomplete_key); + g_string_free(beginning, TRUE); + if (found) { + g_strfreev(args); + return found; + } +#endif } else { found = autocomplete_param_with_ac(input, beginning->str, account_set_ac, TRUE); g_string_free(beginning, TRUE); diff --git a/src/command/commands.c b/src/command/commands.c index ad1e3ff0..7d829e2f 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -4191,7 +4191,17 @@ cmd_pgp(ProfWin *window, const char * const command, gchar **args) return TRUE; } - if (g_strcmp0(args[0], "log") == 0) { + if (strcmp(args[0], "char") == 0) { + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + } else if (strlen(args[1]) != 1) { + cons_bad_cmd_usage(command); + } else { + prefs_set_pgp_char(args[1][0]); + cons_show("PGP char set to %c.", args[1][0]); + } + return TRUE; + } else if (g_strcmp0(args[0], "log") == 0) { char *choice = args[1]; if (g_strcmp0(choice, "on") == 0) { prefs_set_string(PREF_PGP_LOG, "on"); @@ -4270,25 +4280,29 @@ cmd_pgp(ProfWin *window, const char * const command, gchar **args) return TRUE; } - if (g_strcmp0(args[0], "fps") == 0) { + if (g_strcmp0(args[0], "contacts") == 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); + GHashTable *pubkeys = p_gpg_pubkeys(); + GList *jids = g_hash_table_get_keys(pubkeys); if (!jids) { - cons_show("No PGP fingerprints available."); + cons_show("No contacts found with PGP public keys assigned."); return TRUE; } - cons_show("Known PGP fingerprints:"); + cons_show("Assigned PGP public keys:"); GList *curr = jids; while (curr) { char *jid = curr->data; - char *fingerprint = g_hash_table_lookup(fingerprints, jid); - cons_show(" %s: %s", jid, fingerprint); + ProfPGPPubKeyId *pubkeyid = g_hash_table_lookup(pubkeys, jid); + if (pubkeyid->received) { + cons_show(" %s: %s (received)", jid, pubkeyid->id); + } else { + cons_show(" %s: %s (stored)", jid, pubkeyid->id); + } curr = g_list_next(curr); } g_list_free(jids); @@ -4410,7 +4424,17 @@ cmd_otr(ProfWin *window, const char * const command, gchar **args) return TRUE; } - if (strcmp(args[0], "log") == 0) { + if (strcmp(args[0], "char") == 0) { + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + } else if (strlen(args[1]) != 1) { + cons_bad_cmd_usage(command); + } else { + prefs_set_otr_char(args[1][0]); + cons_show("OTR char set to %c.", args[1][0]); + } + return TRUE; + } else if (strcmp(args[0], "log") == 0) { char *choice = args[1]; if (g_strcmp0(choice, "on") == 0) { prefs_set_string(PREF_OTR_LOG, "on"); diff --git a/src/config/preferences.c b/src/config/preferences.c index 7ae9695e..6d63d3e9 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -376,6 +376,60 @@ prefs_get_roster_size(void) } } +char +prefs_get_otr_char(void) +{ + char result = '~'; + + char *resultstr = g_key_file_get_string(prefs, PREF_GROUP_OTR, "otr.char", NULL); + if (!resultstr) { + result = '~'; + } else { + result = resultstr[0]; + } + free(resultstr); + + return result; +} + +void +prefs_set_otr_char(char ch) +{ + char str[2]; + str[0] = ch; + str[1] = '\0'; + + g_key_file_set_string(prefs, PREF_GROUP_OTR, "otr.char", str); + _save_prefs(); +} + +char +prefs_get_pgp_char(void) +{ + char result = '~'; + + char *resultstr = g_key_file_get_string(prefs, PREF_GROUP_PGP, "pgp.char", NULL); + if (!resultstr) { + result = '~'; + } else { + result = resultstr[0]; + } + free(resultstr); + + return result; +} + +void +prefs_set_pgp_char(char ch) +{ + char str[2]; + str[0] = ch; + str[1] = '\0'; + + g_key_file_set_string(prefs, PREF_GROUP_PGP, "pgp.char", str); + _save_prefs(); +} + gboolean prefs_add_alias(const char * const name, const char * const value) { diff --git a/src/config/preferences.h b/src/config/preferences.h index f479f7a6..2a7ab5bf 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -143,6 +143,11 @@ gint prefs_get_roster_size(void); gint prefs_get_autoaway_time(void); void prefs_set_autoaway_time(gint value); +char prefs_get_otr_char(void); +void prefs_set_otr_char(char ch); +char prefs_get_pgp_char(void); +void prefs_set_pgp_char(char ch); + void prefs_add_login(const char *jid); gboolean prefs_add_alias(const char * const name, const char * const value); diff --git a/src/config/theme.c b/src/config/theme.c index ee6bd73f..c4b25c73 100644 --- a/src/config/theme.c +++ b/src/config/theme.c @@ -465,6 +465,21 @@ _load_preferences(void) _set_boolean_preference("intype", PREF_INTYPE); _set_boolean_preference("enc.warn", PREF_ENC_WARN); + + if (g_key_file_has_key(theme, "ui", "otr.char", NULL)) { + gchar *ch = g_key_file_get_string(theme, "ui", "otr.char", NULL); + if (ch && strlen(ch) > 0) { + prefs_set_otr_char(ch[0]); + g_free(ch); + } + } + if (g_key_file_has_key(theme, "ui", "pgp.char", NULL)) { + gchar *ch = g_key_file_get_string(theme, "ui", "pgp.char", NULL); + if (ch && strlen(ch) > 0) { + prefs_set_pgp_char(ch[0]); + g_free(ch); + } + } } static gchar * diff --git a/src/event/client_events.c b/src/event/client_events.c index 2bf48234..f8df6bf4 100644 --- a/src/event/client_events.c +++ b/src/event/client_events.c @@ -97,13 +97,13 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char * const 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); + ui_outgoing_chat_msg(chatwin, msg, id, PROF_ENC_NONE); 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); + ui_outgoing_chat_msg(chatwin, msg, id, PROF_ENC_PGP); free(id); } return; @@ -117,7 +117,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char * const 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); + ui_outgoing_chat_msg(chatwin, msg, id, PROF_ENC_NONE); free(id); } return; @@ -131,12 +131,12 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char * const msg) 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); + ui_outgoing_chat_msg(chatwin, msg, id, PROF_ENC_NONE); 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); + ui_outgoing_chat_msg(chatwin, msg, id, PROF_ENC_PGP); free(id); } return; @@ -148,7 +148,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char * const msg) #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); + ui_outgoing_chat_msg(chatwin, msg, id, PROF_ENC_NONE; free(id); return; #endif diff --git a/src/event/server_events.c b/src/event/server_events.c index 0755aae5..283fc264 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -187,7 +187,7 @@ sv_ev_incoming_carbon(char *barejid, char *resource, char *message) new_win = TRUE; } - ui_incoming_msg(chatwin, resource, message, NULL, new_win); + ui_incoming_msg(chatwin, resource, message, NULL, new_win, PROF_ENC_NONE); chat_log_msg_in(barejid, message); } @@ -215,12 +215,12 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *enc_m if (enc_mode == PROF_ENC_NONE) { win_println((ProfWin*)chatwin, 0, "PGP encryption enabled."); } - ui_incoming_msg(chatwin, resource, decrypted, NULL, new_win); + ui_incoming_msg(chatwin, resource, decrypted, NULL, new_win, PROF_ENC_PGP); chat_log_pgp_msg_in(barejid, decrypted); chatwin->enc_mode = PROF_ENC_PGP; p_gpg_free_decrypted(decrypted); } else { - ui_incoming_msg(chatwin, resource, message, NULL, new_win); + ui_incoming_msg(chatwin, resource, message, NULL, new_win, PROF_ENC_NONE); chat_log_msg_in(barejid, message); chatwin->enc_mode = PROF_ENC_NONE; } @@ -228,14 +228,18 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *enc_m } else { if (enc_mode == PROF_ENC_PGP) { win_println((ProfWin*)chatwin, 0, "PGP encryption disabled."); - ui_incoming_msg(chatwin, resource, message, NULL, new_win); + ui_incoming_msg(chatwin, resource, message, NULL, new_win, PROF_ENC_NONE); 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); + if (decrypted && g_strrstr(message, otr_res) == NULL) { + ui_incoming_msg(chatwin, resource, otr_res, NULL, new_win, PROF_ENC_OTR); + } else { + ui_incoming_msg(chatwin, resource, otr_res, NULL, new_win, PROF_ENC_NONE); + } chat_log_otr_msg_in(barejid, otr_res, decrypted); otr_free_message(otr_res); } @@ -251,7 +255,11 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *enc_m 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); + if (decrypted && g_strrstr(message, otr_res) == NULL) { + ui_incoming_msg(chatwin, resource, otr_res, NULL, new_win, PROF_ENC_OTR); + } else { + ui_incoming_msg(chatwin, resource, otr_res, NULL, new_win, PROF_ENC_NONE); + } chat_log_otr_msg_in(barejid, otr_res, decrypted); otr_free_message(otr_res); } @@ -265,17 +273,17 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *enc_m if (enc_message) { char *decrypted = p_gpg_decrypt(enc_message); if (decrypted) { - ui_incoming_msg(chatwin, resource, decrypted, NULL, new_win); + ui_incoming_msg(chatwin, resource, decrypted, NULL, new_win, PROF_ENC_PGP); chat_log_pgp_msg_in(barejid, decrypted); chatwin->enc_mode = PROF_ENC_PGP; p_gpg_free_decrypted(decrypted); } else { - ui_incoming_msg(chatwin, resource, message, NULL, new_win); + ui_incoming_msg(chatwin, resource, message, NULL, new_win, PROF_ENC_NONE); chat_log_msg_in(barejid, message); chatwin->enc_mode = PROF_ENC_NONE; } } else { - ui_incoming_msg(chatwin, resource, message, NULL, new_win); + ui_incoming_msg(chatwin, resource, message, NULL, new_win, PROF_ENC_NONE); chat_log_msg_in(barejid, message); chatwin->enc_mode = PROF_ENC_NONE; } @@ -286,7 +294,7 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *enc_m // OTR unsupported, PGP unsupported #ifndef HAVE_LIBOTR #ifndef HAVE_LIBGPGME - ui_incoming_msg(chatwin, resource, message, NULL, new_win); + ui_incoming_msg(chatwin, resource, message, NULL, new_win, PROF_ENC_NONE); chat_log_msg_in(barejid, message); chatwin->enc_mode = PROF_ENC_NONE; return; @@ -311,7 +319,7 @@ sv_ev_delayed_message(char *barejid, char *message, GDateTime *timestamp) new_win = TRUE; } - ui_incoming_msg(chatwin, NULL, message, timestamp, new_win); + ui_incoming_msg(chatwin, NULL, message, timestamp, new_win, PROF_ENC_NONE); chat_log_msg_in_delayed(barejid, message, timestamp); } diff --git a/src/otr/otr.c b/src/otr/otr.c index 53433642..7953733d 100644 --- a/src/otr/otr.c +++ b/src/otr/otr.c @@ -325,7 +325,7 @@ otr_on_message_send(ProfChatWin *chatwin, const char * const message) if (encrypted) { id = message_send_chat_otr(chatwin->barejid, encrypted); chat_log_otr_msg_out(chatwin->barejid, message); - ui_outgoing_chat_msg(chatwin, message, id); + ui_outgoing_chat_msg(chatwin, message, id, PROF_ENC_OTR); otr_free_message(encrypted); free(id); return TRUE; @@ -345,7 +345,7 @@ otr_on_message_send(ProfChatWin *chatwin, const char * const message) if (policy == PROF_OTRPOLICY_OPPORTUNISTIC) { char *otr_tagged_msg = otr_tag_message(message); id = message_send_chat_otr(chatwin->barejid, otr_tagged_msg); - ui_outgoing_chat_msg(chatwin, message, id); + ui_outgoing_chat_msg(chatwin, message, id, PROF_ENC_NONE); chat_log_msg_out(chatwin->barejid, message); free(otr_tagged_msg); free(id); diff --git a/src/pgp/gpg.c b/src/pgp/gpg.c index 9d594be4..8d075cd9 100644 --- a/src/pgp/gpg.c +++ b/src/pgp/gpg.c @@ -47,6 +47,7 @@ #include "pgp/gpg.h" #include "log.h" #include "common.h" +#include "tools/autocomplete.h" #define PGP_SIGNATURE_HEADER "-----BEGIN PGP SIGNATURE-----" #define PGP_SIGNATURE_FOOTER "-----END PGP SIGNATURE-----" @@ -54,14 +55,25 @@ #define PGP_MESSAGE_FOOTER "-----END PGP MESSAGE-----" static const char *libversion; -static GHashTable *fingerprints; +static GHashTable *pubkeys; -static gchar *fpsloc; -static GKeyFile *fpskeyfile; +static gchar *pubsloc; +static GKeyFile *pubkeyfile; + +static Autocomplete key_ac; 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); +static void _save_pubkeys(void); + +void +_p_gpg_free_pubkeyid(ProfPGPPubKeyId *pubkeyid) +{ + if (pubkeyid) { + free(pubkeyid->id); + } + free(pubkeyid); +} void p_gpg_init(void) @@ -70,65 +82,72 @@ p_gpg_init(void) 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); + pubkeys = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)_p_gpg_free_pubkeyid); + + key_ac = autocomplete_new(); + GHashTable *keys = p_gpg_list_keys(); + p_gpg_free_keys(keys); } void p_gpg_close(void) { - if (fingerprints) { - g_hash_table_destroy(fingerprints); - fingerprints = NULL; + if (pubkeys) { + g_hash_table_destroy(pubkeys); + pubkeys = NULL; } - if (fpskeyfile) { - g_key_file_free(fpskeyfile); - fpskeyfile = NULL; + if (pubkeyfile) { + g_key_file_free(pubkeyfile); + pubkeyfile = NULL; } - free(fpsloc); - fpsloc = NULL; + free(pubsloc); + pubsloc = NULL; + + autocomplete_free(key_ac); + key_ac = NULL; } void p_gpg_on_connect(const char * const barejid) { gchar *data_home = xdg_get_data_home(); - GString *fpsfile = g_string_new(data_home); + GString *pubsfile = 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); + g_string_append(pubsfile, "/profanity/pgp/"); + g_string_append(pubsfile, account_dir); free(account_dir); // mkdir if doesn't exist for account errno = 0; - int res = g_mkdir_with_parents(fpsfile->str, S_IRWXU); + int res = g_mkdir_with_parents(pubsfile->str, S_IRWXU); if (res == -1) { char *errmsg = strerror(errno); if (errmsg) { - log_error("Error creating directory: %s, %s", fpsfile->str, errmsg); + log_error("Error creating directory: %s, %s", pubsfile->str, errmsg); } else { - log_error("Error creating directory: %s", fpsfile->str); + log_error("Error creating directory: %s", pubsfile->str); } } - // create or read fingerprints keyfile - g_string_append(fpsfile, "/fingerprints"); - fpsloc = fpsfile->str; - g_string_free(fpsfile, FALSE); + // create or read publickeys + g_string_append(pubsfile, "/pubkeys"); + pubsloc = pubsfile->str; + g_string_free(pubsfile, FALSE); - if (g_file_test(fpsloc, G_FILE_TEST_EXISTS)) { - g_chmod(fpsloc, S_IRUSR | S_IWUSR); + if (g_file_test(pubsloc, G_FILE_TEST_EXISTS)) { + g_chmod(pubsloc, S_IRUSR | S_IWUSR); } - fpskeyfile = g_key_file_new(); - g_key_file_load_from_file(fpskeyfile, fpsloc, G_KEY_FILE_KEEP_COMMENTS, NULL); + pubkeyfile = g_key_file_new(); + g_key_file_load_from_file(pubkeyfile, pubsloc, G_KEY_FILE_KEEP_COMMENTS, NULL); // load each keyid gsize len = 0; - gchar **jids = g_key_file_get_groups(fpskeyfile, &len); + gchar **jids = g_key_file_get_groups(pubkeyfile, &len); gpgme_ctx_t ctx; gpgme_error_t error = gpgme_new(&ctx); @@ -143,21 +162,24 @@ p_gpg_on_connect(const char * const barejid) for (i = 0; i < len; i++) { GError *gerr = NULL; gchar *jid = jids[i]; - gchar *keyid = g_key_file_get_string(fpskeyfile, jid, "keyid", &gerr); + gchar *keyid = g_key_file_get_string(pubkeyfile, jid, "keyid", &gerr); if (gerr) { log_error("Error loading PGP key id for %s", jid); g_error_free(gerr); g_free(keyid); } else { gpgme_key_t key = NULL; - error = gpgme_get_key(ctx, keyid, &key, 1); - g_free(keyid); + error = gpgme_get_key(ctx, keyid, &key, 0); if (error || key == NULL) { log_warning("GPG: Failed to get key for %s: %s %s", jid, gpgme_strsource(error), gpgme_strerror(error)); continue; } - g_hash_table_replace(fingerprints, strdup(jid), strdup(key->subkeys->fpr)); + ProfPGPPubKeyId *pubkeyid = malloc(sizeof(ProfPGPPubKeyId)); + pubkeyid->id = strdup(keyid); + pubkeyid->received = FALSE; + g_hash_table_replace(pubkeys, strdup(jid), pubkeyid); + g_free(keyid); gpgme_key_unref(key); } } @@ -165,24 +187,24 @@ p_gpg_on_connect(const char * const barejid) gpgme_release(ctx); g_strfreev(jids); - _save_fps(); + _save_pubkeys(); } 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 (pubkeys) { + g_hash_table_destroy(pubkeys); + pubkeys = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)_p_gpg_free_pubkeyid); } - if (fpskeyfile) { - g_key_file_free(fpskeyfile); - fpskeyfile = NULL; + if (pubkeyfile) { + g_key_file_free(pubkeyfile); + pubkeyfile = NULL; } - free(fpsloc); - fpsloc = NULL; + free(pubsloc); + pubsloc = NULL; } gboolean @@ -196,7 +218,7 @@ p_gpg_addkey(const char * const jid, const char * const keyid) } gpgme_key_t key = NULL; - error = gpgme_get_key(ctx, keyid, &key, 1); + error = gpgme_get_key(ctx, keyid, &key, 0); gpgme_release(ctx); if (error || key == NULL) { @@ -204,12 +226,15 @@ p_gpg_addkey(const char * const jid, const char * const keyid) return FALSE; } - // save to ID keyfile - g_key_file_set_string(fpskeyfile, jid, "keyid", keyid); - _save_fps(); + // save to public key file + g_key_file_set_string(pubkeyfile, jid, "keyid", keyid); + _save_pubkeys(); - // update in memory fingerprint list - g_hash_table_replace(fingerprints, strdup(jid), strdup(key->subkeys->fpr)); + // update in memory pubkeys list + ProfPGPPubKeyId *pubkeyid = malloc(sizeof(ProfPGPPubKeyId)); + pubkeyid->id = strdup(keyid); + pubkeyid->received = FALSE; + g_hash_table_replace(pubkeys, strdup(jid), pubkeyid); gpgme_key_unref(key); return TRUE; @@ -312,6 +337,16 @@ p_gpg_list_keys(void) gpgme_release(ctx); + autocomplete_clear(key_ac); + GList *ids = g_hash_table_get_keys(result); + GList *curr = ids; + while (curr) { + ProfPGPKey *key = g_hash_table_lookup(result, curr->data); + autocomplete_add(key_ac, key->id); + curr = curr->next; + } + g_list_free(ids); + return result; } @@ -323,9 +358,9 @@ p_gpg_free_keys(GHashTable *keys) GHashTable * -p_gpg_fingerprints(void) +p_gpg_pubkeys(void) { - return fingerprints; + return pubkeys; } const char* @@ -366,8 +401,8 @@ p_gpg_valid_key(const char * const keyid) gboolean p_gpg_available(const char * const barejid) { - char *fp = g_hash_table_lookup(fingerprints, barejid); - return (fp != NULL); + char *pubkey = g_hash_table_lookup(pubkeys, barejid); + return (pubkey != NULL); } void @@ -406,8 +441,19 @@ p_gpg_verify(const char * const barejid, const char *const sign) 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_key_t key = NULL; + error = gpgme_get_key(ctx, result->signatures->fpr, &key, 0); + if (error) { + log_debug("Could not find PGP key with ID %s for %s", result->signatures->fpr, barejid); + } else { + log_debug("Fingerprint found for %s: %s ", barejid, key->subkeys->fpr); + ProfPGPPubKeyId *pubkeyid = malloc(sizeof(ProfPGPPubKeyId)); + pubkeyid->id = strdup(key->subkeys->keyid); + pubkeyid->received = TRUE; + g_hash_table_replace(pubkeys, strdup(barejid), pubkeyid); + } + + gpgme_key_unref(key); } } @@ -485,9 +531,11 @@ p_gpg_sign(const char * const str, const char * const fp) char * p_gpg_encrypt(const char * const barejid, const char * const message) { - char *fp = g_hash_table_lookup(fingerprints, barejid); - - if (!fp) { + ProfPGPPubKeyId *pubkeyid = g_hash_table_lookup(pubkeys, barejid); + if (!pubkeyid) { + return NULL; + } + if (!pubkeyid->id) { return NULL; } @@ -504,7 +552,7 @@ p_gpg_encrypt(const char * const barejid, const char * const message) } gpgme_key_t key; - error = gpgme_get_key(ctx, fp, &key, 0); + error = gpgme_get_key(ctx, pubkeyid->id, &key, 0); if (error || key == NULL) { log_error("GPG: Failed to get key. %s %s", gpgme_strsource(error), gpgme_strerror(error)); @@ -580,7 +628,7 @@ p_gpg_decrypt(const char * const cipher) gpgme_recipient_t recipient = res->recipients; if (recipient) { gpgme_key_t key; - error = gpgme_get_key(ctx, recipient->keyid, &key, 0); + error = gpgme_get_key(ctx, recipient->keyid, &key, 1); if (!error && key) { const char *addr = gpgme_key_get_string_attr(key, GPGME_ATTR_EMAIL, NULL, 0); @@ -611,6 +659,18 @@ p_gpg_free_decrypted(char *decrypted) g_free(decrypted); } +char * +p_gpg_autocomplete_key(const char * const search_str) +{ + return autocomplete_complete(key_ac, search_str, TRUE); +} + +void +p_gpg_autocomplete_key_reset(void) +{ + autocomplete_reset(key_ac); +} + static char* _remove_header_footer(char *str, const char * const footer) { @@ -653,11 +713,11 @@ _add_header_footer(const char * const str, const char * const header, const char } static void -_save_fps(void) +_save_pubkeys(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); + gchar *g_pubkeys_data = g_key_file_to_data(pubkeyfile, &g_data_size, NULL); + g_file_set_contents(pubsloc, g_pubkeys_data, g_data_size, NULL); + g_chmod(pubsloc, S_IRUSR | S_IWUSR); + g_free(g_pubkeys_data); } diff --git a/src/pgp/gpg.h b/src/pgp/gpg.h index f4c3f4a2..48742dd7 100644 --- a/src/pgp/gpg.h +++ b/src/pgp/gpg.h @@ -46,6 +46,11 @@ typedef struct pgp_key_t { gboolean secret; } ProfPGPKey; +typedef struct pgp_pubkeyid_t { + char *id; + gboolean received; +} ProfPGPPubKeyId; + void p_gpg_init(void); void p_gpg_close(void); void p_gpg_on_connect(const char * const barejid); @@ -53,7 +58,7 @@ void p_gpg_on_disconnect(void); GHashTable* p_gpg_list_keys(void); void p_gpg_free_keys(GHashTable *keys); gboolean p_gpg_addkey(const char * const jid, const char * const keyid); -GHashTable* p_gpg_fingerprints(void); +GHashTable* p_gpg_pubkeys(void); gboolean p_gpg_valid_key(const char * const keyid); gboolean p_gpg_available(const char * const barejid); const char* p_gpg_libver(void); @@ -62,5 +67,7 @@ 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 cipher); void p_gpg_free_decrypted(char *decrypted); +char* p_gpg_autocomplete_key(const char * const search_str); +void p_gpg_autocomplete_key_reset(void); #endif diff --git a/src/ui/console.c b/src/ui/console.c index 6497e809..2b05448b 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -1453,6 +1453,9 @@ cons_show_otr_prefs(void) } prefs_free_string(log_value); + char ch = prefs_get_otr_char(); + cons_show("OTR char (/otr char) : %c", ch); + cons_alert(); } @@ -1472,6 +1475,9 @@ cons_show_pgp_prefs(void) } prefs_free_string(log_value); + char ch = prefs_get_pgp_char(); + cons_show("PGP char (/pgp char) : %c", ch); + cons_alert(); } diff --git a/src/ui/core.c b/src/ui/core.c index f947577b..d66806d4 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -412,7 +412,7 @@ ui_message_receipt(const char * const barejid, const char * const id) } void -ui_incoming_msg(ProfChatWin *chatwin, const char * const resource, const char * const message, GDateTime *timestamp, gboolean win_created) +ui_incoming_msg(ProfChatWin *chatwin, const char * const resource, const char * const message, GDateTime *timestamp, gboolean win_created, prof_enc_t enc_mode) { ProfWin *window = (ProfWin*)chatwin; int num = wins_get_num(window); @@ -421,7 +421,7 @@ ui_incoming_msg(ProfChatWin *chatwin, const char * const resource, const char * // currently viewing chat window with sender if (wins_is_current(window)) { - win_print_incoming_message(window, timestamp, display_name, message); + win_print_incoming_message(window, timestamp, display_name, message, enc_mode); title_bar_set_typing(FALSE); status_bar_active(num); @@ -447,7 +447,7 @@ ui_incoming_msg(ProfChatWin *chatwin, const char * const resource, const char * } } - win_print_incoming_message(window, timestamp, display_name, message); + win_print_incoming_message(window, timestamp, display_name, message, enc_mode); } if (prefs_get_boolean(PREF_BEEP)) { @@ -478,7 +478,7 @@ ui_incoming_private_msg(const char * const fulljid, const char * const message, // currently viewing chat window with sender if (wins_is_current(window)) { - win_print_incoming_message(window, timestamp, display_from, message); + win_print_incoming_message(window, timestamp, display_from, message, PROF_ENC_NONE); title_bar_set_typing(FALSE); status_bar_active(num); @@ -487,7 +487,7 @@ ui_incoming_private_msg(const char * const fulljid, const char * const message, privatewin->unread++; status_bar_new(num); cons_show_incoming_message(display_from, num); - win_print_incoming_message(window, timestamp, display_from, message); + win_print_incoming_message(window, timestamp, display_from, message, PROF_ENC_NONE); if (prefs_get_boolean(PREF_FLASH)) { flash(); @@ -1287,12 +1287,19 @@ ui_new_chat_win(const char * const barejid) } void -ui_outgoing_chat_msg(ProfChatWin *chatwin, const char * const message, char *id) +ui_outgoing_chat_msg(ProfChatWin *chatwin, const char * const message, char *id, prof_enc_t enc_mode) { + char enc_char = '-'; + if (enc_mode == PROF_ENC_OTR) { + enc_char = prefs_get_otr_char(); + } else if (enc_mode == PROF_ENC_PGP) { + enc_char = prefs_get_pgp_char(); + } + if (prefs_get_boolean(PREF_RECEIPTS_REQUEST) && id) { - win_print_with_receipt((ProfWin*)chatwin, '-', 0, NULL, 0, THEME_TEXT_ME, "me", message, id); + win_print_with_receipt((ProfWin*)chatwin, enc_char, 0, NULL, 0, THEME_TEXT_ME, "me", message, id); } else { - win_print((ProfWin*)chatwin, '-', 0, NULL, 0, THEME_TEXT_ME, "me", message); + win_print((ProfWin*)chatwin, enc_char, 0, NULL, 0, THEME_TEXT_ME, "me", message); } } diff --git a/src/ui/ui.h b/src/ui/ui.h index cfda6384..bd38cbe8 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -102,14 +102,14 @@ 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(ProfChatWin *chatwin, const char * const resource, const char * const message, GDateTime *timestamp, gboolean win_created); +void ui_incoming_msg(ProfChatWin *chatwin, const char * const resource, const char * const message, GDateTime *timestamp, gboolean win_created, prof_enc_t enc_mode); void ui_incoming_private_msg(const char * const fulljid, const char * const message, GDateTime *timestamp); void ui_message_receipt(const char * const barejid, const char * const id); void ui_disconnected(void); void ui_recipient_gone(const char * const barejid, const char * const resource); -void ui_outgoing_chat_msg(ProfChatWin *chatwin, const char * const message, char *id); +void ui_outgoing_chat_msg(ProfChatWin *chatwin, const char * const message, char *id, prof_enc_t enc_mode); void ui_outgoing_chat_msg_carbon(const char * const barejid, const char * const message); void ui_outgoing_private_msg(ProfPrivateWin *privwin, const char * const message); diff --git a/src/ui/window.c b/src/ui/window.c index ef675c09..f6ad7cee 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -877,11 +877,20 @@ win_show_status_string(ProfWin *window, const char * const from, void win_print_incoming_message(ProfWin *window, GDateTime *timestamp, - const char * const from, const char * const message) + const char * const from, const char * const message, prof_enc_t enc_mode) { + char enc_char = '-'; + switch (window->type) { case WIN_CHAT: + if (enc_mode == PROF_ENC_OTR) { + enc_char = prefs_get_otr_char(); + } else if (enc_mode == PROF_ENC_PGP) { + enc_char = prefs_get_pgp_char(); + } + win_print(window, enc_char, 0, timestamp, NO_ME, THEME_TEXT_THEM, from, message); + break; case WIN_PRIVATE: win_print(window, '-', 0, timestamp, NO_ME, THEME_TEXT_THEM, from, message); break; diff --git a/src/ui/window.h b/src/ui/window.h index 64989e4c..603f1cc3 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -60,7 +60,7 @@ void win_show_status_string(ProfWin *window, const char * const from, GDateTime *last_activity, const char * const pre, const char * const default_show); void win_print_incoming_message(ProfWin *window, GDateTime *timestamp, - const char * const from, const char * const message); + const char * const from, const char * const message, prof_enc_t enc_mode); void win_print_with_receipt(ProfWin *window, const char show_char, int pad_indent, GTimeVal *tstamp, int flags, theme_item_t theme_item, const char * const from, const char * const message, char *id); void win_newline(ProfWin *window); diff --git a/tests/unittests/pgp/stub_gpg.c b/tests/unittests/pgp/stub_gpg.c index bb5a9eca..4ac51ab5 100644 --- a/tests/unittests/pgp/stub_gpg.c +++ b/tests/unittests/pgp/stub_gpg.c @@ -11,7 +11,7 @@ GHashTable* p_gpg_list_keys(void) } GHashTable* -p_gpg_fingerprints(void) +p_gpg_pubkeys(void) { return NULL; } @@ -54,3 +54,10 @@ void p_gpg_free_decrypted(char *decrypted) {} void p_gpg_free_keys(GHashTable *keys) {} +void p_gpg_autocomplete_key_reset(void) {} + +char * p_gpg_autocomplete_key(const char * const search_str) +{ + return NULL; +} + diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 43fc2954..8c5f9701 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -179,7 +179,7 @@ void ui_contact_online(char *barejid, Resource *resource, GDateTime *last_activi } void ui_contact_typing(const char * const barejid, const char * const resource) {} -void ui_incoming_msg(ProfChatWin *chatwin, const char * const resource, const char * const message, GDateTime *timestamp, gboolean win_created) {} +void ui_incoming_msg(ProfChatWin *chatwin, const char * const resource, const char * const message, GDateTime *timestamp, gboolean win_created, prof_enc_t enc_mode) {} void ui_message_receipt(const char * const barejid, const char * const id) {} void ui_incoming_private_msg(const char * const fulljid, const char * const message, GDateTime *timestamp) {} @@ -187,7 +187,7 @@ void ui_incoming_private_msg(const char * const fulljid, const char * const mess void ui_disconnected(void) {} void ui_recipient_gone(const char * const barejid, const char * const resource) {} -void ui_outgoing_chat_msg(ProfChatWin *chatwin, const char * const message, char *id) {} +void ui_outgoing_chat_msg(ProfChatWin *chatwin, const char * const message, char *id, prof_enc_t enc_mode) {} void ui_outgoing_chat_msg_carbon(const char * const barejid, const char * const message) {} void ui_outgoing_private_msg(ProfPrivateWin *privwin, const char * const message) {} diff --git a/theme_template b/theme_template index 56932ca2..c49d5f9c 100644 --- a/theme_template +++ b/theme_template @@ -75,3 +75,5 @@ occupants= occupants.size= occupants.jid= wins.autotidy= +otr.char= +pgp.char= diff --git a/themes/boothj5 b/themes/boothj5 index 22401681..a408d3b5 100644 --- a/themes/boothj5 +++ b/themes/boothj5 @@ -75,3 +75,5 @@ occupants=true occupants.size=15 occupants.jid=false wins.autotidy=true +otr.char=@ +pgp.char=%