/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*
* Authors: Jeffrey Stedfast
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "camel-debug.h"
#include "camel-mime-utils.h"
#include "camel-sasl-anonymous.h"
#include "camel-sasl-cram-md5.h"
#include "camel-sasl-digest-md5.h"
#include "camel-sasl-gssapi.h"
#include "camel-sasl-login.h"
#include "camel-sasl-ntlm.h"
#include "camel-sasl-plain.h"
#include "camel-sasl-popb4smtp.h"
#include "camel-sasl.h"
#include "camel-service.h"
#define CAMEL_SASL_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), CAMEL_TYPE_SASL, CamelSaslPrivate))
#define w(x)
typedef struct _AsyncContext AsyncContext;
struct _CamelSaslPrivate {
CamelService *service;
gboolean authenticated;
gchar *service_name;
gchar *mechanism;
};
struct _AsyncContext {
GByteArray *token;
gchar *base64_token;
};
enum {
PROP_0,
PROP_AUTHENTICATED,
PROP_MECHANISM,
PROP_SERVICE,
PROP_SERVICE_NAME
};
G_DEFINE_ABSTRACT_TYPE (CamelSasl, camel_sasl, G_TYPE_OBJECT)
static void
async_context_free (AsyncContext *async_context)
{
if (async_context->token != NULL)
g_byte_array_free (async_context->token, TRUE);
g_free (async_context->base64_token);
g_slice_free (AsyncContext, async_context);
}
static void
sasl_build_class_table_rec (GType type,
GHashTable *class_table)
{
GType *children;
guint n_children, ii;
children = g_type_children (type, &n_children);
for (ii = 0; ii < n_children; ii++) {
GType type = children[ii];
CamelSaslClass *sasl_class;
gpointer key;
/* Recurse over the child's children. */
sasl_build_class_table_rec (type, class_table);
/* Skip abstract types. */
if (G_TYPE_IS_ABSTRACT (type))
continue;
sasl_class = g_type_class_ref (type);
if (sasl_class->auth_type == NULL) {
g_critical (
"%s has an empty CamelServiceAuthType",
G_OBJECT_CLASS_NAME (sasl_class));
g_type_class_unref (sasl_class);
continue;
}
key = (gpointer) sasl_class->auth_type->authproto;
g_hash_table_insert (class_table, key, sasl_class);
}
g_free (children);
}
static GHashTable *
sasl_build_class_table (void)
{
GHashTable *class_table;
/* Register known types. */
CAMEL_TYPE_SASL_ANONYMOUS;
CAMEL_TYPE_SASL_CRAM_MD5;
CAMEL_TYPE_SASL_DIGEST_MD5;
#ifdef HAVE_KRB5
CAMEL_TYPE_SASL_GSSAPI;
#endif
CAMEL_TYPE_SASL_LOGIN;
CAMEL_TYPE_SASL_NTLM;
CAMEL_TYPE_SASL_PLAIN;
CAMEL_TYPE_SASL_POPB4SMTP;
class_table = g_hash_table_new_full (
(GHashFunc) g_str_hash,
(GEqualFunc) g_str_equal,
(GDestroyNotify) NULL,
(GDestroyNotify) g_type_class_unref);
sasl_build_class_table_rec (CAMEL_TYPE_SASL, class_table);
return class_table;
}
static void
sasl_set_mechanism (CamelSasl *sasl,
const gchar *mechanism)
{
g_return_if_fail (mechanism != NULL);
g_return_if_fail (sasl->priv->mechanism == NULL);
sasl->priv->mechanism = g_strdup (mechanism);
}
static void
sasl_set_service (CamelSasl *sasl,
CamelService *service)
{
g_return_if_fail (!service || CAMEL_IS_SERVICE (service));
g_return_if_fail (sasl->priv->service == NULL);
if (service)
sasl->priv->service = g_object_ref (service);
}
static void
sasl_set_service_name (CamelSasl *sasl,
const gchar *service_name)
{
g_return_if_fail (service_name != NULL);
g_return_if_fail (sasl->priv->service_name == NULL);
sasl->priv->service_name = g_strdup (service_name);
}
static void
sasl_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_AUTHENTICATED:
camel_sasl_set_authenticated (
CAMEL_SASL (object),
g_value_get_boolean (value));
return;
case PROP_MECHANISM:
sasl_set_mechanism (
CAMEL_SASL (object),
g_value_get_string (value));
return;
case PROP_SERVICE:
sasl_set_service (
CAMEL_SASL (object),
g_value_get_object (value));
return;
case PROP_SERVICE_NAME:
sasl_set_service_name (
CAMEL_SASL (object),
g_value_get_string (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
sasl_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_AUTHENTICATED:
g_value_set_boolean (
value, camel_sasl_get_authenticated (
CAMEL_SASL (object)));
return;
case PROP_MECHANISM:
g_value_set_string (
value, camel_sasl_get_mechanism (
CAMEL_SASL (object)));
return;
case PROP_SERVICE:
g_value_set_object (
value, camel_sasl_get_service (
CAMEL_SASL (object)));
return;
case PROP_SERVICE_NAME:
g_value_set_string (
value, camel_sasl_get_service_name (
CAMEL_SASL (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
sasl_dispose (GObject *object)
{
CamelSaslPrivate *priv;
priv = CAMEL_SASL_GET_PRIVATE (object);
if (priv->service != NULL) {
g_object_unref (priv->service);
priv->service = NULL;
}
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (camel_sasl_parent_class)->dispose (object);
}
static void
sasl_finalize (GObject *object)
{
CamelSaslPrivate *priv;
priv = CAMEL_SASL_GET_PRIVATE (object);
g_free (priv->mechanism);
g_free (priv->service_name);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (camel_sasl_parent_class)->finalize (object);
}
static void
camel_sasl_class_init (CamelSaslClass *class)
{
GObjectClass *object_class;
g_type_class_add_private (class, sizeof (CamelSaslPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->set_property = sasl_set_property;
object_class->get_property = sasl_get_property;
object_class->dispose = sasl_dispose;
object_class->finalize = sasl_finalize;
g_object_class_install_property (
object_class,
PROP_AUTHENTICATED,
g_param_spec_boolean (
"authenticated",
"Authenticated",
NULL,
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (
object_class,
PROP_MECHANISM,
g_param_spec_string (
"mechanism",
"Mechanism",
NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (
object_class,
PROP_SERVICE,
g_param_spec_object (
"service",
"Service",
NULL,
CAMEL_TYPE_SERVICE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (
object_class,
PROP_SERVICE_NAME,
g_param_spec_string (
"service-name",
"Service Name",
NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
camel_sasl_init (CamelSasl *sasl)
{
sasl->priv = CAMEL_SASL_GET_PRIVATE (sasl);
}
/**
* camel_sasl_new:
* @service_name: the SASL service name
* @mechanism: the SASL mechanism
* @service: the CamelService that will be using this SASL
*
* Returns: a new #CamelSasl object for the given @service_name,
* @mechanism, and @service, or %NULL if the mechanism is not
* supported.
**/
CamelSasl *
camel_sasl_new (const gchar *service_name,
const gchar *mechanism,
CamelService *service)
{
GHashTable *class_table;
CamelSaslClass *sasl_class;
CamelSasl *sasl = NULL;
g_return_val_if_fail (service_name != NULL, NULL);
g_return_val_if_fail (mechanism != NULL, NULL);
g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
class_table = sasl_build_class_table ();
sasl_class = g_hash_table_lookup (class_table, mechanism);
if (sasl_class != NULL)
sasl = g_object_new (
G_OBJECT_CLASS_TYPE (sasl_class),
"mechanism", mechanism,
"service", service,
"service-name", service_name,
NULL);
g_hash_table_destroy (class_table);
return sasl;
}
/**
* camel_sasl_get_authenticated:
* @sasl: a #CamelSasl
*
* Returns: whether or not @sasl has successfully authenticated the
* user. This will be %TRUE after it returns the last needed response.
* The caller must still pass that information on to the server and
* verify that it has accepted it.
**/
gboolean
camel_sasl_get_authenticated (CamelSasl *sasl)
{
g_return_val_if_fail (CAMEL_IS_SASL (sasl), FALSE);
return sasl->priv->authenticated;
}
/**
* camel_sasl_try_empty_password_sync:
* @sasl: a #CamelSasl object
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* Returns: whether or not @sasl can attempt to authenticate without a
* password being provided by the caller. This will be %TRUE for an
* authentication method which can attempt to use single-sign-on
* credentials, but which can fall back to using a provided password
* so it still has the @need_password flag set in its description.
*
* Since: 3.2
**/
gboolean
camel_sasl_try_empty_password_sync (CamelSasl *sasl,
GCancellable *cancellable,
GError **error)
{
CamelSaslClass *class;
g_return_val_if_fail (CAMEL_IS_SASL (sasl), FALSE);
class = CAMEL_SASL_GET_CLASS (sasl);
if (class->try_empty_password_sync == NULL)
return FALSE;
return class->try_empty_password_sync (sasl, cancellable, error);
}
/* Helpder for camel_sasl_try_empty_password() */
static void
sasl_try_empty_password_thread (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
gboolean result;
GError *local_error = NULL;
result = camel_sasl_try_empty_password_sync (
CAMEL_SASL (source_object),
cancellable, &local_error);
if (local_error != NULL) {
g_task_return_error (task, local_error);
} else {
g_task_return_boolean (task, result);
}
}
/**
* camel_sasl_try_empty_password:
* @sasl: a #CamelSasl
* @io_priority: the I/O priority of the request
* @cancellable: optional #GCancellable object, or %NULL
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: data to pass to the callback function
*
* Asynchronously determine whether @sasl can be used for password-less
* authentication, for example single-sign-on using system credentials.
*
* When the operation is finished, @callback will be called. You can then
* call camel_sasl_try_empty_password_finish() to get the result of the
* operation.
*
* Since: 3.2
**/
void
camel_sasl_try_empty_password (CamelSasl *sasl,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
g_return_if_fail (CAMEL_IS_SASL (sasl));
task = g_task_new (sasl, cancellable, callback, user_data);
g_task_set_source_tag (task, camel_sasl_try_empty_password);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, sasl_try_empty_password_thread);
g_object_unref (task);
}
/**
* camel_sasl_try_empty_password_finish:
* @sasl: a #CamelSasl
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finishes the operation started with camel_sasl_try_empty_password().
*
* Returns: the SASL response. If an error occurred, @error will also be set.
*
* Since: 3.2
**/
gboolean
camel_sasl_try_empty_password_finish (CamelSasl *sasl,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (CAMEL_IS_SASL (sasl), FALSE);
g_return_val_if_fail (g_task_is_valid (result, sasl), FALSE);
g_return_val_if_fail (
g_async_result_is_tagged (
result, camel_sasl_try_empty_password), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
/**
* camel_sasl_set_authenticated:
* @sasl: a #CamelSasl
* @authenticated: whether we have successfully authenticated
*
* Since: 2.32
**/
void
camel_sasl_set_authenticated (CamelSasl *sasl,
gboolean authenticated)
{
g_return_if_fail (CAMEL_IS_SASL (sasl));
if (sasl->priv->authenticated == authenticated)
return;
sasl->priv->authenticated = authenticated;
g_object_notify (G_OBJECT (sasl), "authenticated");
}
/**
* camel_sasl_get_mechanism:
* @sasl: a #CamelSasl
*
* Since: 2.32
**/
const gchar *
camel_sasl_get_mechanism (CamelSasl *sasl)
{
g_return_val_if_fail (CAMEL_IS_SASL (sasl), NULL);
return sasl->priv->mechanism;
}
/**
* camel_sasl_get_service:
* @sasl: a #CamelSasl
*
* Since: 2.32
**/
CamelService *
camel_sasl_get_service (CamelSasl *sasl)
{
g_return_val_if_fail (CAMEL_IS_SASL (sasl), NULL);
return sasl->priv->service;
}
/**
* camel_sasl_get_service_name:
* @sasl: a #CamelSasl
*
* Since: 2.32
**/
const gchar *
camel_sasl_get_service_name (CamelSasl *sasl)
{
g_return_val_if_fail (CAMEL_IS_SASL (sasl), NULL);
return sasl->priv->service_name;
}
/**
* camel_sasl_challenge_sync:
* @sasl: a #CamelSasl
* @token: a token, or %NULL
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* If @token is %NULL, generate the initial SASL message to send to
* the server. (This will be %NULL if the client doesn't initiate the
* exchange.) Otherwise, @token is a challenge from the server, and
* the return value is the response.
*
* Free the returned #GByteArray with g_byte_array_free().
*
* Returns: the SASL response or %NULL. If an error occurred, @error will
* also be set.
**/
GByteArray *
camel_sasl_challenge_sync (CamelSasl *sasl,
GByteArray *token,
GCancellable *cancellable,
GError **error)
{
CamelSaslClass *class;
GByteArray *response;
g_return_val_if_fail (CAMEL_IS_SASL (sasl), NULL);
class = CAMEL_SASL_GET_CLASS (sasl);
g_return_val_if_fail (class->challenge_sync != NULL, NULL);
response = class->challenge_sync (sasl, token, cancellable, error);
if (token != NULL)
CAMEL_CHECK_GERROR (
sasl, challenge_sync, response != NULL, error);
return response;
}
/* Helper for camel_sasl_challenge() */
static void
sasl_challenge_thread (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
GByteArray *response;
AsyncContext *async_context;
GError *local_error = NULL;
async_context = (AsyncContext *) task_data;
response = camel_sasl_challenge_sync (
CAMEL_SASL (source_object),
async_context->token,
cancellable, &local_error);
if (local_error != NULL) {
g_warn_if_fail (response == NULL);
g_task_return_error (task, local_error);
} else {
g_task_return_pointer (
task, response,
(GDestroyNotify) g_byte_array_unref);
}
}
/**
* camel_sasl_challenge:
* @sasl: a #CamelSasl
* @token: a token, or %NULL
* @io_priority: the I/O priority of the request
* @cancellable: optional #GCancellable object, or %NULL
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: data to pass to the callback function
*
* If @token is %NULL, asynchronously generate the initial SASL message
* to send to the server. (This will be %NULL if the client doesn't
* initiate the exchange.) Otherwise, @token is a challenge from the
* server, and the asynchronous result is the response.
*
* When the operation is finished, @callback will be called. You can then
* call camel_sasl_challenge_finish() to get the result of the operation.
*
* Since: 3.0
**/
void
camel_sasl_challenge (CamelSasl *sasl,
GByteArray *token,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
AsyncContext *async_context;
g_return_if_fail (CAMEL_IS_SASL (sasl));
async_context = g_slice_new0 (AsyncContext);
async_context->token = g_byte_array_new ();
g_byte_array_append (async_context->token, token->data, token->len);
task = g_task_new (sasl, cancellable, callback, user_data);
g_task_set_source_tag (task, camel_sasl_challenge);
g_task_set_priority (task, io_priority);
g_task_set_task_data (
task, async_context,
(GDestroyNotify) async_context_free);
g_task_run_in_thread (task, sasl_challenge_thread);
g_object_unref (task);
}
/**
* camel_sasl_challenge_finish:
* @sasl: a #CamelSasl
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finishes the operation started with camel_sasl_challenge(). Free the
* returned #GByteArray with g_byte_array_free().
*
* Returns: the SASL response or %NULL. If an error occurred, @error will
* also be set.
*
* Since: 3.0
**/
GByteArray *
camel_sasl_challenge_finish (CamelSasl *sasl,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (CAMEL_IS_SASL (sasl), NULL);
g_return_val_if_fail (g_task_is_valid (result, sasl), NULL);
g_return_val_if_fail (
g_async_result_is_tagged (
result, camel_sasl_challenge), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
/**
* camel_sasl_challenge_base64_sync:
* @sasl: a #CamelSasl
* @token: a base64-encoded token
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* As with camel_sasl_challenge_sync(), but the challenge @token and the
* response are both base64-encoded.
*
* Returns: the base64-encoded response
*
* Since: 3.0
**/
gchar *
camel_sasl_challenge_base64_sync (CamelSasl *sasl,
const gchar *token,
GCancellable *cancellable,
GError **error)
{
GByteArray *token_binary;
GByteArray *response_binary;
gchar *response;
g_return_val_if_fail (CAMEL_IS_SASL (sasl), NULL);
if (token != NULL && *token != '\0') {
guchar *data;
gsize length = 0;
data = g_base64_decode (token, &length);
token_binary = g_byte_array_new ();
g_byte_array_append (token_binary, data, length);
g_free (data);
} else
token_binary = NULL;
response_binary = camel_sasl_challenge_sync (
sasl, token_binary, cancellable, error);
if (token_binary)
g_byte_array_free (token_binary, TRUE);
if (response_binary == NULL)
return NULL;
if (response_binary->len > 0)
response = g_base64_encode (
response_binary->data, response_binary->len);
else
response = g_strdup ("");
g_byte_array_free (response_binary, TRUE);
return response;
}
/* Helper for camel_sasl_challenge_base64() */
static void
sasl_challenge_base64_thread (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
gchar *base64_response;
AsyncContext *async_context;
GError *local_error = NULL;
async_context = (AsyncContext *) task_data;
base64_response = camel_sasl_challenge_base64_sync (
CAMEL_SASL (source_object),
async_context->base64_token,
cancellable, &local_error);
if (local_error != NULL) {
g_warn_if_fail (base64_response == NULL);
g_task_return_error (task, local_error);
} else {
g_task_return_pointer (
task, base64_response,
(GDestroyNotify) g_free);
}
}
/**
* camel_sasl_challenge_base64:
* @sasl: a #CamelSasl
* @token: a base64-encoded token
* @io_priority: the I/O priority of the request
* @cancellable: optional #GCancellable object, or %NULL
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: data to pass to the callback function
*
* As with camel_sasl_challenge(), but the challenge @token and the
* response are both base64-encoded.
*
* When the operation is finished, @callback will be called. You can
* then call camel_store_challenge_base64_finish() to get the result of
* the operation.
*
* Since: 3.0
**/
void
camel_sasl_challenge_base64 (CamelSasl *sasl,
const gchar *token,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
AsyncContext *async_context;
g_return_if_fail (CAMEL_IS_SASL (sasl));
async_context = g_slice_new0 (AsyncContext);
async_context->base64_token = g_strdup (token);
task = g_task_new (sasl, cancellable, callback, user_data);
g_task_set_source_tag (task, camel_sasl_challenge_base64);
g_task_set_priority (task, io_priority);
g_task_set_task_data (
task, async_context,
(GDestroyNotify) async_context_free);
g_task_run_in_thread (task, sasl_challenge_base64_thread);
g_object_unref (task);
}
/**
* camel_sasl_challenge_base64_finish:
* @sasl: a #CamelSasl
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finishes the operation started with camel_sasl_challenge_base64().
*
* Returns: the base64-encoded response
*
* Since: 3.0
**/
gchar *
camel_sasl_challenge_base64_finish (CamelSasl *sasl,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (CAMEL_IS_SASL (sasl), NULL);
g_return_val_if_fail (g_task_is_valid (result, sasl), NULL);
g_return_val_if_fail (
g_async_result_is_tagged (
result, camel_sasl_challenge_base64), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
/**
* camel_sasl_authtype_list:
* @include_plain: whether or not to include the PLAIN mechanism
*
* Returns: a #GList of SASL-supported authtypes. The caller must
* free the list, but not the contents.
**/
GList *
camel_sasl_authtype_list (gboolean include_plain)
{
CamelSaslClass *sasl_class;
GHashTable *class_table;
GList *types = NULL;
/* XXX I guess these are supposed to be common SASL auth types,
* since this is called by the IMAP, POP and SMTP providers.
* The returned list can be extended with other auth types
* by way of camel_sasl_authtype(), so maybe we should just
* drop the ad-hoc "include_plain" parameter? */
class_table = sasl_build_class_table ();
sasl_class = g_hash_table_lookup (class_table, "CRAM-MD5");
g_return_val_if_fail (sasl_class != NULL, types);
types = g_list_prepend (types, sasl_class->auth_type);
sasl_class = g_hash_table_lookup (class_table, "DIGEST-MD5");
g_return_val_if_fail (sasl_class != NULL, types);
types = g_list_prepend (types, sasl_class->auth_type);
#ifdef HAVE_KRB5
sasl_class = g_hash_table_lookup (class_table, "GSSAPI");
g_return_val_if_fail (sasl_class != NULL, types);
types = g_list_prepend (types, sasl_class->auth_type);
#endif
sasl_class = g_hash_table_lookup (class_table, "NTLM");
g_return_val_if_fail (sasl_class != NULL, types);
types = g_list_prepend (types, sasl_class->auth_type);
if (include_plain) {
sasl_class = g_hash_table_lookup (class_table, "PLAIN");
g_return_val_if_fail (sasl_class != NULL, types);
types = g_list_prepend (types, sasl_class->auth_type);
}
g_hash_table_destroy (class_table);
return types;
}
/**
* camel_sasl_authtype:
* @mechanism: the SASL mechanism to get an authtype for
*
* Returns: a #CamelServiceAuthType for the given mechanism, if
* it is supported.
**/
CamelServiceAuthType *
camel_sasl_authtype (const gchar *mechanism)
{
GHashTable *class_table;
CamelSaslClass *sasl_class;
CamelServiceAuthType *auth_type;
g_return_val_if_fail (mechanism != NULL, NULL);
class_table = sasl_build_class_table ();
sasl_class = g_hash_table_lookup (class_table, mechanism);
auth_type = (sasl_class != NULL) ? sasl_class->auth_type : NULL;
g_hash_table_destroy (class_table);
return auth_type;
}