diff --git a/Makefile.am b/Makefile.am index 9ef40992..845ed788 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,7 @@ core_sources = \ src/tools/autocomplete.c src/tools/autocomplete.h \ src/tools/tinyurl.c src/tools/tinyurl.h \ src/config/accounts.c src/config/accounts.h \ + src/config/tlscerts.c src/config/tlscerts.h \ src/config/account.c src/config/account.h \ src/config/preferences.c src/config/preferences.h \ src/config/theme.c src/config/theme.h @@ -57,6 +58,7 @@ unittest_sources = \ src/tools/tinyurl.c src/tools/tinyurl.h \ src/config/accounts.h \ src/config/account.c src/config/account.h \ + src/config/tlscerts.c src/config/tlscerts.h \ src/config/preferences.c src/config/preferences.h \ src/config/theme.c src/config/theme.h \ src/window_list.c src/window_list.h \ diff --git a/src/command/command.c b/src/command/command.c index 27c8e078..891cf4ea 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -50,6 +50,7 @@ #include "config/accounts.h" #include "config/preferences.h" #include "config/theme.h" +#include "config/tlscerts.h" #include "contact.h" #include "roster_list.h" #include "jid.h" @@ -106,6 +107,7 @@ static char * _time_autocomplete(ProfWin *window, const char * const input); static char * _receipts_autocomplete(ProfWin *window, const char * const input); static char * _help_autocomplete(ProfWin *window, const char * const input); static char * _wins_autocomplete(ProfWin *window, const char * const input); +static char * _tls_autocomplete(ProfWin *window, const char * const input); GHashTable *commands = NULL; @@ -186,6 +188,33 @@ static struct cmd_t command_defs[] = "/connect me@chatty server chatty.com port 5443") }, + { "/tls", + cmd_tls, parse_args, 1, 3, NULL, + CMD_TAGS( + CMD_TAG_CONNECTION) + CMD_SYN( + "/tls allow", + "/tls always", + "/tls deny", + "/tls trusted", + "/tls revoke ", + "/tls certpath", + "/tls certpath set ", + "/tls certpath clear") + CMD_DESC( + "Handle TLS certificates. ") + CMD_ARGS( + { "allow", "Allow connection to continue with an invalid TLS certificate." }, + { "always", "Always allow connections with this invalid TLS certificate." }, + { "deny", "Terminate TLS connection." }, + { "trusted", "List manually trusted certificates (with /tls always)." }, + { "revoke ", "Remove a manually trusted certificate." }, + { "certpath", "Show the trusted certificate path." }, + { "certpath set ", "Specify filesystem path containing trusted certificates." }, + { "certpath clear", "Clear the trusted certificate path." }) + CMD_NOEXAMPLES + }, + { "/disconnect", cmd_disconnect, parse_args, 0, 0, NULL, CMD_TAGS( @@ -1674,6 +1703,8 @@ static Autocomplete inpblock_ac; static Autocomplete receipts_ac; static Autocomplete pgp_ac; static Autocomplete pgp_log_ac; +static Autocomplete tls_ac; +static Autocomplete tls_certpath_ac; /* * Initialise command autocompleter and history @@ -2069,6 +2100,18 @@ cmd_init(void) autocomplete_add(pgp_log_ac, "on"); autocomplete_add(pgp_log_ac, "off"); autocomplete_add(pgp_log_ac, "redact"); + + tls_ac = autocomplete_new(); + autocomplete_add(tls_ac, "allow"); + autocomplete_add(tls_ac, "always"); + autocomplete_add(tls_ac, "deny"); + autocomplete_add(tls_ac, "trusted"); + autocomplete_add(tls_ac, "revoke"); + autocomplete_add(tls_ac, "certpath"); + + tls_certpath_ac = autocomplete_new(); + autocomplete_add(tls_certpath_ac, "set"); + autocomplete_add(tls_certpath_ac, "clear"); } void @@ -2133,6 +2176,8 @@ cmd_uninit(void) autocomplete_free(receipts_ac); autocomplete_free(pgp_ac); autocomplete_free(pgp_log_ac); + autocomplete_free(tls_ac); + autocomplete_free(tls_certpath_ac); } gboolean @@ -2245,6 +2290,7 @@ cmd_reset_autocomplete(ProfWin *window) muc_invites_reset_ac(); accounts_reset_all_search(); accounts_reset_enabled_search(); + tlscerts_reset_ac(); prefs_reset_boolean_choice(); presence_reset_sub_request_search(); #ifdef HAVE_LIBGPGME @@ -2313,6 +2359,8 @@ cmd_reset_autocomplete(ProfWin *window) autocomplete_reset(receipts_ac); autocomplete_reset(pgp_ac); autocomplete_reset(pgp_log_ac); + autocomplete_reset(tls_ac); + autocomplete_reset(tls_certpath_ac); if (window->type == WIN_CHAT) { ProfChatWin *chatwin = (ProfChatWin*)window; @@ -2566,6 +2614,7 @@ _cmd_complete_parameters(ProfWin *window, const char * const input) g_hash_table_insert(ac_funcs, "/time", _time_autocomplete); g_hash_table_insert(ac_funcs, "/receipts", _receipts_autocomplete); g_hash_table_insert(ac_funcs, "/wins", _wins_autocomplete); + g_hash_table_insert(ac_funcs, "/tls", _tls_autocomplete); int len = strlen(input); char parsed[len+1]; @@ -3472,6 +3521,29 @@ _wins_autocomplete(ProfWin *window, const char * const input) return NULL; } +static char * +_tls_autocomplete(ProfWin *window, const char * const input) +{ + char *result = NULL; + + result = autocomplete_param_with_func(input, "/tls revoke", tlscerts_complete); + if (result) { + return result; + } + + result = autocomplete_param_with_ac(input, "/tls certpath", tls_certpath_ac, TRUE); + if (result) { + return result; + } + + result = autocomplete_param_with_ac(input, "/tls", tls_ac, TRUE); + if (result) { + return result; + } + + return result; +} + static char * _receipts_autocomplete(ProfWin *window, const char * const input) { diff --git a/src/command/commands.c b/src/command/commands.c index 61c0373b..3f695cc1 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -49,6 +49,7 @@ #include "config/account.h" #include "config/preferences.h" #include "config/theme.h" +#include "config/tlscerts.h" #include "contact.h" #include "roster_list.h" #include "jid.h" @@ -156,6 +157,93 @@ cmd_execute_alias(ProfWin *window, const char * const inp, gboolean *ran) return TRUE; } +gboolean +cmd_tls(ProfWin *window, const char * const command, gchar **args) +{ + if (g_strcmp0(args[0], "certpath") == 0) { + if (g_strcmp0(args[1], "set") == 0) { + if (args[2] == NULL) { + cons_bad_cmd_usage(command); + return TRUE; + } + + if (g_file_test(args[2], G_FILE_TEST_IS_DIR)) { + prefs_set_string(PREF_CERT_PATH, args[2]); + cons_show("Certificate path set to: %s", args[2]); + } else { + cons_show("Directory %s does not exist.", args[2]); + } + return TRUE; + } else if (g_strcmp0(args[1], "clear") == 0) { + prefs_set_string(PREF_CERT_PATH, NULL); + cons_show("Certificate path cleared"); + return TRUE; + } else if (args[1] == NULL) { + char *path = prefs_get_string(PREF_CERT_PATH); + if (path) { + cons_show("Trusted certificate path: %s", path); + prefs_free_string(path); + } else { + cons_show("No trusted certificate path set."); + } + return TRUE; + } else { + cons_bad_cmd_usage(command); + return TRUE; + } + } else if (g_strcmp0(args[0], "trusted") == 0) { + GList *certs = tlscerts_list(); + GList *curr = certs; + + if (curr) { + cons_show("Trusted certificates:"); + cons_show(""); + } else { + cons_show("No trustes certificates found."); + } + while (curr) { + TLSCertificate *cert = curr->data; + if (cert->fingerprint) { + cons_show("Fingerprint : %s", cert->fingerprint); + } + if (cert->domain) { + cons_show("Domain : %s", cert->domain); + } + if (cert->organisation) { + cons_show("Organisation : %s", cert->organisation); + } + if (cert->email) { + cons_show("Email : %s", cert->email); + } + if (cert->notbefore) { + cons_show("Start : %s", cert->notbefore); + } + if (cert->notafter) { + cons_show("End : %s", cert->notafter); + } + cons_show(""); + curr = g_list_next(curr); + } + g_list_free_full(certs, (GDestroyNotify)tlscerts_free); + return TRUE; + } else if (g_strcmp0(args[0], "revoke") == 0) { + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + } else { + gboolean res = tlscerts_revoke(args[1]); + if (res) { + cons_show("Trusted certificate revoked: %s", args[1]); + } else { + cons_show("Could not find certificate: %s", args[0]); + } + } + return TRUE; + } else { + cons_bad_cmd_usage(command); + return TRUE; + } +} + gboolean cmd_connect(ProfWin *window, const char * const command, gchar **args) { diff --git a/src/command/commands.h b/src/command/commands.h index 89f923ff..3c1d52c7 100644 --- a/src/command/commands.h +++ b/src/command/commands.h @@ -82,6 +82,7 @@ gboolean cmd_chlog(ProfWin *window, const char * const command, gchar **args); gboolean cmd_clear(ProfWin *window, const char * const command, gchar **args); gboolean cmd_close(ProfWin *window, const char * const command, gchar **args); gboolean cmd_connect(ProfWin *window, const char * const command, gchar **args); +gboolean cmd_tls(ProfWin *window, const char * const command, gchar **args); gboolean cmd_decline(ProfWin *window, const char * const command, gchar **args); gboolean cmd_disco(ProfWin *window, const char * const command, gchar **args); gboolean cmd_disconnect(ProfWin *window, const char * const command, gchar **args); diff --git a/src/config/preferences.c b/src/config/preferences.c index 6d63d3e9..7a198ac7 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -612,6 +612,7 @@ _get_group(preference_t pref) case PREF_CARBONS: case PREF_RECEIPTS_SEND: case PREF_RECEIPTS_REQUEST: + case PREF_CERT_PATH: return PREF_GROUP_CONNECTION; case PREF_OTR_LOG: case PREF_OTR_POLICY: @@ -744,6 +745,8 @@ _get_key(preference_t pref) return "enc.warn"; case PREF_PGP_LOG: return "log"; + case PREF_CERT_PATH: + return "certpath"; default: return NULL; } diff --git a/src/config/preferences.h b/src/config/preferences.h index 2a7ab5bf..89b3fe24 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -103,7 +103,8 @@ typedef enum { PREF_RESOURCE_MESSAGE, PREF_INPBLOCK_DYNAMIC, PREF_ENC_WARN, - PREF_PGP_LOG + PREF_PGP_LOG, + PREF_CERT_PATH, } preference_t; typedef struct prof_alias_t { diff --git a/src/config/tlscerts.c b/src/config/tlscerts.c new file mode 100644 index 00000000..3d0a0b24 --- /dev/null +++ b/src/config/tlscerts.c @@ -0,0 +1,250 @@ +/* + * tlscerts.c + * + * Copyright (C) 2012 - 2015 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 . + * + * In addition, as a special exception, the copyright holders give permission to + * link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, you + * may extend this exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from all + * source files in the program, then also delete it here. + * + */ + +#include +#include + +#include +#include + +#include "config/tlscerts.h" +#include "log.h" +#include "common.h" +#include "tools/autocomplete.h" + +static gchar *tlscerts_loc; +static GKeyFile *tlscerts; + +static gchar* _get_tlscerts_file(void); +static void _save_tlscerts(void); + +static Autocomplete certs_ac; + +void +tlscerts_init(void) +{ + log_info("Loading TLS certificates"); + tlscerts_loc = _get_tlscerts_file(); + + if (g_file_test(tlscerts_loc, G_FILE_TEST_EXISTS)) { + g_chmod(tlscerts_loc, S_IRUSR | S_IWUSR); + } + + tlscerts = g_key_file_new(); + g_key_file_load_from_file(tlscerts, tlscerts_loc, G_KEY_FILE_KEEP_COMMENTS, NULL); + + certs_ac = autocomplete_new(); + gsize len = 0; + gchar **groups = g_key_file_get_groups(tlscerts, &len); + + int i = 0; + for (i = 0; i < g_strv_length(groups); i++) { + autocomplete_add(certs_ac, groups[i]); + } + g_strfreev(groups); +} + +gboolean +tlscerts_exists(const char * const fingerprint) +{ + return g_key_file_has_group(tlscerts, fingerprint); +} + +GList* +tlscerts_list(void) +{ + GList *res = NULL; + gsize len = 0; + gchar **groups = g_key_file_get_groups(tlscerts, &len); + + int i = 0; + for (i = 0; i < g_strv_length(groups); i++) { + char *fingerprint = strdup(groups[i]); + char *domain = g_key_file_get_string(tlscerts, fingerprint, "domain", NULL); + char *organisation = g_key_file_get_string(tlscerts, fingerprint, "organisation", NULL); + char *email = g_key_file_get_string(tlscerts, fingerprint, "email", NULL); + char *notbefore = g_key_file_get_string(tlscerts, fingerprint, "start", NULL); + char *notafter = g_key_file_get_string(tlscerts, fingerprint, "end", NULL); + + TLSCertificate *cert = tlscerts_new(fingerprint, domain, organisation, email, notbefore, notafter); + + res = g_list_append(res, cert); + } + + if (groups) { + g_strfreev(groups); + } + + return res; +} + +TLSCertificate* +tlscerts_new(const char * const fingerprint, const char * const domain, const char * const organisation, + const char * const email, const char * const notbefore, const char * const notafter) +{ + TLSCertificate *cert = malloc(sizeof(TLSCertificate)); + if (fingerprint) { + cert->fingerprint = strdup(fingerprint); + } else { + cert->fingerprint = NULL; + } + if (domain) { + cert->domain = strdup(domain); + } else { + cert->domain = NULL; + } + if (organisation) { + cert->organisation = strdup(organisation); + } else { + cert->organisation = NULL; + } + if (email) { + cert->email = strdup(email); + } else { + cert->email= NULL; + } + if (notbefore) { + cert->notbefore = strdup(notbefore); + } else { + cert->notbefore = NULL; + } + if (notafter) { + cert->notafter = strdup(notafter); + } else { + cert->notafter = NULL; + } + + return cert; +} + +void +tlscerts_add(TLSCertificate *cert) +{ + if (!cert) { + return; + } + + if (!cert->fingerprint) { + return; + } + + autocomplete_add(certs_ac, cert->fingerprint); + + if (cert->domain) { + g_key_file_set_string(tlscerts, cert->fingerprint, "domain", cert->domain); + } + if (cert->organisation) { + g_key_file_set_string(tlscerts, cert->fingerprint, "organisation", cert->organisation); + } + if (cert->email) { + g_key_file_set_string(tlscerts, cert->fingerprint, "email", cert->email); + } + if (cert->notbefore) { + g_key_file_set_string(tlscerts, cert->fingerprint, "start", cert->notbefore); + } + if (cert->notafter) { + g_key_file_set_string(tlscerts, cert->fingerprint, "end", cert->notafter); + } + + _save_tlscerts(); +} + +gboolean +tlscerts_revoke(const char * const fingerprint) +{ + gboolean result = g_key_file_remove_group(tlscerts, fingerprint, NULL); + if (result) { + autocomplete_remove(certs_ac, fingerprint); + } + + _save_tlscerts(); + + return result; +} + +char * +tlscerts_complete(const char * const prefix) +{ + return autocomplete_complete(certs_ac, prefix, TRUE); +} + +void +tlscerts_reset_ac(void) +{ + autocomplete_reset(certs_ac); +} + +void +tlscerts_free(TLSCertificate *cert) +{ + if (cert) { + free(cert->fingerprint); + free(cert->domain); + free(cert->organisation); + free(cert->email); + free(cert->notbefore); + free(cert->notafter); + } +} + +void +tlscerts_close(void) +{ + g_key_file_free(tlscerts); + tlscerts = NULL; + autocomplete_free(certs_ac); +} + +static gchar * +_get_tlscerts_file(void) +{ + gchar *xdg_data = xdg_get_data_home(); + GString *tlscerts_file = g_string_new(xdg_data); + g_string_append(tlscerts_file, "/profanity/tlscerts"); + gchar *result = strdup(tlscerts_file->str); + g_free(xdg_data); + g_string_free(tlscerts_file, TRUE); + + return result; +} + +static void +_save_tlscerts(void) +{ + gsize g_data_size; + gchar *g_tlscerts_data = g_key_file_to_data(tlscerts, &g_data_size, NULL); + g_file_set_contents(tlscerts_loc, g_tlscerts_data, g_data_size, NULL); + g_chmod(tlscerts_loc, S_IRUSR | S_IWUSR); + g_free(g_tlscerts_data); +} diff --git a/src/config/tlscerts.h b/src/config/tlscerts.h new file mode 100644 index 00000000..b8a15c5b --- /dev/null +++ b/src/config/tlscerts.h @@ -0,0 +1,69 @@ +/* + * tlscerts.h + * + * Copyright (C) 2012 - 2015 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 . + * + * In addition, as a special exception, the copyright holders give permission to + * link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, you + * may extend this exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from all + * source files in the program, then also delete it here. + * + */ + +#ifndef TLSCERTS_H +#define TLSCERTS_H + +typedef struct tls_cert_t { + char *fingerprint; + char *domain; + char *organisation; + char *email; + char *notbefore; + char *notafter; +} TLSCertificate; + +void tlscerts_init(void); + +TLSCertificate *tlscerts_new(const char * const fingerprint, const char * const domain, + const char * const organisation, const char * const email, + const char * const notbefore, const char * const notafter); + +gboolean tlscerts_exists(const char * const fingerprint); + +void tlscerts_add(TLSCertificate *cert); + +gboolean tlscerts_revoke(const char * const fingerprint); + +void tlscerts_free(TLSCertificate *cert); + +GList* tlscerts_list(void); + +char* tlscerts_complete(const char * const prefix); + +void tlscerts_reset_ac(void); + +void tlscerts_close(void); + +#endif diff --git a/src/event/server_events.c b/src/event/server_events.c index 160d4472..4eb4f785 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -44,6 +44,7 @@ #include "config/account.h" #include "roster_list.h" #include "window_list.h" +#include "config/tlscerts.h" #ifdef HAVE_LIBOTR #include "otr/otr.h" @@ -638,3 +639,94 @@ sv_ev_muc_occupant_online(const char * const room, const char * const nick, cons occupantswin_occupants(room); } } + +int +sv_ev_certfail(const char * const errormsg, const char * const certname, const char * const certfp, + const char * const notbefore, const char * const notafter) +{ + if (tlscerts_exists(certfp)) { + return 1; + } + + char *domain = NULL; + char *org = NULL; + char *email = NULL; + gchar** fields = g_strsplit(certname, "/", 0); + int i = 0; + for (i = 0; i < g_strv_length(fields); i++) { + gchar** keyval = g_strsplit(fields[i], "=", 2); + if (g_strv_length(keyval) == 2) { + if (g_strcmp0(keyval[0], "CN") == 0) { + domain = strdup(keyval[1]); + } + if (g_strcmp0(keyval[0], "O") == 0) { + org = strdup(keyval[1]); + } + if (g_strcmp0(keyval[0], "emailAddress") == 0) { + email = strdup(keyval[1]); + } + } + g_strfreev(keyval); + } + g_strfreev(fields); + + cons_show(""); + cons_show_error("TLS certificate verification failed: %s", errormsg); + if (domain) { + cons_show(" Domain : %s", domain); + } + if (org) { + cons_show(" Organisation : %s", org); + } + if (email) { + cons_show(" Email : %s", email); + } + cons_show(" Fingerprint : %s", certfp); + cons_show(" Start : %s", notbefore); + cons_show(" End : %s", notafter); + cons_show(""); + cons_show("Use '/tls allow' to accept this certificate"); + cons_show("Use '/tls always' to accept this certificate permanently"); + cons_show("Use '/tls deny' to reject this certificate"); + cons_show(""); + ui_update(); + + char *cmd = ui_get_line(); + + while ((g_strcmp0(cmd, "/tls allow") != 0) + && (g_strcmp0(cmd, "/tls always") != 0) + && (g_strcmp0(cmd, "/tls deny") != 0)) { + cons_show("Use '/tls allow' to accept this certificate"); + cons_show("Use '/tls always' to accept this certificate permanently"); + cons_show("Use '/tls deny' to reject this certificate"); + cons_show(""); + ui_update(); + free(cmd); + cmd = ui_get_line(); + } + + if (g_strcmp0(cmd, "/tls allow") == 0) { + free(cmd); + free(domain); + free(org); + free(email); + return 1; + } else if (g_strcmp0(cmd, "/tls always") == 0) { + if (!tlscerts_exists(certfp)) { + TLSCertificate *cert = tlscerts_new(certfp, domain, org, email, notbefore, notafter); + tlscerts_add(cert); + tlscerts_free(cert); + } + free(cmd); + free(domain); + free(org); + free(email); + return 1; + } else { + free(cmd); + free(domain); + free(org); + free(email); + return 0; + } +} diff --git a/src/event/server_events.h b/src/event/server_events.h index 3ef8eae4..2aa90754 100644 --- a/src/event/server_events.h +++ b/src/event/server_events.h @@ -86,5 +86,7 @@ void sv_ev_muc_occupant_online(const char * const room, const char * const nick, void sv_ev_roster_update(const char * const barejid, const char * const name, GSList *groups, const char * const subscription, gboolean pending_out); void sv_ev_roster_received(void); +int sv_ev_certfail(const char * const errormsg, const char * const certname, const char * const certfp, + const char * const notbefore, const char * const notafter); #endif diff --git a/src/profanity.c b/src/profanity.c index a56eb5e9..79e008d7 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -55,6 +55,7 @@ #include "common.h" #include "contact.h" #include "roster_list.h" +#include "config/tlscerts.h" #include "log.h" #include "muc.h" #ifdef HAVE_LIBOTR @@ -68,6 +69,7 @@ #include "ui/ui.h" #include "window_list.h" #include "event/client_events.h" +#include "config/tlscerts.h" static void _check_autoaway(void); static void _init(const int disable_tls, char *log_level); @@ -248,6 +250,7 @@ _init(const int disable_tls, char *log_level) log_info("Initialising contact list"); roster_init(); muc_init(); + tlscerts_init(); #ifdef HAVE_LIBOTR otr_init(); #endif @@ -284,6 +287,7 @@ _shutdown(void) chat_log_close(); theme_close(); accounts_close(); + tlscerts_close(); cmd_uninit(); log_stderr_close(); log_close(); diff --git a/src/ui/core.c b/src/ui/core.c index 5735f462..9f7ffcd7 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -2106,6 +2106,13 @@ ui_ask_password(void) return inp_get_password(); } +char * +ui_get_line(void) +{ + status_bar_update_virtual(); + return inp_get_line(); +} + char * ui_ask_pgp_passphrase(const char *hint, int prev_fail) { diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c index 9b4eddfb..5ab2c281 100644 --- a/src/ui/inputwin.c +++ b/src/ui/inputwin.c @@ -225,6 +225,21 @@ inp_close(void) rl_callback_handler_remove(); } +char * +inp_get_line(void) +{ + werase(inp_win); + wmove(inp_win, 0, 0); + _inp_win_update_virtual(); + doupdate(); + char *line = NULL; + while (!line) { + line = inp_readline(); + } + status_bar_clear(); + return line; +} + char* inp_get_password(void) { @@ -275,6 +290,7 @@ _inp_write(char *line, int offset) _inp_win_handle_scroll(); _inp_win_update_virtual(); + doupdate(); } static int diff --git a/src/ui/inputwin.h b/src/ui/inputwin.h index f49a6a76..4f732e67 100644 --- a/src/ui/inputwin.h +++ b/src/ui/inputwin.h @@ -47,5 +47,6 @@ void inp_win_clear(void); void inp_win_resize(void); void inp_put_back(void); char* inp_get_password(void); +char * inp_get_line(void); #endif diff --git a/src/ui/ui.h b/src/ui/ui.h index 27395048..07cccd20 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -102,6 +102,7 @@ win_type_t ui_win_type(int index); void ui_close_win(int index); int ui_win_unread(int index); char * ui_ask_password(void); +char * ui_get_line(void); char * ui_ask_pgp_passphrase(const char *hint, int prev_fail); void ui_handle_stanza(const char * const msg); diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index 6f9de18d..768a4476 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -94,13 +94,16 @@ static GTimer *reconnect_timer; static log_level_t _get_log_level(xmpp_log_level_t xmpp_level); static xmpp_log_level_t _get_xmpp_log_level(); + static void _xmpp_file_logger(void * const userdata, const xmpp_log_level_t level, const char * const area, const char * const msg); + static xmpp_log_t * _xmpp_get_file_logger(); static jabber_conn_status_t _jabber_connect(const char * const fulljid, const char * const passwd, const char * const altdomain, int port); + static void _jabber_reconnect(void); static void _connection_handler(xmpp_conn_t * const conn, @@ -357,6 +360,15 @@ _connection_free_session_data(void) presence_clear_sub_requests(); } +#ifdef HAVE_LIBMESODE +static int +_connection_certfail_cb(const char * const certname, const char * const certfp, + char * const notbefore, const char * const notafter, const char * const errormsg) +{ + return sv_ev_certfail(errormsg, certname, certfp, notbefore, notafter); +} +#endif + static jabber_conn_status_t _jabber_connect(const char * const fulljid, const char * const passwd, const char * const altdomain, int port) @@ -407,8 +419,20 @@ _jabber_connect(const char * const fulljid, const char * const passwd, xmpp_conn_disable_tls(jabber_conn.conn); } +#ifdef HAVE_LIBMESODE + char *cert_path = prefs_get_string(PREF_CERT_PATH); + if (cert_path) { + xmpp_conn_tlscert_path(jabber_conn.conn, cert_path); + } +#endif + +#ifdef HAVE_LIBMESODE + int connect_status = xmpp_connect_client(jabber_conn.conn, altdomain, port, + _connection_certfail_cb, _connection_handler, jabber_conn.ctx); +#else int connect_status = xmpp_connect_client(jabber_conn.conn, altdomain, port, _connection_handler, jabber_conn.ctx); +#endif if (connect_status == 0) jabber_conn.conn_status = JABBER_CONNECTING; diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 8c5f9701..032564fd 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -168,6 +168,11 @@ char * ui_ask_password(void) return mock_ptr_type(char *); } +char *ui_get_line(void) +{ + return NULL; +} + void ui_handle_stanza(const char * const msg) {} // ui events