Re: Adding tny_camel_account_get_supported_secure_authentication().



Here is the patch to add that function with async callbacks, based on
async stuff that saw in TnyCamelFolder.

This is quite complicated, so I'd like someone to look it over. That's
why it also adds TODO comments for documentation. There are also debug
printfs that I would remove before committing it.

-- 
Murray Cumming
murrayc murrayc com
www.murrayc.com
www.openismus.com
Index: libtinymail-camel/tny-camel-account-priv.h
===================================================================
--- libtinymail-camel/tny-camel-account-priv.h	(revision 1954)
+++ libtinymail-camel/tny-camel-account-priv.h	(working copy)
@@ -27,8 +27,12 @@
 struct _TnyCamelAccountPriv
 {
 	TnySessionCamel *session;
+	
 	GStaticRecMutex *service_lock;
+	
+	/* Set in tny_camel_store_account_prepare(). */
 	CamelService *service;
+	
 	CamelException *ex;
 	gchar *url_string, *id, *user, *host, *proto, *mech;
 	TnyGetPassFunc get_pass_func;
Index: libtinymail-camel/tny-camel-common.c
===================================================================
--- libtinymail-camel/tny-camel-common.c	(revision 1954)
+++ libtinymail-camel/tny-camel-common.c	(working copy)
@@ -29,6 +29,18 @@
 
 #include <tny-folder-store-query.h>
 
+/* TODOL Rename to tny_camel_session_check_operation. */
+/** _tny_session_check_operation:
+ * @session: A camel session.
+ * @err: A pointer to a GError*, which will be set if the session is not ready. 
+ * This should be freed with g_error_free().
+ * @domain The error domain for the GError, if necessary.
+ * @code The error code for the GError if necessary.
+ * @result: TRUE if the session is ready to be used.
+ *
+ * Check that the session is ready to be used, and create a GError with the specified 
+ * domain and code if the session is not ready.
+ **/
 gboolean 
 _tny_session_check_operation (TnySessionCamel *session, GError **err, GQuark domain, gint code)
 {
Index: libtinymail-camel/tny-camel-store-account.c
===================================================================
--- libtinymail-camel/tny-camel-store-account.c	(revision 1954)
+++ libtinymail-camel/tny-camel-store-account.c	(working copy)
@@ -91,8 +91,11 @@
 		GList *options = apriv->options;
 		gchar *proto;
 
-		if (apriv->proto == NULL)
+		if (apriv->proto == NULL) {
+			g_warning ("%s: apriv->proto is NULL. "
+				"You might need to call tny_account_set_proto().", __FUNCTION__);
 			return;
+		}
 
 		proto = g_strdup_printf ("%s://", apriv->proto); 
 
Index: libtinymail-camel/tny-camel-account.c
===================================================================
--- libtinymail-camel/tny-camel-account.c	(revision 1954)
+++ libtinymail-camel/tny-camel-account.c	(working copy)
@@ -47,6 +47,10 @@
 #include "tny-camel-account-priv.h"
 #include "tny-session-camel-priv.h"
 
+#define TINYMAIL_ENABLE_PRIVATE_API
+#include "tny-common-priv.h"
+#undef TINYMAIL_ENABLE_PRIVATE_API
+
 static GObjectClass *parent_class = NULL;
 
 static gboolean 
@@ -282,6 +286,8 @@
 	return NULL;
 }
 
+/* TODO: Documentation.
+ */
 void 
 _tny_camel_account_start_camel_operation_n (TnyCamelAccount *self, CamelOperationStatusFunc func, gpointer user_data, const gchar *what, gboolean cancel)
 {
@@ -331,12 +337,21 @@
 	return;
 }
 
+/* TODO: Documentation. 
+ * Why would we use this instead of just calling the callback directly in an 
+ * idle handler? 
+ * How does this allow the operation to be cancelled? Can the user cancel it, 
+ * or does cancel happen only if there is an internal error?
+ * murrayc.
+ */
 void 
 _tny_camel_account_start_camel_operation (TnyCamelAccount *self, CamelOperationStatusFunc func, gpointer user_data, const gchar *what)
 {
 	_tny_camel_account_start_camel_operation_n (self, func, user_data, what, TRUE);
 }
 
+/* TODO: Documentation.
+ */
 void 
 _tny_camel_account_stop_camel_operation (TnyCamelAccount *self)
 {
@@ -1109,3 +1124,250 @@
 
 	return type;
 }
+
+typedef struct 
+{
+	TnyCamelAccount *self;
+	TnyCamelGetSupportedSecureAuthCallback callback;
+	TnyStatusCallback status_callback;
+	gpointer user_data;
+	gboolean cancelled;
+	GList *result;
+	/* This stops us from calling a status callback after the operation has 
+	 * finished. */
+	TnyIdleStopper* stopper;
+	GError *err;
+	TnySessionCamel *session;
+} GetSupportedAuthInfo;
+
+
+static void
+tny_camel_account_get_supported_secure_authentication_async_status (struct _CamelOperation *op, const char *what, int sofar, int oftotal, void *thr_user_data)
+{
+	printf ("DEBUG: %s\n", __FUNCTION__);
+	
+	/* Use the generic tny_progress* idle callback system 
+	 * to call our status callback,
+	 * using the IdleStopper to stop the status callback 
+	 * from being called after the main callback: */
+	GetSupportedAuthInfo *info = thr_user_data;
+	TnyProgressInfo *progress_info = tny_progress_info_new (
+		G_OBJECT (info->self), 
+		info->status_callback, 
+		TNY_GET_SUPPORTED_SECURE_AUTH_STATUS, 
+		TNY_GET_SUPPORTED_SECURE_AUTH_STATUS_GET_SECURE_AUTH, 
+		what, sofar, 
+		oftotal, info->stopper, info->user_data);
+
+	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+		tny_progress_info_idle_func, progress_info, 
+		tny_progress_info_destroy);
+}
+		
+/** This is the GDestroyNotify callback provided to g_idle_add_full()
+ * for tny_camel_account_get_supported_secure_authentication_async_callback().
+ */
+static void
+tny_camel_account_get_supported_secure_authentication_async_destroyer (gpointer thr_user_data)
+{
+	printf ("DEBUG: %s)\n", __FUNCTION__);
+		
+	GetSupportedAuthInfo *info = thr_user_data;
+	TnyCamelAccount *self = info->self;
+	TnyCamelAccountPriv *priv = TNY_CAMEL_ACCOUNT_GET_PRIVATE (self);
+
+	/* thread reference */
+	g_object_unref (G_OBJECT (self));
+	if (info->err) {
+		g_error_free (info->err);
+		info->err = NULL;	
+	}
+
+	_tny_session_stop_operation (info->session);
+
+	tny_idle_stopper_destroy (info->stopper);
+	info->stopper = NULL;
+	
+	g_slice_free (GetSupportedAuthInfo, thr_user_data);
+}
+
+static gboolean
+tny_camel_account_get_supported_secure_authentication_async_callback (gpointer thr_user_data)
+{
+	printf ("DEBUG: %s\n", __FUNCTION__);
+		
+	GetSupportedAuthInfo *info = thr_user_data;
+	TnyCamelAccount *self = info->self;
+	TnyCamelAccountPriv *priv = TNY_CAMEL_ACCOUNT_GET_PRIVATE (self);
+
+	if (info->callback) {
+		printf ("DEBUG: %s: Calling callback.\n", __FUNCTION__);
+		info->callback (info->self, info->cancelled, info->result, &info->err, info->user_data);
+	}
+
+	/* Prevent status callbacks from being called after this
+	 * (can happen because the 2 idle callbacks have different priorities)
+	 * by causing tny_idle_stopper_is_stopped() to return TRUE. */
+	tny_idle_stopper_stop (info->stopper);
+
+	return FALSE;
+}
+
+/* Starts the operation in the thread: */
+static gpointer 
+tny_camel_account_get_supported_secure_authentication_async_thread (
+	gpointer thr_user_data)
+{
+	printf ("DEBUG: %s\n", __FUNCTION__);
+		
+	GetSupportedAuthInfo *info = thr_user_data;
+	TnyCamelAccount *self = info->self;
+	TnyCamelAccountPriv *priv = TNY_CAMEL_ACCOUNT_GET_PRIVATE (self);
+	CamelException ex = CAMEL_EXCEPTION_INITIALISER;
+	GError *err = NULL;
+
+	g_static_rec_mutex_lock (priv->service_lock);
+
+	info->cancelled = FALSE;
+
+	/* Make sure that the status callback is called (in an idle handler) 
+	 * while this thread is running: */
+	_tny_camel_account_start_camel_operation (TNY_CAMEL_ACCOUNT (self), 
+		tny_camel_account_get_supported_secure_authentication_async_status, info, 
+		"Querying supported secure authentication methods from server.");
+
+	/* Do the actual work:
+	 * This is happening in a thread, 
+	 * and the status callback is being called regularly while this is 
+	 * happening. */	
+	GList *authtypes = camel_service_query_auth_types (priv->service, &ex);
+	GList * result = NULL;
+	GList *iter = authtypes;
+	while (iter) {
+		CamelServiceAuthType *item = (CamelServiceAuthType *)iter->data;
+		if (item) {
+			/* Get the name of the auth method:
+			 * Note that, at least for IMAP, authproto=NULL when 
+			 * name=Password. */
+			printf ("DEBUG: %s: authproto =%s, name=%s\n", __FUNCTION__, item->authproto, item->name);
+			result = g_list_append (result, g_strdup (item->authproto));
+		}
+		
+		iter = g_list_next (iter);	
+	}
+	g_list_free (authtypes);
+	authtypes = NULL;
+	
+	
+	/* The work has finished, so clean up and provide the result via the 
+	 * main callback: */
+	info->result = result;
+	
+	info->cancelled = camel_operation_cancel_check (priv->cancel);
+	
+	_tny_camel_account_stop_camel_operation (TNY_CAMEL_ACCOUNT (self));
+
+	/* Create the GError if necessary,
+	 * from the CamelException: */
+	info->err = NULL;
+	if (camel_exception_is_set (&ex))
+	{
+		g_set_error (&err, TNY_FOLDER_ERROR, 
+			TNY_FOLDER_ERROR_REFRESH,
+			camel_exception_get_description (&ex));
+		if (err != NULL)
+			info->err = g_error_copy ((const GError *) err);
+	}
+
+	g_static_rec_mutex_unlock (priv->service_lock);
+
+	/* Call the callback, with the result, in an idle thread,
+	 * and stop this thread: */
+	if (info->callback) {
+		g_idle_add_full (G_PRIORITY_HIGH, 
+				tny_camel_account_get_supported_secure_authentication_async_callback, 
+				info, tny_camel_account_get_supported_secure_authentication_async_destroyer);
+	} else {
+		/* Thread reference */
+		g_object_unref (G_OBJECT (self));
+	}
+	g_thread_exit (NULL);
+
+	return NULL;
+}
+
+
+
+/* tny_camel_account_get_supported_secure_authentication:
+ * @self: a #TnyCamelAccount object.
+ * @result: A GSList of gchar* secure authentication mechanisms. This 
+ * should be freed with g_slist_free() after calling g_free() on each 
+ * item's data.
+ * 
+ * Query the server for the list of supported secure authentication mechanisms.
+ * The #TnyCamelAccount must have a valid hostname and the port number 
+ * must be set if appropriate.
+ * The returned strings may be used as parameters to 
+ * tny_account_set_secure_auth_mech().
+ */
+void tny_camel_account_get_supported_secure_authentication (
+  TnyCamelAccount *self,
+  TnyCamelGetSupportedSecureAuthCallback callback,
+  TnyStatusCallback status_callback,
+  gpointer user_data)
+{ 
+	TnyCamelAccountPriv *priv = TNY_CAMEL_ACCOUNT_GET_PRIVATE (self);
+	g_return_if_fail (callback);
+	g_return_if_fail (priv->session);
+	
+	
+	/* Store all the interesting info in a struct 
+	 * launch a thread and keep that struct-instance around.
+	 * - While the thread is running, we regularly call the status callback in 
+	 * an idle handler.
+	 * - When the thread is finished, the main callback will 
+	 * then be called in an idle handler.
+	 */
+
+	/* Check that the session is ready, and stop with a GError if it is not: */
+	GError *err = NULL;
+	if (!_tny_session_check_operation (priv->session, &err, 
+			TNY_ACCOUNT_ERROR, TNY_ACCOUNT_ERROR_GET_SUPPORTED_AUTH))
+	{
+		if (callback)
+			callback (self, TRUE /* cancelled */, NULL, &err, user_data);
+		g_error_free (err);
+		return;
+	}
+
+
+	/* Idle info for the status callback: */
+	GetSupportedAuthInfo *info = g_slice_new (GetSupportedAuthInfo);
+	info->session = priv->session;
+	info->err = NULL;
+	info->self = self;
+	info->callback = callback;
+	info->result = NULL;
+	info->status_callback = status_callback;
+	info->user_data = user_data;
+	
+	/* Use a ref count because we do not know which of the 2 idle callbacks 
+	 * will be the last, and we can only unref self in the last callback:
+	 * This is destroyed in the idle GDestroyNotify callback.
+	 * A shared copy is taken and released by the _tny_progress* callback,
+	 * so that it can prevent the stats callback from being called after 
+	 * the main callback. */
+	info->stopper = tny_idle_stopper_new();
+
+	/* thread reference */
+	g_object_ref (G_OBJECT (self));
+
+	/* This will cause the idle status callback to be called,
+	 * via _tny_camel_account_start_camel_operation,
+	 * and also calls the idle main callback: */
+	printf ("DEBUG: %s: before calling g_thread_create()\n", __FUNCTION__);
+	GThread *thread = g_thread_create (
+		tny_camel_account_get_supported_secure_authentication_async_thread,
+		info, FALSE, NULL);
+}
+
Index: libtinymail-camel/tny-camel-account.h
===================================================================
--- libtinymail-camel/tny-camel-account.h	(revision 1954)
+++ libtinymail-camel/tny-camel-account.h	(working copy)
@@ -88,6 +88,17 @@
 void tny_camel_account_set_session (TnyCamelAccount *self, TnySessionCamel *session);
 void tny_camel_account_set_online (TnyCamelAccount *self, gboolean online, GError **err);
 
