[balsa/oauth2-support: 2/2] improve authentication mode selection




commit 1c0c6f89acb9d6e3e561addcf68b4d4adf998586
Author: Albrecht Dreß <albrecht dress arcor de>
Date:   Mon Aug 31 21:54:18 2020 +0200

    improve authentication mode selection
    
    Let the user choose between OAuth2, Kerberos, user/password and
    anonymous authentication instead of auto-selecting the “best” method.
    Rationale: even if OAuth2/Kerberos is supported, the user may prefer a
    different method. To support the user in configuring the account,
    implement server probing.
    
    Note 1: OAuth2 is yet untested!
    Note 2: Auth mode is stored differently in the user config file!
    
    Backend changes:
    - libbalsa/imap:
      - imap-auth.c: rework authentication
      - imap-handle.[ch], imap_private.h, imap_tst.c: new method
    imap_handle_set_auth_mode(), detect XOAUTH2 capability, implement probe
    function
    - libnetclient:
      - net-client.h, net-client-pop.[ch], net-client-smtp.[ch]: replace
    net_client_*_allow_auth() by net_client_*_set_auth_mode(), detect
    XOAUTH2 capability, rework authentication including OAuth2, implement
    probe functions
      - net-client-utils.[ch]: add net_client_host_reachable() used for
    probing, add net_client_auth_oauth2_calc(), fix GSSAPI paranoia check
    - libbalsa:
      - imap-server.c, mailbox_pop3.c, send.c: use new auth configuration
    methods
      - server.[ch]: save/restore/set/get auth mode as config item AuthMode,
    remove deprecated items from config file
    
    User frontend changes:
    - libbalsa/server-config.c: add a button for probing a server, add combo
    for selecting the auth mode
    
    Signed-off-by: Albrecht Dreß <albrecht dress arcor de>

 libbalsa/imap-server.c          |   2 +-
 libbalsa/imap/imap-auth.c       |  37 ++++---
 libbalsa/imap/imap-handle.c     | 135 +++++++++++++++++++++--
 libbalsa/imap/imap-handle.h     |   6 +-
 libbalsa/imap/imap_private.h    |   3 +-
 libbalsa/imap/imap_tst.c        |   2 +-
 libbalsa/mailbox_pop3.c         |  11 +-
 libbalsa/send.c                 |   1 +
 libbalsa/server-config.c        | 220 ++++++++++++++++++++++++++++++-------
 libbalsa/server.c               |  29 +++--
 libbalsa/server.h               |   4 +-
 libnetclient/net-client-pop.c   | 228 ++++++++++++++++++++++++++++++++++-----
 libnetclient/net-client-pop.h   |  69 +++++-------
 libnetclient/net-client-smtp.c  | 232 ++++++++++++++++++++++++++++++++++------
 libnetclient/net-client-smtp.h  |  58 +++++-----
 libnetclient/net-client-utils.c |  38 ++++++-
 libnetclient/net-client-utils.h |  40 ++++++-
 libnetclient/net-client.h       |  18 +++-
 18 files changed, 907 insertions(+), 226 deletions(-)
---
diff --git a/libbalsa/imap-server.c b/libbalsa/imap-server.c
index 29dad11d3..56025e92e 100644
--- a/libbalsa/imap-server.c
+++ b/libbalsa/imap-server.c
@@ -247,7 +247,7 @@ lb_imap_server_info_new(LibBalsaServer *server)
     imap_handle_set_authcb(handle, G_CALLBACK(libbalsa_server_get_auth), server);
     imap_handle_set_certcb(handle, G_CALLBACK(libbalsa_server_check_cert));
     imap_handle_set_tls_mode(handle, libbalsa_server_get_security(server));
-    imap_handle_set_option(handle, IMAP_OPT_ANONYMOUS, libbalsa_server_get_try_anonymous(server));
+    imap_handle_set_auth_mode(handle, libbalsa_server_get_auth_mode(server));
     imap_handle_set_option(handle, IMAP_OPT_CLIENT_SORT, TRUE);
     /* binary fetches change encoding and the checksums, and
        signatures, disable them if we ever consider verifying message
diff --git a/libbalsa/imap/imap-auth.c b/libbalsa/imap/imap-auth.c
index d94ca3dd4..94258a5d4 100644
--- a/libbalsa/imap/imap-auth.c
+++ b/libbalsa/imap/imap-auth.c
@@ -37,11 +37,8 @@ static ImapResult imap_auth_login(ImapMboxHandle* handle);
 
 typedef ImapResult (*ImapAuthenticator)(ImapMboxHandle* handle);
 
-/* ordered from strongest to weakest. Auth anonymous does not really
- * belong here, does it? */
+/* User name/password methods, ordered from strongest to weakest. */
 static ImapAuthenticator imap_authenticators_arr[] = {
-  imap_auth_anonymous, /* will be tried only if enabled */
-  imap_auth_gssapi,
   imap_auth_cram,
   imap_auth_plain,
   imap_auth_login, /* login is deprecated */
@@ -59,16 +56,28 @@ imap_authenticate(ImapMboxHandle* handle)
   if (imap_mbox_is_authenticated(handle) || imap_mbox_is_selected(handle))
     return IMAP_SUCCESS;
 
-  for(authenticator = imap_authenticators_arr;
-      *authenticator; authenticator++) {
-    if ((r = (*authenticator)(handle)) 
-        != IMAP_AUTH_UNAVAIL) {
-      if (r == IMAP_SUCCESS)
-       imap_mbox_handle_set_state(handle, IMHS_AUTHENTICATED);
-      return r;
-    }
+  if ((handle->auth_mode & NET_CLIENT_AUTH_OAUTH2) != 0U) {
+         /* FIXME!! */
+  }
+
+  if ((r != IMAP_SUCCESS) && (handle->auth_mode & NET_CLIENT_AUTH_KERBEROS) != 0U) {
+         r = imap_auth_gssapi(handle);
+  }
+
+  if ((r != IMAP_SUCCESS) && (handle->auth_mode & NET_CLIENT_AUTH_ANONYMOUS) != 0U) {
+         r = imap_auth_anonymous(handle);
+  }
+
+  for (authenticator = imap_authenticators_arr; (r != IMAP_SUCCESS) && *authenticator; authenticator++) {
+         r = (*authenticator)(handle);
+         if (r == IMAP_SUCCESS) {
+                 imap_mbox_handle_set_state(handle, IMHS_AUTHENTICATED);
+         }
+  }
+
+  if (r != IMAP_SUCCESS) {
+         imap_mbox_handle_set_msg(handle, _("No way to authenticate is known"));
   }
-  imap_mbox_handle_set_msg(handle, _("No way to authenticate is known"));
   return r;
 }
 
@@ -223,8 +232,6 @@ getmsg_anonymous(ImapMboxHandle *h, char **retmsg, int *retmsglen)
 static ImapResult
 imap_auth_anonymous(ImapMboxHandle* handle)
 {
-  if(!handle->enable_anonymous)
-    return IMAP_AUTH_UNAVAIL;
   return imap_auth_sasl(handle, IMCAP_AANONYMOUS, "AUTHENTICATE ANONYMOUS",
                        getmsg_anonymous);
 }
diff --git a/libbalsa/imap/imap-handle.c b/libbalsa/imap/imap-handle.c
index 625a121eb..1ae777482 100644
--- a/libbalsa/imap/imap-handle.c
+++ b/libbalsa/imap/imap-handle.c
@@ -86,6 +86,7 @@ imap_mbox_handle_init(ImapMboxHandle *handle)
                                                NULL, NULL);
   handle->state = IMHS_DISCONNECTED;
   handle->tls_mode = NET_CLIENT_CRYPT_STARTTLS;
+  handle->auth_mode = NET_CLIENT_AUTH_USER_PASS | NET_CLIENT_AUTH_KERBEROS | NET_CLIENT_AUTH_OAUTH2;
   handle->idle_state = IDLE_INACTIVE;
   handle->enable_idle = 1;
 
