diff --git a/docs/profanity.1 b/docs/profanity.1 index f2a85314..1f4a61b7 100644 --- a/docs/profanity.1 +++ b/docs/profanity.1 @@ -20,6 +20,9 @@ Show version information. .BI "\-h, \-\-help" Show help on command line arguments. .TP +.BI "\-a, \-\-account" +Auto connect to an account on startup. +.TP .BI "\-d, \-\-disable-tls" Disable TLS for servers that either don't support it, or claim to but do not complete the handshake. diff --git a/src/command/command.c b/src/command/command.c index 245d0cfa..17c7d314 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -593,9 +593,9 @@ static struct cmd_t command_defs[] = { "/otr", cmd_otr, parse_args, 1, 2, NULL, - { "/otr gen|myfp|theirfp|start|end|trust|untrust|log|warn|libver", "Off The Record encryption commands.", - { "/otr gen|myfp|theirfp|start|end|trust|untrust|log|warn|libver", - "-------------------------------------------------------------", + { "/otr gen|myfp|theirfp|start|end|trust|untrust|log|warn|libver|policy", "Off The Record encryption commands.", + { "/otr gen|myfp|theirfp|start|end|trust|untrust|log|warn|libver|policy", + "--------------------------------------------------------------------", "gen - Generate your private key.", "myfp - Show your fingerprint.", "theirfp - Show contacts fingerprint.", @@ -606,6 +606,7 @@ static struct cmd_t command_defs[] = "log - How to log OTR messages, options are 'on', 'off' and 'redact', with redaction being the default.", "warn - Show when unencrypted messaging is being used in the title bar, options are 'on' and 'off' with 'on' being the default.", "libver - Show which version of the libotr library is being used.", + "policy - manual, opportunistic or always.", NULL } } }, { "/outtype", @@ -884,6 +885,7 @@ static Autocomplete group_ac; static Autocomplete bookmark_ac; static Autocomplete otr_ac; static Autocomplete otr_log_ac; +static Autocomplete otr_policy_ac; static Autocomplete connect_property_ac; static Autocomplete statuses_ac; static Autocomplete statuses_setting_ac; @@ -1072,12 +1074,18 @@ cmd_init(void) autocomplete_add(otr_ac, "log"); autocomplete_add(otr_ac, "warn"); autocomplete_add(otr_ac, "libver"); + autocomplete_add(otr_ac, "policy"); otr_log_ac = autocomplete_new(); autocomplete_add(otr_log_ac, "on"); autocomplete_add(otr_log_ac, "off"); autocomplete_add(otr_log_ac, "redact"); + otr_policy_ac = autocomplete_new(); + autocomplete_add(otr_policy_ac, "manual"); + autocomplete_add(otr_policy_ac, "opportunistic"); + autocomplete_add(otr_policy_ac, "always"); + connect_property_ac = autocomplete_new(); autocomplete_add(connect_property_ac, "server"); autocomplete_add(connect_property_ac, "port"); @@ -1133,6 +1141,7 @@ cmd_uninit(void) autocomplete_free(bookmark_ac); autocomplete_free(otr_ac); autocomplete_free(otr_log_ac); + autocomplete_free(otr_policy_ac); autocomplete_free(connect_property_ac); autocomplete_free(statuses_ac); autocomplete_free(statuses_setting_ac); @@ -1254,6 +1263,7 @@ cmd_reset_autocomplete() autocomplete_reset(bookmark_ac); autocomplete_reset(otr_ac); autocomplete_reset(otr_log_ac); + autocomplete_reset(otr_policy_ac); autocomplete_reset(connect_property_ac); autocomplete_reset(statuses_ac); autocomplete_reset(statuses_setting_ac); @@ -1347,6 +1357,11 @@ cmd_execute_default(const char * const inp) char *plugin_message = plugins_on_message_send(recipient_jid, inp); #ifdef PROF_HAVE_LIBOTR + if ((strcmp(prefs_get_string(PREF_OTR_POLICY), "always") == 0) && !otr_is_secure(recipient)) { + cons_show_error("Failed to send message. Please check OTR policy"); + return TRUE; + } + if (otr_is_secure(recipient)) { char *encrypted = otr_encrypt_message(recipient, plugin_message); if (encrypted != NULL) { @@ -1754,6 +1769,11 @@ _otr_autocomplete(char *input, int *size) return result; } + result = autocomplete_param_with_ac(input, size, "/otr policy", otr_policy_ac); + if (result != NULL) { + return result; + } + result = autocomplete_param_with_func(input, size, "/otr warn", prefs_autocomplete_boolean_choice); if (result != NULL) { diff --git a/src/command/commands.c b/src/command/commands.c index a53b9aac..7594819a 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "chat_session.h" #include "command/commands.h" @@ -969,7 +970,24 @@ cmd_msg(gchar **args, struct cmd_help_t help) cons_show_error("Failed to encrypt and send message,"); } } else { - message_send(plugin_message, usr_jid); + char *policy = prefs_get_string(PREF_OTR_POLICY); + + if (strcmp(policy, "always") == 0) { + cons_show_error("Failed to send message. Please check OTR policy"); + return TRUE; + } else if (strcmp(policy, "opportunistic") == 0) { + char *otr_base_tag = OTRL_MESSAGE_TAG_BASE; + char *otr_v2_tag = OTRL_MESSAGE_TAG_V2; + int N = strlen(otr_base_tag) + strlen(otr_v2_tag) + strlen(plugin_message) + 1; + char *temp = (char *) malloc( (unsigned) N*sizeof(char *) ); + strcpy( temp , plugin_message ); + strcat( temp , otr_base_tag); + strcat( temp, otr_v2_tag); + message_send(temp, usr_jid); + free(temp); + } else { + message_send(plugin_message, usr_jid); + } ui_outgoing_msg("me", usr_jid, plugin_message); if (((win_type == WIN_CHAT) || (win_type == WIN_CONSOLE)) && prefs_get_boolean(PREF_CHLOG)) { @@ -2634,6 +2652,27 @@ cmd_otr(gchar **args, struct cmd_help_t help) char *version = otr_libotr_version(); cons_show("Using libotr version %s", version); return TRUE; + } else if (strcmp(args[0], "policy") == 0) { + if (args[1] == NULL) { + char *policy = prefs_get_string(PREF_OTR_POLICY); + cons_show("OTR policy is now set to: %s", policy); + return TRUE; + } + + char *choice = args[1]; + if (g_strcmp0(choice, "manual") == 0) { + prefs_set_string(PREF_OTR_POLICY, "manual"); + cons_show("OTR policy is now set to: manual"); + } else if (g_strcmp0(choice, "opportunistic") == 0) { + prefs_set_string(PREF_OTR_POLICY, "opportunistic"); + cons_show("OTR policy is now set to: opportunistic"); + } else if (g_strcmp0(choice, "always") == 0) { + prefs_set_string(PREF_OTR_POLICY, "always"); + cons_show("OTR policy is now set to: always"); + } else { + cons_show("OTR policy can be set to: manual, opportunistic or always."); + } + return TRUE; } if (jabber_get_connection_status() != JABBER_CONNECTED) { diff --git a/src/config/preferences.c b/src/config/preferences.c index 21f603cf..1aba56b7 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -45,6 +45,7 @@ #define PREF_GROUP_PRESENCE "presence" #define PREF_GROUP_CONNECTION "connection" #define PREF_GROUP_ALIAS "alias" +#define PREF_GROUP_OTR_POLICY "policy" static gchar *prefs_loc; static GKeyFile *prefs; @@ -409,6 +410,8 @@ _get_group(preference_t pref) case PREF_LOG_ROTATE: case PREF_LOG_SHARED: return PREF_GROUP_LOGGING; + case PREF_OTR_POLICY: + return PREF_GROUP_OTR_POLICY; case PREF_AUTOAWAY_CHECK: case PREF_AUTOAWAY_MODE: case PREF_AUTOAWAY_MESSAGE: @@ -479,6 +482,8 @@ _get_key(preference_t pref) return "otr"; case PREF_OTR_WARN: return "otr.warn"; + case PREF_OTR_POLICY: + return "otr.policy"; case PREF_LOG_ROTATE: return "rotate"; case PREF_LOG_SHARED: @@ -513,6 +518,8 @@ _get_default_string(preference_t pref) return "off"; case PREF_OTR_LOG: return "redact"; + case PREF_OTR_POLICY: + return "manual"; case PREF_STATUSES_CONSOLE: case PREF_STATUSES_CHAT: case PREF_STATUSES_MUC: diff --git a/src/config/preferences.h b/src/config/preferences.h index df1cf7ac..98ebe346 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -64,7 +64,8 @@ typedef enum { PREF_OTR_LOG, PREF_OTR_WARN, PREF_LOG_ROTATE, - PREF_LOG_SHARED + PREF_LOG_SHARED, + PREF_OTR_POLICY } preference_t; typedef struct prof_alias_t { diff --git a/src/main.c b/src/main.c index b03c7596..33f66b04 100644 --- a/src/main.c +++ b/src/main.c @@ -69,7 +69,7 @@ main(int argc, char **argv) { { "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Show version information", NULL }, { "disable-tls", 'd', 0, G_OPTION_ARG_NONE, &disable_tls, "Disable TLS", NULL }, - { "account", 'a', 0, G_OPTION_ARG_STRING, &account_name, "Auto connect to an account on start-up" }, + { "account", 'a', 0, G_OPTION_ARG_STRING, &account_name, "Auto connect to an account on startup" }, { "log",'l', 0, G_OPTION_ARG_STRING, &log, "Set logging levels, DEBUG, INFO (default), WARN, ERROR", "LEVEL" }, { NULL } }; diff --git a/src/muc.c b/src/muc.c index 4ba88191..88b5e2cd 100644 --- a/src/muc.c +++ b/src/muc.c @@ -297,6 +297,26 @@ muc_get_room_nick(const char * const room) } } +/* + * Return password for the specified room + * The password is owned by the chat room and should not be modified or freed + */ +char * +muc_get_room_password(const char * const room) +{ + if (rooms != NULL) { + ChatRoom *chat_room = g_hash_table_lookup(rooms, room); + + if (chat_room != NULL) { + return chat_room->password; + } else { + return NULL; + } + } else { + return NULL; + } +} + /* * Returns TRUE if the specified nick exists in the room's roster */ diff --git a/src/muc.h b/src/muc.h index 34058bfe..2dc4aa85 100644 --- a/src/muc.h +++ b/src/muc.h @@ -37,7 +37,8 @@ void muc_leave_room(const char * const room); gboolean muc_room_is_active(const char * const room); gboolean muc_room_is_autojoin(const char * const room); GList* muc_get_active_room_list(void); -char * muc_get_room_nick(const char * const room); +char* muc_get_room_nick(const char * const room); +char* muc_get_room_password(const char * const room); void muc_set_room_pending_nick_change(const char * const room, const char * const new_nick); gboolean muc_is_room_pending_nick_change(const char * const room); diff --git a/src/otr/otrlibv3.c b/src/otr/otrlibv3.c index 633d07b8..d1495c9a 100644 --- a/src/otr/otrlibv3.c +++ b/src/otr/otrlibv3.c @@ -34,7 +34,7 @@ otrlib_policy(void) char * otrlib_start_query(void) { - return "?OTR?v2?"; + return "?OTR?v2? This user has requested an Off-the-Record private conversation. However, you do not have a plugin to support that. See http://otr.cypherpunks.ca/ for more information."; } static int diff --git a/src/otr/otrlibv4.c b/src/otr/otrlibv4.c index 01afa868..c55d2972 100644 --- a/src/otr/otrlibv4.c +++ b/src/otr/otrlibv4.c @@ -35,7 +35,7 @@ otrlib_policy(void) char * otrlib_start_query(void) { - return "?OTR?v2?"; + return "?OTR?v2? This user has requested an Off-the-Record private conversation. However, you do not have a plugin to support that. See http://otr.cypherpunks.ca/ for more information."; } static const char* diff --git a/src/server_events.c b/src/server_events.c index 90fff306..7ae34f5e 100644 --- a/src/server_events.c +++ b/src/server_events.c @@ -35,6 +35,7 @@ #ifdef PROF_HAVE_LIBOTR #include "otr/otr.h" +#include #endif #include "ui/ui.h" @@ -95,6 +96,18 @@ handle_login_account_success(char *account_name) ui_handle_login_account_success(account); + // attempt to rejoin rooms with passwords + GList *curr = muc_get_active_room_list(); + while (curr != NULL) { + char *password = muc_get_room_password(curr->data); + if (password != NULL) { + char *nick = muc_get_room_nick(curr->data); + presence_join_room(curr->data, nick, password); + } + curr = g_list_next(curr); + } + g_list_free(curr); + log_info("%s logged in successfully", account->jid); account_free(account); } @@ -218,25 +231,52 @@ handle_incoming_message(char *from, char *message, gboolean priv) #ifdef PROF_HAVE_LIBOTR gboolean was_decrypted = FALSE; - char *decrypted; + char *newmessage; + + char *policy = prefs_get_string(PREF_OTR_POLICY); + char *whitespace_base = strstr(message,OTRL_MESSAGE_TAG_BASE); + if (!priv) { - decrypted = otr_decrypt_message(from, message, &was_decrypted); + //check for OTR whitespace (opportunistic or always) + if (strcmp(policy, "opportunistic") == 0 || strcmp(policy, "always") == 0) { + if (whitespace_base) { + if (strstr(message, OTRL_MESSAGE_TAG_V2) || strstr(message, OTRL_MESSAGE_TAG_V1)) { + // Remove whitespace pattern for proper display in UI + // Handle both BASE+TAGV1/2(16+8) and BASE+TAGV1+TAGV2(16+8+8) + int tag_length = 24; + if (strstr(message, OTRL_MESSAGE_TAG_V2) && strstr(message, OTRL_MESSAGE_TAG_V1)) { + tag_length = 32; + } + memmove(whitespace_base, whitespace_base+tag_length, tag_length); + char *otr_query_message = otr_start_query(); + cons_show("OTR Whitespace pattern detected. Attempting to start OTR session..."); + message_send(otr_query_message, from); + } + } + } + newmessage = otr_decrypt_message(from, message, &was_decrypted); + // internal OTR message - if (decrypted == NULL) { + if (newmessage == NULL) { return; } } else { - decrypted = message; + newmessage = message; } if (priv) { Jid *jid = jid_create(from); char *room = jid->barejid; char *nick = jid->resourcepart; - plugin_message = plugins_on_private_message_received(room, nick, decrypted); + plugin_message = plugins_on_private_message_received(room, nick, newmessage); jid_destroy(jid); } else { - plugin_message = plugins_on_message_received(from, decrypted); + plugin_message = plugins_on_message_received(from, newmessage); + } + if (strcmp(policy, "always") == 0 && !was_decrypted && !whitespace_base) { + char *otr_query_message = otr_start_query(); + cons_show("Attempting to start OTR session..."); + message_send(otr_query_message, from); } ui_incoming_msg(from, plugin_message, NULL, priv); @@ -257,7 +297,7 @@ handle_incoming_message(char *from, char *message, gboolean priv) } if (!priv) - otr_free_message(decrypted); + otr_free_message(newmessage); #else if (priv) { Jid *jid = jid_create(from); diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index c058dab2..fad0f574 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -209,22 +209,28 @@ _jabber_shutdown(void) static void _jabber_process_events(void) { - // run xmpp event loop if connected, connecting or disconnecting - if (jabber_conn.conn_status == JABBER_CONNECTED - || jabber_conn.conn_status == JABBER_CONNECTING - || jabber_conn.conn_status == JABBER_DISCONNECTING) { - xmpp_run_once(jabber_conn.ctx, 10); + int reconnect_sec; + int elapsed_sec; - // check timer and reconnect if disconnected and timer set - } else if (prefs_get_reconnect() != 0) { - if ((jabber_conn.conn_status == JABBER_DISCONNECTED) && - (reconnect_timer != NULL)) { - if (g_timer_elapsed(reconnect_timer, NULL) > prefs_get_reconnect()) { - _jabber_reconnect(); + switch (jabber_conn.conn_status) + { + case JABBER_CONNECTED: + case JABBER_CONNECTING: + case JABBER_DISCONNECTING: + xmpp_run_once(jabber_conn.ctx, 10); + break; + case JABBER_DISCONNECTED: + reconnect_sec = prefs_get_reconnect(); + if ((reconnect_sec != 0) && (reconnect_timer != NULL)) { + elapsed_sec = g_timer_elapsed(reconnect_timer, NULL); + if (elapsed_sec > reconnect_sec) { + _jabber_reconnect(); + } } - } + break; + default: + break; } - } static GList * diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 6da2b367..4fc71f63 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -522,4 +522,3 @@ message_init_module(void) message_send_inactive = _message_send_inactive; message_send_gone = _message_send_gone; } -