=== modified file 'addressbook/libebook/e-book.c' --- addressbook/libebook/e-book.c 2007-12-18 16:36:30 +0000 +++ addressbook/libebook/e-book.c 2007-12-18 18:57:46 +0000 @@ -17,6 +17,10 @@ #include #include "libedataserver/e-component-listener.h" +#ifdef HAVE_GNOME_CERTAUTH +#include +#endif + #include "e-book-listener.h" #define d(x) @@ -3277,6 +3281,9 @@ GError **error) { GNOME_Evolution_Addressbook_Book corba_book = CORBA_OBJECT_NIL; +#ifdef HAVE_GNOME_CERTAUTH + GObject *nss_source; +#endif gchar *uri; gchar *source_xml; GList *factories; @@ -3293,6 +3300,7 @@ /* try to find a list of factories that can handle the protocol */ factories = activate_factories_for_uri (book, uri); if (!factories) { + g_free (uri); g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_PROTOCOL_NOT_SUPPORTED, _("%s: no factories available for URI `%s'"), "e_book_load_uri", uri); return FALSE; @@ -3304,11 +3312,28 @@ */ book->priv->listener = e_book_listener_new (); if (book->priv->listener == NULL) { + g_free (uri); g_warning ("e_book_load_uri: Could not create EBookListener!\n"); g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_OTHER_ERROR, _("%s: Could not create EBookListener"), "e_book_load_uri"); return FALSE; } +#ifdef HAVE_GNOME_CERTAUTH + nss_source = g_object_new (GCA_TYPE_NSS_CERTIFICATE_SOURCE, + "poa", bonobo_poa_get_threaded (ORBIT_THREAD_HINT_PER_REQUEST), + NULL); + if (!nss_source) { + g_object_unref (book->priv->listener); + book->priv->listener = NULL; + g_free (uri); + g_warning ("e_book_load_uri: Could not create GcaNssCertificateSource!\n"); + g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_OTHER_ERROR, + _("%s: Could not create GcaNssCertificateSource"), "e_book_load_uri"); + return FALSE; + } + bonobo_object_add_interface (BONOBO_OBJECT (book->priv->listener), + BONOBO_OBJECT (nss_source)); +#endif book->priv->listener_signal = g_signal_connect_object (book->priv->listener, "response", G_CALLBACK (e_book_handle_response), book, 0); === modified file 'addressbook/libedata-book/e-book-backend.c' --- addressbook/libedata-book/e-book-backend.c 2007-12-18 16:36:30 +0000 +++ addressbook/libedata-book/e-book-backend.c 2007-12-18 18:57:46 +0000 @@ -27,6 +27,8 @@ /* Signal IDs */ enum { LAST_CLIENT_GONE, + ADD_CLIENT, + REMOVE_CLIENT, LAST_SIGNAL }; @@ -572,6 +574,8 @@ backend->priv->clients = g_list_prepend (backend->priv->clients, book); g_mutex_unlock (backend->priv->clients_mutex); + g_signal_emit (backend, e_book_backend_signals[ADD_CLIENT], 0, book); + return TRUE; } @@ -594,6 +598,8 @@ lock) */ g_object_ref (backend); + g_signal_emit (backend, e_book_backend_signals[REMOVE_CLIENT], 0, book); + /* Disconnect */ g_mutex_lock (backend->priv->clients_mutex); backend->priv->clients = g_list_remove (backend->priv->clients, book); @@ -1088,6 +1094,24 @@ NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + e_book_backend_signals[ADD_CLIENT] = + g_signal_new ("add_client", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EBookBackendClass, add_client), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + e_book_backend_signals[REMOVE_CLIENT] = + g_signal_new ("remove_client", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EBookBackendClass, remove_client), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); } /** === modified file 'addressbook/libedata-book/e-book-backend.h' --- addressbook/libedata-book/e-book-backend.h 2007-12-18 16:36:30 +0000 +++ addressbook/libedata-book/e-book-backend.h 2007-12-18 18:57:46 +0000 @@ -73,9 +73,10 @@ void (*sync) (EBookBackend *backend); + void (*add_client) (EBookBackend *backend, EDataBook *book); + void (*remove_client) (EBookBackend *backend, EDataBook *book); + /* Padding for future expansion */ - void (*_pas_reserved1) (void); - void (*_pas_reserved2) (void); void (*_pas_reserved3) (void); void (*_pas_reserved4) (void); }; === modified file 'calendar/libecal/e-cal.c' --- calendar/libecal/e-cal.c 2007-12-18 16:36:30 +0000 +++ calendar/libecal/e-cal.c 2007-12-18 18:57:46 +0000 @@ -42,6 +42,9 @@ #include "e-cal-view-private.h" #include "e-cal.h" +#ifdef HAVE_GNOME_CERTAUTH +#include +#endif static gboolean open_calendar (ECal *ecal, gboolean only_if_exists, GError **error, ECalendarStatus *status, gboolean needs_auth); @@ -891,20 +894,19 @@ e_flag_set (op->done); } -static gboolean +static gpointer reopen_with_auth (gpointer data) { ECalendarStatus status; open_calendar (E_CAL (data), TRUE, NULL, &status, TRUE); - return FALSE; + return NULL; } static void auth_required_cb (ECalListener *listener, gpointer data) { - g_idle_add (reopen_with_auth, data); - + g_thread_create (reopen_with_auth, data, FALSE, NULL); } /* Handle the cal_set_mode notification from the listener */ @@ -1045,7 +1047,9 @@ e_cal_init (ECal *ecal, ECalClass *klass) { ECalPrivate *priv; - +#ifdef HAVE_GNOME_CERTAUTH + GObject *nss_source; +#endif priv = g_new0 (ECalPrivate, 1); ecal->priv = priv; @@ -1055,6 +1059,18 @@ priv->mutex = g_mutex_new (); priv->listener = e_cal_listener_new (cal_set_mode_cb, ecal); +#ifdef HAVE_GNOME_CERTAUTH + nss_source = g_object_new (GCA_TYPE_NSS_CERTIFICATE_SOURCE, + "poa", bonobo_poa_get_threaded (ORBIT_THREAD_HINT_PER_REQUEST), + NULL); + if (!nss_source) { + g_warning ("e_cal_init: Could not create GcaNssCertificateSource!\n"); + } else { + bonobo_object_add_interface (BONOBO_OBJECT (priv->listener), + BONOBO_OBJECT (nss_source)); + } +#endif + priv->cal_address = NULL; priv->alarm_email_address = NULL; priv->ldap_attribute = NULL; === modified file 'calendar/libedata-cal/e-cal-backend.c' --- calendar/libedata-cal/e-cal-backend.c 2007-12-18 16:36:30 +0000 +++ calendar/libedata-cal/e-cal-backend.c 2007-12-18 18:57:46 +0000 @@ -66,6 +66,8 @@ LAST_CLIENT_GONE, OPENED, REMOVED, + ADD_CLIENT, + REMOVE_CLIENT, LAST_SIGNAL }; static guint e_cal_backend_signals[LAST_SIGNAL]; @@ -237,6 +239,22 @@ G_TYPE_NONE, 1, G_TYPE_INT); + e_cal_backend_signals[ADD_CLIENT] = + g_signal_new ("add_client", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + e_cal_backend_signals[ADD_CLIENT] = + g_signal_new ("remove_client", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + class->last_client_gone = NULL; class->opened = NULL; class->obj_updated = NULL; @@ -422,6 +440,8 @@ g_mutex_lock (priv->clients_mutex); priv->clients = g_list_append (priv->clients, cal); g_mutex_unlock (priv->clients_mutex); + + g_signal_emit (backend, e_cal_backend_signals[ADD_CLIENT], 0, cal); } /** @@ -446,6 +466,8 @@ priv = backend->priv; + g_signal_emit (backend, e_cal_backend_signals[REMOVE_CLIENT], 0, cal); + /* Disconnect */ g_mutex_lock (priv->clients_mutex); priv->clients = g_list_remove (priv->clients, cal); === modified file 'configure.in' --- configure.in 2007-12-18 16:37:20 +0000 +++ configure.in 2007-12-18 18:57:46 +0000 @@ -1383,6 +1383,11 @@ dnl Flags for the various libraries we build dnl **************************************** +PKG_CHECK_EXISTS([gnome-certauth-0.0],[ + gnome_certauth="gnome-certauth-0.0 >= 0.0" + AC_DEFINE([HAVE_GNOME_CERTAUTH], [1], [Define if you have gnome-certauth]) +]) + dnl --- libedataserver and libedataserverui flags E_DATA_SERVER_DEPS="libxml-2.0 libbonobo-2.0 gconf-2.0 $mozilla_nspr" @@ -1399,7 +1404,7 @@ dnl --- evolution-addressbook flags -EVOLUTION_ADDRESSBOOK_DEPS="libxml-2.0 libgnome-2.0 gnome-vfs-2.0" +EVOLUTION_ADDRESSBOOK_DEPS="libxml-2.0 libgnome-2.0 gnome-vfs-2.0 $gnome_certauth" EVO_SET_COMPILE_FLAGS(EVOLUTION_ADDRESSBOOK, $EVOLUTION_ADDRESSBOOK_DEPS) AC_SUBST(EVOLUTION_ADDRESSBOOK_CFLAGS) @@ -1407,7 +1412,7 @@ dnl --- evolution-calendar flags if test "x${enable_calendar}" = "xyes"; then - EVOLUTION_CALENDAR_DEPS="libxml-2.0 libgnome-2.0 gnome-vfs-2.0" + EVOLUTION_CALENDAR_DEPS="libxml-2.0 libgnome-2.0 gnome-vfs-2.0 $gnome_certauth" EVO_SET_COMPILE_FLAGS(EVOLUTION_CALENDAR, $EVOLUTION_CALENDAR_DEPS) AC_SUBST(EVOLUTION_CALENDAR_CFLAGS) === modified file 'servers/exchange/lib/e2k-autoconfig.c' --- servers/exchange/lib/e2k-autoconfig.c 2007-12-18 16:36:30 +0000 +++ servers/exchange/lib/e2k-autoconfig.c 2007-12-18 18:57:46 +0000 @@ -339,6 +339,13 @@ } } +void +e2k_autoconfig_set_creds (E2kAutoconfig *ac, + E2kCreds *creds) +{ + ac->creds = creds; +} + /* * e2k_autoconfig_get_context: * @ac: an autoconfig context @@ -397,8 +404,12 @@ *result = E2K_AUTOCONFIG_FAILED; return NULL; } - e2k_context_set_auth (ctx, ac->username, ac->nt_domain, - ac->use_ntlm ? "NTLM" : "Basic", ac->password); + if (ac->creds) { + e2k_context_set_creds (ctx, ac->creds); + } else { + e2k_context_set_auth (ctx, ac->username, ac->nt_domain, + ac->use_ntlm ? "NTLM" : "Basic", ac->password); + } msg = e2k_soup_message_new (ctx, ac->owa_uri, SOUP_METHOD_GET); === modified file 'servers/exchange/lib/e2k-autoconfig.h' --- servers/exchange/lib/e2k-autoconfig.h 2007-12-18 16:36:30 +0000 +++ servers/exchange/lib/e2k-autoconfig.h 2007-12-18 18:57:46 +0000 @@ -47,6 +47,7 @@ gboolean require_ntlm, use_ntlm; gboolean saw_basic, saw_ntlm; gboolean nt_domain_defaulted, gc_server_autodetected; + E2kCreds *creds; } E2kAutoconfig; E2kAutoconfig *e2k_autoconfig_new (const char *owa_uri, @@ -64,7 +65,8 @@ const char *username); void e2k_autoconfig_set_password (E2kAutoconfig *ac, const char *password); - +void e2k_autoconfig_set_creds (E2kAutoconfig *ac, + E2kCreds *creds); E2kContext *e2k_autoconfig_get_context (E2kAutoconfig *ac, E2kOperation *op, E2kAutoconfigResult *result); === modified file 'servers/exchange/lib/e2k-context.c' --- servers/exchange/lib/e2k-context.c 2007-12-18 17:02:13 +0000 +++ servers/exchange/lib/e2k-context.c 2007-12-18 18:57:46 +0000 @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include @@ -59,6 +58,15 @@ #include #include +#define GCA_TRACE(s, ...) d(fprintf (stderr, "%d:%s:%s():%d "s"\n", getpid(), __FILE__, __PRETTY_FUNCTION__, __LINE__, __VA_ARGS__)) +#define d(x) + +#define GCA_TRACEMSG(s) GCA_TRACE("%s", (s)) +#define GCA_ENTER GCA_TRACEMSG("ENTER") +#define GCA_EXIT GCA_TRACEMSG("EXIT") +#define GCA_HASLOCK GCA_TRACEMSG("HAS LOCK") +#define GCA_ALREADYLOCKED GCA_TRACEMSG("ALREADY LOCKED") + #ifdef G_OS_WIN32 /* The strtok() in Microsoft's C library is MT-safe (not stateless, * but that is not needed here). @@ -89,8 +97,8 @@ static guint signals [LAST_SIGNAL] = { 0 }; struct _E2kContextPrivate { - SoupSession *session, *async_session; - char *owa_uri, *username, *password; + SoupSession *session; + char *owa_uri; time_t last_timestamp; /* Notification listener */ @@ -105,8 +113,18 @@ char *cookie; gboolean cookie_verified; EProxy* proxy; + + E2kCreds *creds; + gboolean free_creds; }; +typedef struct { + E2kCreds creds; + char *username; + char *authmech; + char *password; +} E2kAuthCreds; + /* For operations with progress */ #define E2K_CONTEXT_MIN_BATCH_SIZE 25 #define E2K_CONTEXT_MAX_BATCH_SIZE 100 @@ -135,9 +153,7 @@ { SoupUri *proxy_uri = NULL; E2kContext* ctx = (E2kContext *)user_data; - if (!ctx || !ctx->priv || - (!ctx->priv->session && !ctx->priv->async_session) || - (!ctx->priv->owa_uri)) + if (!ctx || !ctx->priv || !ctx->priv->session || !ctx->priv->owa_uri) return; if (!e_proxy_require_proxy_for_uri (proxy, ctx->priv->owa_uri)) @@ -148,9 +164,6 @@ if (ctx->priv->session) g_object_set (ctx->priv->session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL); - if (ctx->priv->async_session) - g_object_set (ctx->priv->async_session, SOUP_SESSION_PROXY_URI, - proxy_uri, NULL); } static void @@ -176,6 +189,31 @@ } static void +release_creds (E2kContext *ctx) +{ + E2kAuthCreds *creds; + + if (!ctx->priv->creds) { + return; + } + + if (!ctx->priv->free_creds) { + ctx->priv->creds = NULL; + return; + } + + creds = (E2kAuthCreds *)ctx->priv->creds; + + g_free (creds->username); + g_free (creds->authmech); + g_free (creds->password); + + g_free (creds); + ctx->priv->free_creds = FALSE; + ctx->priv->creds = NULL; +} + +static void dispose (GObject *object) { E2kContext *ctx = E2K_CONTEXT (object); @@ -183,10 +221,6 @@ if (ctx->priv) { if (ctx->priv->owa_uri) g_free (ctx->priv->owa_uri); - if (ctx->priv->username) - g_free (ctx->priv->username); - if (ctx->priv->password) - g_free (ctx->priv->password); if (ctx->priv->get_local_address_sock) g_object_unref (ctx->priv->get_local_address_sock); @@ -207,11 +241,11 @@ if (ctx->priv->session) g_object_unref (ctx->priv->session); - if (ctx->priv->async_session) - g_object_unref (ctx->priv->async_session); g_free (ctx->priv->cookie); + release_creds (ctx); + if (ctx->priv->proxy) { g_object_unref (ctx->priv->proxy); ctx->priv->proxy = NULL; @@ -377,51 +411,73 @@ { E2kContext *ctx = user_data; - *username = g_strdup (ctx->priv->username); - *password = g_strdup (ctx->priv->password); + if (ctx->priv->creds && ctx->priv->creds->http_authenticate) { + ctx->priv->creds->http_authenticate (ctx->priv->creds, + auth_type, auth_realm, + username, password); + } + } + + static int + session_certificate_requested (SoupSession *session, + const gnutls_datum_t *req_ca_rdn, + int nreqs, + gnutls_datum_t **cert_der_ret, + int *cert_der_ret_length, + gpointer user_data) + { + E2kContext *ctx = user_data; + int ret = 0; + GCA_ENTER; + g_return_val_if_fail (E2K_IS_CONTEXT (ctx), -1); + + if (ctx->priv->creds && ctx->priv->creds->ssl_cert_request) { + ret = ctx->priv->creds->ssl_cert_request ( + ctx->priv->creds, + req_ca_rdn, nreqs, + cert_der_ret, cert_der_ret_length); + } + GCA_EXIT; + return ret; + } + + static int + session_sign_data (SoupSession *session, + gnutls_datum_t *cert, + const gnutls_datum_t *hash_data, + gnutls_datum_t *sig_data, + gpointer user_data) + { + E2kContext *ctx = user_data; + int ret = 0; + GCA_ENTER; + g_return_val_if_fail (E2K_IS_CONTEXT (ctx), -1); + + if (ctx->priv->creds && ctx->priv->creds->ssl_cert_sign) { + ret = ctx->priv->creds->ssl_cert_sign ( + ctx->priv->creds, cert, + hash_data, sig_data); + } + GCA_EXIT; + return ret; } -/** - * e2k_context_set_auth: - * @ctx: the context - * @username: the Windows username (not including domain) of the user - * @domain: the NT domain, or %NULL to use the default (if using NTLM) - * @authmech: the HTTP Authorization type to use; either "Basic" or "NTLM" - * @password: the user's password - * - * Sets the authentication information on @ctx. This will have the - * side effect of cancelling any pending requests on @ctx. - **/ void -e2k_context_set_auth (E2kContext *ctx, const char *username, - const char *domain, const char *authmech, - const char *password) + e2k_context_set_creds (E2kContext *ctx, + E2kCreds *creds) { guint timeout = E2K_SOUP_SESSION_TIMEOUT; SoupUri* uri = NULL; - + char *authmech = NULL; g_return_if_fail (E2K_IS_CONTEXT (ctx)); - - if (username) { - g_free (ctx->priv->username); - if (domain) { - ctx->priv->username = - g_strdup_printf ("%s\\%s", domain, - username); - } else - ctx->priv->username = g_strdup (username); - } - - if (password) { - g_free (ctx->priv->password); - ctx->priv->password = g_strdup (password); - } + g_return_if_fail (creds != NULL); /* Destroy the old sessions so we don't reuse old auths */ if (ctx->priv->session) g_object_unref (ctx->priv->session); - if (ctx->priv->async_session) - g_object_unref (ctx->priv->async_session); + + release_creds (ctx); + ctx->priv->creds = creds; /* Set a default timeout value of 30 seconds. FIXME: Make timeout configurable @@ -433,6 +489,9 @@ if (e_proxy_require_proxy_for_uri (ctx->priv->proxy, ctx->priv->owa_uri)) uri = e_proxy_peek_uri (ctx->priv->proxy); + if (ctx->priv->creds->http_auth_method) { + authmech = ctx->priv->creds->http_auth_method (ctx->priv->creds); + } ctx->priv->session = soup_session_sync_new_with_options ( SOUP_SESSION_USE_NTLM, !authmech || !strcmp (authmech, "NTLM"), @@ -441,16 +500,76 @@ NULL); g_signal_connect (ctx->priv->session, "authenticate", G_CALLBACK (session_authenticate), ctx); + g_signal_connect (ctx->priv->session, "certificate-requested", + G_CALLBACK (session_certificate_requested), ctx); + g_signal_connect (ctx->priv->session, "sign-data", + G_CALLBACK (session_sign_data), ctx); soup_session_add_filter (ctx->priv->session, SOUP_MESSAGE_FILTER (ctx)); - - ctx->priv->async_session = soup_session_async_new_with_options ( - SOUP_SESSION_USE_NTLM, !authmech || !strcmp (authmech, "NTLM"), - SOUP_SESSION_PROXY_URI, uri, NULL); - g_signal_connect (ctx->priv->async_session, "authenticate", - G_CALLBACK (session_authenticate), ctx); - soup_session_add_filter (ctx->priv->async_session, - SOUP_MESSAGE_FILTER (ctx)); + g_free (authmech); +} + +static char * +auth_creds_http_auth_method (E2kCreds *e2k_creds) +{ + E2kAuthCreds *creds = (E2kAuthCreds *)e2k_creds; + return creds->authmech ? g_strdup (creds->authmech) : NULL; +} + +static void +auth_creds_http_authenticate (E2kCreds *e2k_creds, + const char *auth_type, const char *auth_realm, + char **username, char **password) +{ + E2kAuthCreds *creds = (E2kAuthCreds *)e2k_creds; + *username = g_strdup (creds->username); + *password = g_strdup (creds->password); +} + +/** + * e2k_context_set_auth: + * @ctx: the context + * @username: the Windows username (not including domain) of the user + * @domain: the NT domain, or %NULL to use the default (if using NTLM) + * @authmech: the HTTP Authorization type to use; either "Basic" or "NTLM" + * @password: the user's password + * + * Sets the authentication information on @ctx. This will have the + * side effect of cancelling any pending requests on @ctx. + **/ +void +e2k_context_set_auth (E2kContext *ctx, const char *username, + const char *domain, const char *authmech, + const char *password) +{ + E2kAuthCreds *creds; + + g_return_if_fail (E2K_IS_CONTEXT (ctx)); + + creds = g_new0 (E2kAuthCreds, 1); + if (username) { + if (domain) { + creds->username = + g_strdup_printf ("%s\\%s", domain, username); + } else + creds->username = g_strdup (username); + } + + if (authmech) { + creds->authmech = g_strdup (authmech); + } + + if (password) { + creds->password = g_strdup (password); + } + + creds->creds.http_auth_method = auth_creds_http_auth_method; + creds->creds.http_authenticate = auth_creds_http_authenticate; + + e2k_context_set_creds (ctx, &creds->creds); + /* we can't set this before because release_creds would then + * free it */ + ctx->priv->free_creds = TRUE; } /** @@ -594,6 +713,8 @@ SoupMessage *post_msg; GString *form_body, *cookie_str; const GSList *cookies, *c; + char *username = NULL; + char *password = NULL; g_return_val_if_fail (E2K_IS_CONTEXT (ctx), FALSE); @@ -612,7 +733,7 @@ /* Otherwise, it's just expired. */ } - if (!ctx->priv->username || !ctx->priv->password) + if (!ctx->priv->creds || !ctx->priv->creds->http_authenticate) return FALSE; in_fba_auth = TRUE; @@ -653,6 +774,15 @@ action = g_strdup (value); xmlFree (value); + /* we already checked this above */ + ctx->priv->creds->http_authenticate (ctx->priv->creds, + "", "", /* fake these... */ + &username, &password); + + if (!username || !password) { + goto failed; + } + form_body = g_string_new (NULL); while ((node = e2k_xml_find (node, "input"))) { name = xmlGetProp (node, "name"); @@ -671,11 +801,11 @@ g_string_append_c (form_body, '&'); } else if (!g_ascii_strcasecmp (name, "username")) { g_string_append (form_body, "username="); - e2k_uri_append_encoded (form_body, ctx->priv->username, FALSE, NULL); + e2k_uri_append_encoded (form_body, username, FALSE, NULL); g_string_append_c (form_body, '&'); } else if (!g_ascii_strcasecmp (name, "password")) { g_string_append (form_body, "password="); - e2k_uri_append_encoded (form_body, ctx->priv->password, FALSE, NULL); + e2k_uri_append_encoded (form_body, password, FALSE, NULL); g_string_append_c (form_body, '&'); } @@ -683,6 +813,12 @@ xmlFree (value); xmlFree (name); } + g_free (username); + username = NULL; + + g_free (password); + password = NULL; + g_string_append_printf (form_body, "trusted=%d", E2K_FBA_FLAG_TRUSTED); xmlFreeDoc (doc); doc = NULL; @@ -732,6 +868,8 @@ in_fba_auth = FALSE; if (doc) xmlFreeDoc (doc); + g_free (username); + g_free (password); return FALSE; } @@ -892,7 +1030,7 @@ { g_return_if_fail (E2K_IS_CONTEXT (ctx)); - soup_session_queue_message (ctx->priv->async_session, msg, + soup_session_queue_message (ctx->priv->session, msg, callback, user_data); } === modified file 'servers/exchange/lib/e2k-context.h' --- servers/exchange/lib/e2k-context.h 2007-12-18 16:36:30 +0000 +++ servers/exchange/lib/e2k-context.h 2007-12-18 18:57:46 +0000 @@ -9,6 +9,8 @@ #include +#include + #include "e2k-types.h" #include "e2k-operation.h" #include "e2k-http-utils.h" @@ -47,6 +49,25 @@ const char *domain, const char *authmech, const char *password); +struct _E2kCreds { + char *(*http_auth_method) (E2kCreds *creds); + void (*http_authenticate) (E2kCreds *creds, + const char *auth_type, const char *auth_realm, + char **username, char **password); + int (*ssl_cert_request) (E2kCreds *creds, + const gnutls_datum_t *req_ca_rdn_der, + int nreqs, + gnutls_datum_t **cert_der_ret, + int *cert_der_ret_length); + int (*ssl_cert_sign) (E2kCreds *creds, + const gnutls_datum_t *cert, + const gnutls_datum_t *hash_data, + gnutls_datum_t *sig_data); +}; + +void e2k_context_set_creds (E2kContext *ctx, + E2kCreds *creds); + gboolean e2k_context_fba (E2kContext *ctx, SoupMessage *failed_msg); === modified file 'servers/exchange/lib/e2k-types.h' --- servers/exchange/lib/e2k-types.h 2007-12-18 16:36:30 +0000 +++ servers/exchange/lib/e2k-types.h 2007-12-18 18:57:46 +0000 @@ -14,6 +14,7 @@ typedef struct _E2kContext E2kContext; typedef struct _E2kContextPrivate E2kContextPrivate; typedef struct _E2kContextClass E2kContextClass; +typedef struct _E2kCreds E2kCreds; typedef struct _E2kGlobalCatalog E2kGlobalCatalog; typedef struct _E2kGlobalCatalogPrivate E2kGlobalCatalogPrivate; === modified file 'servers/exchange/storage/exchange-account.c' --- servers/exchange/storage/exchange-account.c 2007-12-18 17:02:35 +0000 +++ servers/exchange/storage/exchange-account.c 2007-12-18 18:57:46 +0000 @@ -38,6 +38,9 @@ #include "e2k-utils.h" #include "exchange-hierarchy-foreign.h" +#include +#include + /* This is an ugly hack to avoid API break */ /* Added for get_authtype */ #include "exchange-esource.h" @@ -52,11 +55,22 @@ #include #include +#define TRACE(s, ...) d(fprintf (stderr, "%d:%s:%s():%d "s"\n", getpid(), __FILE__, __PRETTY_FUNCTION__, __LINE__, __VA_ARGS__)) +#define TRACEMSG(s) TRACE("%s", (s)) +#define ENTER TRACEMSG("ENTER") +#define EXIT TRACEMSG("EXIT") +#define HASLOCK TRACEMSG("HAS LOCK") +#define ALREADYLOCKED TRACEMSG("ALREADY LOCKED") #define d(x) #define ADS_UF_DONT_EXPIRE_PASSWORD 0x10000 #define ONE_HUNDRED_NANOSECOND 0.000000100 #define SECONDS_IN_DAY 86400 +typedef struct { + E2kCreds creds; + ExchangeAccount *account; +} AccountCreds; + struct _ExchangeAccountPrivate { E2kContext *ctx; E2kGlobalCatalog *gc; @@ -85,6 +99,10 @@ GMutex *discover_data_lock; GList *discover_datas; + + AccountCreds creds; + GMutex *creds_mutex; + GSList *creds_list; }; enum { @@ -228,6 +246,13 @@ account->priv->fresh_folders = NULL; } + if (account->priv->creds_mutex) { + g_mutex_free (account->priv->creds_mutex); + account->priv->creds_mutex = NULL; + } + g_slist_free (account->priv->creds_list); + account->priv->creds_list = NULL; + G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -1170,14 +1195,21 @@ g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), FALSE); - g_mutex_lock (account->priv->connect_lock); - if (account->priv->ctx) { - g_object_unref (account->priv->ctx); - account->priv->ctx = NULL; + ENTER; + + if (g_mutex_trylock (account->priv->connect_lock)) { + HASLOCK; + if (account->priv->ctx) { + g_object_unref (account->priv->ctx); + account->priv->ctx = NULL; + } + + account->priv->account_online = OFFLINE_MODE; + g_mutex_unlock (account->priv->connect_lock); + } else { + ALREADYLOCKED; } - - account->priv->account_online = OFFLINE_MODE; - g_mutex_unlock (account->priv->connect_lock); + EXIT; return TRUE; } @@ -1197,9 +1229,15 @@ { g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), FALSE); - g_mutex_lock (account->priv->connect_lock); - account->priv->account_online = ONLINE_MODE; - g_mutex_unlock (account->priv->connect_lock); + ENTER; + if (g_mutex_trylock (account->priv->connect_lock)) { + HASLOCK; + account->priv->account_online = ONLINE_MODE; + g_mutex_unlock (account->priv->connect_lock); + } else { + ALREADYLOCKED; + } + EXIT; return TRUE; } @@ -1343,6 +1381,117 @@ return TRUE; } +/* we are just a patsy */ +static char * +account_creds_http_auth_method (E2kCreds *ecreds) +{ + AccountCreds *creds = (AccountCreds *)ecreds; + ExchangeAccount *account = creds->account; + E2kCreds *sub_creds; + char *method; + + g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL); + + g_mutex_lock (account->priv->creds_mutex); + sub_creds = g_slist_nth_data (account->priv->creds_list, 0); + method = sub_creds ? sub_creds->http_auth_method (sub_creds) : NULL; + g_mutex_unlock (account->priv->creds_mutex); + + return method; +} + +static void +account_creds_http_authenticate (E2kCreds *ecreds, + const char *auth_type, const char *auth_realm, + char **username, char **password) +{ + AccountCreds *creds = (AccountCreds *)ecreds; + ExchangeAccount *account = creds->account; + E2kCreds *sub_creds; + + g_return_if_fail (EXCHANGE_IS_ACCOUNT (account)); + + g_mutex_lock (account->priv->creds_mutex); + sub_creds = g_slist_nth_data (account->priv->creds_list, 0); + if (sub_creds) { + sub_creds->http_authenticate (sub_creds, auth_type, auth_realm, username, password); + } + g_mutex_unlock (account->priv->creds_mutex); +} + +static int +account_creds_ssl_cert_request (E2kCreds *ecreds, + const gnutls_datum_t *req_ca_rdn_der, + int nreqs, + gnutls_datum_t **cert_der_ret, + int *cert_der_ret_length) +{ + AccountCreds *creds = (AccountCreds *)ecreds; + ExchangeAccount *account = creds->account; + E2kCreds *sub_creds; + int ret = 0; + ENTER; + g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), -1); + + g_mutex_lock (account->priv->creds_mutex); + HASLOCK; + sub_creds = g_slist_nth_data (account->priv->creds_list, 0); + if (sub_creds) { + TRACEMSG("delegating cert request..."); + sub_creds->ssl_cert_request (sub_creds, + req_ca_rdn_der, nreqs, + cert_der_ret, cert_der_ret_length); + } + g_mutex_unlock (account->priv->creds_mutex); + EXIT; + return ret; +} + +static int +account_creds_ssl_cert_sign (E2kCreds *ecreds, + const gnutls_datum_t *cert_der, + const gnutls_datum_t *hash_data, + gnutls_datum_t *sig_data) +{ + AccountCreds *creds = (AccountCreds *)ecreds; + ExchangeAccount *account = creds->account; + E2kCreds *sub_creds; + int ret = 0; + + g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), -1); + + g_mutex_lock (account->priv->creds_mutex); + sub_creds = g_slist_nth_data (account->priv->creds_list, 0); + if (sub_creds) { + ret = sub_creds->ssl_cert_sign (sub_creds, + cert_der, + hash_data, sig_data); + } + g_mutex_unlock (account->priv->creds_mutex); + + return ret; +} + +void +exchange_account_add_creds (ExchangeAccount *account, + E2kCreds *creds) +{ + g_return_if_fail (EXCHANGE_IS_ACCOUNT (account)); + g_mutex_lock (account->priv->creds_mutex); + account->priv->creds_list = g_slist_prepend (account->priv->creds_list, creds); + g_mutex_unlock (account->priv->creds_mutex); +} + +void +exchange_account_remove_creds (ExchangeAccount *account, + E2kCreds *creds) +{ + g_return_if_fail (EXCHANGE_IS_ACCOUNT (account)); + g_mutex_lock (account->priv->creds_mutex); + account->priv->creds_list = g_slist_remove (account->priv->creds_list, creds); + g_mutex_unlock (account->priv->creds_mutex); +} + /** * exchange_account_connect: * @account: an #ExchangeAccount @@ -1371,14 +1520,18 @@ E2kOperation gcop; char *user_name = NULL; + ENTER; + *info_result = EXCHANGE_ACCOUNT_UNKNOWN_ERROR; g_return_val_if_fail (EXCHANGE_IS_ACCOUNT (account), NULL); *info_result = EXCHANGE_ACCOUNT_CONNECT_SUCCESS; + + g_mutex_lock (account->priv->connect_lock); + + HASLOCK; exchange_account_is_offline (account, &mode); - g_mutex_lock (account->priv->connect_lock); - if (mode == UNSUPPORTED_MODE) { *info_result = EXCHANGE_ACCOUNT_CONNECT_ERROR; account->priv->connecting = FALSE; @@ -1395,10 +1548,17 @@ else { *info_result = EXCHANGE_ACCOUNT_CONNECT_ERROR; } + EXIT; return NULL; } else if (account->priv->ctx) { g_mutex_unlock (account->priv->connect_lock); + EXIT; return account->priv->ctx; + } else if (!pword && !account->priv->creds_list) { + g_mutex_unlock (account->priv->connect_lock); + *info_result = EXCHANGE_ACCOUNT_PASSWORD_INCORRECT; + EXIT; + return NULL; } account->priv->connecting = TRUE; @@ -1410,7 +1570,7 @@ ac = e2k_autoconfig_new (account->home_uri, user_name, - NULL, + pword, account->priv->auth_pref); g_free (user_name); @@ -1418,15 +1578,10 @@ account->priv->ad_limit); if (!pword) { - account->priv->connecting = FALSE; - g_mutex_unlock (account->priv->connect_lock); - *info_result = EXCHANGE_ACCOUNT_PASSWORD_INCORRECT; - e2k_autoconfig_free (ac); - return NULL; + TRACEMSG("setting creds..."); + e2k_autoconfig_set_creds (ac, &account->priv->creds.creds); } - e2k_autoconfig_set_password (ac, pword); - try_connect_again: account->priv->ctx = e2k_autoconfig_get_context (ac, NULL, &result); @@ -1436,12 +1591,14 @@ account->priv->nt_domain = NULL; if (result != E2K_AUTOCONFIG_OK) { + TRACE("result was not ok: %d", result); #ifdef HAVE_KRB5 if ( is_password_expired (account, ac)) { *info_result = EXCHANGE_ACCOUNT_PASSWORD_EXPIRED; account->priv->connecting = FALSE; g_mutex_unlock (account->priv->connect_lock); e2k_autoconfig_free (ac); + EXIT; return NULL; } #endif @@ -1452,6 +1609,7 @@ e2k_autoconfig_free (ac); account->priv->connecting = FALSE; g_mutex_unlock (account->priv->connect_lock); + EXIT; return NULL; case E2K_AUTOCONFIG_AUTH_ERROR_TRY_DOMAIN: @@ -1459,6 +1617,7 @@ e2k_autoconfig_free (ac); account->priv->connecting = FALSE; g_mutex_unlock (account->priv->connect_lock); + EXIT; return NULL; case E2K_AUTOCONFIG_AUTH_ERROR_TRY_NTLM: @@ -1516,6 +1675,7 @@ } g_mutex_unlock (account->priv->connect_lock); + EXIT; return NULL; } @@ -1532,6 +1692,7 @@ account->priv->connecting = FALSE; *info_result = EXCHANGE_ACCOUNT_UNKNOWN_ERROR; g_mutex_unlock (account->priv->connect_lock); + EXIT; return NULL; /* FIXME: what error has happened? */ } @@ -1556,6 +1717,7 @@ g_mutex_unlock (account->priv->connect_lock); if (nresults) e2k_results_free (results, nresults); + EXIT; return NULL; /* FIXME: what error has happened? */ } @@ -1601,6 +1763,7 @@ g_mutex_unlock (account->priv->connect_lock); if (nresults) e2k_results_free (results, nresults); + EXIT; return account->priv->ctx; } @@ -2301,6 +2464,14 @@ e2k_uri_free (uri); + account->priv->creds.creds.http_auth_method = account_creds_http_auth_method; + account->priv->creds.creds.http_authenticate = account_creds_http_authenticate; + account->priv->creds.creds.ssl_cert_request = account_creds_ssl_cert_request; + account->priv->creds.creds.ssl_cert_sign = account_creds_ssl_cert_sign; + account->priv->creds.account = account; + + account->priv->creds_mutex = g_mutex_new (); + return account; } === modified file 'servers/exchange/storage/exchange-account.h' --- servers/exchange/storage/exchange-account.h 2007-12-18 16:36:30 +0000 +++ servers/exchange/storage/exchange-account.h 2007-12-18 18:57:46 +0000 @@ -95,6 +95,11 @@ char *exchange_account_get_authtype (ExchangeAccount *account); +void exchange_account_add_creds (ExchangeAccount *acct, + E2kCreds *creds); +void exchange_account_remove_creds (ExchangeAccount *acct, + E2kCreds *creds); + E2kContext *exchange_account_connect (ExchangeAccount *acct, const char *pword, ExchangeAccountResult *result);