@@ -172,7 +173,6 @@ void
 imap_handle_set_option(ImapMboxHandle *h, ImapOption opt, gboolean state)
 {
   switch(opt) {
-  case IMAP_OPT_ANONYMOUS:   h->enable_anonymous   = !!state; break;
   case IMAP_OPT_BINARY:      h->enable_binary      = !!state; break;
   case IMAP_OPT_CLIENT_SORT: h->enable_client_sort = !!state; break;
   case IMAP_OPT_COMPRESS:    h->enable_compress    = !!state; break;
@@ -612,15 +612,26 @@ imap_handle_force_disconnect(ImapMboxHandle *h)
 }
 
 NetClientCryptMode
-imap_handle_set_tls_mode(ImapMboxHandle* r, NetClientCryptMode state)
+imap_handle_set_tls_mode(ImapMboxHandle* h, NetClientCryptMode state)
 {
   NetClientCryptMode res;
-  g_return_val_if_fail(r,0);
-  res = r->tls_mode;
-  r->tls_mode = state;
+  g_return_val_if_fail(h,0);
+  res = h->tls_mode;
+  h->tls_mode = state;
   return res;
 }
 
+NetClientAuthMode
+imap_handle_set_auth_mode(ImapMboxHandle *h, NetClientAuthMode mode)
+{
+       NetClientAuthMode res;
+
+       g_return_val_if_fail(h != NULL, 0U);
+       res = h->auth_mode;
+       h->auth_mode = mode;
+       return res;
+}
+
 const char* imap_msg_flags[6] = { 
   "seen", "answered", "flagged", "deleted", "draft", "recent"
 };
@@ -2096,7 +2107,7 @@ ir_capability_data(ImapMboxHandle *handle)
   /* ordered identically as ImapCapability constants */
   static const char* capabilities[] = {
     "IMAP4", "IMAP4rev1", "STATUS",
-    "AUTH=ANONYMOUS", "AUTH=CRAM-MD5", "AUTH=GSSAPI", "AUTH=PLAIN",
+    "AUTH=ANONYMOUS", "AUTH=CRAM-MD5", "AUTH=GSSAPI", "AUTH=PLAIN", "AUTH=XOAUTH2",
     "ACL", "RIGHTS=", "BINARY", "CHILDREN",
     "COMPRESS=DEFLATE",
     "ESEARCH", "IDLE", "LITERAL+",
@@ -4385,3 +4396,115 @@ mbox_view_get_str(MboxView *mv)
 {
   return mv->filter_str ? mv->filter_str : "";
 }
+
+gboolean
+imap_server_probe(const gchar *host, guint timeout_secs, NetClientProbeResult *result, GCallback cert_cb, 
GError **error)
+{
+       guint16 probe_ports[] = {993U, 143U, 0U};               /* imaps, imap */
+       gchar *host_only;
+       gchar *colon;
+       gboolean retval = FALSE;
+       gint check_id;
+
+       /* paranoia check */
+       g_return_val_if_fail((host != NULL) && (result != NULL), FALSE);
+
+       host_only = g_strdup(host);
+       colon = strchr(host_only, ':');
+       if (colon != NULL) {
+               colon[0] = '\0';
+       }
+
+       if (!net_client_host_reachable(host_only, error)) {
+               g_free(host_only);
+               return FALSE;
+       }
+
+       for (check_id = 0; !retval && (probe_ports[check_id] > 0U); check_id++) {
+               ImapMboxHandle *handle;
+
+               g_debug("%s: probing %s:%u…", __func__, host_only, probe_ports[check_id]);
+               handle = imap_mbox_handle_new();
+               handle->sio = net_client_siobuf_new(host_only, probe_ports[check_id]);
+               net_client_set_timeout(NET_CLIENT(handle->sio), timeout_secs);
+               if (net_client_connect(NET_CLIENT(handle->sio), NULL)) {
+                       gboolean this_success;
+                       ImapResponse resp;
+                       gboolean can_starttls = FALSE;
+
+                       if (cert_cb != NULL) {
+                               g_signal_connect(handle->sio, "cert-check", cert_cb, handle->sio);
+                       }
+                       if (check_id == 0) {    /* imaps */
+                               this_success = net_client_start_tls(NET_CLIENT(handle->sio), NULL);
+                       } else {
+                               this_success = TRUE;
+                       }
+
+                       /* get the server greeting and initialise the capabilities */
+                       if (this_success) {
+                               handle->state = IMHS_CONNECTED;
+                               resp = imap_cmd_step(handle, 0);
+                               if (resp != IMR_UNTAGGED) {
+                                       imap_handle_disconnect(handle);
+                                       this_success = FALSE;
+                               } else {
+                                       /* fetch capabilities */
+                                       can_starttls = imap_mbox_handle_can_do(handle, IMCAP_STARTTLS) != 0;
+                               }
+                       }
+
+                       /* try to perform STARTTLS if supported */
+                       if (this_success && can_starttls) {
+                               can_starttls = FALSE;
+                               resp = imap_cmd_exec(handle, "StartTLS");
+                               if (resp == IMR_OK) {
+                                       if (net_client_start_tls(NET_CLIENT(handle->sio), NULL)) {
+                                               handle->has_capabilities = 0;
+                                               can_starttls = TRUE;
+                                       }
+                               }
+                       }
+
+                       /* evaluate on success */
+                       if (this_success) {
+                               result->port = probe_ports[check_id];
+
+                               if (check_id == 0) {
+                                       result->crypt_mode = NET_CLIENT_CRYPT_ENCRYPTED;
+                               } else if (can_starttls) {
+                                       result->crypt_mode = NET_CLIENT_CRYPT_STARTTLS;
+                               } else {
+                                       result->crypt_mode = NET_CLIENT_CRYPT_NONE;
+                               }
+
+                               result->auth_mode = 0U;
+                               if (imap_mbox_handle_can_do(handle, IMCAP_AANONYMOUS) != 0) {
+                                       result->auth_mode |= NET_CLIENT_AUTH_ANONYMOUS;
+                               }
+                               if ((imap_mbox_handle_can_do(handle, IMCAP_ACRAM_MD5) != 0) ||
+                                       (imap_mbox_handle_can_do(handle, IMCAP_APLAIN) != 0)) {
+                                       result->auth_mode |= NET_CLIENT_AUTH_USER_PASS;
+                               }
+                               if (imap_mbox_handle_can_do(handle, IMCAP_AGSSAPI) != 0) {
+                                       result->auth_mode |= NET_CLIENT_AUTH_KERBEROS;
+                               }
+                               if (imap_mbox_handle_can_do(handle, IMCAP_AOAUTH2) != 0) {
+                                       result->auth_mode |= NET_CLIENT_AUTH_OAUTH2;
+                               }
+                               retval = TRUE;
+                       }
+               }
+
+               g_object_unref(handle);
+       }
+
+       if (!retval) {
+               g_set_error(error, NET_CLIENT_ERROR_QUARK, NET_CLIENT_PROBE_FAILED,
+                       _("the server %s does not offer the IMAP service at port 993 or 143"), host_only);
+       }
+
+       g_free(host_only);
+
+       return retval;
+}
diff --git a/libbalsa/imap/imap-handle.h b/libbalsa/imap/imap-handle.h
index fad466a66..d79d54841 100644
--- a/libbalsa/imap/imap-handle.h
+++ b/libbalsa/imap/imap-handle.h
@@ -20,6 +20,7 @@
 #include <glib.h>
 
 #include "net-client.h"
+#include "net-client-utils.h"
 #include "libimap.h"
 
 typedef enum {
@@ -61,6 +62,7 @@ typedef enum
   IMCAP_ACRAM_MD5,             /* RFC 2195: CRAM-MD5 authentication */
   IMCAP_AGSSAPI,               /* RFC 1731: GSSAPI authentication */
   IMCAP_APLAIN,                 /* RFC 2595: */
+  IMCAP_AOAUTH2,                               /* RFC 6749: OAUTH2 authentication */
   IMCAP_ACL,                   /* RFC 2086: IMAP4 ACL extension */
   IMCAP_RIGHTS,                 /* RFC 4314: IMAP4 RIGHTS= extension */
   IMCAP_BINARY,                 /* RFC 3516 */
@@ -88,7 +90,6 @@ typedef enum
 } ImapCapability;
 
 typedef enum {
-  IMAP_OPT_ANONYMOUS,   /**< try anonymous authentication */
   IMAP_OPT_CLIENT_SORT, /**< allow client-side sorting */
   IMAP_OPT_BINARY,      /**< enable binary=no-transfer-encoding msg transfer */
   IMAP_OPT_IDLE,        /**< enable IDLE */
@@ -135,6 +136,7 @@ ImapResult imap_mbox_handle_reconnect(ImapMboxHandle* r,
 void imap_handle_force_disconnect(ImapMboxHandle *h);
 
 NetClientCryptMode imap_handle_set_tls_mode(ImapMboxHandle *h, NetClientCryptMode option);
+NetClientAuthMode imap_handle_set_auth_mode(ImapMboxHandle *h, NetClientAuthMode mode);
 
 /* int below is a boolean */
 int      imap_mbox_handle_can_do(ImapMboxHandle* handle, ImapCapability cap);
@@ -214,4 +216,6 @@ const char *mbox_view_get_str(MboxView *mv);
 
 /* ================ END OF MBOX_VIEW FUNCTIONS ========================= */
 
+gboolean imap_server_probe(const gchar *host, guint timeout_secs, NetClientProbeResult *result, GCallback 
cert_cb, GError **error);
+
 #endif
diff --git a/libbalsa/imap/imap_private.h b/libbalsa/imap/imap_private.h
index 323d4dc75..11af1f8ca 100644
--- a/libbalsa/imap/imap_private.h
+++ b/libbalsa/imap/imap_private.h
@@ -115,6 +115,8 @@ struct _ImapMboxHandle {
   guint idle_enable_id; /* callback to issue IDLE after a period of
                            inactivity */
   NetClientCryptMode tls_mode; /* disabled, enabled, required */
+  NetClientAuthMode auth_mode;
+
   enum { IDLE_INACTIVE, IDLE_RESPONSE_PENDING, IDLE_ACTIVE }
     idle_state; /*  IDLE State? */
   unsigned op_cancelled:1; /* last op timed out and was cancelled by user */
@@ -122,7 +124,6 @@ struct _ImapMboxHandle {
   unsigned can_fetch_body:1; /* set for servers that always respond
                               * correctly to FETCH x BODY[y]
                               * requests. */
-  unsigned enable_anonymous:1;   /**< try anonymous if possible */
   unsigned enable_binary:1;      /**< enable binary extension */
   unsigned enable_client_sort:1; /**< client side sorting allowed */
   unsigned enable_compress:1; /**< enable compress extension */
diff --git a/libbalsa/imap/imap_tst.c b/libbalsa/imap/imap_tst.c
index a180ae36e..7174f066f 100644
--- a/libbalsa/imap/imap_tst.c
+++ b/libbalsa/imap/imap_tst.c
@@ -124,7 +124,7 @@ get_handle(const char *host)
 
   imap_handle_set_tls_mode(h, TestContext.tls_mode);
   if(TestContext.anonymous)
-    imap_handle_set_option(h, IMAP_OPT_ANONYMOUS, TRUE);
+         imap_handle_set_auth_mode(h, NET_CLIENT_AUTH_ANONYMOUS);
 
   if(TestContext.compress)
     imap_handle_set_option(h, IMAP_OPT_COMPRESS, TRUE);
diff --git a/libbalsa/mailbox_pop3.c b/libbalsa/mailbox_pop3.c
index 97f347fa0..f47e342ab 100644
--- a/libbalsa/mailbox_pop3.c
+++ b/libbalsa/mailbox_pop3.c
@@ -460,7 +460,6 @@ libbalsa_mailbox_pop3_startup(LibBalsaServer      *server,
         const gchar *host;
        NetClientPop *pop;
        GError *error = NULL;
-       guint allow_auth;
 
        /* create the mailbox connection */
         security = libbalsa_server_get_security(server);
@@ -474,14 +473,8 @@ libbalsa_mailbox_pop3_startup(LibBalsaServer      *server,
                return NULL;
        }
 
-       /* configure the mailbox connection; allow all (including plain text) auth methods even for 
unencrypted connections so using
-        * e.g. popfile on localhost is possible, i.e. the user is responsible for choosing a proper security 
mode */
-       allow_auth = NET_CLIENT_POP_AUTH_ALL;
-       if (mailbox_pop3->disable_apop) {
-               allow_auth &= ~NET_CLIENT_POP_AUTH_APOP;
-       }
-       net_client_pop_allow_auth(pop, TRUE, allow_auth);
-       net_client_pop_allow_auth(pop, FALSE, allow_auth);
+       /* configure the mailbox connection; allow configured auth methods */
+       net_client_pop_set_auth_mode(pop, libbalsa_server_get_auth_mode(server), mailbox_pop3->disable_apop);
        net_client_set_timeout(NET_CLIENT(pop), 60U);
 
        /* load client certificate if configured */
diff --git a/libbalsa/send.c b/libbalsa/send.c
index 834193034..0de018a4d 100644
--- a/libbalsa/send.c
+++ b/libbalsa/send.c
@@ -791,6 +791,7 @@ lbs_process_queue_init_session(LibBalsaServer* server)
                // FIXME - submission (587) is the standard, but most isp's use 25...
                session = net_client_smtp_new(host, 587U, security);
        }
+       net_client_smtp_set_auth_mode(session, libbalsa_server_get_auth_mode(server));
 
        /* connect signals */
        g_signal_connect(session, "cert-check", G_CALLBACK(libbalsa_server_check_cert), session);
diff --git a/libbalsa/server-config.c b/libbalsa/server-config.c
index 37360213d..1bf2f3659 100644
--- a/libbalsa/server-config.c
+++ b/libbalsa/server-config.c
@@ -24,6 +24,9 @@
 
 #include <glib/gi18n.h>
 #include "misc.h"
+#include "net-client-smtp.h"
+#include "net-client-pop.h"
+#include "imap-handle.h"
 #include "server-config.h"
 
 
@@ -35,8 +38,9 @@ struct _LibBalsaServerCfg {
        guint basic_rows;                               /* count of rows */
        GtkWidget *name;                                /* descriptive name */
        GtkWidget *host_port;                   /* host and optionally port */
+       GtkWidget *probe_host;                  /* button for probing the host */
        GtkWidget *security;                    /* security (SSL/TLS/...) */
-       GtkWidget *require_auth;                /* require authentication */
+       GtkWidget *auth_mode;                   /* authentication mode */
        GtkWidget *username;                    /* user name for authentication */
        GtkWidget *password;                    /* password for authentication */
        GtkWidget *remember_pass;               /* remember password */
@@ -49,6 +53,9 @@ struct _LibBalsaServerCfg {
        GtkWidget *cert_pass;                   /* client certificate pass phrase */
        GtkWidget *remember_cert_pass;  /* remember certificate pass phrase */
 
+       /* function for probing a server's capabilities */
+       gboolean (*probe_fn)(const gchar *, guint, NetClientProbeResult *, GCallback, GError **);
+
        gboolean cfg_valid;                             /* whether the config options are valid (parent may 
enable OK) */
 };
 
@@ -57,14 +64,16 @@ G_DEFINE_TYPE(LibBalsaServerCfg, libbalsa_server_cfg, GTK_TYPE_NOTEBOOK)
 
 
 static GtkWidget *server_cfg_add_entry(GtkWidget *grid, guint row, const gchar *label, const gchar *value, 
GCallback callback,
-                                                               gpointer cb_data)
+               GtkWidget *button, gpointer cb_data)
        G_GNUC_WARN_UNUSED_RESULT;
 static GtkWidget *server_cfg_add_check(GtkWidget *grid, guint row, const gchar *label, gboolean value, 
GCallback callback,
                                                                           gpointer cb_data)
        G_GNUC_WARN_UNUSED_RESULT;
 static void server_cfg_add_widget(GtkWidget *grid, guint row, const gchar *text, GtkWidget *widget);
 static GtkWidget *server_cfg_security_widget(LibBalsaServer *server);
+static GtkWidget *server_cfg_auth_widget(LibBalsaServer *server);
 static void on_server_cfg_changed(GtkWidget *widget, LibBalsaServerCfg *server_cfg);
+static void on_server_probe(GtkWidget *widget, LibBalsaServerCfg *server_cfg);
 
 
 static guint changed_sig;
@@ -87,8 +96,8 @@ LibBalsaServerCfg *
 libbalsa_server_cfg_new(LibBalsaServer *server, const gchar *name)
 {
        LibBalsaServerCfg *server_cfg;
-        const gchar *protocol;
-        const gchar *cert_file;
+       const gchar *protocol;
+       const gchar *cert_file;
 
        g_return_val_if_fail(LIBBALSA_IS_SERVER(server), NULL);
 
@@ -104,35 +113,46 @@ libbalsa_server_cfg_new(LibBalsaServer *server, const gchar *name)
 
     /* server descriptive name */
     server_cfg->name = server_cfg_add_entry(server_cfg->basic_grid, server_cfg->basic_rows++, 
_("_Descriptive Name:"), name,
-        G_CALLBACK(on_server_cfg_changed), server_cfg);
+        G_CALLBACK(on_server_cfg_changed), NULL, server_cfg);
+
+    /* probe button */
+    server_cfg->probe_host = gtk_button_new_from_icon_name("system-run", GTK_ICON_SIZE_MENU);
+    gtk_button_set_label(GTK_BUTTON(server_cfg->probe_host), _("probe…"));
+    gtk_button_set_always_show_image(GTK_BUTTON(server_cfg->probe_host), TRUE);
+    g_signal_connect(server_cfg->probe_host, "clicked", G_CALLBACK(on_server_probe), server_cfg);
+    protocol = libbalsa_server_get_protocol(server);
+    if (strcmp(protocol, "smtp") == 0) {
+       server_cfg->probe_fn = net_client_smtp_probe;
+    } else if (strcmp(protocol, "pop3") == 0) {
+       server_cfg->probe_fn = net_client_pop_probe;
+    } else if (strcmp(protocol, "imap") == 0) {
+       server_cfg->probe_fn = imap_server_probe;
+    } else {
+       g_assert_not_reached();
+    }
 
     /* host and port */
     server_cfg->host_port = server_cfg_add_entry(server_cfg->basic_grid, server_cfg->basic_rows++, 
_("_Server:"),
-                                           libbalsa_server_get_host(server),
-                                           G_CALLBACK(on_server_cfg_changed), server_cfg);
+       libbalsa_server_get_host(server), G_CALLBACK(on_server_cfg_changed), server_cfg->probe_host, 
server_cfg);
 
     /* security settings */
     server_cfg->security = server_cfg_security_widget(server);
     server_cfg_add_widget(server_cfg->basic_grid, server_cfg->basic_rows++, _("Se_curity:"), 
server_cfg->security);
     g_signal_connect(server_cfg->security, "changed", G_CALLBACK(on_server_cfg_changed), server_cfg);
 
-    /* check box for authentication or anonymous access - smtp and imap only */
-    protocol = libbalsa_server_get_protocol(server);
-    if ((strcmp(protocol, "smtp") == 0) || (strcmp(protocol, "imap") == 0)) {
-        server_cfg->require_auth = server_cfg_add_check(server_cfg->basic_grid, server_cfg->basic_rows++,
-                                                  _("Server requires _authentication"),
-                                                  !libbalsa_server_get_try_anonymous(server),
-                                                  G_CALLBACK(on_server_cfg_changed), server_cfg);
-    }
+    /* authentication mode */
+    server_cfg->auth_mode = server_cfg_auth_widget(server);
+    server_cfg_add_widget(server_cfg->basic_grid, server_cfg->basic_rows++, _("_Authentication:"), 
server_cfg->auth_mode);
+    g_signal_connect(server_cfg->auth_mode, "changed", G_CALLBACK(on_server_cfg_changed), server_cfg);
 
     /* user name and password */
     server_cfg->username = server_cfg_add_entry(server_cfg->basic_grid, server_cfg->basic_rows++, _("_User 
Name:"),
                                           libbalsa_server_get_user(server),
-                                          G_CALLBACK(on_server_cfg_changed), server_cfg);
+                                          G_CALLBACK(on_server_cfg_changed), NULL, server_cfg);
 
     server_cfg->password = server_cfg_add_entry(server_cfg->basic_grid, server_cfg->basic_rows++, _("_Pass 
Phrase:"),
                                           libbalsa_server_get_password(server),
-        G_CALLBACK(on_server_cfg_changed), server_cfg);
+                                                                                 
G_CALLBACK(on_server_cfg_changed), NULL, server_cfg);
     g_object_set(server_cfg->password, "input-purpose", GTK_INPUT_PURPOSE_PASSWORD, NULL);
     gtk_entry_set_visibility(GTK_ENTRY(server_cfg->password), FALSE);
 
@@ -158,8 +178,9 @@ libbalsa_server_cfg_new(LibBalsaServer *server, const gchar *name)
     }
     g_signal_connect(server_cfg->cert_file, "file-set", G_CALLBACK(on_server_cfg_changed), server_cfg);
 
-       server_cfg->cert_pass = server_cfg_add_entry(server_cfg->advanced_grid, server_cfg->advanced_rows++, 
_("Certificate _Pass Phrase:"),
-               libbalsa_server_get_cert_passphrase(server), G_CALLBACK(on_server_cfg_changed), server_cfg);
+       server_cfg->cert_pass = server_cfg_add_entry(server_cfg->advanced_grid, server_cfg->advanced_rows++,
+               _("Certificate _Pass Phrase:"), libbalsa_server_get_cert_passphrase(server), 
G_CALLBACK(on_server_cfg_changed), NULL,
+               server_cfg);
     g_object_set(server_cfg->cert_pass, "input-purpose", GTK_INPUT_PURPOSE_PASSWORD, NULL);
     gtk_entry_set_visibility(GTK_ENTRY(server_cfg->cert_pass), FALSE);
 
@@ -207,9 +228,10 @@ libbalsa_server_cfg_add_entry(LibBalsaServerCfg *server_cfg, gboolean basic, con
        g_return_val_if_fail(LIBBALSA_IS_SERVER_CFG(server_cfg) && (label != NULL), NULL);
 
        if (basic) {
-               new_entry = server_cfg_add_entry(server_cfg->basic_grid, server_cfg->basic_rows++, label, 
initval, callback, cb_data);
+               new_entry = server_cfg_add_entry(server_cfg->basic_grid, server_cfg->basic_rows++, label, 
initval, callback, NULL, cb_data);
        } else {
-               new_entry = server_cfg_add_entry(server_cfg->advanced_grid, server_cfg->advanced_rows++, 
label, initval, callback, cb_data);
+               new_entry = server_cfg_add_entry(server_cfg->advanced_grid, server_cfg->advanced_rows++, 
label, initval, callback, NULL,
+                       cb_data);
        }
        return new_entry;
 }
@@ -267,21 +289,16 @@ libbalsa_server_cfg_get_name(LibBalsaServerCfg *server_cfg)
 void
 libbalsa_server_cfg_assign_server(LibBalsaServerCfg *server_cfg, LibBalsaServer *server)
 {
-        gchar *cert_file;
+       gchar *cert_file;
 
        g_return_if_fail(LIBBALSA_IS_SERVER_CFG(server_cfg) && LIBBALSA_IS_SERVER(server));
 
-
        /* host, post and security */
     libbalsa_server_set_security(server, (NetClientCryptMode) 
(gtk_combo_box_get_active(GTK_COMBO_BOX(server_cfg->security)) + 1));
     libbalsa_server_set_host(server, gtk_entry_get_text(GTK_ENTRY(server_cfg->host_port)), 
libbalsa_server_get_security(server));
 
     /* authentication stuff */
-    if (server_cfg->require_auth != NULL) {
-        libbalsa_server_set_try_anonymous(server, 
!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(server_cfg->require_auth)));
-    } else {
-        libbalsa_server_set_try_anonymous(server, FALSE);
-    }
+    libbalsa_server_set_auth_mode(server, 
atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(server_cfg->auth_mode))));
     libbalsa_server_set_username(server, gtk_entry_get_text(GTK_ENTRY(server_cfg->username)));
     libbalsa_server_set_password(server, gtk_entry_get_text(GTK_ENTRY(server_cfg->password)), FALSE);
     libbalsa_server_set_remember_password(server, 
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(server_cfg->remember_pass)));
@@ -314,12 +331,22 @@ libbalsa_server_cfg_init(LibBalsaServerCfg *self)
 
 
 static GtkWidget *
-server_cfg_add_entry(GtkWidget *grid, guint row, const gchar *label, const gchar *value, GCallback callback, 
gpointer cb_data)
+server_cfg_add_entry(GtkWidget *grid, guint row, const gchar *label, const gchar *value, GCallback callback, 
GtkWidget *button,
+       gpointer cb_data)
 {
        GtkWidget *new_entry;
 
        new_entry = gtk_entry_new();
-    server_cfg_add_widget(grid, row, label, new_entry);
+       if (button != NULL) {
+               GtkWidget *hbox;
+
+               hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, HIG_PADDING / 2);
+               server_cfg_add_widget(grid, row, label, hbox);
+               gtk_box_pack_start(GTK_BOX(hbox), new_entry, TRUE, TRUE, 0U);
+               gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0U);
+       } else {
+               server_cfg_add_widget(grid, row, label, new_entry);
+       }
     if (value != NULL) {
         gtk_entry_set_text(GTK_ENTRY(new_entry), value);
     }
