From 10f556f4127c248d0a25a97d03a0731c554cfa1a Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 13 Aug 2013 22:23:47 +0100 Subject: [PATCH 01/32] Added libotr --- Makefile.am | 3 ++- configure.ac | 18 ++++++++++++++++-- src/otr.c | 35 +++++++++++++++++++++++++++++++++++ src/otr.h | 28 ++++++++++++++++++++++++++++ src/profanity.c | 5 ++++- 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 src/otr.c create mode 100644 src/otr.h diff --git a/Makefile.am b/Makefile.am index f7c3f825..954263a1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,7 +21,8 @@ profanity_SOURCES = \ src/tools/tinyurl.c src/tools/tinyurl.h \ src/config/accounts.c src/config/accounts.h \ src/config/preferences.c src/config/preferences.h \ - src/config/theme.c src/config/theme.h + src/config/theme.c src/config/theme.h \ + src/otr.c src/otr.h TESTS = tests/testsuite check_PROGRAMS = tests/testsuite diff --git a/configure.ac b/configure.ac index a30c96d8..a147ae44 100644 --- a/configure.ac +++ b/configure.ac @@ -26,6 +26,8 @@ AC_ARG_WITH([libxml2], [AS_HELP_STRING([--with-libxml2], [link with libxml2 instead of expat])]) AC_ARG_WITH([xscreensaver], [AS_HELP_STRING([--with-xscreensaver], [use libXScrnSaver to determine indle time])]) +AC_ARG_WITH([otr], + [AS_HELP_STRING([--with-libotr], [enable otr entryption using libtr library])]) # Checks for libraries. if test "x$with_libxml2" = xyes; then @@ -54,7 +56,14 @@ elif test "x$with_xscreensaver" = x; then [AC_MSG_NOTICE([libXss not found, falling back to profanity auto-away])]) AC_CHECK_LIB([X11], [main], [], [AC_MSG_NOTICE([libX11 not found, falling back to profanity auto-away])]) +fi +if test "x$with_otr" = xyes; then + AC_CHECK_LIB([otr], [main], [], + [AC_MSG_ERROR([libotr is required for otr encryption support])]) +elif test "x$enable_otr" = x; then + AC_CHECK_LIB([otr], [main], [], + [AC_MSG_NOTICE([libotr not found, otr entryption support no enabled])]) fi AC_CHECK_LIB([resolv], [main], [], @@ -87,14 +96,19 @@ if test "x$enable_notifications" != xno; then [AC_MSG_NOTICE([libnotify module not found])]) fi +if test "x$with_otr" != xno; then + PKG_CHECK_MODULES([OTR], [libotr], [], + [AC_MSG_NOTICE([libotr module not found])]) +fi + # Default parameters AM_CFLAGS="-Wall" if test "x$PACKAGE_STATUS" = xdevelopment; then AM_CFLAGS="$AM_CFLAGS -Wunused -Werror" fi -LIBS="$LIBS $DEPS_LIBS $NOTIFY_LIBS" +LIBS="$LIBS $DEPS_LIBS $NOTIFY_LIBS $OTR_LIBS" -AM_CPPFLAGS="$DEPS_CFLAGS $NOTIFY_CFLAGS" +AM_CPPFLAGS="$DEPS_CFLAGS $NOTIFY_CFLAGS $OTR_CLAGS" AC_SUBST(AM_CFLAGS) AC_SUBST(AM_CPPFLAGS) diff --git a/src/otr.c b/src/otr.c new file mode 100644 index 00000000..7dd6aafd --- /dev/null +++ b/src/otr.c @@ -0,0 +1,35 @@ +/* + * otr.c + * + * Copyright (C) 2012, 2013 James Booth + * + * 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 . + * + */ + +#include +#include +#include +#include + +#include "ui/ui.h" + +void +otr_init(void) +{ + cons_debug("otr_init()"); + OTRL_INIT; +} diff --git a/src/otr.h b/src/otr.h new file mode 100644 index 00000000..772c0f34 --- /dev/null +++ b/src/otr.h @@ -0,0 +1,28 @@ +/* + * otr.h + * + * Copyright (C) 2012, 2013 James Booth + * + * 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 . + * + */ + +#ifndef OTR_H +#define OTR_H + +void otr_init(void); + +#endif diff --git a/src/profanity.c b/src/profanity.c index 9c729ca1..ded96890 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -19,7 +19,6 @@ * along with Profanity. If not, see . * */ - #include "config.h" #include @@ -40,6 +39,7 @@ #include "contact.h" #include "log.h" #include "muc.h" +#include "otr.h" #include "resource.h" #include "ui/notifier.h" #include "ui/ui.h" @@ -632,6 +632,9 @@ _init(const int disable_tls, char *log_level) log_info("Initialising contact list"); roster_init(); muc_init(); +#ifdef HAVE_LIBOTR + otr_init(); +#endif atexit(_shutdown); } From bc82a7d0f804a5ad42ac41be6c41eddc3cf65582 Mon Sep 17 00:00:00 2001 From: James Booth Date: Wed, 14 Aug 2013 22:27:44 +0100 Subject: [PATCH 02/32] Load keys and fingerprints for account on connect --- src/otr.c | 41 +++++++++++++++++++++++++++++++++++++++-- src/otr.h | 3 +++ src/profanity.c | 3 +++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/otr.c b/src/otr.c index 7dd6aafd..6b10e3be 100644 --- a/src/otr.c +++ b/src/otr.c @@ -21,15 +21,52 @@ */ #include -#include -#include #include +#include +#include "otr.h" #include "ui/ui.h" +static OtrlUserState user_state; + void otr_init(void) { cons_debug("otr_init()"); OTRL_INIT; } + +void +otr_account_load(ProfAccount *account) +{ + gcry_error_t err = 0; + cons_debug("otr_account_load()"); + GString *keys_filename = g_string_new("./"); + g_string_append(keys_filename, account->jid); + g_string_append(keys_filename, "_keys.txt"); + + GString *fp_filename = g_string_new("./"); + g_string_append(fp_filename, account->jid); + g_string_append(fp_filename, "_fingerprints.txt"); + + user_state = otrl_userstate_create(); + + err = otrl_privkey_read(user_state, keys_filename->str); + if (err != 0) { + cons_debug("Failed to load private keys"); + g_string_free(keys_filename, TRUE); + g_string_free(fp_filename, TRUE); + return; + } + + err = otrl_privkey_read_fingerprints(user_state, fp_filename->str, NULL, NULL); + if (err != 0) { + cons_debug("Failed to load fingerprints"); + g_string_free(keys_filename, TRUE); + g_string_free(fp_filename, TRUE); + return; + } + + g_string_free(keys_filename, TRUE); + g_string_free(fp_filename, TRUE); +} diff --git a/src/otr.h b/src/otr.h index 772c0f34..caa38ee5 100644 --- a/src/otr.h +++ b/src/otr.h @@ -23,6 +23,9 @@ #ifndef OTR_H #define OTR_H +#include "config/accounts.h" + void otr_init(void); +void otr_account_load(ProfAccount *account); #endif diff --git a/src/profanity.c b/src/profanity.c index ded96890..ac45b5f6 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -249,6 +249,9 @@ void prof_handle_login_account_success(char *account_name) { ProfAccount *account = accounts_get_account(account_name); +#ifdef HAVE_LIBOTR + otr_account_load(account); +#endif resource_presence_t resource_presence = accounts_get_login_presence(account->name); contact_presence_t contact_presence = contact_presence_from_resource_presence(resource_presence); cons_show_login_success(account); From 41574ccdc62030cb9d24e6d923ad907754402834 Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 15 Aug 2013 00:01:01 +0100 Subject: [PATCH 03/32] Generate privatekey --- src/otr.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/otr.c b/src/otr.c index 6b10e3be..44058cdf 100644 --- a/src/otr.c +++ b/src/otr.c @@ -39,34 +39,35 @@ otr_init(void) void otr_account_load(ProfAccount *account) { - gcry_error_t err = 0; cons_debug("otr_account_load()"); + + gcry_error_t err = 0; GString *keys_filename = g_string_new("./"); g_string_append(keys_filename, account->jid); g_string_append(keys_filename, "_keys.txt"); - GString *fp_filename = g_string_new("./"); - g_string_append(fp_filename, account->jid); - g_string_append(fp_filename, "_fingerprints.txt"); - user_state = otrl_userstate_create(); - err = otrl_privkey_read(user_state, keys_filename->str); + if (!g_file_test(keys_filename->str, G_FILE_TEST_IS_REGULAR)) { + cons_debug("Private key not found, generating one"); + err = otrl_privkey_generate(user_state, keys_filename->str, account->jid, "xmpp"); + if (err != 0) { + cons_debug("Failed to generate private key"); + g_string_free(keys_filename, TRUE); + return; + } + cons_debug("Generated private key"); + } + + cons_debug("Loading private key"); + err = otrl_privkey_read(user_state, keys_filename->str); if (err != 0) { - cons_debug("Failed to load private keys"); + cons_debug("Failed to load private key"); g_string_free(keys_filename, TRUE); - g_string_free(fp_filename, TRUE); return; } - - err = otrl_privkey_read_fingerprints(user_state, fp_filename->str, NULL, NULL); - if (err != 0) { - cons_debug("Failed to load fingerprints"); - g_string_free(keys_filename, TRUE); - g_string_free(fp_filename, TRUE); - return; - } - + cons_debug("Loaded private key"); + g_string_free(keys_filename, TRUE); - g_string_free(fp_filename, TRUE); + return; } From 73eb4baf27d4b12704f6f7eceec24e8a43416256 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 17 Aug 2013 18:45:51 +0100 Subject: [PATCH 04/32] Create fingerprints file, callbacks, and message send --- .gitignore | 2 + src/command/command.c | 20 ++++ src/otr.c | 215 +++++++++++++++++++++++++++++++++++++++--- src/otr.h | 2 + 4 files changed, 225 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 6c29afec..3748015d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ core bugs/ TODO plugins/ +*_key.txt +*_fingerprints.txt diff --git a/src/command/command.c b/src/command/command.c index d8b955a2..1d87b7c9 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -38,6 +38,7 @@ #include "jid.h" #include "log.h" #include "muc.h" +#include "otr.h" #include "profanity.h" #include "tools/autocomplete.h" #include "tools/parser.h" @@ -1129,7 +1130,13 @@ cmd_execute_default(const char * const inp) if (status != JABBER_CONNECTED) { ui_current_print_line("You are not currently connected."); } else { +#ifdef HAVE_LIBOTR + char *encrypted = otr_encrypt_message(recipient, inp); + message_send(encrypted, recipient); + otr_free_message(encrypted); +#else message_send(inp, recipient); +#endif if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); @@ -2151,7 +2158,14 @@ _cmd_msg(gchar **args, struct cmd_help_t help) usr_jid = usr; } if (msg != NULL) { +#ifdef HAVE_LIBOTR + cons_debug("HAVE_LIBOTR, user_jid: %sm msg: %s", usr_jid, msg); + char *encrypted = otr_encrypt_message(usr_jid, msg); + message_send(encrypted, usr_jid); + otr_free_message(encrypted); +#else message_send(msg, usr_jid); +#endif ui_outgoing_msg("me", usr_jid, msg); if (((win_type == WIN_CHAT) || (win_type == WIN_CONSOLE)) && prefs_get_boolean(PREF_CHLOG)) { @@ -2874,7 +2888,13 @@ _cmd_tiny(gchar **args, struct cmd_help_t help) if (tiny != NULL) { if (win_type == WIN_CHAT) { char *recipient = ui_current_recipient(); +#ifdef HAVE_LIBOTR + char *encrypted = otr_encrypt_message(recipient, tiny); + message_send(encrypted, recipient); + otr_free_message(encrypted); +#else message_send(tiny, recipient); +#endif if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); diff --git a/src/otr.c b/src/otr.c index 44058cdf..e468bdea 100644 --- a/src/otr.c +++ b/src/otr.c @@ -22,18 +22,143 @@ #include #include +#include #include #include "otr.h" #include "ui/ui.h" static OtrlUserState user_state; +static OtrlMessageAppOps ops; +static char *jid; + +// ops callbacks +static OtrlPolicy +cb_policy(void *opdata, ConnContext *context) +{ + cons_debug("cb_policy"); + return OTRL_POLICY_DEFAULT ^ OTRL_POLICY_ALLOW_V1; +} + +static void +cb_create_privkey(void *opdata, const char *accountname, + const char *protocol) +{ + cons_debug("cb_create_privkey()"); +} + +static int +cb_is_logged_in(void *opdata, const char *accountname, + const char *protocol, const char *recipient) +{ + cons_debug("cb_is_logged_in: account: %s, protocol: %s, recipient: %s", + accountname, protocol, recipient); + return -1; +} + +static void +cb_inject_message(void *opdata, const char *accountname, + const char *protocol, const char *recipient, const char *message) +{ + cons_debug("cb_inject_message: account: %s, protocol, %s, recipient: %s, message: %s", + accountname, protocol, recipient, message); +} + +static void +cb_notify(void *opdata, OtrlNotifyLevel level, + const char *accountname, const char *protocol, const char *username, + const char *title, const char *primary, const char *secondary) +{ + cons_debug("cb_notify"); +} + +static int +cb_display_otr_message(void *opdata, const char *accountname, + const char *protocol, const char *username, const char *msg) +{ + cons_debug("cb_display_otr_message: account: %s, protocol: %s, user: %s, msg: %s", + accountname, protocol, username, msg); + return 0; +} + +static const char * +cb_protocol_name(void *opdata, const char *protocol) +{ + cons_debug("cb_protocol_name: %s", protocol); + return protocol; +} + +static void +cb_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, + const char *protocol, const char *username, unsigned char fingerprint[20]) +{ + cons_debug("cb_new_fingerprint: account: %s, protocol: %s, username: %s", + accountname, protocol, username); +} + +static void +cb_protocol_name_free(void *opdata, const char *protocol_name) +{ + cons_debug("cb_protocol_name_free: %s", protocol_name); +} + +static void +cb_update_context_list(void *opdata) +{ + cons_debug("cb_update_context_list"); +} + +static void +cb_write_fingerprints(void *opdata) +{ + cons_debug("cb_write_fingerprints"); +} + +static void +cb_gone_secure(void *opdata, ConnContext *context) +{ + cons_debug("cb_gone_secure"); +} + +static void +cb_gone_insecure(void *opdata, ConnContext *context) +{ + cons_debug("cb_gone_insecure"); +} + +static void +cb_still_secure(void *opdata, ConnContext *context, int is_reply) +{ + cons_debug("cb_still_secure: is_reply = %d", is_reply); +} + +static void +cb_log_message(void *opdata, const char *message) +{ + cons_debug("cb_log_message: %s", message); +} void otr_init(void) { cons_debug("otr_init()"); OTRL_INIT; + + ops.policy = cb_policy; + ops.create_privkey = cb_create_privkey; + ops.is_logged_in = cb_is_logged_in; + ops.inject_message = cb_inject_message; + ops.notify = cb_notify; + ops.display_otr_message = cb_display_otr_message; + ops.update_context_list = cb_update_context_list; + ops.protocol_name = cb_protocol_name; + ops.protocol_name_free = cb_protocol_name_free; + ops.new_fingerprint = cb_new_fingerprint; + ops.write_fingerprints = cb_write_fingerprints; + ops.gone_secure = cb_gone_secure; + ops.gone_insecure = cb_gone_insecure; + ops.still_secure = cb_still_secure; + ops.log_message = cb_log_message; } void @@ -41,33 +166,95 @@ otr_account_load(ProfAccount *account) { cons_debug("otr_account_load()"); - gcry_error_t err = 0; - GString *keys_filename = g_string_new("./"); - g_string_append(keys_filename, account->jid); - g_string_append(keys_filename, "_keys.txt"); + jid = strdup(account->jid); + + GString *key_filename = g_string_new("./"); + g_string_append(key_filename, account->jid); + g_string_append(key_filename, "_key.txt"); + + GString *fp_filename = g_string_new("./"); + g_string_append(fp_filename, account->jid); + g_string_append(fp_filename, "_fingerprints.txt"); user_state = otrl_userstate_create(); - if (!g_file_test(keys_filename->str, G_FILE_TEST_IS_REGULAR)) { + gcry_error_t err = 0; + + if (!g_file_test(key_filename->str, G_FILE_TEST_IS_REGULAR)) { cons_debug("Private key not found, generating one"); - err = otrl_privkey_generate(user_state, keys_filename->str, account->jid, "xmpp"); - if (err != 0) { + err = otrl_privkey_generate(user_state, key_filename->str, account->jid, "xmpp"); + if (!err == GPG_ERR_NO_ERROR) { cons_debug("Failed to generate private key"); - g_string_free(keys_filename, TRUE); + g_string_free(key_filename, TRUE); return; } cons_debug("Generated private key"); } - + + if (!g_file_test(fp_filename->str, G_FILE_TEST_IS_REGULAR)) { + cons_debug("Fingerprints not found, creating file"); + err = otrl_privkey_write_fingerprints(user_state, fp_filename->str); + if (!err == GPG_ERR_NO_ERROR) { + cons_debug("Failed to create fingerprints file"); + g_string_free(key_filename, TRUE); + return; + } + cons_debug("Created fingerprints file"); + } + cons_debug("Loading private key"); - err = otrl_privkey_read(user_state, keys_filename->str); - if (err != 0) { + err = otrl_privkey_read(user_state, key_filename->str); + if (!err == GPG_ERR_NO_ERROR) { cons_debug("Failed to load private key"); - g_string_free(keys_filename, TRUE); + g_string_free(key_filename, TRUE); return; } cons_debug("Loaded private key"); - - g_string_free(keys_filename, TRUE); + + cons_debug("Loading fingerprints"); + err = otrl_privkey_read_fingerprints(user_state, fp_filename->str, NULL, NULL); + if (!err == GPG_ERR_NO_ERROR) { + cons_debug("Failed to load fingerprints"); + g_string_free(fp_filename, TRUE); + return; + } + cons_debug("Loaded fingerprints"); + + g_string_free(key_filename, TRUE); + g_string_free(fp_filename, TRUE); return; } + +char * +otr_encrypt_message(const char * const to, const char * const message) +{ + cons_debug("otr_encrypt_message, account: %s, protocol: xmpp, recipient: %s", jid, to); + gcry_error_t err; + char *newmessage = NULL; + + err = otrl_message_sending( + user_state, + &ops, + NULL, + jid, + "xmpp", + to, + message, + 0, + &newmessage, + NULL, + &ops); + if (!err == GPG_ERR_NO_ERROR) { + cons_debug("Error encrypting, result: %s", newmessage); + return NULL; + } else { + cons_debug("Encrypted, result: %s", newmessage); + return newmessage; + } +} + +void +otr_free_message(char *message) +{ + otrl_message_free(message); +} diff --git a/src/otr.h b/src/otr.h index caa38ee5..42005382 100644 --- a/src/otr.h +++ b/src/otr.h @@ -27,5 +27,7 @@ void otr_init(void); void otr_account_load(ProfAccount *account); +char * otr_encrypt_message(const char * const to, const char * const message); +void otr_free_message(char *message); #endif From 1d32413b99d2c42865b5bbd5fff63306e02c712e Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 21 Sep 2013 22:33:43 +0100 Subject: [PATCH 05/32] Added /otr command --- src/command/command.c | 32 ++++++++++++++++++++++++++++++++ src/profanity.c | 3 --- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index 6e79dc48..18d104bf 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -126,6 +126,7 @@ static gboolean _cmd_nick(gchar **args, struct cmd_help_t help); static gboolean _cmd_notify(gchar **args, struct cmd_help_t help); static gboolean _cmd_online(gchar **args, struct cmd_help_t help); static gboolean _cmd_outtype(gchar **args, struct cmd_help_t help); +static gboolean _cmd_otr(gchar **args, struct cmd_help_t help); static gboolean _cmd_prefs(gchar **args, struct cmd_help_t help); static gboolean _cmd_priority(gchar **args, struct cmd_help_t help); static gboolean _cmd_quit(gchar **args, struct cmd_help_t help); @@ -638,6 +639,14 @@ static struct cmd_t command_defs[] = "Such as whether you have become inactive, or have closed the chat window.", NULL } } }, + { "/otr", + _cmd_otr, parse_args, 1, 2, NULL, + { "/otr gen|start|end|trust|untrust [contact]", "Off The Record encryption commands.", + { "/otr gen|start|end|trust|untrust [contact]", + "-----------------------------------------", + "gen - Load or create private key and fingerprints.", + NULL } } }, + { "/outtype", _cmd_outtype, parse_args, 1, 1, cons_outtype_setting, { "/outtype on|off", "Send typing notification to recipient.", @@ -3181,6 +3190,29 @@ _cmd_outtype(gchar **args, struct cmd_help_t help) return result; } +static gboolean +_cmd_otr(gchar **args, struct cmd_help_t help) +{ +#ifdef HAVE_LIBOTR + if (strcmp(args[0], "gen") == 0) { + if (jabber_get_connection_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to load OTR information."); + return TRUE; + } else { + ProfAccount *account = accounts_get_account(jabber_get_account_name()); + otr_account_load(account); + return TRUE; + } + } else { + cons_show("Usage: %s", help.usage); + return TRUE; + } +#else + cons_show("This version of Profanity has not been build with OTR support enabled"); + return TRUE; +#endif +} + static gboolean _cmd_gone(gchar **args, struct cmd_help_t help) { diff --git a/src/profanity.c b/src/profanity.c index 3aa75538..0dd9808a 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -253,9 +253,6 @@ void prof_handle_login_account_success(char *account_name) { ProfAccount *account = accounts_get_account(account_name); -#ifdef HAVE_LIBOTR - otr_account_load(account); -#endif resource_presence_t resource_presence = accounts_get_login_presence(account->name); contact_presence_t contact_presence = contact_presence_from_resource_presence(resource_presence); cons_show_login_success(account); From bd1c139429487f173d973be5810ef15549b4da07 Mon Sep 17 00:00:00 2001 From: James Booth Date: Tue, 13 Aug 2013 22:23:47 +0100 Subject: [PATCH 06/32] Added libotr --- Makefile.am | 3 ++- configure.ac | 18 ++++++++++++++++-- src/otr.c | 35 +++++++++++++++++++++++++++++++++++ src/otr.h | 28 ++++++++++++++++++++++++++++ src/profanity.c | 5 ++++- 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 src/otr.c create mode 100644 src/otr.h diff --git a/Makefile.am b/Makefile.am index 9da258eb..9b60ec2d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,7 +36,8 @@ core_sources = \ src/tools/tinyurl.c src/tools/tinyurl.h \ src/config/accounts.c src/config/accounts.h \ src/config/preferences.c src/config/preferences.h \ - src/config/theme.c src/config/theme.h + src/config/theme.c src/config/theme.h \ + src/otr.c src/otr.h test_sources = \ tests/test_roster.c tests/test_common.c tests/test_history.c \ diff --git a/configure.ac b/configure.ac index 8468792c..282f0573 100644 --- a/configure.ac +++ b/configure.ac @@ -35,6 +35,8 @@ AC_ARG_WITH([libxml2], [AS_HELP_STRING([--with-libxml2], [link with libxml2 instead of expat])]) AC_ARG_WITH([xscreensaver], [AS_HELP_STRING([--with-xscreensaver], [use libXScrnSaver to determine indle time])]) +AC_ARG_WITH([otr], + [AS_HELP_STRING([--with-libotr], [enable otr entryption using libtr library])]) # Checks for libraries. if test "x$with_libxml2" = xyes; then @@ -63,7 +65,14 @@ elif test "x$with_xscreensaver" = x; then [AC_MSG_NOTICE([libXss not found, falling back to profanity auto-away])]) AC_CHECK_LIB([X11], [main], [], [AC_MSG_NOTICE([libX11 not found, falling back to profanity auto-away])]) +fi +if test "x$with_otr" = xyes; then + AC_CHECK_LIB([otr], [main], [], + [AC_MSG_ERROR([libotr is required for otr encryption support])]) +elif test "x$enable_otr" = x; then + AC_CHECK_LIB([otr], [main], [], + [AC_MSG_NOTICE([libotr not found, otr entryption support no enabled])]) fi AC_CHECK_LIB([resolv], [main], [], @@ -96,14 +105,19 @@ if test "x$enable_notifications" != xno; then [AC_MSG_NOTICE([libnotify module not found])]) fi +if test "x$with_otr" != xno; then + PKG_CHECK_MODULES([OTR], [libotr], [], + [AC_MSG_NOTICE([libotr module not found])]) +fi + # Default parameters AM_CFLAGS="-Wall" if test "x$PACKAGE_STATUS" = xdevelopment; then AM_CFLAGS="$AM_CFLAGS -Wunused -Werror" fi -LIBS="$LIBS $DEPS_LIBS $NOTIFY_LIBS" +LIBS="$LIBS $DEPS_LIBS $NOTIFY_LIBS $OTR_LIBS" -AM_CPPFLAGS="$DEPS_CFLAGS $NOTIFY_CFLAGS" +AM_CPPFLAGS="$DEPS_CFLAGS $NOTIFY_CFLAGS $OTR_CLAGS" AC_SUBST(AM_CFLAGS) AC_SUBST(AM_CPPFLAGS) diff --git a/src/otr.c b/src/otr.c new file mode 100644 index 00000000..7dd6aafd --- /dev/null +++ b/src/otr.c @@ -0,0 +1,35 @@ +/* + * otr.c + * + * Copyright (C) 2012, 2013 James Booth + * + * 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 . + * + */ + +#include +#include +#include +#include + +#include "ui/ui.h" + +void +otr_init(void) +{ + cons_debug("otr_init()"); + OTRL_INIT; +} diff --git a/src/otr.h b/src/otr.h new file mode 100644 index 00000000..772c0f34 --- /dev/null +++ b/src/otr.h @@ -0,0 +1,28 @@ +/* + * otr.h + * + * Copyright (C) 2012, 2013 James Booth + * + * 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 . + * + */ + +#ifndef OTR_H +#define OTR_H + +void otr_init(void); + +#endif diff --git a/src/profanity.c b/src/profanity.c index 116e8b9c..913f3350 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -19,7 +19,6 @@ * along with Profanity. If not, see . * */ - #include "config.h" #ifdef HAVE_GIT_VERSION @@ -44,6 +43,7 @@ #include "contact.h" #include "log.h" #include "muc.h" +#include "otr.h" #include "resource.h" #include "ui/notifier.h" #include "ui/ui.h" @@ -648,6 +648,9 @@ _init(const int disable_tls, char *log_level) log_info("Initialising contact list"); roster_init(); muc_init(); +#ifdef HAVE_LIBOTR + otr_init(); +#endif atexit(_shutdown); } From bdcc70f36d4545060e61dd7c9a51f63be5ab7d38 Mon Sep 17 00:00:00 2001 From: James Booth Date: Wed, 14 Aug 2013 22:27:44 +0100 Subject: [PATCH 07/32] Load keys and fingerprints for account on connect --- src/otr.c | 41 +++++++++++++++++++++++++++++++++++++++-- src/otr.h | 3 +++ src/profanity.c | 3 +++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/otr.c b/src/otr.c index 7dd6aafd..6b10e3be 100644 --- a/src/otr.c +++ b/src/otr.c @@ -21,15 +21,52 @@ */ #include -#include -#include #include +#include +#include "otr.h" #include "ui/ui.h" +static OtrlUserState user_state; + void otr_init(void) { cons_debug("otr_init()"); OTRL_INIT; } + +void +otr_account_load(ProfAccount *account) +{ + gcry_error_t err = 0; + cons_debug("otr_account_load()"); + GString *keys_filename = g_string_new("./"); + g_string_append(keys_filename, account->jid); + g_string_append(keys_filename, "_keys.txt"); + + GString *fp_filename = g_string_new("./"); + g_string_append(fp_filename, account->jid); + g_string_append(fp_filename, "_fingerprints.txt"); + + user_state = otrl_userstate_create(); + + err = otrl_privkey_read(user_state, keys_filename->str); + if (err != 0) { + cons_debug("Failed to load private keys"); + g_string_free(keys_filename, TRUE); + g_string_free(fp_filename, TRUE); + return; + } + + err = otrl_privkey_read_fingerprints(user_state, fp_filename->str, NULL, NULL); + if (err != 0) { + cons_debug("Failed to load fingerprints"); + g_string_free(keys_filename, TRUE); + g_string_free(fp_filename, TRUE); + return; + } + + g_string_free(keys_filename, TRUE); + g_string_free(fp_filename, TRUE); +} diff --git a/src/otr.h b/src/otr.h index 772c0f34..caa38ee5 100644 --- a/src/otr.h +++ b/src/otr.h @@ -23,6 +23,9 @@ #ifndef OTR_H #define OTR_H +#include "config/accounts.h" + void otr_init(void); +void otr_account_load(ProfAccount *account); #endif diff --git a/src/profanity.c b/src/profanity.c index 913f3350..09bd77d8 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -261,6 +261,9 @@ void prof_handle_login_account_success(char *account_name) { ProfAccount *account = accounts_get_account(account_name); +#ifdef HAVE_LIBOTR + otr_account_load(account); +#endif resource_presence_t resource_presence = accounts_get_login_presence(account->name); contact_presence_t contact_presence = contact_presence_from_resource_presence(resource_presence); cons_show_login_success(account); From 3fce5572eaa3af0a1caeb05ec5783cd40134e0e0 Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 15 Aug 2013 00:01:01 +0100 Subject: [PATCH 08/32] Generate privatekey --- src/otr.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/otr.c b/src/otr.c index 6b10e3be..44058cdf 100644 --- a/src/otr.c +++ b/src/otr.c @@ -39,34 +39,35 @@ otr_init(void) void otr_account_load(ProfAccount *account) { - gcry_error_t err = 0; cons_debug("otr_account_load()"); + + gcry_error_t err = 0; GString *keys_filename = g_string_new("./"); g_string_append(keys_filename, account->jid); g_string_append(keys_filename, "_keys.txt"); - GString *fp_filename = g_string_new("./"); - g_string_append(fp_filename, account->jid); - g_string_append(fp_filename, "_fingerprints.txt"); - user_state = otrl_userstate_create(); - err = otrl_privkey_read(user_state, keys_filename->str); + if (!g_file_test(keys_filename->str, G_FILE_TEST_IS_REGULAR)) { + cons_debug("Private key not found, generating one"); + err = otrl_privkey_generate(user_state, keys_filename->str, account->jid, "xmpp"); + if (err != 0) { + cons_debug("Failed to generate private key"); + g_string_free(keys_filename, TRUE); + return; + } + cons_debug("Generated private key"); + } + + cons_debug("Loading private key"); + err = otrl_privkey_read(user_state, keys_filename->str); if (err != 0) { - cons_debug("Failed to load private keys"); + cons_debug("Failed to load private key"); g_string_free(keys_filename, TRUE); - g_string_free(fp_filename, TRUE); return; } - - err = otrl_privkey_read_fingerprints(user_state, fp_filename->str, NULL, NULL); - if (err != 0) { - cons_debug("Failed to load fingerprints"); - g_string_free(keys_filename, TRUE); - g_string_free(fp_filename, TRUE); - return; - } - + cons_debug("Loaded private key"); + g_string_free(keys_filename, TRUE); - g_string_free(fp_filename, TRUE); + return; } From 599bee2ce8ddc7974dc4e0592e0c84f9b243a007 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 17 Aug 2013 18:45:51 +0100 Subject: [PATCH 09/32] Create fingerprints file, callbacks, and message send --- .gitignore | 2 + src/command/command.c | 20 ++++ src/otr.c | 215 +++++++++++++++++++++++++++++++++++++++--- src/otr.h | 2 + 4 files changed, 225 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 1c5662be..9e2a34f5 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ bugs/ TODO plugins/ src/gitversion.c +*_key.txt +*_fingerprints.txt diff --git a/src/command/command.c b/src/command/command.c index f7737464..7a74f394 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -39,6 +39,7 @@ #include "jid.h" #include "log.h" #include "muc.h" +#include "otr.h" #include "profanity.h" #include "tools/autocomplete.h" #include "tools/parser.h" @@ -1176,7 +1177,13 @@ cmd_execute_default(const char * const inp) if (status != JABBER_CONNECTED) { ui_current_print_line("You are not currently connected."); } else { +#ifdef HAVE_LIBOTR + char *encrypted = otr_encrypt_message(recipient, inp); + message_send(encrypted, recipient); + otr_free_message(encrypted); +#else message_send(inp, recipient); +#endif if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); @@ -2230,7 +2237,14 @@ _cmd_msg(gchar **args, struct cmd_help_t help) usr_jid = usr; } if (msg != NULL) { +#ifdef HAVE_LIBOTR + cons_debug("HAVE_LIBOTR, user_jid: %sm msg: %s", usr_jid, msg); + char *encrypted = otr_encrypt_message(usr_jid, msg); + message_send(encrypted, usr_jid); + otr_free_message(encrypted); +#else message_send(msg, usr_jid); +#endif ui_outgoing_msg("me", usr_jid, msg); if (((win_type == WIN_CHAT) || (win_type == WIN_CONSOLE)) && prefs_get_boolean(PREF_CHLOG)) { @@ -3015,7 +3029,13 @@ _cmd_tiny(gchar **args, struct cmd_help_t help) if (tiny != NULL) { if (win_type == WIN_CHAT) { char *recipient = ui_current_recipient(); +#ifdef HAVE_LIBOTR + char *encrypted = otr_encrypt_message(recipient, tiny); + message_send(encrypted, recipient); + otr_free_message(encrypted); +#else message_send(tiny, recipient); +#endif if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); diff --git a/src/otr.c b/src/otr.c index 44058cdf..e468bdea 100644 --- a/src/otr.c +++ b/src/otr.c @@ -22,18 +22,143 @@ #include #include +#include #include #include "otr.h" #include "ui/ui.h" static OtrlUserState user_state; +static OtrlMessageAppOps ops; +static char *jid; + +// ops callbacks +static OtrlPolicy +cb_policy(void *opdata, ConnContext *context) +{ + cons_debug("cb_policy"); + return OTRL_POLICY_DEFAULT ^ OTRL_POLICY_ALLOW_V1; +} + +static void +cb_create_privkey(void *opdata, const char *accountname, + const char *protocol) +{ + cons_debug("cb_create_privkey()"); +} + +static int +cb_is_logged_in(void *opdata, const char *accountname, + const char *protocol, const char *recipient) +{ + cons_debug("cb_is_logged_in: account: %s, protocol: %s, recipient: %s", + accountname, protocol, recipient); + return -1; +} + +static void +cb_inject_message(void *opdata, const char *accountname, + const char *protocol, const char *recipient, const char *message) +{ + cons_debug("cb_inject_message: account: %s, protocol, %s, recipient: %s, message: %s", + accountname, protocol, recipient, message); +} + +static void +cb_notify(void *opdata, OtrlNotifyLevel level, + const char *accountname, const char *protocol, const char *username, + const char *title, const char *primary, const char *secondary) +{ + cons_debug("cb_notify"); +} + +static int +cb_display_otr_message(void *opdata, const char *accountname, + const char *protocol, const char *username, const char *msg) +{ + cons_debug("cb_display_otr_message: account: %s, protocol: %s, user: %s, msg: %s", + accountname, protocol, username, msg); + return 0; +} + +static const char * +cb_protocol_name(void *opdata, const char *protocol) +{ + cons_debug("cb_protocol_name: %s", protocol); + return protocol; +} + +static void +cb_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, + const char *protocol, const char *username, unsigned char fingerprint[20]) +{ + cons_debug("cb_new_fingerprint: account: %s, protocol: %s, username: %s", + accountname, protocol, username); +} + +static void +cb_protocol_name_free(void *opdata, const char *protocol_name) +{ + cons_debug("cb_protocol_name_free: %s", protocol_name); +} + +static void +cb_update_context_list(void *opdata) +{ + cons_debug("cb_update_context_list"); +} + +static void +cb_write_fingerprints(void *opdata) +{ + cons_debug("cb_write_fingerprints"); +} + +static void +cb_gone_secure(void *opdata, ConnContext *context) +{ + cons_debug("cb_gone_secure"); +} + +static void +cb_gone_insecure(void *opdata, ConnContext *context) +{ + cons_debug("cb_gone_insecure"); +} + +static void +cb_still_secure(void *opdata, ConnContext *context, int is_reply) +{ + cons_debug("cb_still_secure: is_reply = %d", is_reply); +} + +static void +cb_log_message(void *opdata, const char *message) +{ + cons_debug("cb_log_message: %s", message); +} void otr_init(void) { cons_debug("otr_init()"); OTRL_INIT; + + ops.policy = cb_policy; + ops.create_privkey = cb_create_privkey; + ops.is_logged_in = cb_is_logged_in; + ops.inject_message = cb_inject_message; + ops.notify = cb_notify; + ops.display_otr_message = cb_display_otr_message; + ops.update_context_list = cb_update_context_list; + ops.protocol_name = cb_protocol_name; + ops.protocol_name_free = cb_protocol_name_free; + ops.new_fingerprint = cb_new_fingerprint; + ops.write_fingerprints = cb_write_fingerprints; + ops.gone_secure = cb_gone_secure; + ops.gone_insecure = cb_gone_insecure; + ops.still_secure = cb_still_secure; + ops.log_message = cb_log_message; } void @@ -41,33 +166,95 @@ otr_account_load(ProfAccount *account) { cons_debug("otr_account_load()"); - gcry_error_t err = 0; - GString *keys_filename = g_string_new("./"); - g_string_append(keys_filename, account->jid); - g_string_append(keys_filename, "_keys.txt"); + jid = strdup(account->jid); + + GString *key_filename = g_string_new("./"); + g_string_append(key_filename, account->jid); + g_string_append(key_filename, "_key.txt"); + + GString *fp_filename = g_string_new("./"); + g_string_append(fp_filename, account->jid); + g_string_append(fp_filename, "_fingerprints.txt"); user_state = otrl_userstate_create(); - if (!g_file_test(keys_filename->str, G_FILE_TEST_IS_REGULAR)) { + gcry_error_t err = 0; + + if (!g_file_test(key_filename->str, G_FILE_TEST_IS_REGULAR)) { cons_debug("Private key not found, generating one"); - err = otrl_privkey_generate(user_state, keys_filename->str, account->jid, "xmpp"); - if (err != 0) { + err = otrl_privkey_generate(user_state, key_filename->str, account->jid, "xmpp"); + if (!err == GPG_ERR_NO_ERROR) { cons_debug("Failed to generate private key"); - g_string_free(keys_filename, TRUE); + g_string_free(key_filename, TRUE); return; } cons_debug("Generated private key"); } - + + if (!g_file_test(fp_filename->str, G_FILE_TEST_IS_REGULAR)) { + cons_debug("Fingerprints not found, creating file"); + err = otrl_privkey_write_fingerprints(user_state, fp_filename->str); + if (!err == GPG_ERR_NO_ERROR) { + cons_debug("Failed to create fingerprints file"); + g_string_free(key_filename, TRUE); + return; + } + cons_debug("Created fingerprints file"); + } + cons_debug("Loading private key"); - err = otrl_privkey_read(user_state, keys_filename->str); - if (err != 0) { + err = otrl_privkey_read(user_state, key_filename->str); + if (!err == GPG_ERR_NO_ERROR) { cons_debug("Failed to load private key"); - g_string_free(keys_filename, TRUE); + g_string_free(key_filename, TRUE); return; } cons_debug("Loaded private key"); - - g_string_free(keys_filename, TRUE); + + cons_debug("Loading fingerprints"); + err = otrl_privkey_read_fingerprints(user_state, fp_filename->str, NULL, NULL); + if (!err == GPG_ERR_NO_ERROR) { + cons_debug("Failed to load fingerprints"); + g_string_free(fp_filename, TRUE); + return; + } + cons_debug("Loaded fingerprints"); + + g_string_free(key_filename, TRUE); + g_string_free(fp_filename, TRUE); return; } + +char * +otr_encrypt_message(const char * const to, const char * const message) +{ + cons_debug("otr_encrypt_message, account: %s, protocol: xmpp, recipient: %s", jid, to); + gcry_error_t err; + char *newmessage = NULL; + + err = otrl_message_sending( + user_state, + &ops, + NULL, + jid, + "xmpp", + to, + message, + 0, + &newmessage, + NULL, + &ops); + if (!err == GPG_ERR_NO_ERROR) { + cons_debug("Error encrypting, result: %s", newmessage); + return NULL; + } else { + cons_debug("Encrypted, result: %s", newmessage); + return newmessage; + } +} + +void +otr_free_message(char *message) +{ + otrl_message_free(message); +} diff --git a/src/otr.h b/src/otr.h index caa38ee5..42005382 100644 --- a/src/otr.h +++ b/src/otr.h @@ -27,5 +27,7 @@ void otr_init(void); void otr_account_load(ProfAccount *account); +char * otr_encrypt_message(const char * const to, const char * const message); +void otr_free_message(char *message); #endif From d9395daa2fdb312ae454d4a10f4e6f31ab71917c Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 21 Sep 2013 22:33:43 +0100 Subject: [PATCH 10/32] Added /otr command --- src/command/command.c | 32 ++++++++++++++++++++++++++++++++ src/profanity.c | 3 --- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index 7a74f394..3828ddcf 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -129,6 +129,7 @@ static gboolean _cmd_nick(gchar **args, struct cmd_help_t help); static gboolean _cmd_notify(gchar **args, struct cmd_help_t help); static gboolean _cmd_online(gchar **args, struct cmd_help_t help); static gboolean _cmd_outtype(gchar **args, struct cmd_help_t help); +static gboolean _cmd_otr(gchar **args, struct cmd_help_t help); static gboolean _cmd_prefs(gchar **args, struct cmd_help_t help); static gboolean _cmd_priority(gchar **args, struct cmd_help_t help); static gboolean _cmd_quit(gchar **args, struct cmd_help_t help); @@ -651,6 +652,14 @@ static struct cmd_t command_defs[] = "Such as whether you have become inactive, or have closed the chat window.", NULL } } }, + { "/otr", + _cmd_otr, parse_args, 1, 2, NULL, + { "/otr gen|start|end|trust|untrust [contact]", "Off The Record encryption commands.", + { "/otr gen|start|end|trust|untrust [contact]", + "-----------------------------------------", + "gen - Load or create private key and fingerprints.", + NULL } } }, + { "/outtype", _cmd_outtype, parse_args, 1, 1, cons_outtype_setting, { "/outtype on|off", "Send typing notification to recipient.", @@ -3206,6 +3215,29 @@ _cmd_outtype(gchar **args, struct cmd_help_t help) return result; } +static gboolean +_cmd_otr(gchar **args, struct cmd_help_t help) +{ +#ifdef HAVE_LIBOTR + if (strcmp(args[0], "gen") == 0) { + if (jabber_get_connection_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to load OTR information."); + return TRUE; + } else { + ProfAccount *account = accounts_get_account(jabber_get_account_name()); + otr_account_load(account); + return TRUE; + } + } else { + cons_show("Usage: %s", help.usage); + return TRUE; + } +#else + cons_show("This version of Profanity has not been build with OTR support enabled"); + return TRUE; +#endif +} + static gboolean _cmd_gone(gchar **args, struct cmd_help_t help) { diff --git a/src/profanity.c b/src/profanity.c index 09bd77d8..913f3350 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -261,9 +261,6 @@ void prof_handle_login_account_success(char *account_name) { ProfAccount *account = accounts_get_account(account_name); -#ifdef HAVE_LIBOTR - otr_account_load(account); -#endif resource_presence_t resource_presence = accounts_get_login_presence(account->name); contact_presence_t contact_presence = contact_presence_from_resource_presence(resource_presence); cons_show_login_success(account); From 84f1c4b96fa36694f70989a1636fe0e315c16023 Mon Sep 17 00:00:00 2001 From: James Booth Date: Fri, 10 Jan 2014 00:08:49 +0000 Subject: [PATCH 11/32] WIP - OTR support --- src/command/command.c | 20 ++++++++--- src/command/commands.c | 20 ++++++++++- src/otr.c | 81 +++++++++++++++++++++++++++++------------- src/otr.h | 1 + src/server_events.c | 18 ++++++++-- 5 files changed, 109 insertions(+), 31 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index 1788e25e..9aca7185 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -1140,12 +1140,22 @@ cmd_execute_default(const char * const inp) } else { #ifdef HAVE_LIBOTR char *encrypted = otr_encrypt_message(recipient, inp); - message_send(encrypted, recipient); - otr_free_message(encrypted); + if (encrypted != NULL) { + message_send(encrypted, recipient); + otr_free_message(encrypted); + if (prefs_get_boolean(PREF_CHLOG)) { + const char *jid = jabber_get_fulljid(); + Jid *jidp = jid_create(jid); + chat_log_chat(jidp->barejid, recipient, inp, PROF_OUT_LOG, NULL); + jid_destroy(jidp); + } + + ui_outgoing_msg("me", recipient, inp); + } else { + cons_show_error("Failed to send message."); + } #else message_send(inp, recipient); -#endif - if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); Jid *jidp = jid_create(jid); @@ -1154,6 +1164,8 @@ cmd_execute_default(const char * const inp) } ui_outgoing_msg("me", recipient, inp); +#endif + } break; diff --git a/src/command/commands.c b/src/command/commands.c index 257abe61..b71e2af8 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -916,6 +916,24 @@ cmd_msg(gchar **args, struct cmd_help_t help) usr_jid = usr; } if (msg != NULL) { +#ifdef HAVE_LIBOTR + char *encrypted = otr_encrypt_message(usr_jid, msg); + if (encrypted != NULL) { + message_send(encrypted, usr_jid); + otr_free_message(encrypted); + ui_outgoing_msg("me", usr_jid, msg); + + if (((win_type == WIN_CHAT) || (win_type == WIN_CONSOLE)) && prefs_get_boolean(PREF_CHLOG)) { + const char *jid = jabber_get_fulljid(); + Jid *jidp = jid_create(jid); + chat_log_chat(jidp->barejid, usr_jid, msg, PROF_OUT_LOG, NULL); + jid_destroy(jidp); + } + } else { + cons_show_error("Failed to send message,"); + } + +#else message_send(msg, usr_jid); ui_outgoing_msg("me", usr_jid, msg); @@ -924,7 +942,7 @@ cmd_msg(gchar **args, struct cmd_help_t help) Jid *jidp = jid_create(jid); chat_log_chat(jidp->barejid, usr_jid, msg, PROF_OUT_LOG, NULL); jid_destroy(jidp); - } +#endif return TRUE; } else { diff --git a/src/otr.c b/src/otr.c index e468bdea..2f2ddf3c 100644 --- a/src/otr.c +++ b/src/otr.c @@ -36,23 +36,37 @@ static char *jid; static OtrlPolicy cb_policy(void *opdata, ConnContext *context) { - cons_debug("cb_policy"); - return OTRL_POLICY_DEFAULT ^ OTRL_POLICY_ALLOW_V1; +// cons_debug("cb_policy"); + return OTRL_POLICY_DEFAULT; } static void cb_create_privkey(void *opdata, const char *accountname, const char *protocol) { - cons_debug("cb_create_privkey()"); +// cons_debug("cb_create_privkey accountname: %s, protocol: %s", accountname, protocol); +// GString *key_filename = g_string_new("./"); +// g_string_append(key_filename, accountname); +// g_string_append(key_filename, "_key.txt"); + +// gcry_error_t err = 0; + +// err = otrl_privkey_generate(user_state, key_filename->str, accountname, protocol); +// if (!err == GPG_ERR_NO_ERROR) { +// cons_debug("Failed to generate private key"); +// g_string_free(key_filename, TRUE); +// return; +// } +// otrl_privkey_read(user_state, key_filename->str); +// cons_debug("Generated private key"); } static int cb_is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient) { - cons_debug("cb_is_logged_in: account: %s, protocol: %s, recipient: %s", - accountname, protocol, recipient); +// cons_debug("cb_is_logged_in: account: %s, protocol: %s, recipient: %s", +// accountname, protocol, recipient); return -1; } @@ -60,8 +74,10 @@ static void cb_inject_message(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) { - cons_debug("cb_inject_message: account: %s, protocol, %s, recipient: %s, message: %s", - accountname, protocol, recipient, message); +// cons_debug("cb_inject_message: account: %s, protocol, %s, recipient: %s, message: %s", +// accountname, protocol, recipient, message); + char *msg_decrypt = NULL; + otrl_message_receiving(user_state, &ops, NULL, recipient, protocol, accountname, message, &msg_decrypt, 0, NULL, NULL); } static void @@ -69,73 +85,72 @@ cb_notify(void *opdata, OtrlNotifyLevel level, const char *accountname, const char *protocol, const char *username, const char *title, const char *primary, const char *secondary) { - cons_debug("cb_notify"); +// cons_debug("cb_notify"); } static int cb_display_otr_message(void *opdata, const char *accountname, const char *protocol, const char *username, const char *msg) { - cons_debug("cb_display_otr_message: account: %s, protocol: %s, user: %s, msg: %s", - accountname, protocol, username, msg); + cons_show_error("%s", msg); return 0; } static const char * cb_protocol_name(void *opdata, const char *protocol) { - cons_debug("cb_protocol_name: %s", protocol); - return protocol; +// cons_debug("cb_protocol_name: %s", protocol); + return "xmpp"; } static void cb_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, const char *protocol, const char *username, unsigned char fingerprint[20]) { - cons_debug("cb_new_fingerprint: account: %s, protocol: %s, username: %s", - accountname, protocol, username); +// cons_debug("cb_new_fingerprint: account: %s, protocol: %s, username: %s", +// accountname, protocol, username); } static void cb_protocol_name_free(void *opdata, const char *protocol_name) { - cons_debug("cb_protocol_name_free: %s", protocol_name); +// cons_debug("cb_protocol_name_free: %s", protocol_name); } static void cb_update_context_list(void *opdata) { - cons_debug("cb_update_context_list"); +// cons_debug("cb_update_context_list"); } static void cb_write_fingerprints(void *opdata) { - cons_debug("cb_write_fingerprints"); +// cons_debug("cb_write_fingerprints"); } static void cb_gone_secure(void *opdata, ConnContext *context) { - cons_debug("cb_gone_secure"); +// cons_debug("cb_gone_secure"); } static void cb_gone_insecure(void *opdata, ConnContext *context) { - cons_debug("cb_gone_insecure"); +// cons_debug("cb_gone_insecure"); } static void cb_still_secure(void *opdata, ConnContext *context, int is_reply) { - cons_debug("cb_still_secure: is_reply = %d", is_reply); +// cons_debug("cb_still_secure: is_reply = %d", is_reply); } static void cb_log_message(void *opdata, const char *message) { - cons_debug("cb_log_message: %s", message); +// cons_debug("cb_log_message: %s", message); } void @@ -170,10 +185,14 @@ otr_account_load(ProfAccount *account) GString *key_filename = g_string_new("./"); g_string_append(key_filename, account->jid); + g_string_append(key_filename, "/"); + g_string_append(key_filename, account->jid); g_string_append(key_filename, "_key.txt"); GString *fp_filename = g_string_new("./"); g_string_append(fp_filename, account->jid); + g_string_append(fp_filename, "/"); + g_string_append(fp_filename, account->jid); g_string_append(fp_filename, "_fingerprints.txt"); user_state = otrl_userstate_create(); @@ -191,6 +210,7 @@ otr_account_load(ProfAccount *account) cons_debug("Generated private key"); } + if (!g_file_test(fp_filename->str, G_FILE_TEST_IS_REGULAR)) { cons_debug("Fingerprints not found, creating file"); err = otrl_privkey_write_fingerprints(user_state, fp_filename->str); @@ -228,7 +248,7 @@ otr_account_load(ProfAccount *account) char * otr_encrypt_message(const char * const to, const char * const message) { - cons_debug("otr_encrypt_message, account: %s, protocol: xmpp, recipient: %s", jid, to); +// cons_debug("otr_encrypt_message, account: %s, recipient: %s, message: %s", jid, to, message); gcry_error_t err; char *newmessage = NULL; @@ -243,9 +263,9 @@ otr_encrypt_message(const char * const to, const char * const message) 0, &newmessage, NULL, - &ops); + NULL); if (!err == GPG_ERR_NO_ERROR) { - cons_debug("Error encrypting, result: %s", newmessage); +// cons_debug("Error encrypting, result: %s", newmessage); return NULL; } else { cons_debug("Encrypted, result: %s", newmessage); @@ -253,6 +273,19 @@ otr_encrypt_message(const char * const to, const char * const message) } } +char * +otr_decrypt_message(const char * const from, const char * const message) +{ +// cons_debug("otr_decrypt_message, account: %s, from: %s, message: %s", jid, from, message); + char *decrypted = NULL; + int ignore_mesage = otrl_message_receiving(user_state, &ops, NULL, jid, "xmpp", from, message, &decrypted, 0, NULL, NULL); + if (!ignore_mesage) { + return decrypted; + } else { + return NULL; + } +} + void otr_free_message(char *message) { diff --git a/src/otr.h b/src/otr.h index 42005382..ae8e2cb1 100644 --- a/src/otr.h +++ b/src/otr.h @@ -28,6 +28,7 @@ void otr_init(void); void otr_account_load(ProfAccount *account); char * otr_encrypt_message(const char * const to, const char * const message); +char * otr_decrypt_message(const char * const from, const char * const message); void otr_free_message(char *message); #endif diff --git a/src/server_events.c b/src/server_events.c index ac940c86..48dae2ea 100644 --- a/src/server_events.c +++ b/src/server_events.c @@ -28,6 +28,7 @@ #include "config/preferences.h" #include "roster_list.h" #include "ui/ui.h" +#include "otr.h" void handle_error_message(const char *from, const char *err_msg) @@ -169,17 +170,30 @@ handle_duck_result(const char * const result) void handle_incoming_message(char *from, char *message, gboolean priv) { - ui_incoming_msg(from, message, NULL, priv); + char *newmessage; + if (!priv) { + newmessage = otr_decrypt_message(from, message); + if (newmessage == NULL) { + return; + } + } else { + newmessage = message; + } + + ui_incoming_msg(from, newmessage, NULL, priv); ui_current_page_off(); if (prefs_get_boolean(PREF_CHLOG) && !priv) { Jid *from_jid = jid_create(from); const char *jid = jabber_get_fulljid(); Jid *jidp = jid_create(jid); - chat_log_chat(jidp->barejid, from_jid->barejid, message, PROF_IN_LOG, NULL); + chat_log_chat(jidp->barejid, from_jid->barejid, newmessage, PROF_IN_LOG, NULL); jid_destroy(jidp); jid_destroy(from_jid); } + + if (!priv) + otr_free_message(newmessage); } void From 9fb828165652ad96f903cc09f649f8c5a5ffa918 Mon Sep 17 00:00:00 2001 From: James Booth Date: Fri, 10 Jan 2014 19:50:59 +0000 Subject: [PATCH 12/32] Use data home for otr keys and fingerprints files --- src/otr.c | 56 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/otr.c b/src/otr.c index 2f2ddf3c..c8096167 100644 --- a/src/otr.c +++ b/src/otr.c @@ -183,65 +183,75 @@ otr_account_load(ProfAccount *account) jid = strdup(account->jid); - GString *key_filename = g_string_new("./"); - g_string_append(key_filename, account->jid); - g_string_append(key_filename, "/"); - g_string_append(key_filename, account->jid); - g_string_append(key_filename, "_key.txt"); + gchar *data_home = xdg_get_data_home(); + gchar *account_dir = str_replace(jid, "@", "_at_"); - GString *fp_filename = g_string_new("./"); - g_string_append(fp_filename, account->jid); - g_string_append(fp_filename, "/"); - g_string_append(fp_filename, account->jid); - g_string_append(fp_filename, "_fingerprints.txt"); + GString *basedir = g_string_new(data_home); + g_string_append(basedir, "/profanity/otr/"); + g_string_append(basedir, account_dir); + g_string_append(basedir, "/"); + + if (!mkdir_recursive(basedir->str)) { + g_string_free(basedir, TRUE); + cons_show_error("Could not create otr directory for account %s.", jid); + return; + } user_state = otrl_userstate_create(); gcry_error_t err = 0; - if (!g_file_test(key_filename->str, G_FILE_TEST_IS_REGULAR)) { + GString *keysfilename = g_string_new(basedir->str); + g_string_append(keysfilename, "keys.txt"); + if (!g_file_test(keysfilename->str, G_FILE_TEST_IS_REGULAR)) { cons_debug("Private key not found, generating one"); - err = otrl_privkey_generate(user_state, key_filename->str, account->jid, "xmpp"); + err = otrl_privkey_generate(user_state, keysfilename->str, account->jid, "xmpp"); if (!err == GPG_ERR_NO_ERROR) { + g_string_free(basedir, TRUE); + g_string_free(keysfilename, TRUE); cons_debug("Failed to generate private key"); - g_string_free(key_filename, TRUE); return; } cons_debug("Generated private key"); } - - if (!g_file_test(fp_filename->str, G_FILE_TEST_IS_REGULAR)) { + GString *fpsfilename = g_string_new(basedir->str); + g_string_append(fpsfilename, "fingerprints.txt"); + if (!g_file_test(fpsfilename->str, G_FILE_TEST_IS_REGULAR)) { cons_debug("Fingerprints not found, creating file"); - err = otrl_privkey_write_fingerprints(user_state, fp_filename->str); + err = otrl_privkey_write_fingerprints(user_state, fpsfilename->str); if (!err == GPG_ERR_NO_ERROR) { + g_string_free(basedir, TRUE); + g_string_free(keysfilename, TRUE); cons_debug("Failed to create fingerprints file"); - g_string_free(key_filename, TRUE); return; } cons_debug("Created fingerprints file"); } cons_debug("Loading private key"); - err = otrl_privkey_read(user_state, key_filename->str); + err = otrl_privkey_read(user_state, keysfilename->str); if (!err == GPG_ERR_NO_ERROR) { + g_string_free(basedir, TRUE); + g_string_free(keysfilename, TRUE); cons_debug("Failed to load private key"); - g_string_free(key_filename, TRUE); return; } cons_debug("Loaded private key"); cons_debug("Loading fingerprints"); - err = otrl_privkey_read_fingerprints(user_state, fp_filename->str, NULL, NULL); + err = otrl_privkey_read_fingerprints(user_state, fpsfilename->str, NULL, NULL); if (!err == GPG_ERR_NO_ERROR) { + g_string_free(basedir, TRUE); + g_string_free(keysfilename, TRUE); cons_debug("Failed to load fingerprints"); - g_string_free(fp_filename, TRUE); return; } cons_debug("Loaded fingerprints"); - g_string_free(key_filename, TRUE); - g_string_free(fp_filename, TRUE); + g_string_free(basedir, TRUE); + g_string_free(keysfilename, TRUE); + g_string_free(fpsfilename, TRUE); return; } From 1b5254010ea1fd2b29d9802ed19de00d35e0cc8d Mon Sep 17 00:00:00 2001 From: James Booth Date: Fri, 10 Jan 2014 20:12:22 +0000 Subject: [PATCH 13/32] Show users fingerprint after generating key --- src/otr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/otr.c b/src/otr.c index c8096167..bb48d1f0 100644 --- a/src/otr.c +++ b/src/otr.c @@ -249,6 +249,10 @@ otr_account_load(ProfAccount *account) } cons_debug("Loaded fingerprints"); + char fingerprint[45]; + otrl_privkey_fingerprint(user_state, fingerprint, jid, "xmpp"); + cons_debug("Your fingerprint: %s", fingerprint); + g_string_free(basedir, TRUE); g_string_free(keysfilename, TRUE); g_string_free(fpsfilename, TRUE); From e294a6db92b23956be777844d3796393147f5f30 Mon Sep 17 00:00:00 2001 From: James Booth Date: Fri, 10 Jan 2014 20:20:38 +0000 Subject: [PATCH 14/32] Added "/otr fp" to show users fingerprint --- src/command/command.c | 7 ++++--- src/command/commands.c | 20 ++++++++++++-------- src/otr.c | 10 ++++++++++ src/otr.h | 1 + 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index 9aca7185..3f220cee 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -570,10 +570,11 @@ static struct cmd_t command_defs[] = { "/otr", cmd_otr, parse_args, 1, 2, NULL, - { "/otr gen|start|end|trust|untrust [contact]", "Off The Record encryption commands.", - { "/otr gen|start|end|trust|untrust [contact]", - "-----------------------------------------", + { "/otr gen|fp", "Off The Record encryption commands.", + { "/otr gen|fp", + "-----------", "gen - Load or create private key and fingerprints.", + "fp - Show your fingerprint.", NULL } } }, { "/outtype", diff --git a/src/command/commands.c b/src/command/commands.c index b71e2af8..cb38d934 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2287,15 +2287,19 @@ gboolean cmd_otr(gchar **args, struct cmd_help_t help) { #ifdef HAVE_LIBOTR + if (jabber_get_connection_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to load OTR information."); + return TRUE; + } + if (strcmp(args[0], "gen") == 0) { - if (jabber_get_connection_status() != JABBER_CONNECTED) { - cons_show("You must be connected with an account to load OTR information."); - return TRUE; - } else { - ProfAccount *account = accounts_get_account(jabber_get_account_name()); - otr_account_load(account); - return TRUE; - } + ProfAccount *account = accounts_get_account(jabber_get_account_name()); + otr_account_load(account); + return TRUE; + } else if (strcmp(args[0], "fp") == 0) { + char *fingerprint = otr_get_fingerprint(); + cons_show("Your fingerprint: %s", fingerprint); + return TRUE; } else { cons_show("Usage: %s", help.usage); return TRUE; diff --git a/src/otr.c b/src/otr.c index bb48d1f0..b6b19252 100644 --- a/src/otr.c +++ b/src/otr.c @@ -259,6 +259,16 @@ otr_account_load(ProfAccount *account) return; } +char * +otr_get_fingerprint(void) +{ + char fingerprint[45]; + otrl_privkey_fingerprint(user_state, fingerprint, jid, "xmpp"); + char *result = strdup(fingerprint); + + return result; +} + char * otr_encrypt_message(const char * const to, const char * const message) { diff --git a/src/otr.h b/src/otr.h index ae8e2cb1..cb964908 100644 --- a/src/otr.h +++ b/src/otr.h @@ -27,6 +27,7 @@ void otr_init(void); void otr_account_load(ProfAccount *account); +char * otr_get_fingerprint(void); char * otr_encrypt_message(const char * const to, const char * const message); char * otr_decrypt_message(const char * const from, const char * const message); void otr_free_message(char *message); From df8c7dedc12b05d29d759e54637c0cbeb37f54fd Mon Sep 17 00:00:00 2001 From: James Booth Date: Fri, 10 Jan 2014 21:57:38 +0000 Subject: [PATCH 15/32] Implemented message encryption, session started with whitspace tags on first message --- src/otr.c | 26 ++++++-------------------- src/otr.h | 2 ++ 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/otr.c b/src/otr.c index b6b19252..cb72b777 100644 --- a/src/otr.c +++ b/src/otr.c @@ -37,7 +37,7 @@ static OtrlPolicy cb_policy(void *opdata, ConnContext *context) { // cons_debug("cb_policy"); - return OTRL_POLICY_DEFAULT; + return OTRL_POLICY_DEFAULT ; } static void @@ -45,20 +45,6 @@ cb_create_privkey(void *opdata, const char *accountname, const char *protocol) { // cons_debug("cb_create_privkey accountname: %s, protocol: %s", accountname, protocol); -// GString *key_filename = g_string_new("./"); -// g_string_append(key_filename, accountname); -// g_string_append(key_filename, "_key.txt"); - -// gcry_error_t err = 0; - -// err = otrl_privkey_generate(user_state, key_filename->str, accountname, protocol); -// if (!err == GPG_ERR_NO_ERROR) { -// cons_debug("Failed to generate private key"); -// g_string_free(key_filename, TRUE); -// return; -// } -// otrl_privkey_read(user_state, key_filename->str); -// cons_debug("Generated private key"); } static int @@ -76,8 +62,7 @@ cb_inject_message(void *opdata, const char *accountname, { // cons_debug("cb_inject_message: account: %s, protocol, %s, recipient: %s, message: %s", // accountname, protocol, recipient, message); - char *msg_decrypt = NULL; - otrl_message_receiving(user_state, &ops, NULL, recipient, protocol, accountname, message, &msg_decrypt, 0, NULL, NULL); + message_send(message, recipient); } static void @@ -272,7 +257,7 @@ otr_get_fingerprint(void) char * otr_encrypt_message(const char * const to, const char * const message) { -// cons_debug("otr_encrypt_message, account: %s, recipient: %s, message: %s", jid, to, message); + cons_debug("Encrypting message: %s", message); gcry_error_t err; char *newmessage = NULL; @@ -292,7 +277,7 @@ otr_encrypt_message(const char * const to, const char * const message) // cons_debug("Error encrypting, result: %s", newmessage); return NULL; } else { - cons_debug("Encrypted, result: %s", newmessage); + cons_debug("Encrypted message: %s", newmessage); return newmessage; } } @@ -300,10 +285,11 @@ otr_encrypt_message(const char * const to, const char * const message) char * otr_decrypt_message(const char * const from, const char * const message) { -// cons_debug("otr_decrypt_message, account: %s, from: %s, message: %s", jid, from, message); + cons_debug("Decrypting message: %s", message); char *decrypted = NULL; int ignore_mesage = otrl_message_receiving(user_state, &ops, NULL, jid, "xmpp", from, message, &decrypted, 0, NULL, NULL); if (!ignore_mesage) { + cons_debug("Decrypted message: %s", decrypted); return decrypted; } else { return NULL; diff --git a/src/otr.h b/src/otr.h index cb964908..c96290c2 100644 --- a/src/otr.h +++ b/src/otr.h @@ -28,8 +28,10 @@ void otr_init(void); void otr_account_load(ProfAccount *account); char * otr_get_fingerprint(void); + char * otr_encrypt_message(const char * const to, const char * const message); char * otr_decrypt_message(const char * const from, const char * const message); + void otr_free_message(char *message); #endif From 5c13538e6b71cc0d1f5fe49017a99dceecfd007a Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 15:48:22 +0000 Subject: [PATCH 16/32] Load OTR private key on connect, gen command checks if already generated --- src/command/commands.c | 2 +- src/otr.c | 146 +++++++++++++++++++++++++++++++---------- src/otr.h | 4 +- src/server_events.c | 1 + 4 files changed, 115 insertions(+), 38 deletions(-) diff --git a/src/command/commands.c b/src/command/commands.c index cb38d934..649c084e 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2294,7 +2294,7 @@ cmd_otr(gchar **args, struct cmd_help_t help) if (strcmp(args[0], "gen") == 0) { ProfAccount *account = accounts_get_account(jabber_get_account_name()); - otr_account_load(account); + otr_keygen(account); return TRUE; } else if (strcmp(args[0], "fp") == 0) { char *fingerprint = otr_get_fingerprint(); diff --git a/src/otr.c b/src/otr.c index cb72b777..b5be978d 100644 --- a/src/otr.c +++ b/src/otr.c @@ -26,11 +26,13 @@ #include #include "otr.h" +#include "log.h" #include "ui/ui.h" static OtrlUserState user_state; static OtrlMessageAppOps ops; static char *jid; +static gboolean data_loaded; // ops callbacks static OtrlPolicy @@ -141,7 +143,7 @@ cb_log_message(void *opdata, const char *message) void otr_init(void) { - cons_debug("otr_init()"); + log_info("Initialising OTR"); OTRL_INIT; ops.policy = cb_policy; @@ -159,12 +161,89 @@ otr_init(void) ops.gone_insecure = cb_gone_insecure; ops.still_secure = cb_still_secure; ops.log_message = cb_log_message; + + data_loaded = FALSE; } void -otr_account_load(ProfAccount *account) +otr_on_connect(ProfAccount *account) { - cons_debug("otr_account_load()"); + jid = strdup(account->jid); + log_info("Loading OTR key for %s", jid); + + gchar *data_home = xdg_get_data_home(); + gchar *account_dir = str_replace(jid, "@", "_at_"); + + GString *basedir = g_string_new(data_home); + g_string_append(basedir, "/profanity/otr/"); + g_string_append(basedir, account_dir); + g_string_append(basedir, "/"); + + if (!mkdir_recursive(basedir->str)) { + g_string_free(basedir, TRUE); + log_error("Could not create %s for account %s.", basedir->str, jid); + cons_show_error("Could not create %s for account %s.", basedir->str, jid); + return; + } + + user_state = otrl_userstate_create(); + + gcry_error_t err = 0; + + GString *keysfilename = g_string_new(basedir->str); + g_string_append(keysfilename, "keys.txt"); + if (!g_file_test(keysfilename->str, G_FILE_TEST_IS_REGULAR)) { + log_info("No private key file found %s", keysfilename->str); + data_loaded = FALSE; + } else { + log_info("Loading OTR private key %s", keysfilename->str); + err = otrl_privkey_read(user_state, keysfilename->str); + if (!err == GPG_ERR_NO_ERROR) { + g_string_free(basedir, TRUE); + g_string_free(keysfilename, TRUE); + log_error("Failed to load private key"); + return; + } else { + log_info("Loaded private key"); + data_loaded = TRUE; + } + } + + GString *fpsfilename = g_string_new(basedir->str); + g_string_append(fpsfilename, "fingerprints.txt"); + if (!g_file_test(fpsfilename->str, G_FILE_TEST_IS_REGULAR)) { + log_info("No fingerprints file found %s", fpsfilename->str); + data_loaded = FALSE; + } else { + log_info("Loading fingerprints %s", fpsfilename->str); + err = otrl_privkey_read_fingerprints(user_state, fpsfilename->str, NULL, NULL); + if (!err == GPG_ERR_NO_ERROR) { + g_string_free(basedir, TRUE); + g_string_free(keysfilename, TRUE); + log_error("Failed to load fingerprints"); + return; + } else { + log_info("Loaded fingerprints"); + data_loaded = TRUE; + } + } + + g_string_free(basedir, TRUE); + g_string_free(keysfilename, TRUE); + g_string_free(fpsfilename, TRUE); + return; +} + +void +otr_keygen(ProfAccount *account) +{ + if (data_loaded) { + cons_show("OTR key already generated."); + return; + } + + jid = strdup(account->jid); + log_info("Generating OTR key for %s", jid); jid = strdup(account->jid); @@ -178,65 +257,60 @@ otr_account_load(ProfAccount *account) if (!mkdir_recursive(basedir->str)) { g_string_free(basedir, TRUE); - cons_show_error("Could not create otr directory for account %s.", jid); + log_error("Could not create %s for account %s.", basedir->str, jid); + cons_show_error("Could not create %s for account %s.", basedir->str, jid); return; } - user_state = otrl_userstate_create(); - gcry_error_t err = 0; GString *keysfilename = g_string_new(basedir->str); g_string_append(keysfilename, "keys.txt"); - if (!g_file_test(keysfilename->str, G_FILE_TEST_IS_REGULAR)) { - cons_debug("Private key not found, generating one"); - err = otrl_privkey_generate(user_state, keysfilename->str, account->jid, "xmpp"); - if (!err == GPG_ERR_NO_ERROR) { - g_string_free(basedir, TRUE); - g_string_free(keysfilename, TRUE); - cons_debug("Failed to generate private key"); - return; - } - cons_debug("Generated private key"); + log_debug("Generating private key file %s for %s", keysfilename->str, jid); + cons_show("Generating private key, this may take some time."); + cons_show("Moving the mouse randomly around the screen may speed up the process!"); + ui_current_page_off(); + ui_refresh(); + err = otrl_privkey_generate(user_state, keysfilename->str, account->jid, "xmpp"); + if (!err == GPG_ERR_NO_ERROR) { + g_string_free(basedir, TRUE); + g_string_free(keysfilename, TRUE); + log_error("Failed to generate private key"); + cons_show_error("Failed to generate private key"); + return; } + log_info("Private key generated"); + cons_show(""); + cons_show("Private key generation complete."); GString *fpsfilename = g_string_new(basedir->str); g_string_append(fpsfilename, "fingerprints.txt"); - if (!g_file_test(fpsfilename->str, G_FILE_TEST_IS_REGULAR)) { - cons_debug("Fingerprints not found, creating file"); - err = otrl_privkey_write_fingerprints(user_state, fpsfilename->str); - if (!err == GPG_ERR_NO_ERROR) { - g_string_free(basedir, TRUE); - g_string_free(keysfilename, TRUE); - cons_debug("Failed to create fingerprints file"); - return; - } - cons_debug("Created fingerprints file"); + log_debug("Generating fingerprints file %s for %s", fpsfilename->str, jid); + err = otrl_privkey_write_fingerprints(user_state, fpsfilename->str); + if (!err == GPG_ERR_NO_ERROR) { + g_string_free(basedir, TRUE); + g_string_free(keysfilename, TRUE); + log_error("Failed to create fingerprints file"); + cons_show_error("Failed to create fingerprints file"); + return; } + log_info("Fingerprints file created"); - cons_debug("Loading private key"); err = otrl_privkey_read(user_state, keysfilename->str); if (!err == GPG_ERR_NO_ERROR) { g_string_free(basedir, TRUE); g_string_free(keysfilename, TRUE); - cons_debug("Failed to load private key"); + log_error("Failed to load private key"); return; } - cons_debug("Loaded private key"); - cons_debug("Loading fingerprints"); err = otrl_privkey_read_fingerprints(user_state, fpsfilename->str, NULL, NULL); if (!err == GPG_ERR_NO_ERROR) { g_string_free(basedir, TRUE); g_string_free(keysfilename, TRUE); - cons_debug("Failed to load fingerprints"); + log_error("Failed to load fingerprints"); return; } - cons_debug("Loaded fingerprints"); - - char fingerprint[45]; - otrl_privkey_fingerprint(user_state, fingerprint, jid, "xmpp"); - cons_debug("Your fingerprint: %s", fingerprint); g_string_free(basedir, TRUE); g_string_free(keysfilename, TRUE); diff --git a/src/otr.h b/src/otr.h index c96290c2..924a18f6 100644 --- a/src/otr.h +++ b/src/otr.h @@ -26,7 +26,9 @@ #include "config/accounts.h" void otr_init(void); -void otr_account_load(ProfAccount *account); +void otr_on_connect(ProfAccount *account); +void otr_keygen(ProfAccount *account); + char * otr_get_fingerprint(void); char * otr_encrypt_message(const char * const to, const char * const message); diff --git a/src/server_events.c b/src/server_events.c index 48dae2ea..e83fbf27 100644 --- a/src/server_events.c +++ b/src/server_events.c @@ -49,6 +49,7 @@ void handle_login_account_success(char *account_name) { ProfAccount *account = accounts_get_account(account_name); + otr_on_connect(account); resource_presence_t resource_presence = accounts_get_login_presence(account->name); contact_presence_t contact_presence = contact_presence_from_resource_presence(resource_presence); cons_show_login_success(account); From 9daefe170d7fa1f50620acdd0a9073b98ba373f9 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 17:03:01 +0000 Subject: [PATCH 17/32] Require '/otr start' from both parties before encryption used both ways --- src/command/command.c | 25 +++++++++++++++------- src/command/commands.c | 47 +++++++++++++++++++++++++++++++++++------- src/otr.c | 24 ++++++++++++++++++--- src/otr.h | 2 ++ src/server_events.c | 14 +++++++++++++ src/ui/core.c | 16 ++++++++++++++ src/ui/ui.h | 2 ++ src/ui/window.c | 1 + src/ui/window.h | 1 + 9 files changed, 114 insertions(+), 18 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index 3f220cee..1a7c23da 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -1140,10 +1140,24 @@ cmd_execute_default(const char * const inp) ui_current_print_line("You are not currently connected."); } else { #ifdef HAVE_LIBOTR - char *encrypted = otr_encrypt_message(recipient, inp); - if (encrypted != NULL) { - message_send(encrypted, recipient); - otr_free_message(encrypted); + if (ui_current_win_is_otr()) { + char *encrypted = otr_encrypt_message(recipient, inp); + if (encrypted != NULL) { + message_send(encrypted, recipient); + otr_free_message(encrypted); + if (prefs_get_boolean(PREF_CHLOG)) { + const char *jid = jabber_get_fulljid(); + Jid *jidp = jid_create(jid); + chat_log_chat(jidp->barejid, recipient, inp, PROF_OUT_LOG, NULL); + jid_destroy(jidp); + } + + ui_outgoing_msg("me", recipient, inp); + } else { + cons_show_error("Failed to send message."); + } + } else { + message_send(inp, recipient); if (prefs_get_boolean(PREF_CHLOG)) { const char *jid = jabber_get_fulljid(); Jid *jidp = jid_create(jid); @@ -1152,8 +1166,6 @@ cmd_execute_default(const char * const inp) } ui_outgoing_msg("me", recipient, inp); - } else { - cons_show_error("Failed to send message."); } #else message_send(inp, recipient); @@ -1166,7 +1178,6 @@ cmd_execute_default(const char * const inp) ui_outgoing_msg("me", recipient, inp); #endif - } break; diff --git a/src/command/commands.c b/src/command/commands.c index 649c084e..6b0b38f9 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -42,6 +42,8 @@ #include "tools/parser.h" #include "tools/tinyurl.h" #include "ui/ui.h" +#include "ui/window.h" +#include "ui/windows.h" #include "xmpp/xmpp.h" #include "xmpp/bookmark.h" @@ -917,10 +919,24 @@ cmd_msg(gchar **args, struct cmd_help_t help) } if (msg != NULL) { #ifdef HAVE_LIBOTR - char *encrypted = otr_encrypt_message(usr_jid, msg); - if (encrypted != NULL) { - message_send(encrypted, usr_jid); - otr_free_message(encrypted); + if (ui_current_win_is_otr()) { + char *encrypted = otr_encrypt_message(usr_jid, msg); + if (encrypted != NULL) { + message_send(encrypted, usr_jid); + otr_free_message(encrypted); + ui_outgoing_msg("me", usr_jid, msg); + + if (((win_type == WIN_CHAT) || (win_type == WIN_CONSOLE)) && prefs_get_boolean(PREF_CHLOG)) { + const char *jid = jabber_get_fulljid(); + Jid *jidp = jid_create(jid); + chat_log_chat(jidp->barejid, usr_jid, msg, PROF_OUT_LOG, NULL); + jid_destroy(jidp); + } + } else { + cons_show_error("Failed to encrypt and send message,"); + } + } else { + message_send(msg, usr_jid); ui_outgoing_msg("me", usr_jid, msg); if (((win_type == WIN_CHAT) || (win_type == WIN_CONSOLE)) && prefs_get_boolean(PREF_CHLOG)) { @@ -929,10 +945,8 @@ cmd_msg(gchar **args, struct cmd_help_t help) chat_log_chat(jidp->barejid, usr_jid, msg, PROF_OUT_LOG, NULL); jid_destroy(jidp); } - } else { - cons_show_error("Failed to send message,"); } - + return TRUE; #else message_send(msg, usr_jid); ui_outgoing_msg("me", usr_jid, msg); @@ -942,9 +956,10 @@ cmd_msg(gchar **args, struct cmd_help_t help) Jid *jidp = jid_create(jid); chat_log_chat(jidp->barejid, usr_jid, msg, PROF_OUT_LOG, NULL); jid_destroy(jidp); + } + return TRUE; #endif - return TRUE; } else { const char * jid = NULL; @@ -2300,6 +2315,22 @@ cmd_otr(gchar **args, struct cmd_help_t help) char *fingerprint = otr_get_fingerprint(); cons_show("Your fingerprint: %s", fingerprint); return TRUE; + } else if (strcmp(args[0], "start") == 0) { + win_type_t win_type = ui_current_win_type(); + + if (win_type != WIN_CHAT) { + ui_current_print_line("You must be in a regular chat window to start an OTR session."); + } else if (ui_current_win_is_otr()) { + ui_current_print_line("You are already in an OTR session."); + } else { + if (!otr_key_loaded()) { + ui_current_print_line("You have not generated or loaded a private key, use '/otr gen'"); + } else { + ui_current_print_line("Starting OTR session"); + ui_current_set_otr(TRUE); + } + } + return TRUE; } else { cons_show("Usage: %s", help.usage); return TRUE; diff --git a/src/otr.c b/src/otr.c index b5be978d..dabf944e 100644 --- a/src/otr.c +++ b/src/otr.c @@ -301,6 +301,7 @@ otr_keygen(ProfAccount *account) g_string_free(basedir, TRUE); g_string_free(keysfilename, TRUE); log_error("Failed to load private key"); + data_loaded = FALSE; return; } @@ -309,15 +310,24 @@ otr_keygen(ProfAccount *account) g_string_free(basedir, TRUE); g_string_free(keysfilename, TRUE); log_error("Failed to load fingerprints"); + data_loaded = FALSE; return; } + data_loaded = TRUE; + g_string_free(basedir, TRUE); g_string_free(keysfilename, TRUE); g_string_free(fpsfilename, TRUE); return; } +gboolean +otr_key_loaded(void) +{ + return data_loaded; +} + char * otr_get_fingerprint(void) { @@ -361,12 +371,20 @@ otr_decrypt_message(const char * const from, const char * const message) { cons_debug("Decrypting message: %s", message); char *decrypted = NULL; - int ignore_mesage = otrl_message_receiving(user_state, &ops, NULL, jid, "xmpp", from, message, &decrypted, 0, NULL, NULL); - if (!ignore_mesage) { + int result = otrl_message_receiving(user_state, &ops, NULL, jid, "xmpp", from, message, &decrypted, 0, NULL, NULL); + + // internal libotr message, ignore + if (result == 1) { + return NULL; + + // message was decrypted, return to user + } else if (decrypted != NULL) { cons_debug("Decrypted message: %s", decrypted); return decrypted; + + // normal non OTR message } else { - return NULL; + return strdup(message); } } diff --git a/src/otr.h b/src/otr.h index 924a18f6..59477ab0 100644 --- a/src/otr.h +++ b/src/otr.h @@ -29,6 +29,8 @@ void otr_init(void); void otr_on_connect(ProfAccount *account); void otr_keygen(ProfAccount *account); +gboolean otr_key_loaded(void); + char * otr_get_fingerprint(void); char * otr_encrypt_message(const char * const to, const char * const message); diff --git a/src/server_events.c b/src/server_events.c index e83fbf27..5e478a51 100644 --- a/src/server_events.c +++ b/src/server_events.c @@ -171,6 +171,7 @@ handle_duck_result(const char * const result) void handle_incoming_message(char *from, char *message, gboolean priv) { +#ifdef HAVE_LIBOTR char *newmessage; if (!priv) { newmessage = otr_decrypt_message(from, message); @@ -195,6 +196,19 @@ handle_incoming_message(char *from, char *message, gboolean priv) if (!priv) otr_free_message(newmessage); +#else + ui_incoming_msg(from, message, NULL, priv); + ui_current_page_off(); + + if (prefs_get_boolean(PREF_CHLOG) && !priv) { + Jid *from_jid = jid_create(from); + const char *jid = jabber_get_fulljid(); + Jid *jidp = jid_create(jid); + chat_log_chat(jidp->barejid, from_jid->barejid, message, PROF_IN_LOG, NULL); + jid_destroy(jidp); + jid_destroy(from_jid); + } +#endif } void diff --git a/src/ui/core.c b/src/ui/core.c index afda184a..4ff43ff1 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -693,6 +693,20 @@ _ui_current_win_type(void) return current->type; } +static gboolean +_ui_current_win_is_otr(void) +{ + ProfWin *current = wins_get_current(); + return current->is_otr; +} + +static void +_ui_current_set_otr(gboolean value) +{ + ProfWin *current = wins_get_current(); + current->is_otr = value; +} + static int _ui_current_win_index(void) { @@ -1609,4 +1623,6 @@ ui_init_module(void) ui_unread = _ui_unread; ui_win_unread = _ui_win_unread; ui_ask_password = _ui_ask_password; + ui_current_win_is_otr = _ui_current_win_is_otr; + ui_current_set_otr = _ui_current_set_otr; } diff --git a/src/ui/ui.h b/src/ui/ui.h index e5d4118d..546f606a 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -77,6 +77,8 @@ void (*ui_close_current)(void); void (*ui_clear_current)(void); win_type_t (*ui_current_win_type)(void); int (*ui_current_win_index)(void); +gboolean (*ui_current_win_is_otr)(void); +void (*ui_current_set_otr)(gboolean value); char* (*ui_current_recipient)(void); void (*ui_current_print_line)(const char * const msg, ...); void (*ui_current_error_line)(const char * const msg); diff --git a/src/ui/window.c b/src/ui/window.c index daf9bef0..c144dc06 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -54,6 +54,7 @@ win_create(const char * const title, int cols, win_type_t type) new_win->unread = 0; new_win->history_shown = 0; new_win->type = type; + new_win->is_otr = FALSE; scrollok(new_win->win, TRUE); return new_win; diff --git a/src/ui/window.h b/src/ui/window.h index 4c97429a..4a65488e 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -48,6 +48,7 @@ typedef struct prof_win_t { char *from; WINDOW *win; win_type_t type; + gboolean is_otr; int y_pos; int paged; int unread; From d189f7ea43e0978b33ad0d4ed3dbcf95774c4598 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 17:15:07 +0000 Subject: [PATCH 18/32] Use version 1 query to start OTR session --- src/command/commands.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/command/commands.c b/src/command/commands.c index 6b0b38f9..e9e70063 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2327,6 +2327,8 @@ cmd_otr(gchar **args, struct cmd_help_t help) ui_current_print_line("You have not generated or loaded a private key, use '/otr gen'"); } else { ui_current_print_line("Starting OTR session"); + char *recipient = ui_current_recipient(); + message_send("?OTR?", recipient); ui_current_set_otr(TRUE); } } From bf494b1b07654103cd0c0867e37326e618b58f4d Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 18:06:46 +0000 Subject: [PATCH 19/32] Refactored setting window recipient --- src/ui/core.c | 57 ++++++++++++++++++++++------------------------- src/ui/titlebar.c | 5 +++-- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/ui/core.c b/src/ui/core.c index 4ff43ff1..622261b6 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -501,6 +501,24 @@ _ui_close_read_wins(void) return count; } +GString * +_get_recipient_string(ProfWin *window) +{ + GString *result = g_string_new(""); + PContact contact = roster_get_contact(window->from); + if (contact != NULL) { + if (p_contact_name(contact) != NULL) { + g_string_append(result, p_contact_name(contact)); + } else { + g_string_append(result, window->from); + } + } else { + g_string_append(result, window->from); + } + + return result; +} + static void _ui_switch_win(const int i) { @@ -517,16 +535,9 @@ _ui_switch_win(const int i) status_bar_current(1); status_bar_active(1); } else { - PContact contact = roster_get_contact(new_current->from); - if (contact != NULL) { - if (p_contact_name(contact) != NULL) { - title_bar_set_recipient(p_contact_name(contact)); - } else { - title_bar_set_recipient(new_current->from); - } - } else { - title_bar_set_recipient(new_current->from); - } + GString *recipient_str = _get_recipient_string(new_current); + title_bar_set_recipient(recipient_str->str); + g_string_free(recipient_str, TRUE); title_bar_draw(); status_bar_current(i); status_bar_active(i); @@ -551,16 +562,9 @@ _ui_next_win(void) status_bar_current(1); status_bar_active(1); } else { - PContact contact = roster_get_contact(new_current->from); - if (contact != NULL) { - if (p_contact_name(contact) != NULL) { - title_bar_set_recipient(p_contact_name(contact)); - } else { - title_bar_set_recipient(new_current->from); - } - } else { - title_bar_set_recipient(new_current->from); - } + GString *recipient_str = _get_recipient_string(new_current); + title_bar_set_recipient(recipient_str->str); + g_string_free(recipient_str, TRUE); title_bar_draw(); status_bar_current(i); status_bar_active(i); @@ -584,16 +588,9 @@ _ui_previous_win(void) status_bar_current(1); status_bar_active(1); } else { - PContact contact = roster_get_contact(new_current->from); - if (contact != NULL) { - if (p_contact_name(contact) != NULL) { - title_bar_set_recipient(p_contact_name(contact)); - } else { - title_bar_set_recipient(new_current->from); - } - } else { - title_bar_set_recipient(new_current->from); - } + GString *recipient_str = _get_recipient_string(new_current); + title_bar_set_recipient(recipient_str->str); + g_string_free(recipient_str, TRUE); title_bar_draw(); status_bar_current(i); status_bar_active(i); diff --git a/src/ui/titlebar.c b/src/ui/titlebar.c index 04492b62..37827979 100644 --- a/src/ui/titlebar.c +++ b/src/ui/titlebar.c @@ -29,7 +29,7 @@ static WINDOW *title_bar; static char *current_title = NULL; -static const char *recipient = NULL; +static char *recipient = NULL; static GTimer *typing_elapsed; static int dirty; static contact_presence_t current_status; @@ -132,7 +132,8 @@ _title_bar_set_recipient(const char * const from) g_timer_destroy(typing_elapsed); typing_elapsed = NULL; } - recipient = from; + free(recipient); + recipient = strdup(from); if (current_title != NULL) { free(current_title); From b5469e2e06523c4ba71a7a215041080c89bb6763 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 18:24:44 +0000 Subject: [PATCH 20/32] Added [OTR] and (trusted/untrusted) to titlebar for OTR sessions --- src/command/commands.c | 2 ++ src/otr.c | 4 ++++ src/ui/core.c | 9 +++++++++ src/ui/window.c | 1 + src/ui/window.h | 1 + 5 files changed, 17 insertions(+) diff --git a/src/command/commands.c b/src/command/commands.c index e9e70063..525b188c 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2330,6 +2330,8 @@ cmd_otr(gchar **args, struct cmd_help_t help) char *recipient = ui_current_recipient(); message_send("?OTR?", recipient); ui_current_set_otr(TRUE); + // refresh to show OTR in titlebar + ui_switch_win(ui_current_win_index()); } } return TRUE; diff --git a/src/otr.c b/src/otr.c index dabf944e..42a02e96 100644 --- a/src/otr.c +++ b/src/otr.c @@ -228,6 +228,10 @@ otr_on_connect(ProfAccount *account) } } + if (data_loaded) { + cons_show("Loaded OTR private key for %s", jid); + } + g_string_free(basedir, TRUE); g_string_free(keysfilename, TRUE); g_string_free(fpsfilename, TRUE); diff --git a/src/ui/core.c b/src/ui/core.c index 622261b6..41fd31e8 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -516,6 +516,15 @@ _get_recipient_string(ProfWin *window) g_string_append(result, window->from); } + if (window->is_otr) { + g_string_append(result, " [OTR]"); + if (window->is_trusted) { + g_string_append(result, " (trusted)"); + } else { + g_string_append(result, " (untrusted)"); + } + } + return result; } diff --git a/src/ui/window.c b/src/ui/window.c index c144dc06..6d6df429 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -55,6 +55,7 @@ win_create(const char * const title, int cols, win_type_t type) new_win->history_shown = 0; new_win->type = type; new_win->is_otr = FALSE; + new_win->is_trusted = FALSE; scrollok(new_win->win, TRUE); return new_win; diff --git a/src/ui/window.h b/src/ui/window.h index 4a65488e..fabf8ae6 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -49,6 +49,7 @@ typedef struct prof_win_t { WINDOW *win; win_type_t type; gboolean is_otr; + gboolean is_trusted; int y_pos; int paged; int unread; From bc8532b79cfa453df4da2fb0464d4106c48e8986 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 18:29:37 +0000 Subject: [PATCH 21/32] Renamed fingerprint command, updated help --- src/command/command.c | 11 ++++++----- src/command/commands.c | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index 1a7c23da..eaa72197 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -570,11 +570,12 @@ static struct cmd_t command_defs[] = { "/otr", cmd_otr, parse_args, 1, 2, NULL, - { "/otr gen|fp", "Off The Record encryption commands.", - { "/otr gen|fp", - "-----------", - "gen - Load or create private key and fingerprints.", - "fp - Show your fingerprint.", + { "/otr gen|myfp|start", "Off The Record encryption commands.", + { "/otr gen|myfp|start", + "-------------------", + "gen - Generate your private key.", + "myfp - Show your fingerprint.", + "start - Start an OTR session with the current recipient.", NULL } } }, { "/outtype", diff --git a/src/command/commands.c b/src/command/commands.c index 525b188c..6d9479b7 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2311,7 +2311,7 @@ cmd_otr(gchar **args, struct cmd_help_t help) ProfAccount *account = accounts_get_account(jabber_get_account_name()); otr_keygen(account); return TRUE; - } else if (strcmp(args[0], "fp") == 0) { + } else if (strcmp(args[0], "myfp") == 0) { char *fingerprint = otr_get_fingerprint(); cons_show("Your fingerprint: %s", fingerprint); return TRUE; From 03086c03841d2f1b44d086d4d17838abf5fb1e4c Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 19:10:00 +0000 Subject: [PATCH 22/32] Added /otr theirfp with hardcoded fingerprint --- src/command/commands.c | 17 ++++++++++++++++- src/otr.c | 9 ++++++++- src/otr.h | 3 ++- src/ui/console.c | 2 +- src/ui/core.c | 11 +++++++---- src/ui/window.c | 10 ++++++++++ src/ui/window.h | 4 +++- 7 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/command/commands.c b/src/command/commands.c index 6d9479b7..4eb8a645 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2312,8 +2312,23 @@ cmd_otr(gchar **args, struct cmd_help_t help) otr_keygen(account); return TRUE; } else if (strcmp(args[0], "myfp") == 0) { - char *fingerprint = otr_get_fingerprint(); + char *fingerprint = otr_get_my_fingerprint(); cons_show("Your fingerprint: %s", fingerprint); + free(fingerprint); + return TRUE; + } else if (strcmp(args[0], "theirfp") == 0) { + win_type_t win_type = ui_current_win_type(); + + if (win_type != WIN_CHAT) { + ui_current_print_line("You must be in a regular chat window to view a recipient's fingerprint."); + } else if (!ui_current_win_is_otr()) { + ui_current_print_line("You not currently in an OTR session with this recipient."); + } else { + char *recipient = ui_current_recipient(); + char *fingerprint = otr_get_their_fingerprint(recipient); + ui_current_print_line("OTR fingerprint for %s: %s", recipient, fingerprint); + free(fingerprint); + } return TRUE; } else if (strcmp(args[0], "start") == 0) { win_type_t win_type = ui_current_win_type(); diff --git a/src/otr.c b/src/otr.c index 42a02e96..52ce6540 100644 --- a/src/otr.c +++ b/src/otr.c @@ -333,7 +333,7 @@ otr_key_loaded(void) } char * -otr_get_fingerprint(void) +otr_get_my_fingerprint(void) { char fingerprint[45]; otrl_privkey_fingerprint(user_state, fingerprint, jid, "xmpp"); @@ -342,6 +342,13 @@ otr_get_fingerprint(void) return result; } +char * +otr_get_their_fingerprint(char *recipient) +{ + char *fingerprint = "1234 5678"; + return strdup(fingerprint); +} + char * otr_encrypt_message(const char * const to, const char * const message) { diff --git a/src/otr.h b/src/otr.h index 59477ab0..5008b2d3 100644 --- a/src/otr.h +++ b/src/otr.h @@ -31,7 +31,8 @@ void otr_keygen(ProfAccount *account); gboolean otr_key_loaded(void); -char * otr_get_fingerprint(void); +char * otr_get_my_fingerprint(void); +char * otr_get_their_fingerprint(char *recipient); char * otr_encrypt_message(const char * const to, const char * const message); char * otr_decrypt_message(const char * const from, const char * const message); diff --git a/src/ui/console.c b/src/ui/console.c index c02201f9..afdc1776 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -131,7 +131,7 @@ _cons_show_typing(const char * const barejid) display_usr = barejid; } - win_print_line(console, '-', COLOUR_TYPING, "!! %s is typing a message...", display_usr); + win_vprint_line(console, '-', COLOUR_TYPING, "!! %s is typing a message...", display_usr); wins_refresh_console(); cons_alert(); diff --git a/src/ui/core.c b/src/ui/core.c index 41fd31e8..74a4d66e 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -746,8 +746,11 @@ _ui_current_print_line(const char * const msg, ...) ProfWin *current = wins_get_current(); va_list arg; va_start(arg, msg); - win_print_line(current, '-', 0, msg, arg); + GString *fmt_msg = g_string_new(NULL); + g_string_vprintf(fmt_msg, msg, arg); + win_print_line(current, '-', 0, fmt_msg->str); va_end(arg); + g_string_free(fmt_msg, TRUE); win_refresh(current); } @@ -775,7 +778,7 @@ _ui_print_error_from_recipient(const char * const from, const char *err_msg) ProfWin *window = wins_get_by_recipient(from); if (window != NULL) { - win_print_line(window, '-', COLOUR_ERROR, "%s", err_msg); + win_vprint_line(window, '-', COLOUR_ERROR, "%s", err_msg); if (wins_is_current(window)) { wins_refresh_current(); } @@ -833,7 +836,7 @@ _ui_recipient_gone(const char * const barejid) ProfWin *window = wins_get_by_recipient(barejid); if (window != NULL) { - win_print_line(window, '!', COLOUR_GONE, "<- %s has left the conversation.", display_usr); + win_vprint_line(window, '!', COLOUR_GONE, "<- %s has left the conversation.", display_usr); if (wins_is_current(window)) { wins_refresh_current(); } @@ -1329,7 +1332,7 @@ _ui_status_room(const char * const contact) if (pcontact != NULL) { win_show_contact(current, pcontact); } else { - win_print_line(current, '-', 0, "No such participant \"%s\" in room.", contact); + win_vprint_line(current, '-', 0, "No such participant \"%s\" in room.", contact); } } diff --git a/src/ui/window.c b/src/ui/window.c index 6d6df429..370f876c 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -84,6 +84,16 @@ win_print_time(ProfWin* window, char show_char) void win_print_line(ProfWin *window, const char show_char, int attrs, + const char * const msg) +{ + win_print_time(window, show_char); + wattron(window->win, attrs); + wprintw(window->win, "%s\n", msg); + wattroff(window->win, attrs); +} + +void +win_vprint_line(ProfWin *window, const char show_char, int attrs, const char * const msg, ...) { va_list arg; diff --git a/src/ui/window.h b/src/ui/window.h index fabf8ae6..752787dc 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -58,8 +58,10 @@ typedef struct prof_win_t { ProfWin* win_create(const char * const title, int cols, win_type_t type); void win_free(ProfWin *window); -void win_print_line(ProfWin *self, const char show_char, int attrs, +void win_vprint_line(ProfWin *self, const char show_char, int attrs, const char * const msg, ...); +void win_print_line(ProfWin *self, const char show_char, int attrs, + const char * const msg); void win_refresh(ProfWin *window); void win_page_off(ProfWin *window); void win_print_time(ProfWin *window, char show_char); From 5a7eba518d4193696051601e77d75dc3d9051567 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 20:02:35 +0000 Subject: [PATCH 23/32] Show contacts fingerprint on /otr theirfp --- src/command/commands.c | 2 +- src/otr.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/command/commands.c b/src/command/commands.c index 4eb8a645..a7bf7087 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2313,7 +2313,7 @@ cmd_otr(gchar **args, struct cmd_help_t help) return TRUE; } else if (strcmp(args[0], "myfp") == 0) { char *fingerprint = otr_get_my_fingerprint(); - cons_show("Your fingerprint: %s", fingerprint); + ui_current_print_line("Your OTR fingerprint: %s", fingerprint); free(fingerprint); return TRUE; } else if (strcmp(args[0], "theirfp") == 0) { diff --git a/src/otr.c b/src/otr.c index 52ce6540..a0274e3a 100644 --- a/src/otr.c +++ b/src/otr.c @@ -345,8 +345,17 @@ otr_get_my_fingerprint(void) char * otr_get_their_fingerprint(char *recipient) { - char *fingerprint = "1234 5678"; - return strdup(fingerprint); + ConnContext *context = otrl_context_find(user_state, recipient, jid, "xmpp", + 0, NULL, NULL, NULL); + + if (context != NULL) { + Fingerprint *fingerprint = context->active_fingerprint; + char readable[45]; + otrl_privkey_hash_to_human(readable, fingerprint->fingerprint); + return strdup(readable); + } else { + return NULL; + } } char * @@ -386,6 +395,7 @@ otr_decrypt_message(const char * const from, const char * const message) // internal libotr message, ignore if (result == 1) { + cons_debug("Internal message."); return NULL; // message was decrypted, return to user @@ -395,6 +405,7 @@ otr_decrypt_message(const char * const from, const char * const message) // normal non OTR message } else { + cons_debug("Non OTR message."); return strdup(message); } } From f35e485bd4d5b6bfa7997b48f5b1f9c13576e26b Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 21:44:24 +0000 Subject: [PATCH 24/32] Handle window creation for incoming and outgoing OTR sessions --- src/command/command.c | 2 +- src/command/commands.c | 6 +----- src/otr.c | 20 +++++++++++++++++++- src/otr.h | 3 ++- src/ui/core.c | 29 +++++++++++++++++++++++++++++ src/ui/ui.h | 1 + 6 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index eaa72197..9d028801 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -1141,7 +1141,7 @@ cmd_execute_default(const char * const inp) ui_current_print_line("You are not currently connected."); } else { #ifdef HAVE_LIBOTR - if (ui_current_win_is_otr()) { + if (otr_is_secure(recipient)) { char *encrypted = otr_encrypt_message(recipient, inp); if (encrypted != NULL) { message_send(encrypted, recipient); diff --git a/src/command/commands.c b/src/command/commands.c index a7bf7087..bb18ff2f 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -919,7 +919,7 @@ cmd_msg(gchar **args, struct cmd_help_t help) } if (msg != NULL) { #ifdef HAVE_LIBOTR - if (ui_current_win_is_otr()) { + if (otr_is_secure(usr_jid)) { char *encrypted = otr_encrypt_message(usr_jid, msg); if (encrypted != NULL) { message_send(encrypted, usr_jid); @@ -2341,12 +2341,8 @@ cmd_otr(gchar **args, struct cmd_help_t help) if (!otr_key_loaded()) { ui_current_print_line("You have not generated or loaded a private key, use '/otr gen'"); } else { - ui_current_print_line("Starting OTR session"); char *recipient = ui_current_recipient(); message_send("?OTR?", recipient); - ui_current_set_otr(TRUE); - // refresh to show OTR in titlebar - ui_switch_win(ui_current_win_index()); } } return TRUE; diff --git a/src/otr.c b/src/otr.c index a0274e3a..7e25bdf5 100644 --- a/src/otr.c +++ b/src/otr.c @@ -120,6 +120,7 @@ static void cb_gone_secure(void *opdata, ConnContext *context) { // cons_debug("cb_gone_secure"); + ui_gone_secure(context->username); } static void @@ -332,6 +333,23 @@ otr_key_loaded(void) return data_loaded; } +gboolean +otr_is_secure(const char * const recipient) +{ + ConnContext *context = otrl_context_find(user_state, recipient, jid, "xmpp", + 0, NULL, NULL, NULL); + + if (context == NULL) { + return FALSE; + } + + if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED) { + return FALSE; + } else { + return TRUE; + } +} + char * otr_get_my_fingerprint(void) { @@ -343,7 +361,7 @@ otr_get_my_fingerprint(void) } char * -otr_get_their_fingerprint(char *recipient) +otr_get_their_fingerprint(const char * const recipient) { ConnContext *context = otrl_context_find(user_state, recipient, jid, "xmpp", 0, NULL, NULL, NULL); diff --git a/src/otr.h b/src/otr.h index 5008b2d3..0331a5a3 100644 --- a/src/otr.h +++ b/src/otr.h @@ -30,9 +30,10 @@ void otr_on_connect(ProfAccount *account); void otr_keygen(ProfAccount *account); gboolean otr_key_loaded(void); +gboolean otr_is_secure(const char * const recipient); char * otr_get_my_fingerprint(void); -char * otr_get_their_fingerprint(char *recipient); +char * otr_get_their_fingerprint(const char * const recipient); char * otr_encrypt_message(const char * const to, const char * const message); char * otr_decrypt_message(const char * const from, const char * const message); diff --git a/src/ui/core.c b/src/ui/core.c index 74a4d66e..397c6a0e 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -48,6 +48,7 @@ #include "jid.h" #include "log.h" #include "muc.h" +#include "otr.h" #include "ui/ui.h" #include "ui/window.h" #include "ui/windows.h" @@ -245,6 +246,11 @@ _ui_incoming_msg(const char * const from, const char * const message, ProfWin *window = wins_get_by_recipient(from); if (window == NULL) { window = wins_new(from, win_type); +#ifdef HAVE_LIBOTR + if (otr_is_secure(from)) { + window->is_otr = TRUE; + } +#endif win_created = TRUE; } @@ -581,6 +587,23 @@ _ui_next_win(void) wins_refresh_current(); } +static void +_ui_gone_secure(char *recipient) +{ + ProfWin *window = wins_get_by_recipient(recipient); + if (window != NULL) { + window->is_otr = TRUE; + + if (wins_is_current(window)) { + GString *recipient_str = _get_recipient_string(window); + title_bar_set_recipient(recipient_str->str); + g_string_free(recipient_str, TRUE); + title_bar_draw(); + wins_refresh_current(); + } + } +} + static void _ui_previous_win(void) { @@ -966,6 +989,11 @@ _ui_outgoing_msg(const char * const from, const char * const to, window = wins_new(to, WIN_PRIVATE); } else { window = wins_new(to, WIN_CHAT); +#ifdef HAVE_LIBOTR + if (otr_is_secure(to)) { + window->is_otr = TRUE; + } +#endif } jid_destroy(jid); @@ -1634,4 +1662,5 @@ ui_init_module(void) ui_ask_password = _ui_ask_password; ui_current_win_is_otr = _ui_current_win_is_otr; ui_current_set_otr = _ui_current_set_otr; + ui_gone_secure = _ui_gone_secure; } diff --git a/src/ui/ui.h b/src/ui/ui.h index 546f606a..6f2e6c4f 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -61,6 +61,7 @@ void (*ui_handle_special_keys)(const wint_t * const ch, const char * const inp, void (*ui_switch_win)(const int i); void (*ui_next_win)(void); void (*ui_previous_win)(void); +void (*ui_gone_secure)(char *recipient); unsigned long (*ui_get_idle_time)(void); void (*ui_reset_idle_time)(void); void (*ui_new_chat_win)(const char * const to); From a3217bcf8c2a8fe64447a0ec02c0d9d9b39e6686 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 23:28:11 +0000 Subject: [PATCH 25/32] End OTR session when user closes chat window --- src/otr.c | 34 ++++++++++++++++++++++++++++++++-- src/otr.h | 2 ++ src/ui/core.c | 28 ++++++++++++++++++++++++++-- src/ui/ui.h | 3 ++- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/otr.c b/src/otr.c index 7e25bdf5..3259543f 100644 --- a/src/otr.c +++ b/src/otr.c @@ -27,6 +27,8 @@ #include "otr.h" #include "log.h" +#include "roster_list.h" +#include "contact.h" #include "ui/ui.h" static OtrlUserState user_state; @@ -53,9 +55,14 @@ static int cb_is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient) { + PContact contact = roster_get_contact(recipient); + if (g_strcmp0(p_contact_presence(contact), "offline") == 0) { + return 0; + } else { + return 1; + } // cons_debug("cb_is_logged_in: account: %s, protocol: %s, recipient: %s", // accountname, protocol, recipient); - return -1; } static void @@ -350,6 +357,17 @@ otr_is_secure(const char * const recipient) } } +void +otr_end_session(const char * const recipient) +{ + ConnContext *context = otrl_context_find(user_state, recipient, jid, "xmpp", + 0, NULL, NULL, NULL); + + if (context != NULL) { + otrl_message_disconnect(user_state, &ops, NULL, jid, "xmpp", recipient); + } +} + char * otr_get_my_fingerprint(void) { @@ -409,11 +427,23 @@ otr_decrypt_message(const char * const from, const char * const message) { cons_debug("Decrypting message: %s", message); char *decrypted = NULL; - int result = otrl_message_receiving(user_state, &ops, NULL, jid, "xmpp", from, message, &decrypted, 0, NULL, NULL); + OtrlTLV *tlvs = NULL; + OtrlTLV *tlv = NULL; + int result = otrl_message_receiving(user_state, &ops, NULL, jid, "xmpp", from, message, &decrypted, &tlvs, NULL, NULL); // internal libotr message, ignore if (result == 1) { cons_debug("Internal message."); + tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED); + if (tlv) { + ConnContext *context = otrl_context_find(user_state, from, jid, "xmpp", + 0, NULL, NULL, NULL); + + if (context != NULL) { + otrl_context_force_plaintext(context); + ui_gone_insecure(from); + } + } return NULL; // message was decrypted, return to user diff --git a/src/otr.h b/src/otr.h index 0331a5a3..ad89359c 100644 --- a/src/otr.h +++ b/src/otr.h @@ -32,6 +32,8 @@ void otr_keygen(ProfAccount *account); gboolean otr_key_loaded(void); gboolean otr_is_secure(const char * const recipient); +void otr_end_session(const char * const recipient); + char * otr_get_my_fingerprint(void); char * otr_get_their_fingerprint(const char * const recipient); diff --git a/src/ui/core.c b/src/ui/core.c index 397c6a0e..7acab4c2 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -439,7 +439,13 @@ _ui_close_connected_win(int index) char *room_jid = ui_recipient(index); presence_leave_chat_room(room_jid); } else if ((win_type == WIN_CHAT) || (win_type == WIN_PRIVATE)) { - +#ifdef HAVE_LIBOTR + ProfWin *window = wins_get_by_num(index); + if (window->is_otr) { + cons_debug("Ending OTR session"); + otr_end_session(window->from); + } +#endif if (prefs_get_boolean(PREF_STATES)) { char *recipient = ui_recipient(index); @@ -588,7 +594,7 @@ _ui_next_win(void) } static void -_ui_gone_secure(char *recipient) +_ui_gone_secure(const char * const recipient) { ProfWin *window = wins_get_by_recipient(recipient); if (window != NULL) { @@ -604,6 +610,23 @@ _ui_gone_secure(char *recipient) } } +static void +_ui_gone_insecure(const char * const recipient) +{ + ProfWin *window = wins_get_by_recipient(recipient); + if (window != NULL) { + window->is_otr = FALSE; + + if (wins_is_current(window)) { + GString *recipient_str = _get_recipient_string(window); + title_bar_set_recipient(recipient_str->str); + g_string_free(recipient_str, TRUE); + title_bar_draw(); + wins_refresh_current(); + } + } +} + static void _ui_previous_win(void) { @@ -1663,4 +1686,5 @@ ui_init_module(void) ui_current_win_is_otr = _ui_current_win_is_otr; ui_current_set_otr = _ui_current_set_otr; ui_gone_secure = _ui_gone_secure; + ui_gone_insecure = _ui_gone_insecure; } diff --git a/src/ui/ui.h b/src/ui/ui.h index 6f2e6c4f..88be3fbb 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -61,7 +61,8 @@ void (*ui_handle_special_keys)(const wint_t * const ch, const char * const inp, void (*ui_switch_win)(const int i); void (*ui_next_win)(void); void (*ui_previous_win)(void); -void (*ui_gone_secure)(char *recipient); +void (*ui_gone_secure)(const char * const recipient); +void (*ui_gone_insecure)(const char * const recipient); unsigned long (*ui_get_idle_time)(void); void (*ui_reset_idle_time)(void); void (*ui_new_chat_win)(const char * const to); From 494aaadba42ebd95566d2b5128b7925c8aa4cde9 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sat, 11 Jan 2014 23:59:20 +0000 Subject: [PATCH 26/32] Added /otr end command --- src/command/commands.c | 14 ++++++++++++++ src/ui/core.c | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/command/commands.c b/src/command/commands.c index bb18ff2f..660ec572 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2346,6 +2346,20 @@ cmd_otr(gchar **args, struct cmd_help_t help) } } return TRUE; + } else if (strcmp(args[0], "end") == 0) { + win_type_t win_type = ui_current_win_type(); + + if (win_type != WIN_CHAT) { + ui_current_print_line("You must be in a regular chat window to use OTR."); + } else if (!ui_current_win_is_otr()) { + ui_current_print_line("You are not currently in an OTR session."); + } else { + char *recipient = ui_current_recipient(); + ui_gone_insecure(recipient); + otr_end_session(recipient); + } + return TRUE; + } else { cons_show("Usage: %s", help.usage); return TRUE; diff --git a/src/ui/core.c b/src/ui/core.c index 7acab4c2..fc6ca40e 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -442,7 +442,6 @@ _ui_close_connected_win(int index) #ifdef HAVE_LIBOTR ProfWin *window = wins_get_by_num(index); if (window->is_otr) { - cons_debug("Ending OTR session"); otr_end_session(window->from); } #endif @@ -599,6 +598,7 @@ _ui_gone_secure(const char * const recipient) ProfWin *window = wins_get_by_recipient(recipient); if (window != NULL) { window->is_otr = TRUE; + win_vprint_line(window, '!', 0, "OTR session started."); if (wins_is_current(window)) { GString *recipient_str = _get_recipient_string(window); @@ -616,6 +616,7 @@ _ui_gone_insecure(const char * const recipient) ProfWin *window = wins_get_by_recipient(recipient); if (window != NULL) { window->is_otr = FALSE; + win_vprint_line(window, '!', 0, "OTR session ended."); if (wins_is_current(window)) { GString *recipient_str = _get_recipient_string(window); From 43deea7c054ce60e72298bcfb620a9ffa3445c57 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sun, 12 Jan 2014 00:10:16 +0000 Subject: [PATCH 27/32] Added /otr autocompleter and updated help --- src/command/command.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index 9d028801..afcfe546 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -570,12 +570,14 @@ static struct cmd_t command_defs[] = { "/otr", cmd_otr, parse_args, 1, 2, NULL, - { "/otr gen|myfp|start", "Off The Record encryption commands.", - { "/otr gen|myfp|start", - "-------------------", + { "/otr gen|myfp|theirfp|start|end", "Off The Record encryption commands.", + { "/otr gen|myfp|theirfp|start|end", + "-------------------------------", "gen - Generate your private key.", "myfp - Show your fingerprint.", + "theirfp - Show contacts fingerprint.", "start - Start an OTR session with the current recipient.", + "end - End the current OTR session,", NULL } } }, { "/outtype", @@ -828,6 +830,7 @@ static Autocomplete wins_ac; static Autocomplete roster_ac; static Autocomplete group_ac; static Autocomplete bookmark_ac; +static Autocomplete otr_ac; /* * Initialise command autocompleter and history @@ -980,6 +983,13 @@ cmd_init(void) autocomplete_add(bookmark_ac, "list"); autocomplete_add(bookmark_ac, "remove"); + otr_ac = autocomplete_new(); + autocomplete_add(otr_ac, "gen"); + autocomplete_add(otr_ac, "start"); + autocomplete_add(otr_ac, "end"); + autocomplete_add(otr_ac, "myfp"); + autocomplete_add(otr_ac, "theirfp"); + cmd_history_init(); } @@ -1010,6 +1020,7 @@ cmd_uninit(void) autocomplete_free(roster_ac); autocomplete_free(group_ac); autocomplete_free(bookmark_ac); + autocomplete_free(otr_ac); } // Command autocompletion functions @@ -1083,6 +1094,7 @@ cmd_reset_autocomplete() autocomplete_reset(roster_ac); autocomplete_reset(group_ac); autocomplete_reset(bookmark_ac); + autocomplete_reset(otr_ac); bookmark_autocomplete_reset(); } @@ -1307,8 +1319,8 @@ _cmd_complete_parameters(char *input, int *size) return; } - gchar *cmds[] = { "/help", "/prefs", "/log", "/disco", "/close", "/wins" }; - Autocomplete completers[] = { help_ac, prefs_ac, log_ac, disco_ac, close_ac, wins_ac }; + gchar *cmds[] = { "/help", "/prefs", "/log", "/disco", "/close", "/wins", "/otr" }; + Autocomplete completers[] = { help_ac, prefs_ac, log_ac, disco_ac, close_ac, wins_ac, otr_ac }; for (i = 0; i < ARRAY_SIZE(cmds); i++) { result = autocomplete_param_with_ac(input, size, cmds[i], completers[i]); From f14a9ed72df27c4ed93489c029967a16d2e72738 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sun, 12 Jan 2014 00:27:13 +0000 Subject: [PATCH 28/32] Implemented write fingerprints callback --- src/otr.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/otr.c b/src/otr.c index 3259543f..ab4b79cd 100644 --- a/src/otr.c +++ b/src/otr.c @@ -120,7 +120,25 @@ cb_update_context_list(void *opdata) static void cb_write_fingerprints(void *opdata) { -// cons_debug("cb_write_fingerprints"); + cons_debug("cb_write_fingerprints"); + gcry_error_t err = 0; + gchar *data_home = xdg_get_data_home(); + gchar *account_dir = str_replace(jid, "@", "_at_"); + + GString *basedir = g_string_new(data_home); + g_string_append(basedir, "/profanity/otr/"); + g_string_append(basedir, account_dir); + g_string_append(basedir, "/"); + + GString *fpsfilename = g_string_new(basedir->str); + g_string_append(fpsfilename, "fingerprints.txt"); + err = otrl_privkey_write_fingerprints(user_state, fpsfilename->str); + if (!err == GPG_ERR_NO_ERROR) { + log_error("Failed to write fingerprints file"); + cons_show_error("Failed to create fingerprints file"); + } + g_string_free(basedir, TRUE); + g_string_free(fpsfilename, TRUE); } static void @@ -228,6 +246,7 @@ otr_on_connect(ProfAccount *account) if (!err == GPG_ERR_NO_ERROR) { g_string_free(basedir, TRUE); g_string_free(keysfilename, TRUE); + g_string_free(fpsfilename, TRUE); log_error("Failed to load fingerprints"); return; } else { From 462e84ea8218d267164ea563ea00412e57e3fb41 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sun, 12 Jan 2014 01:20:22 +0000 Subject: [PATCH 29/32] Added /otr trust|untrust commands --- src/command/command.c | 10 +++++-- src/command/commands.c | 26 ++++++++++++++++ src/otr.c | 68 +++++++++++++++++++++++++++++++++++++++++- src/otr.h | 4 +++ src/ui/core.c | 42 +++++++++++++++++++++++++- src/ui/ui.h | 4 ++- 6 files changed, 148 insertions(+), 6 deletions(-) diff --git a/src/command/command.c b/src/command/command.c index afcfe546..ba976023 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -570,14 +570,16 @@ static struct cmd_t command_defs[] = { "/otr", cmd_otr, parse_args, 1, 2, NULL, - { "/otr gen|myfp|theirfp|start|end", "Off The Record encryption commands.", - { "/otr gen|myfp|theirfp|start|end", - "-------------------------------", + { "/otr gen|myfp|theirfp|start|end|trust|untrust", "Off The Record encryption commands.", + { "/otr gen|myfp|theirfp|start|end|trust|untrust", + "---------------------------------------------", "gen - Generate your private key.", "myfp - Show your fingerprint.", "theirfp - Show contacts fingerprint.", "start - Start an OTR session with the current recipient.", "end - End the current OTR session,", + "trust - Indicate that you have verified the contact's fingerprint.", + "untrust - Indicate the the contact's fingerprint is not verified,", NULL } } }, { "/outtype", @@ -989,6 +991,8 @@ cmd_init(void) autocomplete_add(otr_ac, "end"); autocomplete_add(otr_ac, "myfp"); autocomplete_add(otr_ac, "theirfp"); + autocomplete_add(otr_ac, "trust"); + autocomplete_add(otr_ac, "untrust"); cmd_history_init(); } diff --git a/src/command/commands.c b/src/command/commands.c index 660ec572..69e80414 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2359,6 +2359,32 @@ cmd_otr(gchar **args, struct cmd_help_t help) otr_end_session(recipient); } return TRUE; + } else if (strcmp(args[0], "trust") == 0) { + win_type_t win_type = ui_current_win_type(); + + if (win_type != WIN_CHAT) { + ui_current_print_line("You must be in an OTR session to trust a recipient."); + } else if (!ui_current_win_is_otr()) { + ui_current_print_line("You are not currently in an OTR session."); + } else { + char *recipient = ui_current_recipient(); + ui_trust(recipient); + otr_trust(recipient); + } + return TRUE; + } else if (strcmp(args[0], "untrust") == 0) { + win_type_t win_type = ui_current_win_type(); + + if (win_type != WIN_CHAT) { + ui_current_print_line("You must be in an OTR session to untrust a recipient."); + } else if (!ui_current_win_is_otr()) { + ui_current_print_line("You are not currently in an OTR session."); + } else { + char *recipient = ui_current_recipient(); + ui_untrust(recipient); + otr_untrust(recipient); + } + return TRUE; } else { cons_show("Usage: %s", help.usage); diff --git a/src/otr.c b/src/otr.c index ab4b79cd..95fd7bca 100644 --- a/src/otr.c +++ b/src/otr.c @@ -145,7 +145,7 @@ static void cb_gone_secure(void *opdata, ConnContext *context) { // cons_debug("cb_gone_secure"); - ui_gone_secure(context->username); + ui_gone_secure(context->username, otr_is_trusted(context->username)); } static void @@ -376,6 +376,72 @@ otr_is_secure(const char * const recipient) } } +gboolean +otr_is_trusted(const char * const recipient) +{ + ConnContext *context = otrl_context_find(user_state, recipient, jid, "xmpp", + 0, NULL, NULL, NULL); + + if (context == NULL) { + return FALSE; + } + + if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED) { + return TRUE; + } + + if (context->active_fingerprint && + g_strcmp0(context->active_fingerprint->trust, "trusted") == 0) { + return TRUE; + } + + return FALSE; +} + +void +otr_trust(const char * const recipient) +{ + ConnContext *context = otrl_context_find(user_state, recipient, jid, "xmpp", + 0, NULL, NULL, NULL); + + if (context == NULL) { + return; + } + + if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED) { + return; + } + + if (context->active_fingerprint) { + context->active_fingerprint->trust = "trusted"; + cb_write_fingerprints(NULL); + } + + return; +} + +void +otr_untrust(const char * const recipient) +{ + ConnContext *context = otrl_context_find(user_state, recipient, jid, "xmpp", + 0, NULL, NULL, NULL); + + if (context == NULL) { + return; + } + + if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED) { + return; + } + + if (context->active_fingerprint) { + context->active_fingerprint->trust = NULL; + cb_write_fingerprints(NULL); + } + + return; +} + void otr_end_session(const char * const recipient) { diff --git a/src/otr.h b/src/otr.h index ad89359c..ff1ad5a5 100644 --- a/src/otr.h +++ b/src/otr.h @@ -32,6 +32,10 @@ void otr_keygen(ProfAccount *account); gboolean otr_key_loaded(void); gboolean otr_is_secure(const char * const recipient); +gboolean otr_is_trusted(const char * const recipient); +void otr_trust(const char * const recipient); +void otr_untrust(const char * const recipient); + void otr_end_session(const char * const recipient); char * otr_get_my_fingerprint(void); diff --git a/src/ui/core.c b/src/ui/core.c index fc6ca40e..e0b934ae 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -593,11 +593,12 @@ _ui_next_win(void) } static void -_ui_gone_secure(const char * const recipient) +_ui_gone_secure(const char * const recipient, gboolean trusted) { ProfWin *window = wins_get_by_recipient(recipient); if (window != NULL) { window->is_otr = TRUE; + window->is_trusted = trusted; win_vprint_line(window, '!', 0, "OTR session started."); if (wins_is_current(window)) { @@ -616,6 +617,7 @@ _ui_gone_insecure(const char * const recipient) ProfWin *window = wins_get_by_recipient(recipient); if (window != NULL) { window->is_otr = FALSE; + window->is_trusted = FALSE; win_vprint_line(window, '!', 0, "OTR session ended."); if (wins_is_current(window)) { @@ -628,6 +630,42 @@ _ui_gone_insecure(const char * const recipient) } } +static void +_ui_trust(const char * const recipient) +{ + ProfWin *window = wins_get_by_recipient(recipient); + if (window != NULL) { + window->is_otr = TRUE; + window->is_trusted = TRUE; + + if (wins_is_current(window)) { + GString *recipient_str = _get_recipient_string(window); + title_bar_set_recipient(recipient_str->str); + g_string_free(recipient_str, TRUE); + title_bar_draw(); + wins_refresh_current(); + } + } +} + +static void +_ui_untrust(const char * const recipient) +{ + ProfWin *window = wins_get_by_recipient(recipient); + if (window != NULL) { + window->is_otr = TRUE; + window->is_trusted = FALSE; + + if (wins_is_current(window)) { + GString *recipient_str = _get_recipient_string(window); + title_bar_set_recipient(recipient_str->str); + g_string_free(recipient_str, TRUE); + title_bar_draw(); + wins_refresh_current(); + } + } +} + static void _ui_previous_win(void) { @@ -1688,4 +1726,6 @@ ui_init_module(void) ui_current_set_otr = _ui_current_set_otr; ui_gone_secure = _ui_gone_secure; ui_gone_insecure = _ui_gone_insecure; + ui_trust = _ui_trust; + ui_untrust = _ui_untrust; } diff --git a/src/ui/ui.h b/src/ui/ui.h index 88be3fbb..240f6b4b 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -61,8 +61,10 @@ void (*ui_handle_special_keys)(const wint_t * const ch, const char * const inp, void (*ui_switch_win)(const int i); void (*ui_next_win)(void); void (*ui_previous_win)(void); -void (*ui_gone_secure)(const char * const recipient); +void (*ui_gone_secure)(const char * const recipient, gboolean trusted); void (*ui_gone_insecure)(const char * const recipient); +void (*ui_trust)(const char * const recipient); +void (*ui_untrust)(const char * const recipient); unsigned long (*ui_get_idle_time)(void); void (*ui_reset_idle_time)(void); void (*ui_new_chat_win)(const char * const to); From 264fc55aa22d0069d0031a97c56afd5f86df83c3 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sun, 12 Jan 2014 02:15:16 +0000 Subject: [PATCH 30/32] Added conditionals to makefile for otr support --- Makefile.am | 19 ++++++++++++++----- configure.ac | 3 ++- src/command/commands.c | 2 +- src/profanity.c | 2 ++ src/server_events.c | 4 ++++ 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Makefile.am b/Makefile.am index 17cf94dd..7b8a94c8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,8 +39,7 @@ core_sources = \ src/tools/tinyurl.c src/tools/tinyurl.h \ src/config/accounts.c src/config/accounts.h \ src/config/preferences.c src/config/preferences.h \ - src/config/theme.c src/config/theme.h \ - src/otr.c src/otr.h + src/config/theme.c src/config/theme.h test_sources = \ src/contact.c src/contact.h src/common.c \ @@ -61,7 +60,6 @@ test_sources = \ src/config/accounts.h \ src/config/preferences.c src/config/preferences.h \ src/config/theme.c src/config/theme.h \ - src/otr.c src/otr.h \ tests/xmpp/mock_xmpp.h tests/xmpp/mock_xmpp.c \ tests/ui/mock_ui.h tests/ui/mock_ui.c \ tests/config/mock_accounts.h tests/config/mock_accounts.c \ @@ -84,6 +82,9 @@ main_source = src/main.c git_sources = \ src/gitversion.c +otr_sources = \ + src/otr.c src/otr.h + if INCLUDE_GIT_VERSION with_git_sources = $(git_sources) $(core_sources) tests_with_git_sources = $(git_sources) $(test_sources) @@ -92,12 +93,20 @@ with_git_sources = $(core_sources) tests_with_git_sources = $(test_sources) endif +if BUILD_OTR +with_otr_sources = $(with_git_sources) $(otr_sources) +tests_with_otr_sources = $(tests_with_git_sources) $(otr_sources) +else +with_otr_sources = $(with_git_sources) +tests_with_otr_sources = $(tests_with_git_sources) +endif + bin_PROGRAMS = profanity -profanity_SOURCES = $(with_git_sources) $(main_source) +profanity_SOURCES = $(with_otr_sources) $(main_source) TESTS = tests/testsuite check_PROGRAMS = tests/testsuite -tests_testsuite_SOURCES = $(tests_with_git_sources) +tests_testsuite_SOURCES = $(tests_with_otr_sources) tests_testsuite_LDADD = -lcmocka man_MANS = docs/profanity.1 diff --git a/configure.ac b/configure.ac index 4fb4deda..708432b1 100644 --- a/configure.ac +++ b/configure.ac @@ -130,12 +130,13 @@ elif test "x$with_xscreensaver" = x; then [AC_MSG_NOTICE([libX11 not found, falling back to profanity auto-away])]) fi +AM_CONDITIONAL([BUILD_OTR], [true]) if test "x$enable_otr" = xyes; then AC_CHECK_LIB([otr], [main], [], [AC_MSG_ERROR([libotr is required for otr encryption support])]) elif test "x$enable_otr" = x; then AC_CHECK_LIB([otr], [main], [], - [AC_MSG_NOTICE([libotr not found, otr entryption support no enabled])]) + [AM_CONDITIONAL([BUILD_OTR], [false]) AC_MSG_NOTICE([libotr not found, otr entryption support not enabled])]) fi ### cmocka is required only for tests, profanity shouldn't be linked with it diff --git a/src/command/commands.c b/src/command/commands.c index 69e80414..d7ce2742 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2391,7 +2391,7 @@ cmd_otr(gchar **args, struct cmd_help_t help) return TRUE; } #else - cons_show("This version of Profanity has not been build with OTR support enabled"); + cons_show("This version of Profanity has not been built with OTR support enabled"); return TRUE; #endif } diff --git a/src/profanity.c b/src/profanity.c index b4149d3a..049d4bc4 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -44,7 +44,9 @@ #include "roster_list.h" #include "log.h" #include "muc.h" +#ifdef HAVE_LIBOTR #include "otr.h" +#endif #include "resource.h" #include "ui/ui.h" #include "xmpp/xmpp.h" diff --git a/src/server_events.c b/src/server_events.c index 5e478a51..792415be 100644 --- a/src/server_events.c +++ b/src/server_events.c @@ -28,7 +28,9 @@ #include "config/preferences.h" #include "roster_list.h" #include "ui/ui.h" +#ifdef HAVE_LIBOTR #include "otr.h" +#endif void handle_error_message(const char *from, const char *err_msg) @@ -49,7 +51,9 @@ void handle_login_account_success(char *account_name) { ProfAccount *account = accounts_get_account(account_name); +#ifdef HAVE_LIBOTR otr_on_connect(account); +#endif resource_presence_t resource_presence = accounts_get_login_presence(account->name); contact_presence_t contact_presence = contact_presence_from_resource_presence(resource_presence); cons_show_login_success(account); From 3f60a6a80e7591e1c2645d4839e2996b01d209ed Mon Sep 17 00:00:00 2001 From: James Booth Date: Sun, 12 Jan 2014 02:20:03 +0000 Subject: [PATCH 31/32] Removed debug statements --- src/otr.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/otr.c b/src/otr.c index 95fd7bca..149a81be 100644 --- a/src/otr.c +++ b/src/otr.c @@ -40,7 +40,6 @@ static gboolean data_loaded; static OtrlPolicy cb_policy(void *opdata, ConnContext *context) { -// cons_debug("cb_policy"); return OTRL_POLICY_DEFAULT ; } @@ -48,7 +47,6 @@ static void cb_create_privkey(void *opdata, const char *accountname, const char *protocol) { -// cons_debug("cb_create_privkey accountname: %s, protocol: %s", accountname, protocol); } static int @@ -61,16 +59,12 @@ cb_is_logged_in(void *opdata, const char *accountname, } else { return 1; } -// cons_debug("cb_is_logged_in: account: %s, protocol: %s, recipient: %s", -// accountname, protocol, recipient); } static void cb_inject_message(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) { -// cons_debug("cb_inject_message: account: %s, protocol, %s, recipient: %s, message: %s", -// accountname, protocol, recipient, message); message_send(message, recipient); } @@ -79,7 +73,6 @@ cb_notify(void *opdata, OtrlNotifyLevel level, const char *accountname, const char *protocol, const char *username, const char *title, const char *primary, const char *secondary) { -// cons_debug("cb_notify"); } static int @@ -93,7 +86,6 @@ cb_display_otr_message(void *opdata, const char *accountname, static const char * cb_protocol_name(void *opdata, const char *protocol) { -// cons_debug("cb_protocol_name: %s", protocol); return "xmpp"; } @@ -101,26 +93,21 @@ static void cb_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, const char *protocol, const char *username, unsigned char fingerprint[20]) { -// cons_debug("cb_new_fingerprint: account: %s, protocol: %s, username: %s", -// accountname, protocol, username); } static void cb_protocol_name_free(void *opdata, const char *protocol_name) { -// cons_debug("cb_protocol_name_free: %s", protocol_name); } static void cb_update_context_list(void *opdata) { -// cons_debug("cb_update_context_list"); } static void cb_write_fingerprints(void *opdata) { - cons_debug("cb_write_fingerprints"); gcry_error_t err = 0; gchar *data_home = xdg_get_data_home(); gchar *account_dir = str_replace(jid, "@", "_at_"); @@ -144,26 +131,22 @@ cb_write_fingerprints(void *opdata) static void cb_gone_secure(void *opdata, ConnContext *context) { -// cons_debug("cb_gone_secure"); ui_gone_secure(context->username, otr_is_trusted(context->username)); } static void cb_gone_insecure(void *opdata, ConnContext *context) { -// cons_debug("cb_gone_insecure"); } static void cb_still_secure(void *opdata, ConnContext *context, int is_reply) { -// cons_debug("cb_still_secure: is_reply = %d", is_reply); } static void cb_log_message(void *opdata, const char *message) { -// cons_debug("cb_log_message: %s", message); } void @@ -482,7 +465,6 @@ otr_get_their_fingerprint(const char * const recipient) char * otr_encrypt_message(const char * const to, const char * const message) { - cons_debug("Encrypting message: %s", message); gcry_error_t err; char *newmessage = NULL; @@ -499,10 +481,8 @@ otr_encrypt_message(const char * const to, const char * const message) NULL, NULL); if (!err == GPG_ERR_NO_ERROR) { -// cons_debug("Error encrypting, result: %s", newmessage); return NULL; } else { - cons_debug("Encrypted message: %s", newmessage); return newmessage; } } @@ -510,7 +490,6 @@ otr_encrypt_message(const char * const to, const char * const message) char * otr_decrypt_message(const char * const from, const char * const message) { - cons_debug("Decrypting message: %s", message); char *decrypted = NULL; OtrlTLV *tlvs = NULL; OtrlTLV *tlv = NULL; @@ -518,7 +497,6 @@ otr_decrypt_message(const char * const from, const char * const message) // internal libotr message, ignore if (result == 1) { - cons_debug("Internal message."); tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED); if (tlv) { ConnContext *context = otrl_context_find(user_state, from, jid, "xmpp", @@ -533,12 +511,10 @@ otr_decrypt_message(const char * const from, const char * const message) // message was decrypted, return to user } else if (decrypted != NULL) { - cons_debug("Decrypted message: %s", decrypted); return decrypted; // normal non OTR message } else { - cons_debug("Non OTR message."); return strdup(message); } } From 64f6a54eb97d0d5d672733bf3b8caa1353186ffd Mon Sep 17 00:00:00 2001 From: James Booth Date: Sun, 12 Jan 2014 17:30:35 +0000 Subject: [PATCH 32/32] Added libotr dev packages for ubuntu and fedora in install-all.sh --- install-all.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install-all.sh b/install-all.sh index 42deaf1a..656d9d90 100755 --- a/install-all.sh +++ b/install-all.sh @@ -12,7 +12,7 @@ debian_prepare() echo echo Profanity installer... installing dependencies echo - sudo apt-get -y install git automake autoconf libssl-dev libexpat1-dev libncursesw5-dev libglib2.0-dev libnotify-dev libcurl3-dev libxss-dev + sudo apt-get -y install git automake autoconf libssl-dev libexpat1-dev libncursesw5-dev libglib2.0-dev libnotify-dev libcurl3-dev libxss-dev libotr2-dev } @@ -24,7 +24,7 @@ fedora_prepare() ARCH=`arch` - sudo yum -y install gcc git autoconf automake openssl-devel.$ARCH expat-devel.$ARCH ncurses-devel.$ARCH glib2-devel.$ARCH libnotify-devel.$ARCH libcurl-devel.$ARCH libXScrnSaver-devel.$ARCH + sudo yum -y install gcc git autoconf automake openssl-devel.$ARCH expat-devel.$ARCH ncurses-devel.$ARCH glib2-devel.$ARCH libnotify-devel.$ARCH libcurl-devel.$ARCH libXScrnSaver-devel.$ARCH libotr3-devel.$ARCH } cygwin_prepare()