[balsa] Re-factor the pop3 mailbox code



commit ce5b1d04ffda3e9cd23a4abc2dd0f4b12d7b334d
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date:   Fri Apr 7 19:51:59 2017 -0400

    Re-factor the pop3 mailbox code
    
    This part of the patch includes the re-factoring of the pop3 mailbox code
    and shifting some common stuff from the smtp server to the server base class.
    All files are relative to libbalsa.
    
    * libbalsa/imap/Makefile.am, libbalsa/imap/pop3.c, libbalsa/imap/pop3.h: remove old networking 
implementation for pop3
    * libbalsa/mailbox_pop3.c: implement using NetClientPop, some refactoring (see main message for more 
details)
    * libbalsa/mailbox_pop3.h: use gboolean for bool values
    * libbalsa/send.c: move NetClient callback functions to the server class
    * libbalsa/server.h: add client cert config to the server class
    * libbalsa/server.c: overwrite passwords before freeing the memory; load/store the new common config 
items; add NetClient callback functions (from send.c)
    * libbalsa/smtp-server.c: remove client cert config and loading/storing it (shifted to server class); use 
the values from the server class; remove obsolete methods
    * libbalsa/smtp-server.h: remove obsolete methods

 libbalsa/imap/Makefile.am |    2 -
 libbalsa/imap/pop3.c      |  865 --------------------------------------------
 libbalsa/imap/pop3.h      |  109 ------
 libbalsa/mailbox_pop3.c   |  885 ++++++++++++++++++++++++---------------------
 libbalsa/mailbox_pop3.h   |    7 +-
 libbalsa/send.c           |   98 +-----
 libbalsa/server.c         |  121 ++++++
 libbalsa/server.h         |   14 +
 libbalsa/smtp-server.c    |   70 +---
 libbalsa/smtp-server.h    |    7 -
 10 files changed, 631 insertions(+), 1547 deletions(-)
---
diff --git a/libbalsa/imap/Makefile.am b/libbalsa/imap/Makefile.am
index 23a3265..875aaf1 100644
--- a/libbalsa/imap/Makefile.am
+++ b/libbalsa/imap/Makefile.am
@@ -32,8 +32,6 @@ libimap_a_SOURCES = \
        libimap-marshal.c       \
        libimap-marshal.h       \
        libimap.h       \
-       pop3.c          \
-       pop3.h          \
        siobuf.c        \
        siobuf.h        \
        util.c          \
diff --git a/libbalsa/mailbox_pop3.c b/libbalsa/mailbox_pop3.c
index ede80c7..8a4c099 100644
--- a/libbalsa/mailbox_pop3.c
+++ b/libbalsa/mailbox_pop3.c
@@ -23,26 +23,18 @@
 #endif                          /* HAVE_CONFIG_H */
 
 
-#include <stdio.h>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
 #include <glib.h>
 #include <string.h>
 
 #include "libbalsa.h"
 #include "libbalsa-conf.h"
-#include "pop3.h"
+#include "net-client-pop.h"
 #include "server.h"
 #include "misc.h"
 #include "mailbox.h"
 #include "mailbox_pop3.h"
 #include <glib/gi18n.h>
-
-int PopDebug = 0;
+#include <glib/gstdio.h>
 
 enum {
     LAST_SIGNAL
@@ -69,6 +61,10 @@ static void libbalsa_mailbox_pop3_save_config(LibBalsaMailbox * mailbox,
 static void libbalsa_mailbox_pop3_load_config(LibBalsaMailbox * mailbox,
                                              const gchar * prefix);
 
+
+#define MBOX_POP3_ERROR        (g_quark_from_static_string("mailbox-pop3"))
+
+
 GType
 libbalsa_mailbox_pop3_get_type(void)
 {
@@ -168,107 +164,107 @@ libbalsa_mailbox_pop3_open(LibBalsaMailbox * mailbox, GError **err)
     return TRUE;
 }
 
-static gboolean
-move_from_msgdrop(const gchar *tmp_path, LibBalsaMailbox *dest,
-                  gchar *pop_name, unsigned msgno)
-{
-    gint fd;
-    GMimeStream *stream;
-    gboolean success;
-    GError *err = NULL;
-    struct stat buf;
-
-    if(stat(tmp_path, &buf) != 0)
-        return FALSE;
-    if(buf.st_size == 0) /* filtered out? Nothing to move in any case */
-        return TRUE;
-    fd = open(tmp_path, O_RDWR);
-    stream = g_mime_stream_fs_new(fd);
-    success =
-        libbalsa_mailbox_add_message(dest, stream,
-                                     LIBBALSA_MESSAGE_FLAG_NEW |
-                                     LIBBALSA_MESSAGE_FLAG_RECENT, &err)
-        && ftruncate(fd, 0) == 0;
-    g_object_unref(stream);
-
-    if(!success)
-        libbalsa_information(LIBBALSA_INFORMATION_WARNING,
-                             _("Error appending message %d from %s to %s: %s"),
-                             msgno, pop_name, dest->name,
-                             err ? err->message : "?");
-    g_clear_error(&err);
-
-    return success;
-}
+
+#define POP_UID_FILE   "/.balsa/pop-uids"
+static GMutex uid_mutex;
 
 
-static GHashTable*
-mp_load_uids(void)
+static GHashTable *
+mp_load_uids(const gchar *prefix)
 {
-    GHashTable *res = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                            g_free, NULL);
-    gchar *fname = g_strconcat(g_get_home_dir(), "/.balsa/pop-uids", NULL);
-    FILE *f = fopen(fname, "r");
-    g_free(fname);
-    if(f) {
-        struct flock lck;
-        char line[1024]; /* arbitrary limit of uid len */
-        memset (&lck, 0, sizeof(struct flock));
-        lck.l_type = F_RDLCK; lck.l_whence = SEEK_SET;
-        fcntl(fileno(f), F_SETLK, &lck);
-        while(fgets(line, sizeof(line), f)) {
-            int len = strlen(line);
-            if(len>0&& line[len-1] == '\n') line[len-1] = '\0';
-            g_hash_table_insert(res, g_strdup(line), GINT_TO_POINTER(1));
-        }
-        lck.l_type = F_UNLCK;
-        fcntl(fileno(f), F_SETLK, &lck);
-        fclose(f);
-    }
-    return res;
+       GHashTable *res;
+       gboolean read_res;
+       gchar *fname;
+       gchar *contents;
+
+       res = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+       fname = g_strconcat(g_get_home_dir(), POP_UID_FILE, NULL);
+       g_mutex_lock(&uid_mutex);
+       read_res = g_file_get_contents(fname, &contents, NULL, NULL);
+       g_mutex_unlock(&uid_mutex);
+       g_free(fname);
+
+       if (read_res) {
+               gchar **lines;
+               size_t prefix_len;
+               guint n;
+
+               lines = g_strsplit(contents, "\n", -1);
+               g_free(contents);
+               prefix_len = strlen(prefix);
+               for (n = 0; lines[n] != NULL; n++) {
+                       if (strncmp(lines[n], prefix, prefix_len) == 0) {
+                               g_hash_table_insert(res, g_strdup(lines[n]), GINT_TO_POINTER(1));
+                       }
+               }
+
+               g_strfreev(lines);
+       }
+
+       return res;
 }
 
-struct save_uid_data {
-    FILE *file;
-    const char *exclude_prefix;
-};
 
 static void
-mp_save_uid(gpointer key, gpointer value, gpointer user_data)
+mp_save_uid(gpointer key, gpointer G_GNUC_UNUSED value, gpointer user_data)
 {
-    struct save_uid_data *d = (struct save_uid_data*)user_data;
-    if(d->exclude_prefix &&
-       strncmp(key, d->exclude_prefix, strlen(d->exclude_prefix)) == 0)
-        return;
-    fprintf(d->file, "%s\n", (char*)key);
+       FILE *out = (FILE *) user_data;
+
+       fputs((const gchar *) key, out);
+       fputc('\n', out);
 }
 
-static void
-mp_save_uids(GHashTable *old_uids, GHashTable *new_uids, const char *prefix)
+
+static gboolean
+mp_save_uids(GHashTable *uids, const gchar *prefix, GError **error)
 {
-    gchar *fname = g_strconcat(g_get_home_dir(), "/.balsa/pop-uids", NULL);
-    FILE *f;
-
-    libbalsa_assure_balsa_dir();
-    f = fopen(fname, "w");
-    g_free(fname);
-    if(f) {
-        struct save_uid_data data;
-        struct flock lck;
-
-        memset (&lck, 0, sizeof (struct flock));
-        lck.l_type = F_WRLCK; lck.l_whence = SEEK_SET;
-        data.file = f; data.exclude_prefix = prefix;
-        g_hash_table_foreach(old_uids, mp_save_uid, &data);
-        data.exclude_prefix = NULL;
-        g_hash_table_foreach(new_uids, mp_save_uid, &data);
-        lck.l_type = F_UNLCK;
-        fcntl(fileno(f), F_SETLK, &lck);
-        fclose(f);
-    } /* else COULD NOT SAVE UIDS! SHOUT! */
-    return;
+       gchar *fname;
+       gboolean read_res;
+       gchar *contents = NULL;
+       FILE *out;
+       gboolean result;
+
+       fname = g_strconcat(g_get_home_dir(), POP_UID_FILE, NULL);
+
+       g_mutex_lock(&uid_mutex);
+       read_res = g_file_get_contents(fname, &contents, NULL, NULL);
+       out = fopen(fname, "w");
+       g_free(fname);
+       if (out != NULL) {
+               if (read_res) {
+                       gchar **lines;
+                       size_t prefix_len;
+                       guint n;
+
+                       lines = g_strsplit(contents, "\n", -1);
+                       g_free(contents);
+                       prefix_len = strlen(prefix);
+                       for (n = 0; lines[n] != NULL; n++) {
+                               if ((lines[n][0] != '\0') && (strncmp(lines[n], prefix, prefix_len) != 0)) {
+                                       fputs(lines[n], out);
+                                       fputc('\n', out);
+                               }
+                       }
+
+                       g_strfreev(lines);
+               }
+
+               if (uids != NULL) {
+                       g_hash_table_foreach(uids, mp_save_uid, out);
+               }
+               fclose(out);
+               result = TRUE;
+       } else {
+               g_set_error(error, MBOX_POP3_ERROR, errno, _("Saving the POP3 message UID list failed: %s"), 
g_strerror(errno));
+               result = FALSE;
+       }
+
+       g_mutex_unlock(&uid_mutex);
+       return result;
 }
 
+
 #ifdef POP_SYNC
 static int
 dump_cb(unsigned len, char *buf, void *arg)
@@ -277,348 +273,421 @@ dump_cb(unsigned len, char *buf, void *arg)
     return fwrite(buf, 1, len, (FILE*)arg) == len;
 }
 #endif /* POP_SYNC */