@@ -381,27 +408,59 @@ server_cfg_security_widget(LibBalsaServer *server)
 }
 
 
+static GtkWidget *
+server_cfg_auth_widget(LibBalsaServer *server)
+{
+       const gchar *protocol;
+    GtkWidget *combo_box = gtk_combo_box_text_new();
+    gchar id_buf[8];
+
+    protocol = libbalsa_server_get_protocol(server);
+    if ((strcmp(protocol, "smtp") == 0) || (strcmp(protocol, "imap") == 0)) {
+       gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo_box), "1", _("none required"));
+    }
+    gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo_box), "2", _("user name and pass phrase"));
+#if defined(HAVE_GSSAPI)
+    gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo_box), "4", _("Kerberos (GSSAPI)"));
+#endif
+#if defined(HAVE_OAUTH2)
+    gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo_box), "8", _("OAuth2"));
+#endif
+
+    snprintf(id_buf, sizeof(id_buf), "%d", (gint) libbalsa_server_get_auth_mode(server));
+    gtk_combo_box_set_active_id(GTK_COMBO_BOX(combo_box), id_buf);
+
+    return combo_box;
+}
+
+
 static void
 on_server_cfg_changed(GtkWidget *widget, LibBalsaServerCfg *server_cfg)
 {
+       const gchar *active_auth;
        gboolean sensitive;
+       NetClientAuthMode auth_mode;
 
        /* valid configuration only if a name and a host have been given */
        server_cfg->cfg_valid = (*gtk_entry_get_text(GTK_ENTRY(server_cfg->name)) != '\0') &&
                (*gtk_entry_get_text(GTK_ENTRY(server_cfg->host_port)) != '\0');
 
-       /* user name/password only if authentication is required */
-       if (server_cfg->require_auth != NULL) {
-               sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(server_cfg->require_auth));
+       /* can probe only if name and host/port are given */
+       gtk_widget_set_sensitive(server_cfg->probe_host, server_cfg->cfg_valid);
+
+       /* user name/password depend upon auth mode */
+       active_auth = gtk_combo_box_get_active_id(GTK_COMBO_BOX(server_cfg->auth_mode));
+       if (active_auth != 0) {
+               auth_mode = atoi(active_auth);
        } else {
-               sensitive = TRUE;
+               auth_mode = 0;
        }
-       gtk_widget_set_sensitive(server_cfg->username, sensitive);
-       gtk_widget_set_sensitive(server_cfg->password, sensitive);
-       gtk_widget_set_sensitive(server_cfg->remember_pass, sensitive);
+       gtk_widget_set_sensitive(server_cfg->username, auth_mode != NET_CLIENT_AUTH_ANONYMOUS);
+       gtk_widget_set_sensitive(server_cfg->password, auth_mode == NET_CLIENT_AUTH_USER_PASS);
+       gtk_widget_set_sensitive(server_cfg->remember_pass, auth_mode == NET_CLIENT_AUTH_USER_PASS);
 
        /* invalid configuration if authentication is required, but no user name given */
-       if (sensitive && (*gtk_entry_get_text(GTK_ENTRY(server_cfg->username)) == '\0')) {
+       if ((auth_mode != NET_CLIENT_AUTH_ANONYMOUS) && (*gtk_entry_get_text(GTK_ENTRY(server_cfg->username)) 
== '\0')) {
                server_cfg->cfg_valid = FALSE;
        }
 
@@ -428,3 +487,88 @@ on_server_cfg_changed(GtkWidget *widget, LibBalsaServerCfg *server_cfg)
 
        g_signal_emit(server_cfg, changed_sig, 0);
 }
+
+
+static void
+on_server_probe(GtkWidget *widget, LibBalsaServerCfg *server_cfg)
+{
+       const gchar *server_name;
+       gboolean success;
+       NetClientProbeResult probe_res;
+       GError *error = NULL;
+       GtkWidget *msgdlg;
+       gint action;
+
+       server_name = gtk_entry_get_text(GTK_ENTRY(server_cfg->host_port));
+       g_assert(server_cfg->probe_fn != NULL);
+       success = server_cfg->probe_fn(server_name, 5, &probe_res, G_CALLBACK(libbalsa_server_check_cert), 
&error);
+       if (success) {
+               const gchar *crypt_str;
+               const gchar *auth_str;
+
+               switch (probe_res.crypt_mode) {
+               case NET_CLIENT_CRYPT_ENCRYPTED:
+                       crypt_str = _("yes (SSL/TLS)");
+                       break;
+               case NET_CLIENT_CRYPT_STARTTLS:
+                       crypt_str = _("yes (STARTTLS)");
+                       break;
+               default:
+                       crypt_str = _("no");
+               }
+
+               if ((probe_res.auth_mode & NET_CLIENT_AUTH_OAUTH2) != 0) {
+                       auth_str = _("OAuth2");
+                       probe_res.auth_mode = NET_CLIENT_AUTH_OAUTH2;
+               } else if ((probe_res.auth_mode & NET_CLIENT_AUTH_KERBEROS) != 0) {
+                       auth_str = _("Kerberos (GSSAPI)");
+                       probe_res.auth_mode = NET_CLIENT_AUTH_KERBEROS;
+               } else if ((probe_res.auth_mode & NET_CLIENT_AUTH_USER_PASS) != 0) {
+                       auth_str = _("user name and pass phrase");
+                       probe_res.auth_mode = NET_CLIENT_AUTH_USER_PASS;
+               } else {
+                       auth_str = _("none");
+                       probe_res.auth_mode = NET_CLIENT_AUTH_ANONYMOUS;
+               }
+               msgdlg = gtk_message_dialog_new(NULL,
+                       GTK_DIALOG_DESTROY_WITH_PARENT | libbalsa_dialog_flags(),
+            GTK_MESSAGE_INFO,
+            GTK_BUTTONS_NONE,
+                       _("Probe results for server %s\n"
+                         "\342\200\242 Port: %u\n"
+                         "\342\200\242 Encryption: %s\n"
+                         "\342\200\242 Authentication: %s\n"),
+                       server_name, probe_res.port, crypt_str, auth_str);
+               gtk_dialog_add_button(GTK_DIALOG(msgdlg), _("_Apply"), GTK_RESPONSE_APPLY);
+               gtk_dialog_add_button(GTK_DIALOG(msgdlg), _("_Close"), GTK_RESPONSE_CLOSE);
+       } else {
+               msgdlg = gtk_message_dialog_new(NULL,
+                       GTK_DIALOG_DESTROY_WITH_PARENT | libbalsa_dialog_flags(),
+            GTK_MESSAGE_ERROR,
+            GTK_BUTTONS_CLOSE,
+            _("Error probing server %s: %s"), server_name,
+            (error != NULL) ? error->message : _("unknown"));
+       }
+       g_clear_error(&error);
+
+       action = gtk_dialog_run(GTK_DIALOG(msgdlg));
+       gtk_widget_destroy(msgdlg);
+       if (action == GTK_RESPONSE_APPLY) {
+               gchar *buffer;
+               gchar id_buf[8];
+               const gchar *colon;
+
+               colon = strchr(server_name, ':');
+               if (colon == NULL) {
+                       buffer = g_strdup_printf("%s:%u", server_name, probe_res.port);
+               } else {
+                       buffer = g_strdup_printf("%.*s:%u", (int) (colon - server_name), server_name, 
probe_res.port);
+               }
+               gtk_entry_set_text(GTK_ENTRY(server_cfg->host_port), buffer);
+               g_free(buffer);
+
+               gtk_combo_box_set_active(GTK_COMBO_BOX(server_cfg->security), probe_res.crypt_mode - 1);
+               snprintf(id_buf, sizeof(id_buf), "%d", probe_res.auth_mode);
+               gtk_combo_box_set_active_id(GTK_COMBO_BOX(server_cfg->auth_mode), id_buf);
+       }
+}
diff --git a/libbalsa/server.c b/libbalsa/server.c
index c28e9e181..c49cd2d0d 100644
--- a/libbalsa/server.c
+++ b/libbalsa/server.c
@@ -71,7 +71,7 @@ struct _LibBalsaServerPrivate {
     gchar *cert_passphrase;
     gboolean remember_passwd;
     gboolean remember_cert_passphrase;
-    gboolean try_anonymous; /* user wants anonymous access */
+    NetClientAuthMode auth_mode;
 };
 
 static void libbalsa_server_finalize(GObject * object);
@@ -288,6 +288,7 @@ libbalsa_server_load_security_config(LibBalsaServer *server)
         want_ssl = libbalsa_conf_get_bool_with_default("SSL", &not_found);
         if (want_ssl && !not_found) {
                priv->security = NET_CLIENT_CRYPT_ENCRYPTED;
+               libbalsa_conf_clean_key("SSL");
         } else {
                int want_tls;
 
@@ -305,10 +306,10 @@ libbalsa_server_load_security_config(LibBalsaServer *server)
                        default:
                                priv->security = NET_CLIENT_CRYPT_STARTTLS;
                        }
+               libbalsa_conf_clean_key("SSL");
                }
         }
     }
