+2008-07-03 [colin] 3.5.0cvs2
+
+ * src/imap.c
+ * src/news.c
+ * src/pop.c
+ * src/prefs_account.c
+ * src/prefs_account.h
+ * src/prefs_common.c
+ * src/send_message.c
+ * src/common/session.c
+ * src/common/session.h
+ * src/common/smtp.c
+ * src/common/smtp.h
+ * src/common/socket.h
+ * src/common/ssl.c
+ * src/common/ssl.h
+ * src/common/ssl_certificate.c
+ * src/common/ssl_certificate.h
+ * src/etpan/imap-thread.c
+ * src/etpan/nntp-thread.c
+ * src/gtk/inputdialog.c
+ Add support for SSL client certificates (either PEM files
+ with certificate and private key, either PKCS12 files)
+
2008-06-28 [colin] 3.5.0cvs1
* src/foldersel.c
( cvs diff -u -r 1.654.2.3471 -r 1.654.2.3472 configure.ac; cvs diff -u -r 1.1.2.47 -r 1.1.2.48 src/gtk/authors.h; ) > 3.4.0cvs115.patchset
( cvs diff -u -r 1.1.2.48 -r 1.1.2.49 src/gtk/authors.h; ) > 3.4.0cvs116.patchset
( cvs diff -u -r 1.26.2.36 -r 1.26.2.37 src/foldersel.c; ) > 3.5.0cvs1.patchset
+( cvs diff -u -r 1.179.2.225 -r 1.179.2.226 src/imap.c; cvs diff -u -r 1.101.2.50 -r 1.101.2.51 src/news.c; cvs diff -u -r 1.56.2.61 -r 1.56.2.62 src/pop.c; cvs diff -u -r 1.105.2.132 -r 1.105.2.133 src/prefs_account.c; cvs diff -u -r 1.49.2.36 -r 1.49.2.37 src/prefs_account.h; cvs diff -u -r 1.204.2.172 -r 1.204.2.173 src/prefs_common.c; cvs diff -u -r 1.17.2.47 -r 1.17.2.48 src/send_message.c; cvs diff -u -r 1.23.2.17 -r 1.23.2.18 src/common/session.c; cvs diff -u -r 1.8.2.11 -r 1.8.2.12 src/common/session.h; cvs diff -u -r 1.11.2.24 -r 1.11.2.25 src/common/smtp.c; cvs diff -u -r 1.6.2.13 -r 1.6.2.14 src/common/smtp.h; cvs diff -u -r 1.13.2.18 -r 1.13.2.19 src/common/socket.h; cvs diff -u -r 1.9.2.28 -r 1.9.2.29 src/common/ssl.c; cvs diff -u -r 1.2.2.7 -r 1.2.2.8 src/common/ssl.h; cvs diff -u -r 1.4.2.27 -r 1.4.2.28 src/common/ssl_certificate.c; cvs diff -u -r 1.1.4.10 -r 1.1.4.11 src/common/ssl_certificate.h; cvs diff -u -r 1.1.4.101 -r 1.1.4.102 src/etpan/imap-thread.c; cvs diff -u -r 1.1.2.6 -r 1.1.2.7 src/etpan/nntp-thread.c; cvs diff -u -r 1.2.2.29 -r 1.2.2.30 src/gtk/inputdialog.c; ) > 3.5.0cvs2.patchset
MICRO_VERSION=0
INTERFACE_AGE=0
BINARY_AGE=0
-EXTRA_VERSION=1
+EXTRA_VERSION=2
EXTRA_RELEASE=
EXTRA_GTK2_VERSION=
gpointer data);
-void session_init(Session *session)
+void session_init(Session *session, const void *prefs_account, gboolean is_smtp)
{
session->type = SESSION_UNKNOWN;
session->sock = NULL;
session->timeout_interval = 0;
session->data = NULL;
+ session->account = prefs_account;
+ session->is_smtp = is_smtp;
}
/*!
session->port = port;
sock = sock_connect(server, port);
+ sock->is_smtp = session->is_smtp;
if (sock == NULL) {
g_warning("can't connect to server.");
session_close(session);
}
session->sock = sock;
-
+ sock->account = session->account;
+ sock->is_smtp = session->is_smtp;
#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
if (session->ssl_type == SSL_TUNNEL) {
sock_set_nonblocking_mode(sock, FALSE);
gpointer recv_data_notify_data;
gpointer send_data_progressive_notify_data;
gpointer send_data_notify_data;
+
+ void *account;
+ gboolean is_smtp;
};
-void session_init (Session *session);
+void session_init (Session *session,
+ const void *prefs_account,
+ gboolean is_smtp);
gint session_connect (Session *session,
const gchar *server,
gushort port);
static gint smtp_session_send_data_finished(Session *session, guint len);
-Session *smtp_session_new(void)
+Session *smtp_session_new(void *prefs_account)
{
SMTPSession *session;
session = g_new0(SMTPSession, 1);
- session_init(SESSION(session));
+ session_init(SESSION(session), prefs_account, TRUE);
SESSION(session)->type = SESSION_SMTP;
void *dialog;
};
-Session *smtp_session_new (void);
+Session *smtp_session_new (void *prefs_account);
gint smtp_from(SMTPSession *session);
gint smtp_quit(SMTPSession *session);
#elif USE_GNUTLS
gnutls_session ssl;
gnutls_certificate_credentials_t xcred;
+ gnutls_x509_crt client_crt;
+ gnutls_x509_privkey client_key;
#endif
guint g_source;
GIOChannel *sock_ch;
SockFunc callback;
GIOCondition condition;
gchar *canonical_name;
+
+ void *account;
+ gboolean is_smtp;
};
void refresh_resolvers (void);
#include "utils.h"
#include "ssl.h"
#include "ssl_certificate.h"
+#include "hooks.h"
#ifdef HAVE_LIBETPAN
#include <libetpan/mailstream_ssl.h>
static SSL_CTX *ssl_ctx;
#endif
+#ifdef USE_OPENSSL
+static int openssl_client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
+{
+ SSLClientCertHookData hookdata;
+ SockInfo *sockinfo = (SockInfo *)SSL_CTX_get_app_data(ssl->ctx);
+
+ if (x509 == NULL || pkey == NULL) {
+ return 0;
+ }
+
+ if (sockinfo == NULL)
+ return 0;
+
+ hookdata.account = sockinfo->account;
+ hookdata.cert_path = NULL;
+ hookdata.password = NULL;
+ hookdata.is_smtp = sockinfo->is_smtp;
+ hooks_invoke(SSLCERT_GET_CLIENT_CERT_HOOKLIST, &hookdata);
+
+ if (hookdata.cert_path == NULL)
+ return 0;
+
+ *x509 = ssl_certificate_get_x509_from_pem_file(hookdata.cert_path);
+ *pkey = ssl_certificate_get_pkey_from_pem_file(hookdata.cert_path);
+ if (!(*x509 && *pkey)) {
+ /* try pkcs12 format */
+ ssl_certificate_get_x509_and_pkey_from_p12_file(hookdata.cert_path, hookdata.password, x509, pkey);
+ }
+ if (*x509 && *pkey)
+ return 1;
+ else
+ return 0;
+}
+#endif
+#ifdef USE_GNUTLS
+static int gnutls_client_cert_cb(gnutls_session session,
+ const gnutls_datum *req_ca_rdn, int nreqs,
+ const gnutls_pk_algorithm *sign_algos,
+ int sign_algos_length, gnutls_retr_st *st)
+{
+ SSLClientCertHookData hookdata;
+ SockInfo *sockinfo = (SockInfo *)gnutls_session_get_ptr(session);
+ gnutls_certificate_type type = gnutls_certificate_type_get(session);
+ gnutls_x509_crt crt;
+ gnutls_x509_privkey key;
+
+ st->ncerts = 0;
+
+ hookdata.account = sockinfo->account;
+ hookdata.cert_path = NULL;
+ hookdata.password = NULL;
+ hookdata.is_smtp = sockinfo->is_smtp;
+ hooks_invoke(SSLCERT_GET_CLIENT_CERT_HOOKLIST, &hookdata);
+
+ if (hookdata.cert_path == NULL)
+ return 0;
+
+ sockinfo->client_crt = ssl_certificate_get_x509_from_pem_file(hookdata.cert_path);
+ sockinfo->client_key = ssl_certificate_get_pkey_from_pem_file(hookdata.cert_path);
+ if (!(sockinfo->client_crt && sockinfo->client_key)) {
+ /* try pkcs12 format */
+ ssl_certificate_get_x509_and_pkey_from_p12_file(hookdata.cert_path, hookdata.password,
+ &crt, &key);
+ sockinfo->client_crt = crt;
+ sockinfo->client_key = key;
+ }
+
+ if (type == GNUTLS_CRT_X509 && sockinfo->client_crt && sockinfo->client_key) {
+ st->ncerts = 1;
+ st->type = type;
+ st->cert.x509 = &(sockinfo->client_crt);
+ st->key.x509 = sockinfo->client_key;
+ st->deinit_all = 0;
+ return 0;
+ }
+ return -1;
+}
+#endif
void ssl_init(void)
{
#ifdef USE_OPENSSL
/* Global system initialization*/
SSL_library_init();
SSL_load_error_strings();
+ OpenSSL_add_all_algorithms();
+ OpenSSL_add_all_ciphers();
+ OpenSSL_add_all_digests();
#ifdef HAVE_LIBETPAN
mailstream_openssl_init_not_required();
meth = SSLv23_client_method();
ssl_ctx = SSL_CTX_new(meth);
+
+ SSL_CTX_set_client_cert_cb(ssl_ctx, openssl_client_cert_cb);
+
/* Set default certificate paths */
SSL_CTX_set_default_verify_paths(ssl_ctx);
break;
}
+ SSL_CTX_set_app_data(ssl_ctx, sockinfo);
SSL_set_fd(ssl, sockinfo->sock);
if (SSL_connect_nb(ssl) == -1) {
g_warning(_("SSL connect failed (%s)\n"),
X509_free(server_cert);
sockinfo->ssl = ssl;
+
#else
gnutls_session session;
int r;
gnutls_certificate_set_verify_flags (xcred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
- gnutls_transport_set_ptr(session, (gnutls_transport_ptr)
- sockinfo->sock);
+ gnutls_transport_set_ptr(session, (gnutls_transport_ptr) sockinfo->sock);
+ gnutls_session_set_ptr(session, sockinfo);
+ gnutls_certificate_client_set_retrieve_function(xcred, gnutls_client_cert_cb);
gnutls_dh_set_prime_bits(session, 512);
#else
gnutls_certificate_free_credentials(sockinfo->xcred);
gnutls_deinit(sockinfo->ssl);
+ if (sockinfo->client_crt)
+ gnutls_x509_crt_deinit(sockinfo->client_crt);
+ if (sockinfo->client_key)
+ gnutls_x509_privkey_deinit(sockinfo->client_key);
+ sockinfo->client_key = NULL;
+ sockinfo->client_crt = NULL;
#endif
sockinfo->ssl = NULL;
}
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
+#include <openssl/pkcs12.h>
#else
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
SSLMethod method);
void ssl_done_socket (SockInfo *sockinfo);
+typedef struct _SSLClientCertHookData SSLClientCertHookData;
+struct _SSLClientCertHookData
+{
+ void *account;
+ const gchar *cert_path;
+ const gchar *password;
+ gboolean is_smtp;
+};
+
#endif /* USE_OPENSSL */
#endif /* __SSL_H__ */
#else
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
+#include <gnutls/pkcs12.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
g_warning("failed to write cert\n");
}
}
-static gnutls_x509_crt d2i_X509_fp(FILE *fp, int unused)
+
+size_t i2d_X509(gnutls_x509_crt x509_cert, unsigned char **output)
+{
+ size_t cert_size = 10*1024;
+ int r;
+
+ if (output == NULL)
+ return 0;
+
+ *output = malloc(cert_size);
+
+ if ((r = gnutls_x509_crt_export(x509_cert, GNUTLS_X509_FMT_DER, *output, &cert_size)) < 0) {
+ g_warning("couldn't export cert %s (%d)\n", gnutls_strerror(r), cert_size);
+ free(*output);
+ *output = NULL;
+ return 0;
+ }
+ return cert_size;
+}
+
+size_t i2d_PrivateKey(gnutls_x509_privkey pkey, unsigned char **output)
+{
+ size_t key_size = 10*1024;
+ int r;
+
+ if (output == NULL)
+ return 0;
+
+ *output = malloc(key_size);
+
+ if ((r = gnutls_x509_privkey_export(pkey, GNUTLS_X509_FMT_DER, *output, &key_size)) < 0) {
+ g_warning("couldn't export key %s (%d)\n", gnutls_strerror(r), key_size);
+ free(*output);
+ *output = NULL;
+ return 0;
+ }
+ return key_size;
+}
+
+static gnutls_x509_crt d2i_X509_fp(FILE *fp, int format)
{
gnutls_x509_crt cert = NULL;
gnutls_datum tmp;
tmp.size = s.st_size;
if (fread (tmp.data, 1, s.st_size, fp) < s.st_size) {
perror("fread");
+ free(tmp.data);
return NULL;
}
gnutls_x509_crt_init(&cert);
- if ((r = gnutls_x509_crt_import(cert, &tmp, GNUTLS_X509_FMT_DER)) < 0) {
- g_warning("import failed: %s\n", gnutls_strerror(r));
+ if ((r = gnutls_x509_crt_import(cert, &tmp, (format == 0)?GNUTLS_X509_FMT_DER:GNUTLS_X509_FMT_PEM)) < 0) {
+ debug_print("cert import failed: %s\n", gnutls_strerror(r));
gnutls_x509_crt_deinit(cert);
cert = NULL;
}
+ free(tmp.data);
debug_print("got cert! %p\n", cert);
return cert;
}
+
+static gnutls_x509_privkey d2i_key_fp(FILE *fp, int format)
+{
+ gnutls_x509_privkey key = NULL;
+ gnutls_datum tmp;
+ struct stat s;
+ int r;
+ if (fstat(fileno(fp), &s) < 0) {
+ perror("fstat");
+ return NULL;
+ }
+ tmp.data = malloc(s.st_size);
+ memset(tmp.data, 0, s.st_size);
+ tmp.size = s.st_size;
+ if (fread (tmp.data, 1, s.st_size, fp) < s.st_size) {
+ perror("fread");
+ free(tmp.data);
+ return NULL;
+ }
+
+ gnutls_x509_privkey_init(&key);
+ if ((r = gnutls_x509_privkey_import(key, &tmp, (format == 0)?GNUTLS_X509_FMT_DER:GNUTLS_X509_FMT_PEM)) < 0) {
+ debug_print("key import failed: %s\n", gnutls_strerror(r));
+ gnutls_x509_privkey_deinit(key);
+ key = NULL;
+ }
+ free(tmp.data);
+ debug_print("got key! %p\n", key);
+ return key;
+}
+
+static gnutls_pkcs12_t d2i_PKCS12_fp(FILE *fp, int format)
+{
+ gnutls_pkcs12_t p12 = NULL;
+ gnutls_datum tmp;
+ struct stat s;
+ int r;
+ if (fstat(fileno(fp), &s) < 0) {
+ perror("fstat");
+ return NULL;
+ }
+ tmp.data = malloc(s.st_size);
+ memset(tmp.data, 0, s.st_size);
+ tmp.size = s.st_size;
+ if (fread (tmp.data, 1, s.st_size, fp) < s.st_size) {
+ perror("fread");
+ free(tmp.data);
+ return NULL;
+ }
+
+ gnutls_pkcs12_init(&p12);
+
+ if ((r = gnutls_pkcs12_import(p12, &tmp, (format == 0)?GNUTLS_X509_FMT_DER:GNUTLS_X509_FMT_PEM,0)) < 0) {
+ g_warning("p12 import failed: %s\n", gnutls_strerror(r));
+ gnutls_pkcs12_deinit(p12);
+ p12 = NULL;
+ }
+ free(tmp.data);
+ debug_print("got p12! %p\n", p12);
+ return p12;
+}
+
#endif
static void ssl_certificate_save (SSLCertificate *cert)
return TRUE;
}
+#if USE_OPENSSL
+X509 *ssl_certificate_get_x509_from_pem_file(const gchar *file)
+{
+ X509 *x509 = NULL;
+ if (!file)
+ return NULL;
+ if (is_file_exist(file)) {
+ FILE *fp = g_fopen(file, "r");
+ if (fp) {
+ x509 = PEM_read_X509(fp, NULL, NULL, NULL);
+ fclose(fp);
+ return x509;
+ }
+ } else {
+ log_error(LOG_PROTOCOL, "Can not open certificate file %s\n", file);
+ }
+ return NULL;
+}
+
+static int ssl_pkey_password_cb(char *buf, int max_len, int flag, void *pwd)
+{
+ return 0;
+}
+
+EVP_PKEY *ssl_certificate_get_pkey_from_pem_file(const gchar *file)
+{
+ EVP_PKEY *pkey = NULL;
+ if (!file)
+ return NULL;
+ if (is_file_exist(file)) {
+ FILE *fp = g_fopen(file, "r");
+ if (fp) {
+ pkey = PEM_read_PrivateKey(fp, NULL, ssl_pkey_password_cb, NULL);
+ fclose(fp);
+ return pkey;
+ }
+ } else {
+ log_error(LOG_PROTOCOL, "Can not open private key file %s\n", file);
+ }
+ return NULL;
+}
+
+void ssl_certificate_get_x509_and_pkey_from_p12_file(const gchar *file, const gchar *password,
+ X509 **x509, EVP_PKEY **pkey)
+{
+ PKCS12 *p12 = NULL;
+ *x509 = NULL;
+ *pkey = NULL;
+
+ if (!file)
+ return;
+
+ if (is_file_exist(file)) {
+ FILE *fp = g_fopen(file, "r");
+ if (fp) {
+ p12 = d2i_PKCS12_fp(fp, NULL);
+ fclose(fp);
+ }
+ } else {
+ log_error(LOG_PROTOCOL, "Can not open certificate file %s\n", file);
+ }
+ if (p12 != NULL) {
+ if (PKCS12_parse(p12, password, pkey, x509, NULL) == 1) {
+ /* we got the correct password */
+ } else {
+ gchar *tmp = NULL;
+ hooks_invoke(SSL_CERT_GET_PASSWORD, &tmp);
+ if (PKCS12_parse(p12, tmp, pkey, x509, NULL) == 1) {
+ debug_print("got p12\n");
+ } else {
+ log_error(LOG_PROTOCOL, "%s\n", ERR_error_string(ERR_get_error(),NULL));
+ }
+ }
+ PKCS12_free(p12);
+ }
+}
+#endif
+
+#ifdef USE_GNUTLS
+gnutls_x509_crt ssl_certificate_get_x509_from_pem_file(const gchar *file)
+{
+ gnutls_x509_crt x509 = NULL;
+ if (!file)
+ return NULL;
+
+ if (is_file_exist(file)) {
+ FILE *fp = g_fopen(file, "r");
+ if (fp) {
+ x509 = d2i_X509_fp(fp, 1);
+ fclose(fp);
+ return x509;
+ }
+ } else {
+ log_error(LOG_PROTOCOL, "Can not open certificate file %s\n", file);
+ }
+ return NULL;
+}
+
+gnutls_x509_privkey ssl_certificate_get_pkey_from_pem_file(const gchar *file)
+{
+ gnutls_x509_privkey key = NULL;
+ if (!file)
+ return NULL;
+
+ if (is_file_exist(file)) {
+ FILE *fp = g_fopen(file, "r");
+ if (fp) {
+ key = d2i_key_fp(fp, 1);
+ fclose(fp);
+ return key;
+ }
+ } else {
+ log_error(LOG_PROTOCOL, "Can not open key file %s\n", file);
+ }
+ return NULL;
+}
+
+/* From GnuTLS lib/gnutls_x509.c */
+static int
+parse_pkcs12 (gnutls_pkcs12_t p12,
+ const char *password,
+ gnutls_x509_privkey * key,
+ gnutls_x509_crt_t * cert)
+{
+ gnutls_pkcs12_bag bag = NULL;
+ int index = 0;
+ int ret;
+
+ for (;;)
+ {
+ int elements_in_bag;
+ int i;
+
+ ret = gnutls_pkcs12_bag_init (&bag);
+ if (ret < 0)
+ {
+ bag = NULL;
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_get_bag (p12, index, bag);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ if (ret < 0)
+ {
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_type (bag, 0);
+ if (ret < 0)
+ {
+ goto done;
+ }
+
+ if (ret == GNUTLS_BAG_ENCRYPTED)
+ {
+ ret = gnutls_pkcs12_bag_decrypt (bag, password);
+ if (ret < 0)
+ {
+ goto done;
+ }
+ }
+
+ elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
+ if (elements_in_bag < 0)
+ {
+ goto done;
+ }
+
+ for (i = 0; i < elements_in_bag; i++)
+ {
+ int type;
+ gnutls_datum data;
+
+ type = gnutls_pkcs12_bag_get_type (bag, i);
+ if (type < 0)
+ {
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
+ if (ret < 0)
+ {
+ goto done;
+ }
+
+ switch (type)
+ {
+ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+ case GNUTLS_BAG_PKCS8_KEY:
+ ret = gnutls_x509_privkey_init (key);
+ if (ret < 0)
+ {
+ goto done;
+ }
+
+ ret = gnutls_x509_privkey_import_pkcs8
+ (*key, &data, GNUTLS_X509_FMT_DER, password,
+ type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
+ if (ret < 0)
+ {
+ goto done;
+ }
+ break;
+
+ case GNUTLS_BAG_CERTIFICATE:
+ ret = gnutls_x509_crt_init (cert);
+ if (ret < 0)
+ {
+ goto done;
+ }
+
+ ret =
+ gnutls_x509_crt_import (*cert, &data, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ {
+ goto done;
+ }
+ break;
+
+ case GNUTLS_BAG_ENCRYPTED:
+ /* XXX Bother to recurse one level down? Unlikely to
+ use the same password anyway. */
+ case GNUTLS_BAG_EMPTY:
+ default:
+ break;
+ }
+ }
+
+ index++;
+ gnutls_pkcs12_bag_deinit (bag);
+ }
+
+ ret = 0;
+
+done:
+ if (bag)
+ gnutls_pkcs12_bag_deinit (bag);
+
+ return ret;
+}
+void ssl_certificate_get_x509_and_pkey_from_p12_file(const gchar *file, const gchar *password,
+ gnutls_x509_crt *x509, gnutls_x509_privkey *pkey)
+{
+ gnutls_pkcs12_t p12 = NULL;
+
+ int r;
+
+ *x509 = NULL;
+ *pkey = NULL;
+ if (!file)
+ return;
+
+ if (is_file_exist(file)) {
+ FILE *fp = g_fopen(file, "r");
+ if (fp) {
+ p12 = d2i_PKCS12_fp(fp, 0);
+ fclose(fp);
+ }
+ } else {
+ log_error(LOG_PROTOCOL, "Can not open certificate file %s\n", file);
+ }
+ if (p12 != NULL) {
+ if ((r = parse_pkcs12(p12, password, pkey, x509)) == 0) {
+ debug_print("got p12\n");
+ } else {
+ log_error(LOG_PROTOCOL, "%s\n", gnutls_strerror(r));
+ }
+ gnutls_pkcs12_deinit(p12);
+ }
+}
+#endif
#endif /* USE_OPENSSL */
#include <glib.h>
#define SSLCERT_ASK_HOOKLIST "sslcert_ask"
+#define SSLCERT_GET_CLIENT_CERT_HOOKLIST "sslcert_get_client_cert"
+#define SSL_CERT_GET_PASSWORD "sslcert_get_password"
typedef struct _SSLCertificate SSLCertificate;
char *ssl_certificate_check_signer (gnutls_x509_crt cert, guint status);
#endif
+#if USE_OPENSSL
+X509 *ssl_certificate_get_x509_from_pem_file(const gchar *file);
+EVP_PKEY *ssl_certificate_get_pkey_from_pem_file(const gchar *file);
+void ssl_certificate_get_x509_and_pkey_from_p12_file(const gchar *file,
+ const gchar *password, X509 **x509, EVP_PKEY **pkey);
+#endif
+#ifdef USE_GNUTLS
+gnutls_x509_crt ssl_certificate_get_x509_from_pem_file(const gchar *file);
+gnutls_x509_privkey ssl_certificate_get_pkey_from_pem_file(const gchar *file);
+void ssl_certificate_get_x509_and_pkey_from_p12_file(const gchar *file,
+ const gchar *password, gnutls_x509_crt *crt, gnutls_x509_privkey *key);
+size_t i2d_X509(gnutls_x509_crt x509_cert, unsigned char **output);
+size_t i2d_PrivateKey(gnutls_x509_privkey pkey, unsigned char **output);
+#endif
#endif /* USE_OPENSSL */
#endif /* SSL_CERTIFICATE_H */
#include "etpan-thread-manager.h"
#include "utils.h"
#include "mainwindow.h"
+#include "ssl.h"
#include "ssl_certificate.h"
#include "socket.h"
#include "remotefolder.h"
struct connect_param {
mailimap * imap;
+ PrefsAccount *account;
const char * server;
int port;
};
return 0;
}
+static void connect_ssl_context_cb(struct mailstream_ssl_context * ssl_context, void * data)
+{
+#if (defined(USE_OPENSSL) || defined(USE_GNUTLS))
+ PrefsAccount *account = (PrefsAccount *)data;
+ const gchar *cert_path = NULL;
+ const gchar *password = NULL;
+#ifdef USE_OPENSSL
+ X509 *x509 = NULL;
+ EVP_PKEY *pkey = NULL;
+#else
+ gnutls_x509_crt x509 = NULL;
+ gnutls_x509_privkey pkey = NULL;
+#endif
+
+ if (account->in_ssl_client_cert_file && *account->in_ssl_client_cert_file)
+ cert_path = account->in_ssl_client_cert_file;
+ if (account->in_ssl_client_cert_pass && *account->in_ssl_client_cert_pass)
+ password = account->in_ssl_client_cert_pass;
+
+ if (mailstream_ssl_set_client_certificate_data(ssl_context, NULL, 0) < 0 ||
+ mailstream_ssl_set_client_private_key_data(ssl_context, NULL, 0) < 0)
+ debug_print("Impossible to set the client certificate.\n");
+ x509 = ssl_certificate_get_x509_from_pem_file(cert_path);
+ pkey = ssl_certificate_get_pkey_from_pem_file(cert_path);
+ if (!(x509 && pkey)) {
+ /* try pkcs12 format */
+ ssl_certificate_get_x509_and_pkey_from_p12_file(cert_path, password, &x509, &pkey);
+ }
+ if (x509 && pkey) {
+ unsigned char *x509_der = NULL, *pkey_der = NULL;
+ size_t x509_len, pkey_len;
+
+ x509_len = (size_t)i2d_X509(x509, &x509_der);
+ pkey_len = (size_t)i2d_PrivateKey(pkey, &pkey_der);
+ if (x509_len > 0 && pkey_len > 0) {
+ if (mailstream_ssl_set_client_certificate_data(ssl_context, x509_der, x509_len) < 0 ||
+ mailstream_ssl_set_client_private_key_data(ssl_context, pkey_der, pkey_len) < 0)
+ log_error(LOG_PROTOCOL, "Impossible to set the client certificate.\n");
+ g_free(x509_der);
+ g_free(pkey_der);
+ }
+#ifdef USE_GNUTLS
+ gnutls_x509_crt_deinit(x509);
+ gnutls_x509_privkey_deinit(pkey);
+#endif
+ }
+#endif
+}
+
static void connect_ssl_run(struct etpan_thread_op * op)
{
int r;
CHECK_IMAP();
- r = mailimap_ssl_connect(param->imap,
- param->server, param->port);
+ r = mailimap_ssl_connect_with_callback(param->imap,
+ param->server, param->port,
+ connect_ssl_context_cb, param->account);
result->error = r;
}
param.imap = imap;
param.server = server;
param.port = port;
-
+ param.account = folder->account;
+
refresh_resolvers();
threaded_run(folder, ¶m, &result, connect_ssl_run);
return;
}
- tls_low = mailstream_low_tls_open(fd);
+ tls_low = mailstream_low_tls_open_with_callback(fd, connect_ssl_context_cb, param->account);
if (tls_low == NULL) {
debug_print("imap starttls run - can't tls_open\n");
result->error = MAILIMAP_ERROR_STREAM;
param.imap = get_imap(folder);
param.server = host;
param.port = port;
-
+ param.account = folder->account;
+
threaded_run(folder, ¶m, &result, starttls_run);
debug_print("imap starttls - end\n");
struct connect_param {
newsnntp * nntp;
+ PrefsAccount *account;
const char * server;
int port;
};
return 0;
}
+static void connect_ssl_context_cb(struct mailstream_ssl_context * ssl_context, void * data)
+{
+#if (defined(USE_OPENSSL) || defined(USE_GNUTLS))
+ PrefsAccount *account = (PrefsAccount *)data;
+ const gchar *cert_path = NULL;
+ const gchar *password = NULL;
+#ifdef USE_OPENSSL
+ X509 *x509 = NULL;
+ EVP_PKEY *pkey = NULL;
+#else
+ gnutls_x509_crt x509 = NULL;
+ gnutls_x509_privkey pkey = NULL;
+#endif
+
+ if (account->in_ssl_client_cert_file && *account->in_ssl_client_cert_file)
+ cert_path = account->in_ssl_client_cert_file;
+ if (account->in_ssl_client_cert_pass && *account->in_ssl_client_cert_pass)
+ password = account->in_ssl_client_cert_pass;
+
+ if (mailstream_ssl_set_client_certificate_data(ssl_context, NULL, 0) < 0 ||
+ mailstream_ssl_set_client_private_key_data(ssl_context, NULL, 0) < 0)
+ debug_print("Impossible to set the client certificate.\n");
+ x509 = ssl_certificate_get_x509_from_pem_file(cert_path);
+ pkey = ssl_certificate_get_pkey_from_pem_file(cert_path);
+ if (!(x509 && pkey)) {
+ /* try pkcs12 format */
+ ssl_certificate_get_x509_and_pkey_from_p12_file(cert_path, password, &x509, &pkey);
+ }
+ if (x509 && pkey) {
+ unsigned char *x509_der = NULL, *pkey_der = NULL;
+ size_t x509_len, pkey_len;
+
+ x509_len = (size_t)i2d_X509(x509, &x509_der);
+ pkey_len = (size_t)i2d_PrivateKey(pkey, &pkey_der);
+ if (x509_len > 0 && pkey_len > 0) {
+ if (mailstream_ssl_set_client_certificate_data(ssl_context, x509_der, x509_len) < 0 ||
+ mailstream_ssl_set_client_private_key_data(ssl_context, pkey_der, pkey_len) < 0)
+ log_error(LOG_PROTOCOL, "Impossible to set the client certificate.\n");
+ g_free(x509_der);
+ g_free(pkey_der);
+ }
+#ifdef USE_GNUTLS
+ gnutls_x509_crt_deinit(x509);
+ gnutls_x509_privkey_deinit(pkey);
+#endif
+ }
+#endif
+}
+
static void connect_ssl_run(struct etpan_thread_op * op)
{
int r;
CHECK_NNTP();
- r = newsnntp_ssl_connect(param->nntp,
- param->server, param->port);
+ r = newsnntp_ssl_connect_with_callback(param->nntp,
+ param->server, param->port,
+ connect_ssl_context_cb, param->account);
result->error = r;
}
param.nntp = nntp;
param.server = server;
param.port = port;
-
+ param.account = folder->account;
+
refresh_resolvers();
threaded_run(folder, ¶m, &result, connect_ssl_run);
gchar *message;
gchar *pass;
- message = g_strdup_printf(_("Input password for %s on %s:"),
+ if (server && user)
+ message = g_strdup_printf(_("Input password for %s on %s:"),
user, server);
+ else if (server)
+ message = g_strdup_printf(_("Input password for %s:"),
+ server);
+ else if (user)
+ message = g_strdup_printf(_("Input password for %s:"),
+ user);
+ else
+ message = g_strdup_printf(_("Input password:"));
pass = input_dialog_with_invisible(_("Input password"), message, NULL);
g_free(message);
case MAILIMAP_ERROR_SASL:
log_warning(LOG_PROTOCOL, _("IMAP error: SASL error\n"));
break;
-#if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
case MAILIMAP_ERROR_SSL:
log_warning(LOG_PROTOCOL, _("IMAP error: SSL error\n"));
break;
-#endif
#endif
default:
log_warning(LOG_PROTOCOL, _("IMAP error: Unknown error [%d]\n"),
authenticated = FALSE;
}
else {
-#if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
if (r == MAILIMAP_ERROR_SSL)
log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
else
-#endif
#endif
imap_handle_error(NULL, r);
}
session = g_new0(IMAPSession, 1);
- session_init(SESSION(session));
+ session_init(SESSION(session), account, FALSE);
SESSION(session)->type = SESSION_IMAP;
SESSION(session)->server = g_strdup(account->recv_server);
SESSION(session)->port = port;
log_message(LOG_PROTOCOL, _("creating NNTP connection to %s:%d ...\n"), server, port);
session = g_new0(NewsSession, 1);
- session_init(SESSION(session));
+ session_init(SESSION(session), folder->account, FALSE);
SESSION(session)->type = SESSION_NEWS;
SESSION(session)->server = g_strdup(server);
SESSION(session)->sock = NULL;
session = g_new0(Pop3Session, 1);
- session_init(SESSION(session));
+ session_init(SESSION(session), account, FALSE);
SESSION(session)->type = SESSION_POP3;
#include "combobox.h"
#include "setup.h"
#include "quote_fmt.h"
+#include "hooks.h"
+#include "inputdialog.h"
+#include "ssl_certificate.h"
static gboolean cancelled;
static gboolean new_account;
static GtkWidget *signature_browse_button;
static GtkWidget *signature_edit_button;
+#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
+static GtkWidget *entry_in_cert_file;
+static GtkWidget *entry_out_cert_file;
+static GtkWidget *in_ssl_cert_browse_button;
+static GtkWidget *out_ssl_cert_browse_button;
+#endif
+
static GSList *prefs_pages = NULL;
typedef struct BasicPage
GtkWidget *smtp_ssltunnel_radiobtn;
GtkWidget *smtp_starttls_radiobtn;
+ GtkWidget *entry_in_cert_file;
+ GtkWidget *entry_in_cert_pass;
+ GtkWidget *entry_out_cert_file;
+ GtkWidget *entry_out_cert_pass;
+
GtkWidget *use_nonblocking_ssl_checkbtn;
} SSLPage;
{"use_nonblocking_ssl", "1", &tmp_ac_prefs.use_nonblocking_ssl, P_BOOL,
&ssl_page.use_nonblocking_ssl_checkbtn,
prefs_set_data_from_toggle, prefs_set_toggle},
+
+ {"in_ssl_client_cert_file", "", &tmp_ac_prefs.in_ssl_client_cert_file, P_STRING,
+ &ssl_page.entry_in_cert_file, prefs_set_data_from_entry, prefs_set_entry},
+
+ {"in_ssl_client_cert_pass", "", &tmp_ac_prefs.in_ssl_client_cert_pass, P_PASSWORD,
+ &ssl_page.entry_in_cert_pass, prefs_set_data_from_entry, prefs_set_entry},
+
+ {"out_ssl_client_cert_file", "", &tmp_ac_prefs.out_ssl_client_cert_file, P_STRING,
+ &ssl_page.entry_out_cert_file, prefs_set_data_from_entry, prefs_set_entry},
+
+ {"out_ssl_client_cert_pass", "", &tmp_ac_prefs.out_ssl_client_cert_pass, P_PASSWORD,
+ &ssl_page.entry_out_cert_pass, prefs_set_data_from_entry, prefs_set_entry},
#else
{"ssl_pop", "0", &tmp_ac_prefs.ssl_pop, P_ENUM,
NULL, NULL, NULL},
{"ssl_smtp", "0", &tmp_ac_prefs.ssl_smtp, P_ENUM,
NULL, NULL, NULL},
+ {"in_ssl_client_cert_file", "", &tmp_ac_prefs.in_ssl_client_cert_file, P_STRING,
+ NULL, NULL, NULL},
+
+ {"in_ssl_client_cert_pass", "", &tmp_ac_prefs.in_ssl_client_cert_pass, P_PASSWORD,
+ NULL, NULL, NULL},
+
+ {"out_ssl_client_cert_file", "", &tmp_ac_prefs.out_ssl_client_cert_file, P_STRING,
+ NULL, NULL, NULL},
+
+ {"out_ssl_client_cert_pass", "", &tmp_ac_prefs.out_ssl_client_cert_pass, P_PASSWORD,
+ NULL, NULL, NULL},
+
{"use_nonblocking_ssl", "1", &tmp_ac_prefs.use_nonblocking_ssl, P_BOOL,
NULL, NULL, NULL},
#endif /* USE_OPENSSL */
static void prefs_account_signature_browse_cb (GtkWidget *widget,
gpointer data);
+static void prefs_account_in_cert_browse_cb (GtkWidget *widget,
+ gpointer data);
+
+static void prefs_account_out_cert_browse_cb (GtkWidget *widget,
+ gpointer data);
+
static void prefs_account_signature_edit_cb (GtkWidget *widget,
gpointer data);
GtkWidget *smtp_ssltunnel_radiobtn;
GtkWidget *smtp_starttls_radiobtn;
+ GtkWidget *cert_frame;
GtkWidget *vbox6;
+ GtkWidget *cert_table;
+ GtkWidget *entry_in_cert_pass;
+ GtkWidget *entry_out_cert_pass;
+
+ GtkWidget *vbox7;
GtkWidget *use_nonblocking_ssl_checkbtn;
GtkWidget *hbox;
GtkWidget *hbox_spc;
GtkWidget *label;
+ GtkTooltips *tips = gtk_tooltips_new();
vbox1 = gtk_vbox_new (FALSE, VSPACING);
gtk_widget_show (vbox1);
pop_starttls_radiobtn,
_("Use STARTTLS command to start SSL session"),
SSL_STARTTLS);
-
+
vbox3 = gtkut_get_options_frame(vbox1, &imap_frame, _("IMAP4"));
CREATE_RADIO_BUTTONS(vbox3,
_("Use STARTTLS command to start SSL session"),
SSL_STARTTLS);
- vbox6 = gtk_vbox_new (FALSE, 0);
- gtk_widget_show (vbox6);
- gtk_box_pack_start (GTK_BOX (vbox1), vbox6, FALSE, FALSE, 0);
+ vbox6 = gtkut_get_options_frame(vbox1, &cert_frame, _("Client certificates"));
+ cert_table = gtk_table_new(4,3, FALSE);
+
+ label = gtk_label_new("Reception certificate");
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+ entry_in_cert_file = gtk_entry_new();
+ in_ssl_cert_browse_button = gtkut_get_browse_file_btn(_("Browse"));
+ gtk_tooltips_set_tip(GTK_TOOLTIPS(tips), label, _("Client certificate file as a PKCS12 or PEM file."), NULL);
+ gtk_tooltips_set_tip(GTK_TOOLTIPS(tips), entry_in_cert_file, _("Client certificate file as a PKCS12 or PEM file."), NULL);
+ gtk_table_attach (GTK_TABLE (cert_table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+ gtk_table_attach (GTK_TABLE (cert_table), entry_in_cert_file, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
+ gtk_table_attach (GTK_TABLE (cert_table), in_ssl_cert_browse_button, 2, 3, 0, 1, GTK_FILL, 0, 0, 0);
+
+ label = gtk_label_new("Certificate password");
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+ entry_in_cert_pass = gtk_entry_new();
+ gtk_entry_set_visibility(GTK_ENTRY(entry_in_cert_pass), FALSE);
+ gtk_table_attach (GTK_TABLE (cert_table), label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_table_attach (GTK_TABLE (cert_table), entry_in_cert_pass, 1, 2, 1, 2, GTK_FILL, 0, 0, 0);
+
+ label = gtk_label_new("Send certificate");
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+ entry_out_cert_file = gtk_entry_new();
+ out_ssl_cert_browse_button = gtkut_get_browse_file_btn(_("Browse"));
+ gtk_tooltips_set_tip(GTK_TOOLTIPS(tips), label, _("Client certificate file as a PKCS12 or PEM file."), NULL);
+ gtk_tooltips_set_tip(GTK_TOOLTIPS(tips), entry_out_cert_file, _("Client certificate file as a PKCS12 or PEM file."), NULL);
+ gtk_table_attach (GTK_TABLE (cert_table), label, 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
+ gtk_table_attach (GTK_TABLE (cert_table), entry_out_cert_file, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
+ gtk_table_attach (GTK_TABLE (cert_table), out_ssl_cert_browse_button, 2, 3, 2, 3, GTK_FILL, 0, 0, 0);
+
+ label = gtk_label_new("Certificate password");
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+ entry_out_cert_pass = gtk_entry_new();
+ gtk_entry_set_visibility(GTK_ENTRY(entry_out_cert_pass), FALSE);
+ gtk_table_attach (GTK_TABLE (cert_table), label, 0, 1, 3, 4, GTK_FILL, 0, 0, 0);
+ gtk_table_attach (GTK_TABLE (cert_table), entry_out_cert_pass, 1, 2, 3, 4, GTK_FILL, 0, 0, 0);
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_widget_show (hbox);
+ gtk_box_pack_start (GTK_BOX (vbox6), hbox, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), cert_table, TRUE, TRUE, 0);
+ gtk_widget_show_all(vbox6);
+
+ g_signal_connect(G_OBJECT(in_ssl_cert_browse_button), "clicked",
+ G_CALLBACK(prefs_account_in_cert_browse_cb), NULL);
+ g_signal_connect(G_OBJECT(out_ssl_cert_browse_button), "clicked",
+ G_CALLBACK(prefs_account_out_cert_browse_cb), NULL);
+
+ vbox7 = gtk_vbox_new (FALSE, 0);
+ gtk_widget_show (vbox7);
+ gtk_box_pack_start (GTK_BOX (vbox1), vbox7, FALSE, FALSE, 0);
- PACK_CHECK_BUTTON(vbox6, use_nonblocking_ssl_checkbtn,
+ PACK_CHECK_BUTTON(vbox7, use_nonblocking_ssl_checkbtn,
_("Use non-blocking SSL"));
hbox = gtk_hbox_new (FALSE, 0);
gtk_widget_show (hbox);
- gtk_box_pack_start (GTK_BOX (vbox6), hbox, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox7), hbox, FALSE, FALSE, 0);
hbox_spc = gtk_hbox_new (FALSE, 0);
gtk_widget_show (hbox_spc);
page->smtp_ssltunnel_radiobtn = smtp_ssltunnel_radiobtn;
page->smtp_starttls_radiobtn = smtp_starttls_radiobtn;
+ page->entry_in_cert_file = entry_in_cert_file;
+ page->entry_in_cert_pass = entry_in_cert_pass;
+ page->entry_out_cert_file = entry_out_cert_file;
+ page->entry_out_cert_pass = entry_out_cert_pass;
+
page->use_nonblocking_ssl_checkbtn = use_nonblocking_ssl_checkbtn;
tmp_ac_prefs = *ac_prefs;
}
#endif
+static gboolean sslcert_get_client_cert_hook(gpointer source, gpointer data)
+{
+ SSLClientCertHookData *hookdata = (SSLClientCertHookData *)source;
+ PrefsAccount *account = (PrefsAccount *)hookdata->account;
+
+ hookdata->cert_path = NULL;
+ hookdata->password = NULL;
+
+ if (!g_list_find(account_get_list(), account)) {
+ g_warning("can't find sock account\n");
+ return TRUE;
+ }
+
+ if (hookdata->is_smtp) {
+ if (account->out_ssl_client_cert_file && *account->out_ssl_client_cert_file)
+ hookdata->cert_path = account->out_ssl_client_cert_file;
+ if (account->out_ssl_client_cert_pass && *account->out_ssl_client_cert_pass)
+ hookdata->password = account->out_ssl_client_cert_pass;
+ } else {
+ if (account->in_ssl_client_cert_file && *account->in_ssl_client_cert_file)
+ hookdata->cert_path = account->in_ssl_client_cert_file;
+ if (account->in_ssl_client_cert_pass && *account->in_ssl_client_cert_pass)
+ hookdata->password = account->in_ssl_client_cert_pass;
+ }
+ return TRUE;
+}
+
+struct GetPassData {
+ GCond *cond;
+ GMutex* mutex;
+ gchar **pass;
+};
+
+
+static gboolean do_get_pass(gpointer data)
+{
+ struct GetPassData *pass_data = (struct GetPassData *)data;
+ g_mutex_lock(pass_data->mutex);
+ *(pass_data->pass) = input_dialog_query_password("the PKCS12 client certificate", NULL);
+ g_cond_signal(pass_data->cond);
+ g_mutex_unlock(pass_data->mutex);
+ return FALSE;
+}
+static gboolean sslcert_get_password(gpointer source, gpointer data)
+{
+ struct GetPassData pass_data;
+ /* do complicated stuff to be able to call GTK from the mainloop */
+ pass_data.cond = g_cond_new();
+ pass_data.mutex = g_mutex_new();
+ pass_data.pass = (gchar **)source;
+
+ g_mutex_lock(pass_data.mutex);
+
+ g_idle_add(do_get_pass, &pass_data);
+
+ g_cond_wait(pass_data.cond, pass_data.mutex);
+ g_cond_free(pass_data.cond);
+ g_mutex_unlock(pass_data.mutex);
+ g_mutex_free(pass_data.mutex);
+
+ return TRUE;
+}
+
static void register_advanced_page(void)
{
static gchar *path[3];
register_privacy_page();
#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
register_ssl_page();
+ hooks_register_hook(SSLCERT_GET_CLIENT_CERT_HOOKLIST, sslcert_get_client_cert_hook, NULL);
+ hooks_register_hook(SSL_CERT_GET_PASSWORD, sslcert_get_password, NULL);
#endif
register_advanced_page();
}
g_free(utf8_filename);
}
+#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
+static void prefs_account_in_cert_browse_cb(GtkWidget *widget, gpointer data)
+{
+ gchar *filename;
+ gchar *utf8_filename;
+
+ filename = filesel_select_file_open(_("Select certificate file"), NULL);
+ if (!filename) return;
+
+ utf8_filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
+ if (!utf8_filename) {
+ g_warning("prefs_account_cert_browse_cb(): failed to convert character set.");
+ utf8_filename = g_strdup(filename);
+ }
+ gtk_entry_set_text(GTK_ENTRY(entry_in_cert_file), utf8_filename);
+ g_free(utf8_filename);
+}
+
+static void prefs_account_out_cert_browse_cb(GtkWidget *widget, gpointer data)
+{
+ gchar *filename;
+ gchar *utf8_filename;
+
+ filename = filesel_select_file_open(_("Select certificate file"), NULL);
+ if (!filename) return;
+
+ utf8_filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
+ if (!utf8_filename) {
+ g_warning("prefs_account_cert_browse_cb(): failed to convert character set.");
+ utf8_filename = g_strdup(filename);
+ }
+ gtk_entry_set_text(GTK_ENTRY(entry_out_cert_file), utf8_filename);
+ g_free(utf8_filename);
+}
+#endif
+
static void prefs_account_signature_edit_cb(GtkWidget *widget, gpointer data)
{
const gchar *sigpath = gtk_entry_get_text(GTK_ENTRY(data));
SSLType ssl_imap;
SSLType ssl_nntp;
SSLType ssl_smtp;
+
+ gchar *out_ssl_client_cert_file;
+ gchar *out_ssl_client_cert_pass;
+ gchar *in_ssl_client_cert_file;
+ gchar *in_ssl_client_cert_pass;
+
gboolean use_nonblocking_ssl;
/* Temporarily preserved password */
/* Quote */
{"reply_quote_mark", "> ", &prefs_common.quotemark, P_STRING,
NULL, NULL, NULL},
- {"reply_quote_format", N_("On %d\\n%f wrote:\\n\\n%q"),
+ {"reply_quote_format", N_("On %d\\n%f wrote:\\n\\n%q\\n%X"),
&prefs_common.quotefmt, P_STRING, NULL, NULL, NULL},
{"forward_quote_mark", "> ", &prefs_common.fw_quotemark, P_STRING,
if (!ac_prefs->session) {
/* we can't reuse a previously initialised session */
- session = smtp_session_new();
+ session = smtp_session_new(ac_prefs);
smtp_session = SMTP_SESSION(session);
if (ac_prefs->set_domain && ac_prefs->domain && strlen(ac_prefs->domain)) {