-/* ===================================================================
-   Functions for direct or filtered saving to the output mailbox.
-*/
-static FILE*
-pop_direct_open(const char *path)
+
+
+typedef struct {
+       gboolean filter;
+       GMimeStream *mbx_stream;        /* used if we store directly to a mailbox only */
+       FILE *filter_pipe;                      /* used of we write to a filter pipe only */
+       gchar *path;                            /* needed for error reporting only */
+} pop_handler_t;
+
+
+static pop_handler_t *
+pop_handler_new(const gchar *filter_path,
+                               GError     **error)
 {
-    return fopen(path, "w");
+       pop_handler_t *res;
+
+       res = g_new0(pop_handler_t, 1U);
+       if (filter_path != NULL) {
+               res->filter = TRUE;
+               res->filter_pipe = popen(filter_path, "w");
+               if (res->filter_pipe == NULL) {
+                       g_set_error(error, MBOX_POP3_ERROR, errno, _("Passing POP message to %s failed: %s"), 
filter_path, g_strerror(errno));
+                       g_free(res);
+                       res = NULL;
+               } else {
+                       res->path = g_strdup(filter_path);
+               }
+       } else {
+               res->mbx_stream = g_mime_stream_mem_new();
+       }
+
+       return res;
 }
 
-static FILE*
-pop_filter_open(const char *command)
+
+static gboolean
+pop_handler_write(pop_handler_t *handler,
+                                 const gchar   *buffer,
+                                 gsize          count,
+                                 GError       **error)
 {
-    return popen(command, "w");
+       gboolean result = TRUE;
+
+       if (handler->filter) {
+               if (fwrite(buffer, 1U, count, handler->filter_pipe) != count) {
+                       g_set_error(error, MBOX_POP3_ERROR, errno, _("Passing POP message to %s failed: %s"), 
handler->path, g_strerror(errno));
+                       result = FALSE;
+               }
+       } else {
+               if (g_mime_stream_write(handler->mbx_stream, buffer, count) != (ssize_t) count) {
+                       g_set_error(error, MBOX_POP3_ERROR, -1, _("Saving POP message failed"));
+                       result = FALSE;
+               }
+       }
+       return result;
+}
+
+
+static gboolean
+pop_handler_close(pop_handler_t *handler,
+                                 GError       **error)
+{
+       gboolean result = TRUE;
+
+       if (handler->filter) {
+               int res;
+
+               res = pclose(handler->filter_pipe);
+               if (res != 0) {
+                       g_set_error(error, MBOX_POP3_ERROR, errno, _("Transferring POP message to %s failed: 
%s"), handler->path,
+                               g_strerror(errno));
+                       result = FALSE;
+               }
+       } else {
+               g_object_unref(G_OBJECT(handler->mbx_stream));
+       }
+       g_free(handler->path);
+       g_free(handler);
+       return result;
 }
 
-struct PopDownloadMode {
-    FILE* (*open)(const char *path);
-    int (*close)(FILE *arg);
-} pop_direct = {
-    pop_direct_open,
-    fclose
-}, pop_filter = {
-    pop_filter_open,
-    pclose
-};
 
 /* ===================================================================
    Functions supporting asynchronous retrival of messages.
 */
 struct fetch_data {
     LibBalsaMailbox *mailbox;
-    PopHandle *pop;
-    GError   **err;
-    const gchar     *tmp_path;
-    const gchar     *dest_path;
-    const struct PopDownloadMode *dm;
-    unsigned   msgno, total_messages;
-    unsigned long *current_pos;
-    unsigned long total_size;
-    FILE      *f;
-    unsigned   error_occured:1;
-    unsigned   delete_on_server:1;
+    const gchar *filter_path;                          /* filter path, NULL for storing the message without 
filtering */
+    gsize total_messages;
+    gsize total_size;
+    gchar *total_size_msg;
+    gsize msgno;
+    gsize received;
+    pop_handler_t *handler;
+    gint64 next_notify;
 };
 
-/* GError arguments - if any - are for one way communication from
-   libimap to the callback.
-   All errors are communicated to UI through fetch_data::err.
- */
 static void
-fetch_async_cb(PopReqCode prc, void *arg, ...)
+notify_progress(const struct fetch_data *fd)
 {
-    static time_t last_update = 0, current_time;
-    static unsigned batch_cnt = 0;
-    struct fetch_data *fd = (struct fetch_data*)arg;
-    va_list alist;
-    char threadbuf[160];
-    
-    va_start(alist, arg);
-    switch(prc) {
-    case POP_REQ_OK: 
-       {/* GError **err = va_arg(alist, GError**); */
-        fd->f  = fd->dm->open(fd->dest_path);
-        if(fd->f == NULL) {
-            if(!*fd->err) {
-                g_set_error(fd->err, IMAP_ERROR, IMAP_POP_SAVE_ERROR,
-                            _("Saving POP message to %s failed"),
-                            fd->dest_path);
-            }
-            fd->error_occured = 1;
-            printf("fopen failed for %s %p\n", fd->dest_path, fd->err);
-        } else {
-            snprintf(threadbuf, sizeof(threadbuf),
-                     _("Retrieving Message %d of %d"),
-                    fd->msgno, fd->total_messages);
-            libbalsa_mailbox_progress_notify(LIBBALSA_MAILBOX(fd->mailbox),
-                                             LIBBALSA_NTFY_MSGINFO, 
-                                             fd->msgno, fd->total_messages,
-                                             threadbuf);
-        }
-        }
-       break;
-    case POP_REQ_ERR:
-       {GError **err = va_arg(alist, GError**);
-       if(!*fd->err) {
-           *fd->err  = *err;
-           *err = NULL;
-       }
-       }
-       break;
-    case POP_REQ_DATA:
-       {unsigned len = va_arg(alist, unsigned);
-       char    *buf = va_arg(alist, char*);
-        if( (batch_cnt++ & 0x04) &&
-            last_update != (current_time = time(NULL))) {
-            snprintf(threadbuf, sizeof(threadbuf),
-                     _("Received %ld kB of %ld"),
-                     (long)(*fd->current_pos/1024),
-                     (long)fd->total_size/1024);
-            libbalsa_mailbox_progress_notify(LIBBALSA_MAILBOX(fd->mailbox), 
-                                             LIBBALSA_NTFY_PROGRESS, 
-                                             *fd->current_pos, fd->total_size,
-                                             threadbuf);
-            last_update = current_time;
-        }
-        *fd->current_pos += len;
-       if(fd->f)
-           if(fwrite(buf, 1, len, fd->f) != len) {
-                if(!*fd->err)
-                    g_set_error(fd->err, IMAP_ERROR, IMAP_POP_SAVE_ERROR,
-                                _("Saving POP message to %s failed."),
-                                fd->dest_path);
-                fd->error_occured = 1;
-            }
+       gchar *recvbuf;
+       gchar *msgbuf;
+
+       recvbuf = libbalsa_size_to_gchar(fd->received);
+       msgbuf = g_strdup_printf(_("Message %lu of %lu (%s of %s)"), fd->msgno, fd->total_messages, recvbuf, 
fd->total_size_msg);
+       g_free(recvbuf);
+       libbalsa_mailbox_progress_notify(LIBBALSA_MAILBOX(fd->mailbox), LIBBALSA_NTFY_PROGRESS, fd->received, 
fd->total_size, msgbuf);
+       g_free(msgbuf);
+}
+
+static gboolean
+message_cb(const gchar                    *buffer,
+                  gssize                          count,
+                  gsize                           lines,
+                  const NetClientPopMessageInfo  *info,
+                  gpointer                        user_data,
+                  GError                        **error)
+{
+       struct fetch_data *fd = (struct fetch_data *) user_data;
+       gboolean result = TRUE;
+
+       if (count > 0) {
+               /* message data chunk - initialise for a new message if the output does not exist */
+               if (fd->handler == NULL) {
+                       fd->handler = pop_handler_new(fd->filter_path, error);
+
+                       if (fd->handler == NULL) {
+                               result = FALSE;
+                       } else {
+                               fd->msgno++;
+                               fd->next_notify = g_get_monotonic_time();               /* force immediate 
update of the progress dialogue */
+                       }
+               }
+
+               /* add data if we were successful so far */
+               if (result) {
+                       result = pop_handler_write(fd->handler, buffer, count, error);
+                       if (result) {
+                               gint64 current_time;
+
+                               current_time = g_get_monotonic_time();
+                               /* as the cr is removed from line endings, we have to compensate for it - see 
RFC 1939, sect. 11 */
+                               fd->received += (gsize) count + lines;
+                               if (current_time >= fd->next_notify) {
+                                       notify_progress(fd);
+                                       fd->next_notify = current_time + 500000;
+                               }
+                       }
+               }
+       } else if (count == 0) {
+               gboolean close_res;
+
+               notify_progress(fd);
+               if (fd->filter_path == NULL) {
+                       GError *add_err = NULL;
+
+                   result = libbalsa_mailbox_add_message(LIBBALSA_MAILBOX_POP3(fd->mailbox)->inbox, 
fd->handler->mbx_stream,
+                       LIBBALSA_MESSAGE_FLAG_NEW | LIBBALSA_MESSAGE_FLAG_RECENT, &add_err);
+                   if (!result) {
+                       libbalsa_information(LIBBALSA_INFORMATION_WARNING, _("Error appending message %d from 
%s to %s: %s"),
+                               info->id, fd->mailbox->name, LIBBALSA_MAILBOX_POP3(fd->mailbox)->inbox->name,
+                                       (add_err != NULL) ? add_err->message : "?");
+                       g_error_free(add_err);
+                   }
+               }
+
+               /* current message done */
+               close_res = pop_handler_close(fd->handler, error);
+               fd->handler = NULL;
+               result = close_res & result;
+       } else {
+               /* count < 0: error; note that the handler may already be NULL if the error occurred for 
count == 0 */
+               if (fd->handler != NULL) {
+                       (void) pop_handler_close(fd->handler, NULL);
+                       fd->handler = NULL;
+               }
+               result = FALSE;
        }
-       break;
-    case POP_REQ_DONE:
-        if(fd->dm->close(fd->f) != 0) {
-            if(!*fd->err)
-                g_set_error(fd->err, IMAP_ERROR, IMAP_POP_SAVE_ERROR,
-                            _("Transferring POP message to %s failed."),
-                            fd->dest_path);
-        } else if(!fd->error_occured &&
-                  move_from_msgdrop
-                  (fd->tmp_path,
-                   LIBBALSA_MAILBOX_POP3(fd->mailbox)->inbox,
-                   fd->mailbox->name, fd->msgno) &&
-                  fd->delete_on_server)
-            pop_delete_message(fd->pop, fd->msgno, NULL, NULL, NULL);
-       fd->f = NULL;
-       break;
-    }
-    va_end(alist);
+
+       return result;
 }
 
-static void
-async_notify(struct fetch_data *fd)
+
+static NetClientPop *
+libbalsa_mailbox_pop3_startup(LibBalsaServer            *server,
+                                                         const LibBalsaMailboxPop3 *mbox,
+                                                         const gchar               *name,
+                                                         GList                    **msg_list)
 {
-    /* We need to close the file here only when POP_REQ_ERR
-       arrived. */
-    if (fd->f) {
-        fd->dm->close(fd->f);
-        fd->f = NULL;
-    }
-    g_free(fd);
+       NetClientPop *pop;
+       GError *error = NULL;
+
+       /* create the mailbox connection */
+       if (server->security == NET_CLIENT_CRYPT_ENCRYPTED) {
+               pop = net_client_pop_new(server->host, 995U, server->security, mbox->enable_pipe);
+       } else {
+               pop = net_client_pop_new(server->host, 110U, server->security, mbox->enable_pipe);
+       }
+       if (pop == NULL) {
+               return NULL;
+       }
+
+       /* configure the mailbox connection */
+       if (mbox->disable_apop) {
+               net_client_pop_allow_auth(pop, TRUE, NET_CLIENT_POP_AUTH_ALL & ~NET_CLIENT_POP_AUTH_APOP);
+               net_client_pop_allow_auth(pop, FALSE, NET_CLIENT_POP_AUTH_SAFE & ~NET_CLIENT_POP_AUTH_APOP);
+       }
+       net_client_set_timeout(NET_CLIENT(pop), 60U);
+
+       /* load client certificate if configured */
+       if (server->client_cert) {
+               g_signal_connect(G_OBJECT(pop), "cert-pass", G_CALLBACK(libbalsa_server_get_cert_pass), 
server);
+               if (!net_client_set_cert_from_file(NET_CLIENT(pop), server->cert_file, &error)) {
+                       libbalsa_information(LIBBALSA_INFORMATION_ERROR, _("Cannot load certificate file %s: 
%s"), server->cert_file,
+                               error->message);
+                       g_error_free(error);
+                       g_object_unref(G_OBJECT(pop));
+                       return NULL;
+               }
+       }
+
+       /* connect signals */
+       g_signal_connect(G_OBJECT(pop), "cert-check", G_CALLBACK(libbalsa_server_check_cert), pop);
+       g_signal_connect(G_OBJECT(pop), "auth", G_CALLBACK(libbalsa_server_get_auth), server);
+
+       /* connect server */
+       if (!net_client_pop_connect(pop, NULL, &error)) {
+               libbalsa_information(LIBBALSA_INFORMATION_ERROR, _("POP3 mailbox %s: cannot connect %s: %s"), 
name, server->host,
+                       error->message);
+               g_error_free(error);
+               g_object_unref(G_OBJECT(pop));
+               return NULL;
+       }
+
+       /* load message list */
+       if (!net_client_pop_list(pop, msg_list, !mbox->delete_from_server, &error)) {
+               libbalsa_information(LIBBALSA_INFORMATION_ERROR, _("POP3 mailbox %s error: %s"), name, 
error->message);
+               g_error_free(error);
+               g_object_unref(G_OBJECT(pop));
+               pop = NULL;
+       }
+
+       return pop;
 }
 
-/* libbalsa_mailbox_pop3_check:
-   checks=downloads POP3 mail.
-   LOCKING : assumes gdk lock HELD and other locks (libmutt, mailbox) NOT HELD
-*/
-static void
-monitor_cb(const char *buffer, int length, int direction, void *arg)
+
+static GList *
+update_msg_list(struct fetch_data         *fd,
+                               const LibBalsaMailboxPop3 *mbox,
+                               GHashTable               **current_uids,
+                               LibBalsaServer            *server,
+                               GList                     *msg_list)
 {
-    int i;
-    if(!PopDebug) return;
-
-    if(direction) {
-        if(strncmp(buffer, "Pass ", 5) == 0) {
-            printf("POP C: Pass (passwd hidden)\n");
-            return;
-        }
-    }
-    printf("POP %c: ", direction ? 'C' : 'S');
-    for (i = 0; i < length; i++)
-      putchar(buffer[i]);
-    fflush(NULL);
+       GHashTable *uids = NULL;
+       gchar *uid_prefix = NULL;
+       size_t prefix_len = 0U;
+       GList *p;
+
+       /* load uid's if messages shall be left on the server */
+       if (!mbox->delete_from_server) {
+               uid_prefix = g_strconcat(server->user, "@", server->host, NULL);
+               prefix_len = strlen(uid_prefix);
+               uids = mp_load_uids(uid_prefix);
+               *current_uids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+       }
+
+       /* calculate totals, remove oversized messages, remember in the hash if required */
+       fd->total_messages = 0U;
+       fd->total_size = 0U;
+       p = msg_list;
+       while (p != NULL) {
+               NetClientPopMessageInfo *msg_info = (NetClientPopMessageInfo*) p->data;
+               gboolean skip = FALSE;
+               GList* next = p->next;
+
+               /* check for oversized message */
+               if ((mbox->msg_size_limit > 0) && (msg_info->size >= (gsize) mbox->msg_size_limit)) {
+                       gchar *size_str;
+
+                       size_str = libbalsa_size_to_gchar(msg_info->size);
+                       libbalsa_information(LIBBALSA_INFORMATION_WARNING, _("POP3 message %d oversized: %s — 
skipped."), msg_info->id,
+                               size_str);
+                       g_free(size_str);
+                       skip = TRUE;
+               }
+
+               /* check if we already know this message */
+               if (!skip && !mbox->delete_from_server) {
+                       gchar *full_uid = g_strconcat(uid_prefix, " ", msg_info->uid, NULL);
+
+                       g_hash_table_insert(*current_uids, full_uid, GINT_TO_POINTER(1));
+                       if (g_hash_table_lookup(uids, full_uid) != NULL) {
+                               skip = TRUE;
+                       }
+               }
+
+               /* delete from list if we want to skip the message, update totals otherwise */
+               if (skip) {
+                       net_client_pop_msg_info_free(msg_info);
+                       msg_list = g_list_delete_link(msg_list, p);
+               } else {
+                       fd->total_messages++;
+                       fd->total_size += msg_info->size;
+               }
+               p = next;
+       }
+
+       /* copy all keys /not/ starting with the prefix from the old to the current hash table, and drop the 
old table */
+       if (!mbox->delete_from_server && (msg_list != NULL)) {
+               GHashTableIter iter;
+               gpointer key;
+
+               g_hash_table_iter_init(&iter, uids);
+               while (g_hash_table_iter_next(&iter, &key, NULL)) {
+                       if (strncmp((const char *) key, uid_prefix, prefix_len) != 0) {
+                               g_hash_table_insert(*current_uids, key, GINT_TO_POINTER(1));
+                       }
+               }
+       }
+
+       g_free(uid_prefix);
+       if (uids != NULL) {
+               g_hash_table_destroy(uids);
+       }
+
+       return msg_list;
 }
 
+
 static void
 libbalsa_mailbox_pop3_check(LibBalsaMailbox * mailbox)
 {
-    gchar *tmp_path, *dest_path, *uid_prefix = NULL;
-    gint tmp_file;
-    LibBalsaMailboxPop3 *m = LIBBALSA_MAILBOX_POP3(mailbox);
-    LibBalsaServer *server;
-    gchar *msgbuf;
-    GError *err = NULL;
-    unsigned msgcnt, i;
-    GHashTable *uids = NULL, *current_uids = NULL;
-    const struct PopDownloadMode *mode;
-    unsigned long current_pos = 0, total_size;
-    ImapTlsMode tls_mode;
-    PopHandle *pop;
-
-    if (!m->check || !m->inbox) return;
-
-    server = LIBBALSA_MAILBOX_REMOTE_SERVER(m);
-    switch(server->tls_mode) {
-    case LIBBALSA_TLS_DISABLED: tls_mode = IMAP_TLS_DISABLED; break;
-    default:
-    case LIBBALSA_TLS_ENABLED : tls_mode = IMAP_TLS_ENABLED;  break;
-    case LIBBALSA_TLS_REQUIRED: tls_mode = IMAP_TLS_REQUIRED; break;
-    }
-
-    msgbuf = g_strdup_printf("POP3: %s", mailbox->name);
-    libbalsa_mailbox_progress_notify(mailbox, LIBBALSA_NTFY_SOURCE,0,0,msgbuf);
-    g_free(msgbuf);
-
-    tmp_file = g_file_open_tmp("pop-XXXXXX", &tmp_path, &err);
-    if (tmp_file < 0) {
-       libbalsa_information(LIBBALSA_INFORMATION_WARNING,
-                            _("POP3 mailbox %s temp file error:\n%s"), 
-                            mailbox->name, err->message);
-       g_error_free(err);
-       return;
-    }
-    close(tmp_file);
-
-    pop = pop_new();
-    pop_set_option(pop, IMAP_POP_OPT_FILTER_CR, TRUE);
-    pop_set_option(pop, IMAP_POP_OPT_OVER_SSL, server->use_ssl);
-    pop_set_option(pop, IMAP_POP_OPT_DISABLE_APOP, m->disable_apop);
-    pop_set_option(pop, IMAP_POP_OPT_PIPELINE, m->enable_pipe);
-    pop_set_tls_mode(pop, tls_mode);
-    pop_set_timeout(pop, 60000); /* wait 1.5 minute for packets */
-    pop_set_usercb(pop, libbalsa_server_user_cb, server);
-    pop_set_monitorcb(pop, monitor_cb, NULL);
-
-    if(m->filter) {
-        mode       = &pop_filter;
-        dest_path = m->filter_cmd;
-    } else {
-        mode  = &pop_direct;
-        dest_path = tmp_path;
-    }
-    if(!pop_connect(pop, server->host, &err)) {
-       libbalsa_information(LIBBALSA_INFORMATION_WARNING,
-                            _("POP3 mailbox %s error: %s\n"), 
-                            mailbox->name, err->message);
-        g_error_free(err);
-        unlink(tmp_path); g_free(tmp_path);
-        pop_destroy(pop, NULL);
-       return;
-    }
-
-    /* ===================================================================
-     * Main download loop...
-     * =================================================================== */
-    msgcnt     = pop_get_exists(pop, NULL);
-    total_size = pop_get_total_size(pop);
-    if(!m->delete_from_server) {
-        uids = mp_load_uids();
-        current_uids = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                             g_free, NULL);
-        uid_prefix = g_strconcat(server->user, "@", server->host, NULL);
-    }
+       LibBalsaMailboxPop3 *mbox = LIBBALSA_MAILBOX_POP3(mailbox);
+       LibBalsaServer *server;
+       gchar *msgbuf;
+       NetClientPop *pop;
+       GList *msg_list;
+
+       if (!mbox->check || (mbox->inbox == NULL)) {
+               return;
+       }
 
-    for(i=1; i<=msgcnt; i++) {
-        unsigned msg_size = pop_get_msg_size(pop, i);
-#if POP_SYNC
-        FILE *f;
-        gboolean retval;
-#endif
-        if(!m->delete_from_server) {
-            const char *uid = pop_get_uid(pop, i, NULL);
-            char *full_uid = g_strconcat(uid_prefix, " ", uid, NULL);
-            g_hash_table_insert(current_uids, full_uid, GINT_TO_POINTER(1));
-            if(g_hash_table_lookup(uids, full_uid)) {
-                total_size -= msg_size;
-                continue;
-            }
-        }
-        if(m->msg_size_limit>0 && msg_size >= (unsigned)m->msg_size_limit) {
-            libbalsa_information
-                (LIBBALSA_INFORMATION_WARNING,
-                 _("POP3 message %d oversized: %d kB — skipped."),
-                 i, msg_size);
-            total_size -= msg_size;
-            continue;
-        }
-#if POP_SYNC
-        libbalsa_mailbox_progress_notify(mailbox,
-                                         LIBBALSA_NTFY_PROGRESS, i, msgcnt,
-                                         "next message");
-        f = mode->open(dest_path);
-        if(!f) {
-            libbalsa_information(LIBBALSA_INFORMATION_ERROR,
-                            _("POP3 error: cannot open %s for writing."), 
-                            dest_path);
-            break;
-        }
-        retval = pop_fetch_message_s(pop, i, dump_cb, f, &err);
-
-        if(mode->close(f) != 0) {
-            libbalsa_information(LIBBALSA_INFORMATION_ERROR,
-                            _("POP3 error: cannot close %s."), 
-                            dest_path);
-            break;
-        }
-        if (!retval)
-            break;
-        if(move_from_msgdrop(tmp_path, m->inbox,
-                             mailbox->name, i) &&
-           m->delete_from_server)
-            pop_delete_message_s(pop, i, NULL);
-#else 
-       {struct fetch_data *fd = g_new0(struct fetch_data, 1);
-        fd->mailbox  = mailbox;
-        fd->tmp_path = tmp_path;
-       fd->pop      = pop; 
-       fd->err      = &err;
-       fd->dest_path= dest_path;
-       fd->dm       = mode;
-       fd->msgno    = i;
-        fd->total_messages = msgcnt;
-        fd->current_pos    = &current_pos;
-        fd->total_size     = total_size;
-       fd->delete_on_server = m->delete_from_server;
-        pop_fetch_message(pop, i, fetch_async_cb, fd, 
-                         (GDestroyNotify)async_notify);
+       server = LIBBALSA_MAILBOX_REMOTE_SERVER(mbox);
+
+       msgbuf = g_strdup_printf("POP3: %s", mailbox->name);
+       libbalsa_mailbox_progress_notify(mailbox, LIBBALSA_NTFY_SOURCE, 0, 0, msgbuf);
+       g_free(msgbuf);
+
+       /* open the mailbox connection and get the messages list */
+       pop = libbalsa_mailbox_pop3_startup(server, mbox, mailbox->name, &msg_list);
+
+       /* proceed on success only */
+       if (pop != NULL) {
+               struct fetch_data fd;
+               GHashTable *current_uids = NULL;
+               gboolean result = TRUE;
+               GError *err = NULL;
+
+               libbalsa_mailbox_progress_notify(mailbox, LIBBALSA_NTFY_PROGRESS, 0, 1, _("Connected"));
+               memset(&fd, 0, sizeof(fd));
+
+               /* nothing to do if no messages are on the server */
+               if (msg_list != NULL) {
+                       /* load uid's if messages shall be left on the server */
+                       msg_list = update_msg_list(&fd, mbox, &current_uids, server, msg_list);
+               }
+
+               /* download messages unless the list is empty */
+               if (fd.total_messages > 0U) {
+                       fd.mailbox = mailbox;
+                       fd.total_size_msg = libbalsa_size_to_gchar(fd.total_size);
+
+                       msgbuf = g_strdup_printf(_("%lu new messages (%s)"), fd.total_messages, 
fd.total_size_msg);
+                       libbalsa_mailbox_progress_notify(mailbox, LIBBALSA_NTFY_PROGRESS, 0, 1, msgbuf);
+                       g_free(msgbuf);
+
+                       if (mbox->filter) {
+                               fd.filter_path = mbox->filter_cmd;
+                       }
+
+                       if (result) {
+                               result = net_client_pop_retr(pop, msg_list, message_cb, &fd, &err);
+                               if (result && mbox->delete_from_server) {
+                                       result = net_client_pop_dele(pop, msg_list, &err);
+                               }
+                       }
+
+                       /* clean up */
+                       g_free(fd.total_size_msg);
+                       g_list_free_full(msg_list, (GDestroyNotify) net_client_pop_msg_info_free);
+               }
+
+               /* store uid list */
+               if (result && !mbox->delete_from_server) {
+                       gchar *uid_prefix = g_strconcat(server->user, "@", server->host, NULL);
+
+                       mp_save_uids(current_uids, uid_prefix, &err);
+                       g_free(uid_prefix);
+                       if (current_uids != NULL) {
+                               g_hash_table_destroy(current_uids);
+                       }
+               }
+
+               if (!result) {
+                       libbalsa_information(LIBBALSA_INFORMATION_ERROR, _("POP3 error: %s"), err->message);
+                       g_error_free(err);
+               }
        }
-#endif
-    }
-    pop_destroy(pop, NULL);
-    if(err) {
-        /* 
-        libbalsa_mailbox_progress_notify(mailbox,
-                                         LIBBALSA_NTFY_ERROR, 0,
-                                         1, err->message);*/
-        libbalsa_information(LIBBALSA_INFORMATION_ERROR,
-                            _("POP3 error: %s."), 
-                            err->message);
-        g_error_free(err);
-    }
-    if(!m->delete_from_server) {
-        mp_save_uids(uids, current_uids, uid_prefix);
-        g_hash_table_destroy(uids);
-        g_hash_table_destroy(current_uids);
-        g_free(uid_prefix);
-    }
-    libbalsa_mailbox_progress_notify(mailbox,
-                                     LIBBALSA_NTFY_PROGRESS, 0,
-                                     1, "Finished");
-    
-    /* ===================================================================
-     * Postprocessing...
-     * =================================================================== */
-
-    unlink(tmp_path);
-    g_free(tmp_path);
+
+       /* done - clean up */
+       libbalsa_mailbox_progress_notify(mailbox, LIBBALSA_NTFY_PROGRESS, 0, 1, _("Finished"));
+       g_object_unref(G_OBJECT(pop));
 }
 
+
 static void
 libbalsa_mailbox_pop3_save_config(LibBalsaMailbox * mailbox,
                                  const gchar * prefix)
diff --git a/libbalsa/mailbox_pop3.h b/libbalsa/mailbox_pop3.h
index e6e0cf1..1e345a2 100644
--- a/libbalsa/mailbox_pop3.h
+++ b/libbalsa/mailbox_pop3.h
@@ -47,10 +47,10 @@ struct _LibBalsaMailboxPop3 {
     gchar *filter_cmd;
     LibBalsaMailbox *inbox;
     gint msg_size_limit;
-    unsigned filter:1; /* filter through procmail/filter_cmd? */
-    unsigned disable_apop:1; /* Some servers claim to support it but
+    gboolean filter; /* filter through procmail/filter_cmd? */
+    gboolean disable_apop; /* Some servers claim to support it but
                               * they do not. */
-    unsigned enable_pipe:1;  /* ditto */
+    gboolean enable_pipe;  /* ditto */
 };
 
 LibBalsaMailboxPop3 *libbalsa_mailbox_pop3_new(void);
@@ -58,6 +58,5 @@ void libbalsa_mailbox_pop3_set_inbox(LibBalsaMailbox *mailbox,
                                      LibBalsaMailbox *inbox);
 void libbalsa_mailbox_pop3_set_msg_size_limit(LibBalsaMailboxPop3 *mailbox,
                                               gint sz_limit);
-extern int PopDebug;
 
 #endif                         /* __LIBBALSA_MAILBOX_POP3_H__ */
diff --git a/libbalsa/send.c b/libbalsa/send.c
index 9055e73..ffcc56b 100644
--- a/libbalsa/send.c
+++ b/libbalsa/send.c
@@ -592,91 +592,6 @@ send_message_data_cb(gchar   *buffer,
 }
 
 
-static gboolean
-check_cert(NetClient           *client,
-           GTlsCertificate     *peer_cert,
-           GTlsCertificateFlags errors,
-           gpointer             user_data)
-{
-    GByteArray *cert_der = NULL;
-    gboolean result = FALSE;
-
-    /* FIXME - this a hack, simulating the (OpenSSL based) input for libbalsa_is_cert_known().
-        If we switch completely to
-     * (GnuTLS based) GTlsCertificate/GTlsClientConnection, we can omit this... */
-    g_debug("%s: %p %p %u %p", __func__, client, peer_cert, errors, user_data);
-
-    /* create a OpenSSL X509 object from the certificate's DER data */
-    g_object_get(G_OBJECT(peer_cert), "certificate", &cert_der, NULL);
-    if (cert_der != NULL) {
-        X509 *ossl_cert;
-        const unsigned char *der_p;
-
-        der_p = (const unsigned char *) cert_der->data;
-        ossl_cert = d2i_X509(NULL, &der_p, cert_der->len);
-        g_byte_array_unref(cert_der);
-
-        if (ossl_cert != NULL) {
-            long vfy_result;
-
-            /* convert the GIO error flags into OpenSSL error flags */
-            if ((errors & G_TLS_CERTIFICATE_UNKNOWN_CA) == G_TLS_CERTIFICATE_UNKNOWN_CA) {
-                vfy_result = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
-            } else if ((errors & G_TLS_CERTIFICATE_BAD_IDENTITY) ==
-                       G_TLS_CERTIFICATE_BAD_IDENTITY) {
-                vfy_result = X509_V_ERR_SUBJECT_ISSUER_MISMATCH;
-            } else if ((errors & G_TLS_CERTIFICATE_NOT_ACTIVATED) ==
-                       G_TLS_CERTIFICATE_NOT_ACTIVATED) {
-                vfy_result = X509_V_ERR_CERT_NOT_YET_VALID;
-            } else if ((errors & G_TLS_CERTIFICATE_EXPIRED) == G_TLS_CERTIFICATE_EXPIRED) {
-                vfy_result = X509_V_ERR_CERT_HAS_EXPIRED;
-            } else if ((errors & G_TLS_CERTIFICATE_REVOKED) == G_TLS_CERTIFICATE_REVOKED) {
-                vfy_result = X509_V_ERR_CERT_REVOKED;
-            } else {
-                vfy_result = X509_V_ERR_APPLICATION_VERIFICATION;
-            }
-
-            result = libbalsa_is_cert_known(ossl_cert, vfy_result);
-            X509_free(ossl_cert);
-        }
-    }
-
-    return result;
-}
-
-
-static gchar **
-get_auth(NetClient *client,
-         gpointer   user_data)
-{
-    LibBalsaServer *server = LIBBALSA_SERVER(user_data);
-    gchar **result = NULL;
-
-    g_debug("%s: %p %p: encrypted = %d", __func__, client, user_data,
-            net_client_is_encrypted(client));
-    if (server->try_anonymous == 0U) {
-        result = g_new0(gchar *, 3U);
-        result[0] = g_strdup(server->user);
-        if ((server->passwd != NULL) && (server->passwd[0] != '\0')) {
-            result[1] = g_strdup(server->passwd);
-        } else {
-            result[1] = libbalsa_server_get_password(server, NULL);
-        }
-    }
-    return result;
-}
-
-
-static gchar *
-get_cert_pass(NetClient        *client,
-                         const GByteArray *cert_der,
-                         gpointer          user_data)
-{
-       /* FIXME - we just return the passphrase from the config, but we may also want to show a dialogue 
here... */
-       return g_strdup(libbalsa_smtp_server_get_cert_passphrase(LIBBALSA_SMTP_SERVER(user_data)));
-}
-
-
 /* libbalsa_process_queue:
    treats given mailbox as a set of messages to send. Loads them up and
    launches sending thread/routine.
@@ -711,15 +626,14 @@ lbs_process_queue(LibBalsaMailbox     *outbox,
     }
 
     /* load client certificate if configured */
-    if (libbalsa_smtp_server_require_client_cert(smtp_server)) {
-        const gchar *client_cert = libbalsa_smtp_server_get_cert_file(smtp_server);
+    if (server->client_cert) {
        GError *error = NULL;
 
-       g_signal_connect(G_OBJECT(session), "cert-pass", G_CALLBACK(get_cert_pass), smtp_server);
-       if (!net_client_set_cert_from_file(NET_CLIENT(session), client_cert, &error)) {
+       g_signal_connect(G_OBJECT(session), "cert-pass", G_CALLBACK(libbalsa_server_get_cert_pass), server);
+       if (!net_client_set_cert_from_file(NET_CLIENT(session), server->cert_file, &error)) {
             libbalsa_information(LIBBALSA_INFORMATION_ERROR,
                                  _("Cannot load certificate file %s: %s"),
-                                                                client_cert, error->message);
+                                                                server->cert_file, error->message);
             g_error_free(error);
             g_mutex_unlock(&send_messages_lock);
                return FALSE;
@@ -727,8 +641,8 @@ lbs_process_queue(LibBalsaMailbox     *outbox,
     }
 
     /* connect signals */
-    g_signal_connect(G_OBJECT(session), "cert-check", G_CALLBACK(check_cert), session);
-    g_signal_connect(G_OBJECT(session), "auth", G_CALLBACK(get_auth), smtp_server);
+    g_signal_connect(G_OBJECT(session), "cert-check", G_CALLBACK(libbalsa_server_check_cert), session);
+    g_signal_connect(G_OBJECT(session), "auth", G_CALLBACK(libbalsa_server_get_auth), smtp_server);
 
     send_message_info =
         send_message_info_new(outbox, session, libbalsa_smtp_server_get_name(smtp_server));
diff --git a/libbalsa/server.c b/libbalsa/server.c
index 132c75c..ca11eff 100644
--- a/libbalsa/server.c
+++ b/libbalsa/server.c
@@ -197,8 +197,19 @@ libbalsa_server_finalize(GObject * object)
 
     g_free(server->host);   server->host = NULL;
     g_free(server->user);   server->user = NULL;
+    if (server->passwd != NULL) {
+       memset(server->passwd, 0, strlen(server->passwd));
+    }
     libbalsa_free_password(server->passwd); server->passwd = NULL;
 
+    g_free(server->cert_file);
+    server->cert_file = NULL;
+    if (server->cert_passphrase != NULL) {
+       memset(server->cert_passphrase, 0, strlen(server->cert_passphrase));
+    }
+    g_free(server->cert_passphrase);
+    server->cert_passphrase = NULL;
+
     G_OBJECT_CLASS(parent_class)->finalize(object);
 }
 
@@ -422,6 +433,19 @@ libbalsa_server_load_config(LibBalsaServer * server)
        libbalsa_free_password(server->passwd);
        server->passwd = NULL;
     }
+
+    server->client_cert = libbalsa_conf_get_bool("NeedClientCert=false");
+    server->cert_file = libbalsa_conf_get_string("UserCertificateFile");
+    server->cert_passphrase = libbalsa_conf_private_get_string("CertificatePassphrase");
+    if ((server->cert_passphrase != NULL) && (server->cert_passphrase != '\0')) {
+        gchar *tmp = libbalsa_rot(server->cert_passphrase);
+
+        g_free(server->cert_passphrase);
+        server->cert_passphrase = tmp;
+    } else {
+       g_free(server->cert_passphrase);
+       server->cert_passphrase = NULL;
+    }
 }
 
 /* libbalsa_server_save_config:
@@ -470,6 +494,18 @@ libbalsa_server_save_config(LibBalsaServer * server)
        g_free(buff);
 #endif                          /* defined(HAVE_LIBSECRET) */
     }
+
+    libbalsa_conf_set_bool("NeedClientCert", server->client_cert);
+    if (server->cert_file != NULL) {
+       libbalsa_conf_set_string("UserCertificateFile", server->cert_file);
+    }
+    if (server->cert_passphrase != NULL) {
+        gchar *tmp = libbalsa_rot(server->cert_passphrase);
+
+        libbalsa_conf_private_set_string("CertificatePassphrase", tmp);
+        g_free(tmp);
+    }
+
     libbalsa_conf_set_bool("SSL", server->use_ssl);
     libbalsa_conf_set_int("TLSMode", server->tls_mode);
     libbalsa_conf_set_int("Security", server->security);
@@ -565,3 +601,88 @@ libbalsa_server_connect_signals(LibBalsaServer * server, GCallback cb,
                                       [GET_PASSWORD], 0, TRUE))
         g_signal_connect(server, "get-password", cb, cb_data);
 }
+
+
+gchar **
+libbalsa_server_get_auth(NetClient *client,
+                                        gpointer   user_data)
+{
+    LibBalsaServer *server = LIBBALSA_SERVER(user_data);
+    gchar **result = NULL;
+
+    g_debug("%s: %p %p: encrypted = %d", __func__, client, user_data,
+            net_client_is_encrypted(client));
+    if (server->try_anonymous == 0U) {
+        result = g_new0(gchar *, 3U);
+        result[0] = g_strdup(server->user);
+        if ((server->passwd != NULL) && (server->passwd[0] != '\0')) {
+            result[1] = g_strdup(server->passwd);
+        } else {
+            result[1] = libbalsa_server_get_password(server, NULL);
+        }
+    }
+    return result;
+}
+
+
+gboolean
+libbalsa_server_check_cert(NetClient           *client,
+                                          GTlsCertificate     *peer_cert,
+                                                  GTlsCertificateFlags errors,
+                                                  gpointer             user_data)
+{
+    GByteArray *cert_der = NULL;
+    gboolean result = FALSE;
+
+    /* FIXME - this a hack, simulating the (OpenSSL based) input for libbalsa_is_cert_known().
+        If we switch completely to
+     * (GnuTLS based) GTlsCertificate/GTlsClientConnection, we can omit this... */
+    g_debug("%s: %p %p %u %p", __func__, client, peer_cert, errors, user_data);
+
+    /* create a OpenSSL X509 object from the certificate's DER data */
+    g_object_get(G_OBJECT(peer_cert), "certificate", &cert_der, NULL);
+    if (cert_der != NULL) {
+        X509 *ossl_cert;
+        const unsigned char *der_p;
+
+        der_p = (const unsigned char *) cert_der->data;
+        ossl_cert = d2i_X509(NULL, &der_p, cert_der->len);
+        g_byte_array_unref(cert_der);
+
+        if (ossl_cert != NULL) {
+            long vfy_result;
+
+            /* convert the GIO error flags into OpenSSL error flags */
+            if ((errors & G_TLS_CERTIFICATE_UNKNOWN_CA) == G_TLS_CERTIFICATE_UNKNOWN_CA) {
+                vfy_result = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
+            } else if ((errors & G_TLS_CERTIFICATE_BAD_IDENTITY) ==
+                       G_TLS_CERTIFICATE_BAD_IDENTITY) {
+                vfy_result = X509_V_ERR_SUBJECT_ISSUER_MISMATCH;
+            } else if ((errors & G_TLS_CERTIFICATE_NOT_ACTIVATED) ==
+                       G_TLS_CERTIFICATE_NOT_ACTIVATED) {
+                vfy_result = X509_V_ERR_CERT_NOT_YET_VALID;
+            } else if ((errors & G_TLS_CERTIFICATE_EXPIRED) == G_TLS_CERTIFICATE_EXPIRED) {
+                vfy_result = X509_V_ERR_CERT_HAS_EXPIRED;
+            } else if ((errors & G_TLS_CERTIFICATE_REVOKED) == G_TLS_CERTIFICATE_REVOKED) {
+                vfy_result = X509_V_ERR_CERT_REVOKED;
+            } else {
+                vfy_result = X509_V_ERR_APPLICATION_VERIFICATION;
+            }
+
+            result = libbalsa_is_cert_known(ossl_cert, vfy_result);
+            X509_free(ossl_cert);
+        }
+    }
+
+    return result;
+}
+
+
+gchar *
+libbalsa_server_get_cert_pass(NetClient        *client,
+                                                         const GByteArray *cert_der,
+                                                         gpointer          user_data)
+{
+       /* FIXME - we just return the passphrase from the config, but we may also want to show a dialogue 
here... */
+       return g_strdup(LIBBALSA_SERVER(user_data)->cert_passphrase);
+}
diff --git a/libbalsa/server.h b/libbalsa/server.h
index 81090e1..bab0018 100644
--- a/libbalsa/server.h
+++ b/libbalsa/server.h
@@ -77,6 +77,9 @@ struct _LibBalsaServer {
     gchar *user;
     gchar *passwd;
     NetClientCryptMode security;
+    gboolean client_cert;
+    gchar *cert_file;
+    gchar *cert_passphrase;
     /* We include SSL support in UI unconditionally to preserve config
      * between SSL and non-SSL builds. We just fail if SSL is requested
      * in non-SSL build. */
@@ -114,6 +117,17 @@ void libbalsa_server_save_config(LibBalsaServer * server);
 
 void libbalsa_server_user_cb(ImapUserEventType ue, void *arg, ...);
 
+/* NetClient related signal handlers */
+gchar **libbalsa_server_get_auth(NetClient *client,
+                                                                gpointer   user_data);
+gboolean libbalsa_server_check_cert(NetClient           *client,
+                                                           GTlsCertificate     *peer_cert,
+                                                                       GTlsCertificateFlags errors,
+                                                                       gpointer             user_data);
+gchar *libbalsa_server_get_cert_pass(NetClient        *client,
+                                                                        const GByteArray *cert_der,
+                                                                        gpointer          user_data);
+
 void libbalsa_server_connect_signals(LibBalsaServer * server, GCallback cb,
                                      gpointer cb_data);
 
diff --git a/libbalsa/smtp-server.c b/libbalsa/smtp-server.c
index 2c4f867..ba5ac89 100644
--- a/libbalsa/smtp-server.c
+++ b/libbalsa/smtp-server.c
@@ -45,9 +45,6 @@ struct _LibBalsaSmtpServer {
     LibBalsaServer server;
 
     gchar *name;
-    gboolean client_cert;              // FIXME - maybe move to the server base class?
-    gchar *cert_file;                  // FIXME - maybe move to the server base class?
-    gchar *cert_passphrase;            // FIXME - maybe move to the server base class?
     guint big_message; /* size of partial messages; in kB; 0 disables splitting */
 };
 
@@ -69,8 +66,6 @@ libbalsa_smtp_server_finalize(GObject * object)
     smtp_server = LIBBALSA_SMTP_SERVER(object);
 
     g_free(smtp_server->name);
-    g_free(smtp_server->cert_file);
-    g_free(smtp_server->cert_passphrase);
 
     G_OBJECT_CLASS(parent_class)->finalize(object);
 }
@@ -155,15 +150,6 @@ libbalsa_smtp_server_new_from_config(const gchar * name)
 
     libbalsa_server_load_config(LIBBALSA_SERVER(smtp_server));
 
-    smtp_server->client_cert = libbalsa_conf_get_bool("NeedClientCert=false");
-    smtp_server->cert_file = libbalsa_conf_get_string("UserCertificateFile");
-    smtp_server->cert_passphrase = libbalsa_conf_private_get_string("CertificatePassphrase");
-    if (smtp_server->cert_passphrase) {
-        gchar *tmp = libbalsa_rot(smtp_server->cert_passphrase);
-        g_free(smtp_server->cert_passphrase);
-        smtp_server->cert_passphrase = tmp;
-    }
-
     smtp_server->big_message = libbalsa_conf_get_int("BigMessage=0");
 
     return smtp_server;
@@ -174,15 +160,6 @@ libbalsa_smtp_server_save_config(LibBalsaSmtpServer * smtp_server)
 {
     libbalsa_server_save_config(LIBBALSA_SERVER(smtp_server));
 
-    libbalsa_conf_set_bool("NeedClientCert", smtp_server->client_cert);
-    if (smtp_server->cert_file != NULL) {
-       libbalsa_conf_set_string("UserCertificateFile", smtp_server->cert_file);
-    }
-    if (smtp_server->cert_passphrase) {
-        gchar *tmp = libbalsa_rot(smtp_server->cert_passphrase);
-        libbalsa_conf_private_set_string("CertificatePassphrase", tmp);
-        g_free(tmp);
-    }
     libbalsa_conf_set_int("BigMessage", smtp_server->big_message);
 }
 
@@ -200,32 +177,6 @@ libbalsa_smtp_server_get_name(LibBalsaSmtpServer * smtp_server)
     return smtp_server ? smtp_server->name : _("Default");
 }
 
-gboolean
-libbalsa_smtp_server_require_client_cert(LibBalsaSmtpServer *smtp_server)
-{
-       return smtp_server->client_cert;
-}
-
-const gchar *
-libbalsa_smtp_server_get_cert_file(LibBalsaSmtpServer *smtp_server)
-{
-       return smtp_server->cert_file;
-}
-
-void
-libbalsa_smtp_server_set_cert_passphrase(LibBalsaSmtpServer * smtp_server,
-                                         const gchar * passphrase)
-{
-    g_free(smtp_server->cert_passphrase);
-    smtp_server->cert_passphrase = g_strdup(passphrase);
-}
-
-const gchar *
-libbalsa_smtp_server_get_cert_passphrase(LibBalsaSmtpServer * smtp_server)
-{
-    return smtp_server->cert_passphrase;
-}
-
 guint
 libbalsa_smtp_server_get_big_message(LibBalsaSmtpServer * smtp_server)
 {
@@ -372,12 +323,11 @@ smtp_server_response(GtkDialog * dialog, gint response,
                                      gtk_entry_get_text(GTK_ENTRY
                                                         (sdi->pass)));
         server->security = (NetClientCryptMode) (gtk_combo_box_get_active(GTK_COMBO_BOX(sdi->tlsm)) + 1);
-        sdi->smtp_server->client_cert = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sdi->cert_button));
-        g_free(sdi->smtp_server->cert_file);
-        sdi->smtp_server->cert_file = 
g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(sdi->cert_file)));
-        libbalsa_smtp_server_set_cert_passphrase(sdi->smtp_server,
-                                                 gtk_entry_get_text
-                                                 (GTK_ENTRY(sdi->cert_pass)));
+        server->client_cert = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sdi->cert_button));
+        g_free(server->cert_file);
+        server->cert_file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(sdi->cert_file));
+        g_free(server->cert_passphrase);
+        server->cert_passphrase = g_strdup(gtk_entry_get_text(GTK_ENTRY(sdi->cert_pass)));
         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sdi->split_button))) {
             /* big_message is stored in kB, but the widget is in MB. */
                sdi->smtp_server->big_message =
@@ -576,13 +526,13 @@ libbalsa_smtp_server_dialog(LibBalsaSmtpServer * smtp_server,
     sdi->cert_button = gtk_check_button_new_with_mnemonic(_("Server requires client certificate"));
     smtp_server_add_widget(grid, row, _("_Client Certificate:"), sdi->cert_button);
     g_signal_connect(sdi->cert_button, "toggled", G_CALLBACK(smtp_server_changed), sdi);
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sdi->cert_button), smtp_server->client_cert);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sdi->cert_button), server->client_cert);
 
     sdi->cert_file = gtk_file_chooser_button_new(_("Choose Client Certificate"), 
GTK_FILE_CHOOSER_ACTION_OPEN);
     gtk_widget_set_hexpand(sdi->cert_file, TRUE);
     smtp_server_add_widget(grid, ++row, _("Certificate _File:"), sdi->cert_file);