-
 }
 
 
@@ -444,7 +445,13 @@ libbalsa_server_load_config(LibBalsaServer * server)
         priv->user = g_strdup(g_get_user_name());
     }
 
-    priv->try_anonymous = libbalsa_conf_get_bool("Anonymous=false");
+       priv->auth_mode = libbalsa_conf_get_int("AuthMode=2");  /* default NET_CLIENT_AUTH_USER_PASS */
+    if (libbalsa_conf_has_key("Anonymous")) {
+       if (libbalsa_conf_get_bool("Anonymous")) {
+               priv->auth_mode = NET_CLIENT_AUTH_ANONYMOUS;
+       }
+       libbalsa_conf_clean_key("Anonymous");
+    }
     priv->remember_passwd = libbalsa_conf_get_bool("RememberPasswd=false");
 
     priv->passwd = libbalsa_free_password(priv->passwd);
@@ -490,7 +497,7 @@ libbalsa_server_save_config(LibBalsaServer * server)
 
     libbalsa_conf_set_string("Server", priv->host);
     libbalsa_conf_private_set_string("Username", priv->user, FALSE);
-    libbalsa_conf_set_bool("Anonymous",          priv->try_anonymous);
+    libbalsa_conf_set_int("AuthMode", priv->auth_mode);
 
     if (priv->remember_passwd && (priv->passwd != NULL)) {
         libbalsa_conf_set_bool("RememberPasswd", TRUE);
@@ -556,7 +563,7 @@ libbalsa_server_get_auth(NetClient *client,
 
     g_debug("%s: %p %p: encrypted = %d", __func__, client, user_data,
             net_client_is_encrypted(client));
-    if (!priv->try_anonymous || (strcmp(priv->protocol, "imap") == 0)) {
+    if (priv->auth_mode != NET_CLIENT_AUTH_ANONYMOUS) {
         result = g_new0(gchar *, 3U);
         result[0] = g_strdup(priv->user);
         if (need_passwd) {
@@ -735,14 +742,14 @@ libbalsa_server_get_security(LibBalsaServer *server)
     return priv->security;
 }
 
-gboolean
-libbalsa_server_get_try_anonymous(LibBalsaServer *server)
+NetClientAuthMode
+libbalsa_server_get_auth_mode(LibBalsaServer *server)
 {
     LibBalsaServerPrivate *priv = libbalsa_server_get_instance_private(server);
 
-    g_return_val_if_fail(LIBBALSA_IS_SERVER(server), FALSE);
+    g_return_val_if_fail(LIBBALSA_IS_SERVER(server), (NetClientAuthMode) 0);
 
-    return priv->try_anonymous;
+    return priv->auth_mode;
 }
 
 gboolean
@@ -813,13 +820,13 @@ libbalsa_server_set_security(LibBalsaServer *server, NetClientCryptMode security
 }
 
 void
-libbalsa_server_set_try_anonymous(LibBalsaServer *server, gboolean try_anonymous)
+libbalsa_server_set_auth_mode(LibBalsaServer *server, NetClientAuthMode auth_mode)
 {
     LibBalsaServerPrivate *priv = libbalsa_server_get_instance_private(server);
 
     g_return_if_fail(LIBBALSA_IS_SERVER(server));
 
-    priv->try_anonymous = try_anonymous;
+    priv->auth_mode = auth_mode;
 }
 
 void
diff --git a/libbalsa/server.h b/libbalsa/server.h
index 6fa0ca5b1..34584ef31 100644
--- a/libbalsa/server.h
+++ b/libbalsa/server.h
@@ -98,7 +98,7 @@ const gchar * libbalsa_server_get_protocol(LibBalsaServer *server);
 const gchar * libbalsa_server_get_password(LibBalsaServer *server);
 const gchar * libbalsa_server_get_cert_passphrase(LibBalsaServer *server);
 NetClientCryptMode libbalsa_server_get_security(LibBalsaServer *server);
-gboolean libbalsa_server_get_try_anonymous(LibBalsaServer *server);
+NetClientAuthMode libbalsa_server_get_auth_mode(LibBalsaServer *server);
 gboolean libbalsa_server_get_client_cert(LibBalsaServer *server);
 gboolean libbalsa_server_get_remember_password(LibBalsaServer *server);
 gboolean libbalsa_server_get_remember_cert_passphrase(LibBalsaServer *server);
@@ -107,7 +107,7 @@ gboolean libbalsa_server_get_remember_cert_passphrase(LibBalsaServer *server);
 void libbalsa_server_set_protocol(LibBalsaServer *server, const gchar *protocol);
 void libbalsa_server_set_cert_file(LibBalsaServer *server, const gchar *cert_file);
 void libbalsa_server_set_security(LibBalsaServer *server, NetClientCryptMode security);
-void libbalsa_server_set_try_anonymous(LibBalsaServer *server, gboolean try_anonymous);
+void libbalsa_server_set_auth_mode(LibBalsaServer *server, NetClientAuthMode auth_mode);
 void libbalsa_server_set_remember_password(LibBalsaServer *server, gboolean remember_password);
 void libbalsa_server_set_client_cert(LibBalsaServer *server, gboolean client_cert);
 void libbalsa_server_set_remember_cert_passphrase(LibBalsaServer *server,
diff --git a/libnetclient/net-client-pop.c b/libnetclient/net-client-pop.c
index abf78b7cb..2f6456948 100644
--- a/libnetclient/net-client-pop.c
+++ b/libnetclient/net-client-pop.c
@@ -25,13 +25,49 @@ struct _NetClientPop {
 
        NetClientCryptMode crypt_mode;
        gchar *apop_banner;
-       guint auth_allowed[2];                  /** 0: encrypted, 1: unencrypted */
+       guint auth_enabled;
        gboolean can_pipelining;
        gboolean can_uidl;
        gboolean use_pipelining;
 };
 
 
+/** @name POP authentication methods
+ *
+ * Note that the availability of these authentication methods depends upon the result of the CAPABILITY 
list.  According to RFC
+ * 1939, Section 4, at least either APOP or USER/PASS @em must be supported.
+ * @{
+ */
+/** RFC 1939 "USER" and "PASS" authentication method. */
+#define NET_CLIENT_POP_AUTH_USER_PASS          0x01U
+/** RFC 1939 "APOP" authentication method. */
+#define NET_CLIENT_POP_AUTH_APOP                       0x02U
+/** RFC 5034 SASL "LOGIN" authentication method. */
+#define NET_CLIENT_POP_AUTH_LOGIN                      0x04U
+/** RFC 5034 SASL "PLAIN" authentication method. */
+#define NET_CLIENT_POP_AUTH_PLAIN                      0x08U
+/** RFC 5034 SASL "CRAM-MD5" authentication method. */
+#define NET_CLIENT_POP_AUTH_CRAM_MD5           0x10U
+/** RFC 5034 SASL "CRAM-SHA1" authentication method. */
+#define NET_CLIENT_POP_AUTH_CRAM_SHA1          0x20U
+/** RFC 4752 "GSSAPI" authentication method. */
+#define NET_CLIENT_POP_AUTH_GSSAPI                     0x40U
+/** RFC 6749 "XOAUTH2" authentication method. */
+#define NET_CLIENT_POP_AUTH_OAUTH2                     0x80U
+
+/** Mask of all authentication methods requiring user name and password. */
+#define NET_CLIENT_POP_AUTH_PASSWORD           \
+       (NET_CLIENT_POP_AUTH_USER_PASS | NET_CLIENT_POP_AUTH_APOP | NET_CLIENT_POP_AUTH_LOGIN | 
NET_CLIENT_POP_AUTH_PLAIN | \
+        NET_CLIENT_POP_AUTH_CRAM_MD5 | NET_CLIENT_POP_AUTH_CRAM_SHA1)
+
+/** Mask of all authentication methods. */
+#define NET_CLIENT_POP_AUTH_ALL                                \
+       (NET_CLIENT_POP_AUTH_PASSWORD + NET_CLIENT_POP_AUTH_GSSAPI + NET_CLIENT_POP_AUTH_OAUTH2)
+/** Mask of all authentication methods which do not require a password (Note: OAuth2 requires the 
authentication token). */
+#define NET_CLIENT_POP_AUTH_NO_PWD                     NET_CLIENT_POP_AUTH_GSSAPI
+/** @} */
+
+
 /* Note: the maximum line length of a message body downloaded from the POP3 server may be up to 998 chars, 
excluding the terminating
  * CRLF, see RFC 5322, Sect. 2.1.1.  However, it also states that "Receiving implementations would do well 
to handle an arbitrarily
  * large number of characters in a line for robustness sake", so we actually accept lines from POP3 of 
unlimited length. */
@@ -68,6 +104,7 @@ static gboolean net_client_pop_auth_apop(NetClientPop *client, const gchar* user
 static gboolean net_client_pop_auth_cram(NetClientPop *client, GChecksumType chksum_type, const gchar *user, 
const gchar *passwd,
                                                                                  GError **error);
 static gboolean net_client_pop_auth_gssapi(NetClientPop *client, const gchar *user, GError **error);
+static gboolean net_client_pop_auth_oauth2(NetClientPop *client, const gchar *user, const gchar 
*access_token, GError **error);
 static gboolean net_client_pop_retr_msg(NetClientPop *client, const NetClientPopMessageInfo *info, 
NetClientPopMsgCb callback,
                                                                                gpointer user_data, GError 
**error);
 
@@ -96,16 +133,127 @@ net_client_pop_new(const gchar *host, guint16 port, NetClientCryptMode crypt_mod
 
 
 gboolean
-net_client_pop_allow_auth(NetClientPop *client, gboolean encrypted, guint allow_auth)
+net_client_pop_probe(const gchar *host, guint timeout_secs, NetClientProbeResult *result, GCallback cert_cb, 
GError **error)
 {
+       guint16 probe_ports[] = {995U, 110U, 0U};               /* pop3s, pop3 */
+       gchar *host_only;
+       gchar *colon;
+       gboolean retval = FALSE;
+       gint check_id;
+
        /* paranoia check */
+       g_return_val_if_fail((host != NULL) && (result != NULL), FALSE);
+
+       host_only = g_strdup(host);
+       colon = strchr(host_only, ':');
+       if (colon != NULL) {
+               colon[0] = '\0';
+       }
+
+       if (!net_client_host_reachable(host_only, error)) {
+               g_free(host_only);
+               return FALSE;
+       }
+
+       for (check_id = 0; !retval && (probe_ports[check_id] > 0U); check_id++) {
+               NetClientPop *client;
+
+               g_debug("%s: probing %s:%u…", __func__, host_only, probe_ports[check_id]);
+               client = net_client_pop_new(host_only, probe_ports[check_id], NET_CLIENT_CRYPT_NONE, FALSE);
+               net_client_set_timeout(NET_CLIENT(client), timeout_secs);
+               if (net_client_connect(NET_CLIENT(client), NULL)) {
+                       gboolean this_success;
+                       guint auth_supported = 0U;
+                       gboolean can_starttls = FALSE;
+
+                       if (cert_cb != NULL) {
+                               g_signal_connect(client, "cert-check", cert_cb, client);
+                       }
+                       if (check_id == 0) {    /* pop3s */
+                               this_success = net_client_start_tls(NET_CLIENT(client), NULL);
+                       } else {
+                               this_success = TRUE;
+                       }
+
+                       /* get the greeting */
+                       if (this_success) {
+                               this_success = net_client_pop_read_reply(client, NULL, error);
+                       }
+
+                       /* send CAPA and read the capabilities of the server */
+                       if (this_success) {
+                               net_client_pop_get_capa(client, &auth_supported);
+
+                       }
+
+                       /* try to perform STARTTLS unless we are already encrypted, and send CAPA again */
+                       if (this_success && (check_id != 0)) {
+                               can_starttls = net_client_pop_starttls(client, NULL);
+                               if (can_starttls) {
+                                       net_client_pop_get_capa(client, &auth_supported);
+                               }
+                       }
+
+                       /* evaluate on success */
+                       if (this_success) {
+                               result->port = probe_ports[check_id];
+
+                               if (check_id == 0) {
+                                       result->crypt_mode = NET_CLIENT_CRYPT_ENCRYPTED;
+                               } else if (can_starttls) {
+                                       result->crypt_mode = NET_CLIENT_CRYPT_STARTTLS;
+                               } else {
+                                       result->crypt_mode = NET_CLIENT_CRYPT_NONE;
+                               }
+
+                               /* RFC 1939, Section 4, require at least either APOP or USER/PASS */
+                               result->auth_mode = NET_CLIENT_AUTH_USER_PASS;
+                               if ((auth_supported & NET_CLIENT_POP_AUTH_GSSAPI) != 0U) {
+                                       result->auth_mode |= NET_CLIENT_AUTH_KERBEROS;
+                               }
+                               if ((auth_supported & NET_CLIENT_POP_AUTH_OAUTH2) != 0U) {
+                                       result->auth_mode |= NET_CLIENT_AUTH_OAUTH2;
+                               }
+                               retval = TRUE;
+                       }
+               }
+               g_object_unref(client);
+       }
+
+       if (!retval) {
+               g_set_error(error, NET_CLIENT_ERROR_QUARK, NET_CLIENT_PROBE_FAILED,
+                       _("the server %s does not offer the POP3 service at port 995 or 110"), host_only);
+       }
+
+       g_free(host_only);
+
+       return retval;
+}
+
+
+gboolean
+net_client_pop_set_auth_mode(NetClientPop *client, NetClientAuthMode auth_mode, gboolean disable_apop)
+{
        g_return_val_if_fail(NET_IS_CLIENT_POP(client), FALSE);
-       if (encrypted) {
-               client->auth_allowed[0] = allow_auth;
-       } else {
-               client->auth_allowed[1] = allow_auth;
+
+       client->auth_enabled = 0U;
+       if ((auth_mode & NET_CLIENT_AUTH_USER_PASS) != 0U) {
+               client->auth_enabled |= NET_CLIENT_POP_AUTH_PASSWORD;
+               if (disable_apop) {
+                       client->auth_enabled &= ~NET_CLIENT_POP_AUTH_APOP;
+               }
        }
-       return TRUE;
+#if defined(HAVE_GSSAPI)
+       if ((auth_mode & NET_CLIENT_AUTH_KERBEROS) != 0U) {
+               client->auth_enabled |= NET_CLIENT_POP_AUTH_GSSAPI;
+       }
+#endif
+#if defined (HAVE_OAUTH2)
+       if ((auth_mode & NET_CLIENT_AUTH_OAUTH2) != 0U) {
+               client->auth_enabled |= NET_CLIENT_POP_AUTH_OAUTH2;
+       }
+#endif
+       return (client->auth_enabled != 0U);
 }
 
 
@@ -182,20 +330,6 @@ net_client_pop_connect(NetClientPop *client, gchar **greeting, GError **error)
                        result = net_client_pop_auth(client, auth_data[0], auth_data[1], auth_supported, 
error);
                        net_client_free_authstr(auth_data[0]);
                        net_client_free_authstr(auth_data[1]);
-
-                       /* if passwordless authentication failed, try again with password */
-                       if (!result && !need_pwd) {
-                               g_debug("passwordless authentication failed, retry w/ password: emit 'auth' 
signal for client %p", client);
-                               g_clear_error(error);
-                               g_free(auth_data);
-                               g_signal_emit_by_name(client, "auth", TRUE, &auth_data);        /*lint !e730  
  passing a gboolean is intended here */
-                               if ((auth_data != NULL) && (auth_data[0] != NULL)) {
-                                       result = net_client_pop_auth(client, auth_data[0], auth_data[1],
-                                               auth_supported & ~NET_CLIENT_POP_AUTH_NO_PWD, error);
-                                       net_client_free_authstr(auth_data[0]);
-                                       net_client_free_authstr(auth_data[1]);
-                               }
-                       }
                }
                g_free(auth_data);
        }
@@ -399,8 +533,7 @@ net_client_pop_class_init(NetClientPopClass *klass)
 static void
 net_client_pop_init(NetClientPop *self)
 {
-       self->auth_allowed[0] = NET_CLIENT_POP_AUTH_ALL;
-       self->auth_allowed[1] = NET_CLIENT_POP_AUTH_SAFE;
+       self->auth_enabled = NET_CLIENT_POP_AUTH_ALL;
 }
 
 
@@ -498,11 +631,8 @@ net_client_pop_auth(NetClientPop *client, const gchar *user, const gchar *passwd
        gboolean result = FALSE;
        guint auth_mask;
 
-       if (net_client_is_encrypted(NET_CLIENT(client))) {
-               auth_mask = client->auth_allowed[0] & auth_supported;
-       } else {
-               auth_mask = client->auth_allowed[1] & auth_supported;
-       }
+       /* calculate the possible authentication methods */
+       auth_mask = client->auth_enabled & auth_supported;
 
        if (auth_mask == 0U) {
                g_set_error(error, NET_CLIENT_POP_ERROR_QUARK, (gint) NET_CLIENT_ERROR_POP_NO_AUTH,
@@ -511,7 +641,9 @@ net_client_pop_auth(NetClientPop *client, const gchar *user, const gchar *passwd
                g_set_error(error, NET_CLIENT_POP_ERROR_QUARK, (gint) NET_CLIENT_ERROR_POP_NO_AUTH, 
_("password required"));
        } else {
                /* first try authentication methods w/o password, then safe ones, and finally the plain-text 
methods */
-               if ((auth_mask & NET_CLIENT_POP_AUTH_GSSAPI) != 0U) {
+               if ((auth_mask & NET_CLIENT_POP_AUTH_OAUTH2) != 0U) {
+                       result = net_client_pop_auth_oauth2(client, user, passwd, error);
+               } else if ((auth_mask & NET_CLIENT_POP_AUTH_GSSAPI) != 0U) {
                        result = net_client_pop_auth_gssapi(client, user, error);
                } else if ((auth_mask & NET_CLIENT_POP_AUTH_CRAM_SHA1) != 0U) {
                        result = net_client_pop_auth_cram(client, G_CHECKSUM_SHA1, user, passwd, error);
@@ -698,6 +830,40 @@ net_client_pop_auth_gssapi(NetClientPop G_GNUC_UNUSED *client, const gchar G_GNU
 #endif  /* HAVE_GSSAPI */
 
 
+#if defined(HAVE_OAUTH2)
+
+static gboolean
+net_client_pop_auth_oauth2(NetClientPop *client, const gchar *user, const gchar *access_token, GError 
**error)
+{
+       gboolean result ;
+       gchar *base64_buf;
+
+       base64_buf = net_client_auth_oauth2_calc(user, access_token);
+       if (base64_buf != NULL) {
+               result = net_client_pop_execute_sasl(client, "AUTH XOAUTH2", NULL, error);
+               if (result) {
+                       result = net_client_pop_execute(client, "%s", NULL, error, base64_buf);
+                       // FIXME - grab the JSON response on error
+               }
+               net_client_free_authstr(base64_buf);
+       } else {
+               result = FALSE;
+       }
+       return result;
+}
+
+#else
+
+static gboolean
+net_client_pop_auth_oauth2(NetClientPop G_GNUC_UNUSED *client, const gchar G_GNUC_UNUSED *user,
+       const gcharG_GNUC_UNUSED *access_token, GError G_GNUC_UNUSED **error)
+{
+       g_assert_not_reached();                 /* this should never happen! */
+       return FALSE;                                   /* never reached, make gcc happy */
+}
+
+#endif  /* HAVE_OAUTH2 */
+
 /* Note: if supplied, challenge is never NULL on success */
 static gboolean
 net_client_pop_execute_sasl(NetClientPop *client, const gchar *request_fmt, gchar **challenge, GError 
**error, ...)
@@ -769,6 +935,10 @@ net_client_pop_get_capa(NetClientPop *client, guint *auth_supported)
 #if defined(HAVE_GSSAPI)
                                        } else if (strcmp(auth[n], "GSSAPI") == 0) {
                                                *auth_supported |= NET_CLIENT_POP_AUTH_GSSAPI;
+#endif
+#if defined(HAVE_OAUTH2)
+                                       } else if (strcmp(auth[n], "XOAUTH2") == 0) {
+                                               *auth_supported |= NET_CLIENT_POP_AUTH_OAUTH2;
 #endif
                                        } else {
                                                /* other auth methods are ignored for the time being */
diff --git a/libnetclient/net-client-pop.h b/libnetclient/net-client-pop.h
index 527e009f1..58faeff60 100644
--- a/libnetclient/net-client-pop.h
+++ b/libnetclient/net-client-pop.h
@@ -17,6 +17,7 @@
 
 
 #include "net-client.h"
+#include "net-client-utils.h"
 
 
 G_BEGIN_DECLS
@@ -43,37 +44,6 @@ enum _NetClientPopError {
 };
 
 
-/** @name POP authentication methods
- *
- * Note that the availability of these authentication methods depends upon the result of the CAPABILITY 
list.  According to RFC
- * 1939, Section 4, at least either APOP or USER/PASS @em must be supported.
- * @{
- */
-/** RFC 1939 "USER" and "PASS" authentication method. */
-#define NET_CLIENT_POP_AUTH_USER_PASS          0x01U
-/** RFC 1939 "APOP" authentication method. */
-#define NET_CLIENT_POP_AUTH_APOP                       0x02U
-/** RFC 5034 SASL "LOGIN" authentication method. */
-#define NET_CLIENT_POP_AUTH_LOGIN                      0x04U
-/** RFC 5034 SASL "PLAIN" authentication method. */
-#define NET_CLIENT_POP_AUTH_PLAIN                      0x08U
-/** RFC 5034 SASL "CRAM-MD5" authentication method. */
-#define NET_CLIENT_POP_AUTH_CRAM_MD5           0x10U
-/** RFC 5034 SASL "CRAM-SHA1" authentication method. */
-#define NET_CLIENT_POP_AUTH_CRAM_SHA1          0x20U
-/** RFC 4752 "GSSAPI" authentication method. */
-#define NET_CLIENT_POP_AUTH_GSSAPI                     0x40U
-/** Mask of all safe authentication methods, i.e. all methods which do not send the cleartext password. */
-#define NET_CLIENT_POP_AUTH_SAFE                       \
-       (NET_CLIENT_POP_AUTH_APOP + NET_CLIENT_POP_AUTH_CRAM_MD5 + NET_CLIENT_POP_AUTH_CRAM_SHA1 + 
NET_CLIENT_POP_AUTH_GSSAPI)
-/** Mask of all authentication methods. */
-#define NET_CLIENT_POP_AUTH_ALL                                \
-       (NET_CLIENT_POP_AUTH_USER_PASS + NET_CLIENT_POP_AUTH_PLAIN + NET_CLIENT_POP_AUTH_LOGIN + 
NET_CLIENT_POP_AUTH_SAFE)
-/** Mask of all authentication methods which do not require a password. */
-#define NET_CLIENT_POP_AUTH_NO_PWD                     NET_CLIENT_POP_AUTH_GSSAPI
-/** @} */
-
-
 /** @brief Message information
  *
  * This structure is returned in a GList by net_client_pop_list() and contains information about on message 
in the remote mailbox.
@@ -115,6 +85,21 @@ typedef gboolean (*NetClientPopMsgCb)(const gchar *buffer, gssize count, gsize l
                                                                          gpointer user_data, GError **error);
 
 
+/** @brief Probe a POP3 server
+ *
+ * @param host host name or IP address of the server to probe
+ * @param timeout_secs time-out in seconds
+ * @param result filled with the probe results
+ * @param cert_cb optional server certificate acceptance callback
+ * @param error filled with error information if probing fails
+ * @return TRUE if probing the passed server was successful, FALSE if not
+ *
+ * Probe the passed server by trying to connect to the standard ports (in this order) 995 and 110.
+ */
+gboolean net_client_pop_probe(const gchar *host, guint timeout_secs, NetClientProbeResult *result, GCallback 
cert_cb,
+       GError **error);
+
+
 /** @brief Create a new POP network client
  *
  * @param host host name or IP address to connect
@@ -129,16 +114,17 @@ NetClientPop *net_client_pop_new(const gchar *host, guint16 port, NetClientCrypt
 /** @brief Set allowed POP AUTH methods
  *
  * @param client POP network client object
- * @param encrypted set allowed methods for encrypted or unencrypted connections
- * @param allow_auth mask of allowed authentication methods
- * @return TRUE on success or FALSE on error
+ * @param auth_mode mask of allowed authentication methods
+ * @param disable_apop TRUE to disable APOP authentication which is not supported by some broken servers
+ * @return TRUE on success or FALSE on error or if no authentication method is allowed
  *
- * Set the allowed authentication methods for the passed connection.  The default is @ref 
NET_CLIENT_POP_AUTH_ALL for both encrypted
- * and unencrypted connections.
+ * Set the allowed authentication methods for the passed connection.  The default is to enable all 
authentication methods.
  *
- * @note Call this function @em before calling net_client_pop_connect().
+ * @note Call this function @em before calling net_client_pop_connect().\n
+ *       POP3 does not allow anonymous access without authentication, i.e. the flag @ref 
NET_CLIENT_AUTH_ANONYMOUS in the argument
+ *       is ignored.
  */
-gboolean net_client_pop_allow_auth(NetClientPop *client, gboolean encrypted, guint allow_auth);
+gboolean net_client_pop_set_auth_mode(NetClientPop *client, NetClientAuthMode auth_mode, gboolean 
disable_apop);
 
 
 /** @brief Connect a POP network client
@@ -151,9 +137,10 @@ gboolean net_client_pop_allow_auth(NetClientPop *client, gboolean encrypted, gui
  * Connect the remote POP server, initialise the encryption if requested, and emit the @ref auth signal to 
request authentication
  * information.  Simply ignore the signal for an unauthenticated connection.
  *
- * The function will try only @em one authentication method supported by the server and enabled for the 
current encryption state
- * (see net_client_pop_allow_auth() and \ref NET_CLIENT_POP_AUTH_ALL etc.).  The priority is, from highest 
to lowest, GSSAPI (if
- * configured), CRAM-SHA1, CRAM-MD5, APOP, PLAIN, USER/PASS or LOGIN.
+ * The function will try only @em one authentication method which is both supported by the server and 
enabled by calling
+ * net_client_pop_set_auth_mode().  The default is to try all methods, from highest to lowest, OAuth2 (if 
configured), GSSAPI (if
+ * configured), CRAM-SHA1, CRAM-MD5, APOP, PLAIN, USER/PASS or LOGIN.  It is up to the caller to ensure 
encryption or a connection
+ * to @c localhost if one of the plain text methods shall be used.
  *
  * In order to shut down a successfully established connection, just call <tt>g_object_unref()</tt> on the 
POP network client
  * object.
diff --git a/libnetclient/net-client-smtp.c b/libnetclient/net-client-smtp.c
index 3d20fa17a..be0d41332 100644
--- a/libnetclient/net-client-smtp.c
+++ b/libnetclient/net-client-smtp.c
@@ -23,7 +23,7 @@ struct _NetClientSmtp {
     NetClient parent;
 
        NetClientCryptMode crypt_mode;
-       guint auth_allowed[2];                  /** 0: encrypted, 1: unencrypted */
+       guint auth_enabled;                                     /* Note: 0 = anonymous connection w/o 
authentication */
        gboolean can_dsn;
        gboolean data_state;
 };
@@ -46,6 +46,36 @@ typedef struct {
 } smtp_rcpt_t;
 
 
+/** @name SMTP authentication methods
+ * @{
+ */
+/** Anonymous access (no authentication) */
+#define NET_CLIENT_SMTP_AUTH_NONE                      0x01U
+/** RFC 4616 "PLAIN" authentication method. */
+#define NET_CLIENT_SMTP_AUTH_PLAIN                     0x02U
+/** "LOGIN" authentication method. */
+#define NET_CLIENT_SMTP_AUTH_LOGIN                     0x04U
+/** RFC 2195 "CRAM-MD5" authentication method. */
+#define NET_CLIENT_SMTP_AUTH_CRAM_MD5          0x08U
+/** RFC xxxx "CRAM-SHA1" authentication method. */
+#define NET_CLIENT_SMTP_AUTH_CRAM_SHA1         0x10U
+/** RFC 4752 "GSSAPI" authentication method. */
+#define NET_CLIENT_SMTP_AUTH_GSSAPI                    0x20U
+/** RFC 6749 "XOAUTH2" authentication method. */
+#define NET_CLIENT_SMTP_AUTH_OAUTH2                    0x40U
+
+/** Mask of all authentication methods requiring user name and password. */
+#define NET_CLIENT_SMTP_AUTH_PASSWORD          \
+       (NET_CLIENT_SMTP_AUTH_PLAIN | NET_CLIENT_SMTP_AUTH_LOGIN | NET_CLIENT_SMTP_AUTH_CRAM_MD5 | 
NET_CLIENT_SMTP_AUTH_CRAM_SHA1)
+
+/** Mask of all authentication methods. */
+#define NET_CLIENT_SMTP_AUTH_ALL                       \
+       (NET_CLIENT_SMTP_AUTH_NONE | NET_CLIENT_SMTP_AUTH_PASSWORD | NET_CLIENT_SMTP_AUTH_GSSAPI | 
NET_CLIENT_SMTP_AUTH_OAUTH2)
+/** Mask of all authentication methods which do not require a password (Note: OAuth2 requires the 
authentication token). */
+#define NET_CLIENT_SMTP_AUTH_NO_PWD                    NET_CLIENT_SMTP_AUTH_GSSAPI
+/** @} */
+
+
 /* Note: RFC 5321 defines a maximum line length of 512 octets, including the terminating CRLF.  However, RFC 
4954, Sect. 4. defines
  * 12288 octets as safe maximum length for SASL authentication. */
 #define MAX_SMTP_LINE_LEN                      12288U
@@ -68,6 +98,7 @@ static gboolean net_client_smtp_auth_login(NetClientSmtp *client, const gchar *u
 static gboolean net_client_smtp_auth_cram(NetClientSmtp *client, GChecksumType chksum_type, const gchar 
*user, const gchar *passwd,
                                                                                  GError **error);
 static gboolean net_client_smtp_auth_gssapi(NetClientSmtp *client, const gchar *user, GError **error);
+static gboolean net_client_smtp_auth_oauth2(NetClientSmtp *client, const gchar *user, const gchar 
*access_token, GError **error);
 static gboolean net_client_smtp_read_reply(NetClientSmtp *client, gint expect_code, gchar **last_reply, 
GError **error);
 static gboolean net_client_smtp_eval_rescode(gint res_code, const gchar *reply, GError **error);
 static gchar *net_client_smtp_dsn_to_string(const NetClientSmtp *client, NetClientSmtpDsnMode dsn_mode);
@@ -97,16 +128,130 @@ net_client_smtp_new(const gchar *host, guint16 port, NetClientCryptMode crypt_mo
 
 
 gboolean
-net_client_smtp_allow_auth(NetClientSmtp *client, gboolean encrypted, guint allow_auth)
+net_client_smtp_set_auth_mode(NetClientSmtp *client, NetClientAuthMode auth_mode)
 {
-       /* paranoia check */
        g_return_val_if_fail(NET_IS_CLIENT_SMTP(client), FALSE);
-       if (encrypted) {
-               client->auth_allowed[0] = allow_auth;
-       } else {
-               client->auth_allowed[1] = allow_auth;
+
+       client->auth_enabled = 0U;
+       if ((auth_mode & NET_CLIENT_AUTH_ANONYMOUS) != 0U) {
+               client->auth_enabled |= NET_CLIENT_SMTP_AUTH_NONE;
        }
-       return TRUE;
+       if ((auth_mode & NET_CLIENT_AUTH_USER_PASS) != 0U) {
+               client->auth_enabled |= NET_CLIENT_SMTP_AUTH_PASSWORD;
+       }
+#if defined(HAVE_GSSAPI)
+       if ((auth_mode & NET_CLIENT_AUTH_KERBEROS) != 0U) {
+               client->auth_enabled |= NET_CLIENT_SMTP_AUTH_GSSAPI;
+       }
+#endif
+#if defined (HAVE_OAUTH2)
+       if ((auth_mode & NET_CLIENT_AUTH_OAUTH2) != 0U) {
+               client->auth_enabled |= NET_CLIENT_SMTP_AUTH_OAUTH2;
+       }
+#endif
+       return (client->auth_enabled != 0U);
+}
+
+
+gboolean
+net_client_smtp_probe(const gchar *host, guint timeout_secs, NetClientProbeResult *result, GCallback 
cert_cb, GError **error)
+{
+       guint16 probe_ports[] = {587U, 25U, 465U, 0U};          /* submission, smtp, smtps */
+       gchar *host_only;
+       gchar *colon;
+       gboolean retval = FALSE;
+       gint check_id;
+
+       /* paranoia check */
+       g_return_val_if_fail((host != NULL) && (result != NULL), FALSE);
+
+       host_only = g_strdup(host);
+       colon = strchr(host_only, ':');
+       if (colon != NULL) {
+               colon[0] = '\0';
+       }
+
+       if (!net_client_host_reachable(host_only, error)) {
+               g_free(host_only);
+               return FALSE;
+       }
+
+       for (check_id = 0; !retval && (probe_ports[check_id] > 0U); check_id++) {
+               NetClientSmtp *client;
+
+               g_debug("%s: probing %s:%u…", __func__, host_only, probe_ports[check_id]);
+               client = net_client_smtp_new(host_only, probe_ports[check_id], NET_CLIENT_CRYPT_NONE);
+               net_client_set_timeout(NET_CLIENT(client), timeout_secs);
+               if (net_client_connect(NET_CLIENT(client), NULL)) {
+                       gboolean this_success;
+                       guint auth_supported;
+                       gboolean can_starttls;
+
+                       if (cert_cb != NULL) {
+                               g_signal_connect(client, "cert-check", cert_cb, client);
+                       }
+                       if (check_id == 2) {    /* smtps */
+                               this_success = net_client_start_tls(NET_CLIENT(client), NULL);
+                       } else {
+                               this_success = TRUE;
+                       }
+
+                       /* get the greeting */
+                       if (this_success) {
+                               this_success = net_client_smtp_read_reply(client, 220, NULL, NULL);
+                       }
+
+                       /* send EHLO and read the capabilities of the server */
+                       if (this_success) {
+                               this_success = net_client_smtp_ehlo(client, &auth_supported, &can_starttls, 
NULL);
+                       }
+
+                       /* try to perform STARTTLS if supported, and send EHLO again */
+                       if (this_success && can_starttls) {
+                               can_starttls = net_client_smtp_starttls(client, NULL);
+                               if (can_starttls) {
+                                       gboolean dummy;
+
+                                       can_starttls = net_client_smtp_ehlo(client, &auth_supported, &dummy, 
NULL);
+                               }
+                       }
+
+                       /* evaluate on success */
+                       if (this_success) {
+                               result->port = probe_ports[check_id];
+
+                               if (check_id == 2) {
+                                       result->crypt_mode = NET_CLIENT_CRYPT_ENCRYPTED;
+                               } else if (can_starttls) {
+                                       result->crypt_mode = NET_CLIENT_CRYPT_STARTTLS;
+                               } else {
+                                       result->crypt_mode = NET_CLIENT_CRYPT_NONE;
+                               }
+
+                               result->auth_mode = 0U;
+                               if ((auth_supported & NET_CLIENT_SMTP_AUTH_PASSWORD) != 0U) {
+                                       result->auth_mode |= NET_CLIENT_AUTH_USER_PASS;
+                               }
+                               if ((auth_supported & NET_CLIENT_SMTP_AUTH_GSSAPI) != 0U) {
+                                       result->auth_mode |= NET_CLIENT_AUTH_KERBEROS;
+                               }
+                               if ((auth_supported & NET_CLIENT_SMTP_AUTH_OAUTH2) != 0U) {
+                                       result->auth_mode |= NET_CLIENT_AUTH_OAUTH2;
+                               }
+                               retval = TRUE;
+                       }
+               }
+               g_object_unref(client);
+       }
+
+       if (!retval) {
+               g_set_error(error, NET_CLIENT_ERROR_QUARK, NET_CLIENT_PROBE_FAILED,
+                       _("the server %s does not offer the SMTP service at port 587, 25 or 465"), host_only);
+       }
+
+       g_free(host_only);
+
+       return retval;
 }
 
 
@@ -154,8 +299,8 @@ net_client_smtp_connect(NetClientSmtp *client, gchar **greeting, GError **error)
                }
        }
 
-       /* authenticate if we were successful so far */
-       if (result) {
+       /* authenticate if we were successful so far, unless anonymous access is configured */
+       if (result && (client->auth_enabled != NET_CLIENT_SMTP_AUTH_NONE)) {
                gchar **auth_data;
                gboolean need_pwd;
 
@@ -167,20 +312,6 @@ net_client_smtp_connect(NetClientSmtp *client, gchar **greeting, GError **error)
                        result = net_client_smtp_auth(client, auth_data[0], auth_data[1], auth_supported, 
error);
                        net_client_free_authstr(auth_data[0]);
                        net_client_free_authstr(auth_data[1]);
-
-                       /* if passwordless authentication failed, try again with password */
-                       if (!result && !need_pwd) {
-                               g_debug("passwordless authentication failed, retry w/ password: emit 'auth' 
signal for client %p", client);
-                               g_clear_error(error);
-                               g_free(auth_data);
-                               g_signal_emit_by_name(client, "auth", TRUE, &auth_data);        /*lint !e730  
  passing a gboolean is intended here */
-                               if ((auth_data != NULL) && (auth_data[0] != NULL)) {
-                                       result = net_client_smtp_auth(client, auth_data[0], auth_data[1],
-                                               auth_supported & ~NET_CLIENT_SMTP_AUTH_NO_PWD, error);
-                                       net_client_free_authstr(auth_data[0]);
-                                       net_client_free_authstr(auth_data[1]);
-                               }
-                       }
                }
                g_free(auth_data);
        }
@@ -360,8 +491,7 @@ net_client_smtp_class_init(NetClientSmtpClass *klass)
 static void
 net_client_smtp_init(NetClientSmtp *self)
 {
-       self->auth_allowed[0] = NET_CLIENT_SMTP_AUTH_ALL;
-       self->auth_allowed[1] = NET_CLIENT_SMTP_AUTH_SAFE;
+       self->auth_enabled = NET_CLIENT_SMTP_AUTH_ALL;
 }
 
 
@@ -402,11 +532,7 @@ net_client_smtp_auth(NetClientSmtp *client, const gchar *user, const gchar *pass
        guint auth_mask;
 
        /* calculate the possible authentication methods */
-       if (net_client_is_encrypted(NET_CLIENT(client))) {
-               auth_mask = client->auth_allowed[0] & auth_supported;
-       } else {
-               auth_mask = client->auth_allowed[1] & auth_supported;
-       }
+       auth_mask = client->auth_enabled & auth_supported;
 
        if (auth_mask == 0U) {
                g_set_error(error, NET_CLIENT_SMTP_ERROR_QUARK, (gint) NET_CLIENT_ERROR_SMTP_NO_AUTH,
@@ -415,7 +541,9 @@ net_client_smtp_auth(NetClientSmtp *client, const gchar *user, const gchar *pass
                g_set_error(error, NET_CLIENT_SMTP_ERROR_QUARK, (gint) NET_CLIENT_ERROR_SMTP_NO_AUTH, 
_("password required"));
        } else {
                /* first try authentication methods w/o password, then safe ones, and finally the plain-text 
methods */
-               if ((auth_mask & NET_CLIENT_SMTP_AUTH_GSSAPI) != 0U) {
+               if ((auth_mask & NET_CLIENT_SMTP_AUTH_OAUTH2) != 0U) {
+                       result = net_client_smtp_auth_oauth2(client, user, passwd, error);
+               } else if ((auth_mask & NET_CLIENT_SMTP_AUTH_GSSAPI) != 0U) {
                        result = net_client_smtp_auth_gssapi(client, user, error);
                } else if ((auth_mask & NET_CLIENT_SMTP_AUTH_CRAM_SHA1) != 0U) {
                        result = net_client_smtp_auth_cram(client, G_CHECKSUM_SHA1, user, passwd, error);
@@ -437,7 +565,7 @@ net_client_smtp_auth(NetClientSmtp *client, const gchar *user, const gchar *pass
 static gboolean
 net_client_smtp_auth_plain(NetClientSmtp *client, const gchar *user, const gchar *passwd, GError **error)
 {
-       gboolean result ;
+       gboolean result;
        gchar *base64_buf;
 
        base64_buf = net_client_auth_plain_calc(user, passwd);
@@ -552,6 +680,40 @@ net_client_smtp_auth_gssapi(NetClientSmtp G_GNUC_UNUSED *client, const gchar G_G
 #endif  /* HAVE_GSSAPI */
 
 
+#if defined(HAVE_OAUTH2)
+
+static gboolean
+net_client_smtp_auth_oauth2(NetClientSmtp *client, const gchar *user, const gchar *access_token, GError 
**error)
+{
+       gboolean result;
+       gchar *base64_buf;
+
+       base64_buf = net_client_auth_oauth2_calc(user, access_token);
+       if (base64_buf != NULL) {
+               result = net_client_smtp_execute(client, "AUTH XOAUTH2 %s", NULL, error, base64_buf);
+               // FIXME - grab the JSON response on error
+               net_client_free_authstr(base64_buf);
+       } else {
+               result = FALSE;
+       }
+
+       return result;
+}
+
+#else
+
+/*lint -e{715,818} */
+static gboolean
+net_client_smtp_auth_oauth2(NetClientSmtp G_GNUC_UNUSED *client, const gchar G_GNUC_UNUSED *user,
+       const gchar G_GNUC_UNUSED *access_token, GError G_GNUC_UNUSED **error)
+{
+       g_assert_not_reached();                 /* this should never happen! */
+       return FALSE;                                   /* never reached, make gcc happy */
+}
+
+#endif  /* HAVE_GSSAPI */
+
+
 /* note: if supplied, last_reply is never NULL on success */
 static gboolean
 net_client_smtp_execute(NetClientSmtp *client, const gchar *request_fmt, gchar **last_reply, GError **error, 
...)
@@ -621,6 +783,10 @@ net_client_smtp_ehlo(NetClientSmtp *client, guint *auth_supported, gboolean *can
 #if defined(HAVE_GSSAPI)
                                                } else if (strcmp(auth[n], "GSSAPI") == 0) {
                                                        *auth_supported |= NET_CLIENT_SMTP_AUTH_GSSAPI;
+#endif
+#if defined (HAVE_OAUTH2)
+                                               } else if (strcmp(auth[n], "XOAUTH2") == 0) {
+                                                       *auth_supported |= NET_CLIENT_SMTP_AUTH_OAUTH2;
 #endif
                                                } else {
                                                        /* other auth methods are ignored for the time being 
*/
diff --git a/libnetclient/net-client-smtp.h b/libnetclient/net-client-smtp.h
index 0adabdbc7..c26d61c1a 100644
--- a/libnetclient/net-client-smtp.h
+++ b/libnetclient/net-client-smtp.h
@@ -17,6 +17,7 @@
 
 
 #include "net-client.h"
+#include "net-client-utils.h"
 
 
 G_BEGIN_DECLS
@@ -45,30 +46,6 @@ enum _NetClientSmtpError {
 };
 
 
-/** @name SMTP authentication methods
- * @{
- */
-/** RFC 4616 "PLAIN" authentication method. */
-#define NET_CLIENT_SMTP_AUTH_PLAIN                     0x01U
-/** "LOGIN" authentication method. */
-#define NET_CLIENT_SMTP_AUTH_LOGIN                     0x02U
-/** RFC 2195 "CRAM-MD5" authentication method. */
-#define NET_CLIENT_SMTP_AUTH_CRAM_MD5          0x04U
-/** RFC xxxx "CRAM-SHA1" authentication method. */
-#define NET_CLIENT_SMTP_AUTH_CRAM_SHA1         0x08U
-/** RFC 4752 "GSSAPI" authentication method. */
-#define NET_CLIENT_SMTP_AUTH_GSSAPI                    0x10U
-/** Mask of all safe authentication methods, i.e. all methods which do not send the cleartext password. */
-#define NET_CLIENT_SMTP_AUTH_SAFE                      \
-       (NET_CLIENT_SMTP_AUTH_CRAM_MD5 + NET_CLIENT_SMTP_AUTH_CRAM_SHA1 + NET_CLIENT_SMTP_AUTH_GSSAPI)
-/** Mask of all authentication methods. */
-#define NET_CLIENT_SMTP_AUTH_ALL                       \
-       (NET_CLIENT_SMTP_AUTH_PLAIN + NET_CLIENT_SMTP_AUTH_LOGIN + NET_CLIENT_SMTP_AUTH_SAFE)
-/** Mask of all authentication methods which do not require a password. */
-#define NET_CLIENT_SMTP_AUTH_NO_PWD                    NET_CLIENT_SMTP_AUTH_GSSAPI
-/** @} */
-
-
 /** @brief Delivery Status Notification mode
  *
  * See <a href="https://tools.ietf.org/html/rfc3461";>RFC 3461</a> for a description of Delivery Status 
Notifications (DSNs).  The
@@ -99,6 +76,21 @@ enum _NetClientSmtpDsnMode {
 typedef gssize (*NetClientSmtpSendCb)(gchar *buffer, gsize count, gpointer user_data, GError **error);
 
 
+/** @brief Probe a SMTP server
+ *
+ * @param host host name or IP address of the server to probe
+ * @param timeout_secs time-out in seconds
+ * @param result filled with the probe results
+ * @param cert_cb optional server certificate acceptance callback
+ * @param error filled with error information if probing fails
+ * @return TRUE if probing the passed server was successful, FALSE if not
+ *
+ * Probe the passed server by trying to connect to the standard ports (in this order) 587, 25 and 465.
+ */
+gboolean net_client_smtp_probe(const gchar *host, guint timeout_secs, NetClientProbeResult *result, 
GCallback cert_cb,
+       GError **error);
+
+
 /** @brief Create a new SMTP network client
  *
  * @param host host name or IP address to connect
@@ -112,16 +104,14 @@ NetClientSmtp *net_client_smtp_new(const gchar *host, guint16 port, NetClientCry
 /** @brief Set allowed SMTP AUTH methods
  *
  * @param client SMTP network client object
- * @param encrypted set allowed methods for encrypted or unencrypted connections
- * @param allow_auth mask of allowed authentication methods
+ * @param auth_mode mask of allowed authentication methods
  * @return TRUE on success or FALSE on error
  *
- * Set the allowed authentication methods for the passed connection.  The default is @ref 
NET_CLIENT_SMTP_AUTH_ALL for encrypted and
- * @ref NET_CLIENT_SMTP_AUTH_SAFE for unencrypted connections, respectively.
+ * Set the allowed authentication methods for the passed connection.  The default is to enable all 
authentication methods.
  *
  * @note Call this function @em before calling net_client_smtp_connect().
  */
-gboolean net_client_smtp_allow_auth(NetClientSmtp *client, gboolean encrypted, guint allow_auth);
+gboolean net_client_smtp_set_auth_mode(NetClientSmtp *client, NetClientAuthMode auth_mode);
 
 
 /** @brief Connect a SMTP network client
@@ -132,11 +122,12 @@ gboolean net_client_smtp_allow_auth(NetClientSmtp *client, gboolean encrypted, g
  * @return TRUE on success or FALSE if the connection failed
  *
  * Connect the remote SMTP server, initialise the encryption if requested, and emit the @ref auth signal to 
request authentication
- * information.  Simply ignore the signal for an unauthenticated connection.
+ * information unless anonymous access has been configured by calling net_client_smtp_set_auth_mode().
  *
- * The function will try only @em one authentication method supported by the server and enabled for the 
current encryption state
- * (see net_client_smtp_allow_auth() and \ref NET_CLIENT_SMTP_AUTH_ALL etc.).  The priority is, from highest 
to lowest, GSSAPI (if
- * configured), CRAM-SHA1, CRAM-MD5, PLAIN or LOGIN.
+ * The function will try only @em one authentication method which is both supported by the server and 
enabled by calling
+ * net_client_smtp_set_auth_mode().  The default is to try all methods, from highest to lowest, OAuth2 (if 
configured), GSSAPI (if
+ * configured), CRAM-SHA1, CRAM-MD5, PLAIN, LOGIN or anonymous.  It is up to the caller to ensure encryption 
or a connection to
+ * @c localhost if one of the plain text methods shall be used.
  *
  * In order to shut down a successfully established connection, just call <tt>g_object_unref()</tt> on the 
SMTP network client
  * object.
@@ -243,6 +234,7 @@ void net_client_smtp_msg_free(NetClientSmtpMessage *smtp_msg);
  *   - PLAIN according to <a href="https://tools.ietf.org/html/rfc4616";>RFC 4616</a>
  *   - LOGIN
  *   - GSSAPI according to <a href="https://tools.ietf.org/html/rfc4752";>RFC 4752</a> (if configured with 
gssapi support)
+ *   - XOAUTH2 according to <a href="https://tools.ietf.org/html/rfc6749";>RFC 6749</a> (if configured with 
OAuth2 support)
  * - STARTTLS encryption according to <a href="https://tools.ietf.org/html/rfc3207";>RFC 3207</a>
  * - Delivery Status Notifications (DSNs) according to <a href="https://tools.ietf.org/html/rfc3461";>RFC 
3461</a>
  */
diff --git a/libnetclient/net-client-utils.c b/libnetclient/net-client-utils.c
index 8b6e252a3..04b8c422a 100644
--- a/libnetclient/net-client-utils.c
+++ b/libnetclient/net-client-utils.c
@@ -26,6 +26,24 @@
 #endif         /* HAVE_GSSAPI */
 
 
+gboolean
+net_client_host_reachable(const gchar *host, GError **error)
+{
+       GSocketConnectable *remote_address;
+       GNetworkMonitor *monitor;
+       gboolean success;
+
+       g_return_val_if_fail(host != NULL, FALSE);
+
+       remote_address = g_network_address_new(host, 1024U);
+       monitor = g_network_monitor_get_default();
+       success = g_network_monitor_can_reach(monitor, remote_address, NULL, error);
+       g_object_unref(remote_address);
+
+       return success;
+}
+
+
 #if defined(HAVE_GSSAPI)
 
 struct _NetClientGssCtx {
@@ -137,7 +155,7 @@ net_client_gss_ctx_new(const gchar *service, const gchar *host, const gchar *use
     OM_uint32 maj_stat;
     OM_uint32 min_stat;
 
-    g_return_val_if_fail((service != NULL) && (host != NULL), NULL);
+    g_return_val_if_fail((service != NULL) && (host != NULL) && (user != NULL), NULL);
 
        gss_ctx = g_new0(NetClientGssCtx, 1U);
        service_str = g_strconcat(service, "@", host, NULL);
@@ -333,3 +351,21 @@ gss_error_string(OM_uint32 err_maj, OM_uint32 err_min)
 }
 
 #endif         /* HAVE_GSSAPI */
+
+
+#if defined(HAVE_OAUTH2)
+
+gchar *
+net_client_auth_oauth2_calc(const gchar *user, const gchar *access_token)
+{
+       gchar *buffer;
+       gchar *result;
+
+       g_return_val_if_fail((user != NULL) && (access_token != NULL), NULL);
+       buffer = g_strdup_printf("user=%s\001auth=Bearer %s\001\001", user, access_token);
+       result = g_base64_encode(buffer, strlen(buffer));
+       g_free(buffer);
+       return result;
+}
+
+#endif      /* HAVE_OAUTH2 */
diff --git a/libnetclient/net-client-utils.h b/libnetclient/net-client-utils.h
index 88e1a0ffa..b521d4c62 100644
--- a/libnetclient/net-client-utils.h
+++ b/libnetclient/net-client-utils.h
@@ -18,6 +18,7 @@
 
 #include "config.h"
 #include <gio/gio.h>
+#include "net-client.h"
 
 
 G_BEGIN_DECLS
@@ -30,6 +31,25 @@ typedef struct _NetClientGssCtx NetClientGssCtx;
 #endif         /* HAVE_GSSAPI */
 
 
+typedef struct _NetClientProbeResult NetClientProbeResult;
+
+/** @brief Server probe results */
+struct _NetClientProbeResult {
+       guint16 port;                                                   /**< The port where the server 
listens. */
+       NetClientCryptMode crypt_mode;                  /**< The encryption mode the server supports for the 
returned port. */
+       NetClientAuthMode auth_mode;                    /**< The authentication modes the server supports for 
the port and encryption mode. */
+};
+
+
+/** @brief Check is a host is resolvable and reachable
+ *
+ * @param host host name or IP address
+ * @param error filled with error information on error
+ * @return TRUE if the passed host name is resolvable and reachable
+ */
+gboolean net_client_host_reachable(const gchar *host, GError **error);
+
+
 /** @brief Calculate a CRAM authentication string
  *
  * @param base64_challenge base64-encoded challenge sent by the server
@@ -143,9 +163,27 @@ void net_client_gss_ctx_free(NetClientGssCtx *gss_ctx);
 #endif         /* HAVE_GSSAPI */
 
 
+#if defined(HAVE_OAUTH2)
+
+/** @brief Calculate a OAuth2 authentication string
+ *
+ * @param user user name
+ * @param access_token access token
+ * @return a newly allocated string containing the base64-encoded authentication
+ *
+ * This helper function calculates the the base64-encoded authentication string from the user name and the 
access token.  The caller
+ * shall free the returned string when it is not needed any more.
+ *
+ * \sa <a href="https://developers.google.com/gmail/imap/xoauth2-protocol";>Google Developers: OAuth 2.0 
Mechanism</a>.
+ */
+gchar *net_client_auth_oauth2_calc(const gchar *user, const gchar *access_token)
+       G_GNUC_MALLOC;
+
+#endif         /* HAVE_OAUTH2 */
+
 /** @file
  *
- * This module implements authentication-related helper functions for the network client library.
+ * This module implements probing and authentication-related helper functions for the network client library.
  */
 
 
diff --git a/libnetclient/net-client.h b/libnetclient/net-client.h
index e9bc7b57f..ab6102264 100644
--- a/libnetclient/net-client.h
+++ b/libnetclient/net-client.h
@@ -38,6 +38,7 @@ struct _NetClientClass {
 
 typedef enum _NetClientError NetClientError;
 typedef enum _NetClientCryptMode NetClientCryptMode;
+typedef enum _NetClientAuthMode NetClientAuthMode;
 
 
 /** @brief Encryption mode */
@@ -49,6 +50,15 @@ enum _NetClientCryptMode {
 };
 
 
+/** @brief Authentication mode */
+enum _NetClientAuthMode {
+       NET_CLIENT_AUTH_ANONYMOUS = 1,                  /**< No authentication required (e.g. local service, 
user cert authentication). */
+       NET_CLIENT_AUTH_USER_PASS = 2,                  /**< Authenticate with user name and password. */
+       NET_CLIENT_AUTH_KERBEROS = 4,                   /**< Authenticate with user name and Kerberos ticket. 
*/
+       NET_CLIENT_AUTH_OAUTH2 = 8                              /**< OAuth2 authentication (RFC 6749). */
+};
+
+
 /** @brief Error codes */
 enum _NetClientError {
        NET_CLIENT_ERROR_CONNECTED = 1,                 /**< The client is already connected. */
@@ -59,7 +69,8 @@ enum _NetClientError {
        NET_CLIENT_ERROR_LINE_TOO_LONG,                 /**< The line is too long. */
        NET_CLIENT_ERROR_GNUTLS,                                /**< A GnuTLS error occurred (bad certificate 
or key data, or internal error). */
        NET_CLIENT_ERROR_CERT_KEY_PASS,                 /**< GnuTLS could not decrypt the user certificate's 
private key. */
-       NET_CLIENT_ERROR_GSSAPI                                 /**< A GSSAPI error occurred. */
+       NET_CLIENT_ERROR_GSSAPI,                                /**< A GSSAPI error occurred. */
+       NET_CLIENT_PROBE_FAILED                                 /**< Probing a server failed. */
 };
 
 
@@ -335,8 +346,9 @@ gboolean net_client_can_read(NetClient *client);
  *   @code gchar **get_auth(NetClient *client, gboolean need_passwd, gpointer user_data) @endcode 
Authentication is required by the
  *   remote server.  The signal handler shall return a NULL-terminated array of strings, containing the user 
name in the first and
  *   the password in the second element.  If the parameter @em need_passwd is FALSE, no password is required 
(e.g. for kerberos
- *   ticket-based authentication).  In this case, the password element must be present in the reply, but it 
is ignored an may be
- *   NULL.  The strings are wiped and freed when they are not needed any more.  Return NULL if no 
authentication is required.
+ *   ticket-based or for OAuth2 authentication).  In this case, the password element must be present in the 
reply, but it is ignored
+ *   an may be NULL.  The strings are wiped and freed when they are not needed any more.  Return NULL if no 
authentication is
+ *   required.
  */
 
 


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