Index: configure.in =================================================================== --- configure.in.orig +++ configure.in @@ -1369,6 +1369,11 @@ dnl ************************************ 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" @@ -1385,7 +1390,7 @@ AC_SUBST(E_DATA_SERVER_UI_LIBS) 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) @@ -1393,7 +1398,7 @@ AC_SUBST(EVOLUTION_ADDRESSBOOK_LIBS) 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) Index: servers/exchange/storage/exchange-account.c =================================================================== --- servers/exchange/storage/exchange-account.c.orig +++ servers/exchange/storage/exchange-account.c @@ -39,6 +39,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" @@ -53,11 +56,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; @@ -86,6 +100,10 @@ struct _ExchangeAccountPrivate { GMutex *discover_data_lock; GList *discover_datas; + + AccountCreds creds; + GMutex *creds_mutex; + GSList *creds_list; }; enum { @@ -229,6 +247,13 @@ dispose (GObject *object) 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); } @@ -1171,14 +1196,21 @@ exchange_account_set_offline (ExchangeAc 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; - account->priv->account_online = OFFLINE_MODE; - g_mutex_unlock (account->priv->connect_lock); + 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; + } + EXIT; return TRUE; } @@ -1197,11 +1229,15 @@ gboolean exchange_account_set_online (ExchangeAccount *account) { 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; } @@ -1344,6 +1380,117 @@ hierarchies_created: 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 @@ -1372,13 +1519,16 @@ exchange_account_connect (ExchangeAccoun 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; - exchange_account_is_offline (account, &mode); g_mutex_lock (account->priv->connect_lock); + HASLOCK; + exchange_account_is_offline (account, &mode); if (mode == UNSUPPORTED_MODE) { *info_result = EXCHANGE_ACCOUNT_CONNECT_ERROR; @@ -1396,10 +1546,17 @@ exchange_account_connect (ExchangeAccoun 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; @@ -1411,7 +1568,7 @@ exchange_account_connect (ExchangeAccoun ac = e2k_autoconfig_new (account->home_uri, user_name, - NULL, + pword, account->priv->auth_pref); g_free (user_name); @@ -1419,15 +1576,10 @@ exchange_account_connect (ExchangeAccoun 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); @@ -1437,12 +1589,14 @@ exchange_account_connect (ExchangeAccoun 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 @@ -1453,6 +1607,7 @@ exchange_account_connect (ExchangeAccoun 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: @@ -1460,6 +1615,7 @@ exchange_account_connect (ExchangeAccoun 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: @@ -1517,6 +1673,7 @@ exchange_account_connect (ExchangeAccoun } g_mutex_unlock (account->priv->connect_lock); + EXIT; return NULL; } @@ -1533,6 +1690,7 @@ exchange_account_connect (ExchangeAccoun 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? */ } @@ -1557,6 +1715,7 @@ exchange_account_connect (ExchangeAccoun g_mutex_unlock (account->priv->connect_lock); if (nresults) e2k_results_free (results, nresults); + EXIT; return NULL; /* FIXME: what error has happened? */ } @@ -1602,6 +1761,7 @@ skip_quota: g_mutex_unlock (account->priv->connect_lock); if (nresults) e2k_results_free (results, nresults); + EXIT; return account->priv->ctx; } @@ -2273,6 +2433,14 @@ exchange_account_new (EAccountList *acco 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; } Index: servers/exchange/storage/exchange-account.h =================================================================== --- servers/exchange/storage/exchange-account.h.orig +++ servers/exchange/storage/exchange-account.h @@ -95,6 +95,11 @@ ExchangeHierarchy *exchange_account_ 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); Index: servers/exchange/lib/e2k-context.c =================================================================== --- servers/exchange/lib/e2k-context.c.orig +++ servers/exchange/lib/e2k-context.c @@ -52,7 +52,6 @@ #include #include #include -#include #include #include #include @@ -60,6 +59,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). @@ -90,8 +98,8 @@ enum { 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 */ @@ -106,8 +114,18 @@ struct _E2kContextPrivate { 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 @@ -136,9 +154,7 @@ proxy_settings_changed (EProxy *proxy, g { 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)) @@ -149,9 +165,6 @@ proxy_settings_changed (EProxy *proxy, g 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 @@ -177,6 +190,31 @@ destroy_sub_list (gpointer uri, gpointer } 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); @@ -184,10 +222,6 @@ dispose (GObject *object) 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); @@ -208,11 +242,11 @@ dispose (GObject *object) 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; @@ -378,62 +412,89 @@ session_authenticate (SoupSession *sessi { 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 */ if (g_getenv ("SOUP_SESSION_TIMEOUT")) timeout = atoi (g_getenv ("SOUP_SESSION_TIMEOUT")); - + /* Check do we need a proxy to contact the server? */ 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"), SOUP_SESSION_TIMEOUT, timeout, @@ -441,16 +502,76 @@ e2k_context_set_auth (E2kContext *ctx, c 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)); + g_free (authmech); +} - 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)); +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 +715,8 @@ e2k_context_fba (E2kContext *ctx, SoupMe 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 +735,7 @@ e2k_context_fba (E2kContext *ctx, SoupMe /* 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 +776,15 @@ e2k_context_fba (E2kContext *ctx, SoupMe 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 +803,11 @@ e2k_context_fba (E2kContext *ctx, SoupMe 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 +815,12 @@ e2k_context_fba (E2kContext *ctx, SoupMe 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 +870,8 @@ e2k_context_fba (E2kContext *ctx, SoupMe in_fba_auth = FALSE; if (doc) xmlFreeDoc (doc); + g_free (username); + g_free (password); return FALSE; } @@ -887,7 +1027,7 @@ e2k_context_queue_message (E2kContext *c { 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); } Index: servers/exchange/lib/e2k-context.h =================================================================== --- servers/exchange/lib/e2k-context.h.orig +++ servers/exchange/lib/e2k-context.h @@ -9,6 +9,8 @@ #include +#include + #include "e2k-types.h" #include "e2k-operation.h" #include "e2k-http-utils.h" @@ -47,6 +49,25 @@ void e2k_context_set_auth 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); Index: servers/exchange/lib/e2k-autoconfig.c =================================================================== --- servers/exchange/lib/e2k-autoconfig.c.orig +++ servers/exchange/lib/e2k-autoconfig.c @@ -338,6 +338,13 @@ get_ctx_auth_handler (SoupMessage *msg, } } +void +e2k_autoconfig_set_creds (E2kAutoconfig *ac, + E2kCreds *creds) +{ + ac->creds = creds; +} + /* * e2k_autoconfig_get_context: * @ac: an autoconfig context @@ -396,8 +403,12 @@ e2k_autoconfig_get_context (E2kAutoconfi *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); soup_message_add_header (msg->request_headers, "Accept-Language", Index: servers/exchange/lib/e2k-autoconfig.h =================================================================== --- servers/exchange/lib/e2k-autoconfig.h.orig +++ servers/exchange/lib/e2k-autoconfig.h @@ -47,6 +47,7 @@ typedef struct { 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 @@ void e2k_autoconfig_set_ 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); Index: servers/exchange/lib/e2k-types.h =================================================================== --- servers/exchange/lib/e2k-types.h.orig +++ servers/exchange/lib/e2k-types.h @@ -14,6 +14,7 @@ typedef struct _E2kAddrList typedef struct _E2kContext E2kContext; typedef struct _E2kContextPrivate E2kContextPrivate; typedef struct _E2kContextClass E2kContextClass; +typedef struct _E2kCreds E2kCreds; typedef struct _E2kGlobalCatalog E2kGlobalCatalog; typedef struct _E2kGlobalCatalogPrivate E2kGlobalCatalogPrivate; Index: addressbook/libebook/e-book.c =================================================================== --- addressbook/libebook/e-book.c.orig +++ addressbook/libebook/e-book.c @@ -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) @@ -3283,6 +3287,9 @@ fetch_corba_book (EBook *book, 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; @@ -3299,6 +3306,7 @@ fetch_corba_book (EBook *book, /* 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; @@ -3310,11 +3318,28 @@ fetch_corba_book (EBook *book, */ 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); Index: addressbook/libedata-book/e-book-backend.c =================================================================== --- addressbook/libedata-book/e-book-backend.c.orig +++ addressbook/libedata-book/e-book-backend.c @@ -27,6 +27,8 @@ struct _EBookBackendPrivate { /* Signal IDs */ enum { LAST_CLIENT_GONE, + ADD_CLIENT, + REMOVE_CLIENT, LAST_SIGNAL }; @@ -572,6 +574,8 @@ e_book_backend_add_client (EBookBackend 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 @@ e_book_backend_remove_client (EBookBacke 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 @@ e_book_backend_class_init (EBookBackendC 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); } /** Index: addressbook/libedata-book/e-book-backend.h =================================================================== --- addressbook/libedata-book/e-book-backend.h.orig +++ addressbook/libedata-book/e-book-backend.h @@ -73,9 +73,10 @@ struct _EBookBackendClass { 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); }; Index: calendar/libedata-cal/e-cal-backend.c =================================================================== --- calendar/libedata-cal/e-cal-backend.c.orig +++ calendar/libedata-cal/e-cal-backend.c @@ -66,6 +66,8 @@ enum { LAST_CLIENT_GONE, OPENED, REMOVED, + ADD_CLIENT, + REMOVE_CLIENT, LAST_SIGNAL }; static guint e_cal_backend_signals[LAST_SIGNAL]; @@ -237,6 +239,22 @@ e_cal_backend_class_init (ECalBackendCla 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 @@ e_cal_backend_add_client (ECalBackend *b 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 @@ e_cal_backend_remove_client (ECalBackend 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); Index: calendar/libecal/e-cal.c =================================================================== --- calendar/libecal/e-cal.c.orig +++ calendar/libecal/e-cal.c @@ -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,19 +894,19 @@ cal_query_cb (ECalListener *listener, EC 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); } @@ -1045,7 +1048,9 @@ static void 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 +1060,18 @@ e_cal_init (ECal *ecal, ECalClass *klass 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; @@ -1632,7 +1649,7 @@ open_calendar (ECal *ecal, gboolean only g_mutex_unlock (priv->mutex); /* see if the backend needs authentication */ - if ( (priv->mode != CAL_MODE_LOCAL) && e_source_get_property (priv->source, "auth")) { + if ( needs_auth && (priv->mode != CAL_MODE_LOCAL) && e_source_get_property (priv->source, "auth")) { char *prompt, *key; char *parent_user;