-    if (smtp_server->cert_file != NULL) {
-       gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(sdi->cert_file), smtp_server->cert_file);
+    if (server->cert_file != NULL) {
+       gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(sdi->cert_file), server->cert_file);
     }
     g_signal_connect(sdi->cert_file, "file-set", G_CALLBACK(smtp_server_changed), sdi);
 
@@ -590,8 +540,8 @@ libbalsa_smtp_server_dialog(LibBalsaSmtpServer * smtp_server,
     smtp_server_add_widget(grid, ++row, _("Certificate _Pass Phrase:"), sdi->cert_pass);
     g_object_set(G_OBJECT(sdi->cert_pass), "input-purpose", GTK_INPUT_PURPOSE_PASSWORD, NULL);
     gtk_entry_set_visibility(GTK_ENTRY(sdi->cert_pass), FALSE);
-    if (smtp_server->cert_passphrase != NULL) {
-        gtk_entry_set_text(GTK_ENTRY(sdi->cert_pass), smtp_server->cert_passphrase);
+    if (server->cert_passphrase != NULL) {
+        gtk_entry_set_text(GTK_ENTRY(sdi->cert_pass), server->cert_passphrase);
     }
     g_signal_connect(sdi->cert_pass, "changed", G_CALLBACK(smtp_server_changed), sdi);
 
diff --git a/libbalsa/smtp-server.h b/libbalsa/smtp-server.h
index 0915060..59157f9 100644
--- a/libbalsa/smtp-server.h
+++ b/libbalsa/smtp-server.h
@@ -46,13 +46,6 @@ void libbalsa_smtp_server_set_name(LibBalsaSmtpServer * smtp_server,
                                    const gchar * name);
 const gchar *libbalsa_smtp_server_get_name(LibBalsaSmtpServer *
                                            smtp_server);
-gboolean libbalsa_smtp_server_require_client_cert(LibBalsaSmtpServer *smtp_server);
-const gchar *libbalsa_smtp_server_get_cert_file(LibBalsaSmtpServer *smtp_server);
-void libbalsa_smtp_server_set_cert_passphrase(LibBalsaSmtpServer *
-                                              smtp_server,
-                                              const gchar * passphrase);
-const gchar *libbalsa_smtp_server_get_cert_passphrase(LibBalsaSmtpServer *
-                                                      smtp_server);
 guint libbalsa_smtp_server_get_big_message(LibBalsaSmtpServer *
                                            smtp_server);
 void libbalsa_smtp_server_add_to_list(LibBalsaSmtpServer * smtp_server,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]