+typedef void (*TnyCamelGetSupportedSecureAuthCallback) (
+  TnyCamelAccount *self, gboolean cancelled,
+  GList *auth_types, GError **err, 
+  gpointer user_data);
+
+void tny_camel_account_get_supported_secure_authentication(
+  TnyCamelAccount *self,
+  TnyCamelGetSupportedSecureAuthCallback callback,
+  TnyStatusCallback status_callback,
+  gpointer user_data);
+
 G_END_DECLS
 
 #endif
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 1954)
+++ ChangeLog	(working copy)
@@ -1,3 +1,14 @@
+2007-05-15  Murray Cumming  <murrayc murrayc com>
+
+	* libtinymail-camel/tny-camel-account.h:
+	* libtinymail-camel/tny-camel-account.c:
+	Added tny_camel_account_get_supported_secure_authentication(), 
+	to get a list of strings for use with tny_account_set_secure_auth_mech(),
+	by querying the server. This is async, providing the result via a callback.
+
+	* libtinymail/tny-error.h: Added TNY_ACCOUNT_ERROR_GET_SUPPORTED_AUTH, 
+	for use by tny_camel_account_get_supported_secure_authentication
+	
 2007-05-14  Philip Van Hoof  <pvanhoof gnome org>
 
 	* Waiting for the + continuation after IDLE
Index: libtinymail/tny-status.h
===================================================================
--- libtinymail/tny-status.h	(revision 1954)
+++ libtinymail/tny-status.h	(working copy)
@@ -33,7 +33,8 @@
 enum _TnyStatusDomain
 {
 	TNY_FOLDER_STATUS = 1,
-	TNY_GET_MSG_QUEUE_STATUS = 2
+	TNY_GET_MSG_QUEUE_STATUS = 2,
+	TNY_GET_SUPPORTED_SECURE_AUTH_STATUS = 3
 };
 
 #define TNY_TYPE_STATUS (tny_status_get_type())
@@ -45,6 +46,7 @@
 	TNY_GET_MSG_QUEUE_STATUS_GET_MSG = 3,
 	TNY_FOLDER_STATUS_CODE_XFER_MSGS = 4,
 	TNY_FOLDER_STATUS_CODE_COPY_FOLDER = 5,
+	TNY_GET_SUPPORTED_SECURE_AUTH_STATUS_GET_SECURE_AUTH = 6
 };
 
 struct _TnyStatus 
Index: libtinymail/tny-enums.h
===================================================================
--- libtinymail/tny-enums.h	(revision 1954)
+++ libtinymail/tny-enums.h	(working copy)
@@ -10,12 +10,14 @@
 	TNY_FOLDER_STATUS_CODE_REFRESH = 1,
 	TNY_FOLDER_STATUS_CODE_GET_MSG = 2,
 	TNY_GET_MSG_QUEUE_STATUS_GET_MSG = 3
+	TNY_GET_SUPPORTED_SECURE_AUTH_STATUS_GET_SECURE_AUTH = 4
 } TnyStatusCode;
 
 typedef enum 
 {
 	TNY_FOLDER_STATUS = 1,
-	TNY_GET_MSG_QUEUE_STATUS  = 2
+	TNY_GET_MSG_QUEUE_STATUS  = 2,
+	TNY_GET_SUPPORTED_SECURE_AUTH_STATUS = 3
 } TnyStatusDomain;
 
 typedef enum {
Index: libtinymail/tny-progress-info.c
===================================================================
--- libtinymail/tny-progress-info.c	(revision 1954)
+++ libtinymail/tny-progress-info.c	(working copy)
@@ -74,6 +74,8 @@
 };
 
 
+/* TODO: What is the purpose of the status domain and code?
+ */
 /** 
  * tny_progress_info_new:
  * @self: the sender of the status event
Index: libtinymail/tny-error.h
===================================================================
--- libtinymail/tny-error.h	(revision 1954)
+++ libtinymail/tny-error.h	(working copy)
@@ -93,6 +93,7 @@
 	TNY_TRANSPORT_ACCOUNT_ERROR_SEND = 13,
 
 	TNY_ACCOUNT_ERROR_TRY_CONNECT = 14,
+	TNY_ACCOUNT_ERROR_GET_SUPPORTED_AUTH = 16,
 
 	TNY_ACCOUNT_STORE_ERROR_UNKNOWN_ALERT = 15
 };


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