[evolution-patches] First pass at IMAP quota support (RFC 2087)



Attached is a first attempt at IMAP quota support.  A screenshot can be
seen here:
http://people.redhat.com/dmalcolm/ScreenshotOfIMAPQuota.png

It detects the QUOTA capability on an IMAP server.  If present, when
getting folder information, it does a quota query on _every_ folder
returned by the server.  

This is returned as a CamelImapQuotaRoot object, created by the
CamelProvider in its thread, owned by the CamelFolderInfo.  

The UI thread examines these and converts to a percentage, or to
CAMEL_QUOTA_UNAVAILABLE, or to CAMEL_QUOTA_INHERIT_PARENT.  It appends a
% usage figure to every mail folder for which data is available that
doesn't inhereit its quota from its parent.  This means that for typical
usage you get a % full indicator on your INBOX.

Limitations so far:
- There are some hacks/incomplete bits in the RFC2087 parsing code
- Is the additional IMAP traffic too excessive?
- I haven't yet added GUI to the Folder Properties dialog
- There's a crash when I free up the quota structs; I believe I've
messed up somewhere - what are the ownership rules for CamelFolderInfo
structs?  So for now I leak memory :-(
- This should be generalised to handle quotas on other types of mail
store e.g. for Exchange servers.  I don't know the details of these;
suggestions are welcome.

Still it works (with the above caveats), and I wanted to get feedback. 

Dave
Index: camel/camel-folder.h
===================================================================
RCS file: /cvs/gnome/evolution/camel/camel-folder.h,v
retrieving revision 1.124
diff -u -p -r1.124 camel-folder.h
--- camel/camel-folder.h	13 Apr 2004 15:58:56 -0000	1.124
+++ camel/camel-folder.h	7 Oct 2004 16:19:40 -0000
@@ -112,6 +112,9 @@ struct _CamelFolder
 #define CAMEL_FOLDER_IS_JUNK                (1<<5)
 #define CAMEL_FOLDER_FILTER_JUNK  	    (1<<6)
 
+#define CAMEL_QUOTA_UNAVAILABLE (-1)
+#define CAMEL_QUOTA_INHERIT_PARENT (-2)
+
 typedef struct {
 	CamelObjectClass parent_class;
 
Index: camel/camel-store.c
===================================================================
RCS file: /cvs/gnome/evolution/camel/camel-store.c,v
retrieving revision 1.154
diff -u -p -r1.154 camel-store.c
--- camel/camel-store.c	20 Sep 2004 05:59:53 -0000	1.154
+++ camel/camel-store.c	7 Oct 2004 16:19:41 -0000
@@ -850,6 +850,12 @@ void
 camel_folder_info_free (CamelFolderInfo *fi)
 {
 	if (fi) {
+		/* FIXME: disabled for now */
+#if 0
+		if (fi->quota_info) {
+			camel_imap_quota_root_free (fi->quota_info);	
+		}
+#endif
 		camel_folder_info_free (fi->next);
 		camel_folder_info_free (fi->child);
 		g_free (fi->name);
@@ -1025,6 +1031,9 @@ static CamelFolderInfo *folder_info_clon
 	else
 		info->child = NULL;
 
+	/* Eventually do this with reference counting? */
+	info->quota_info = camel_imap_quota_root_clone (fi->quota_info);
+
 	return info;
 }
 
@@ -1154,6 +1163,16 @@ camel_store_noop (CamelStore *store, Cam
 	CS_CLASS (store)->noop (store, ex);
 }
 
+CamelImapQuotaRoot *
+camel_store_get_quota_info (CamelStore *store, CamelFolderInfo *fi, CamelException *ex)
+{
+	if (CS_CLASS (store)->get_quota_info) {
+		return CS_CLASS (store)->get_quota_info (store, fi, ex);
+	} else {
+		return NULL;
+	}
+}
+
 
 /**
  * camel_store_folder_uri_equal:
@@ -1203,4 +1222,107 @@ camel_store_folder_uri_equal (CamelStore
 	camel_url_free (url1);
 	
 	return equal;
+}
+
+void 
+camel_imap_quota_root_free (CamelImapQuotaRoot *quota_root)
+{
+	GList *iter;
+
+	g_return_if_fail (quota_root);
+
+	g_free (quota_root->name);
+
+	for (iter=quota_root->resources; iter; iter=iter->next) {
+		camel_quota_resource_free ((CamelQuotaResource*)(iter->data));
+	}
+
+	g_list_free (quota_root->resources);
+	
+	g_free (quota_root);
+}
+
+int 
+camel_imap_quota_root_get_worst_usage (CamelImapQuotaRoot *quota_root)
+{
+	/* Return most pessimistic usage information for display to the user: */	
+	if (quota_root->resources) {
+		GList *iter;
+		int result = 0;
+		for (iter = quota_root->resources; iter; iter=iter->next) {
+			CamelQuotaResource *qr = (CamelQuotaResource *)iter->data;
+			g_assert (qr);
+			
+			if (qr->limit>0) {
+				int percent = (qr->usage*100)/qr->limit;
+				
+				if (result<percent) {
+					result = percent;
+				}
+			}
+			
+			camel_quota_resource_free (qr);
+		}
+		
+		return result;		
+	} else {
+		return CAMEL_QUOTA_UNAVAILABLE;
+	}
+}
+
+CamelImapQuotaRoot *
+camel_imap_quota_root_clone (CamelImapQuotaRoot *quota_root)
+{
+	CamelImapQuotaRoot *new_qr;
+	GList *iter;
+
+	if (NULL==quota_root) {
+		return NULL;
+	}
+
+	new_qr = g_new0 (CamelImapQuotaRoot, 1);
+	new_qr->name = g_strdup (quota_root->name);
+
+	for (iter = quota_root->resources; iter; iter=iter->next) {
+		new_qr->resources = g_list_append (new_qr->resources,
+						   camel_quota_resource_clone ((CamelQuotaResource*)iter->data));
+	}
+	
+	return new_qr;
+}
+
+CamelQuotaResource *
+camel_quota_resource_new (const gchar *name, CamelQuotaAmount usage, CamelQuotaAmount limit)
+{
+	CamelQuotaResource *qr;
+
+	g_return_val_if_fail (name, NULL);
+
+	qr = g_new0 (CamelQuotaResource, 1);
+	qr->name = g_strdup (name);
+	qr->usage = usage;
+	qr->limit = limit;
+
+	return qr;
+}
+
+void
+camel_quota_resource_free (CamelQuotaResource *qr)
+{
+	g_return_if_fail (qr);
+	g_return_if_fail (qr->name);
+
+	/* FIXME: getting a crash here; looks like I'm messing up the lifetime/ownership of these objects between the threads */
+#if 0
+	g_free (qr->name);
+	g_free (qr);
+#endif
+}
+
+CamelQuotaResource *
+camel_quota_resource_clone (CamelQuotaResource *qr)
+{
+	g_return_val_if_fail (qr, NULL);
+
+	return camel_quota_resource_new (qr->name, qr->usage, qr->limit);
 }
Index: camel/camel-store.h
===================================================================
RCS file: /cvs/gnome/evolution/camel/camel-store.h,v
retrieving revision 1.69
diff -u -p -r1.69 camel-store.h
--- camel/camel-store.h	26 May 2004 04:24:01 -0000	1.69
+++ camel/camel-store.h	7 Oct 2004 16:19:41 -0000
@@ -42,6 +42,21 @@ enum {
 	CAMEL_STORE_ARG_FIRST  = CAMEL_SERVICE_ARG_FIRST + 100,
 };
 
+typedef guint32 CamelQuotaAmount;
+
+/* struct designed to hold the result of an RFC-2087 quota_resource */
+typedef struct _CamelQuotaResource {
+ 	char *name;
+	CamelQuotaAmount usage;
+	CamelQuotaAmount limit;	
+} CamelQuotaResource;
+
+/* Eventually generalise this into a base class, with provider-specific quota info ? */
+typedef struct {
+	char *name;
+	GList *resources; /* list of CamelImapQuotaResource */
+} CamelImapQuotaRoot;
+
 typedef struct _CamelFolderInfo {
 	struct _CamelFolderInfo *next;
 	struct _CamelFolderInfo *parent;
@@ -54,6 +69,11 @@ typedef struct _CamelFolderInfo {
 	guint32 flags;
 	guint32 unread;
 	guint32 total;
+
+#if 1
+	CamelImapQuotaRoot *quota_info;
+#endif
+	
 } CamelFolderInfo;
 
 /* Note: these are abstractions (duh), its upto the provider to make them make sense */
@@ -162,6 +182,10 @@ typedef struct {
 						     CamelException *ex);
 	void            (*noop)                     (CamelStore *store,
 						     CamelException *ex);
+
+	CamelImapQuotaRoot *(*get_quota_info)       (CamelStore *store,
+						     CamelFolderInfo *fi,
+						     CamelException *ex);
 } CamelStoreClass;
 
 
@@ -227,9 +251,25 @@ void             camel_store_unsubscribe
 void             camel_store_noop                     (CamelStore *store,
 						       CamelException *ex);
 
+CamelImapQuotaRoot *camel_store_get_quota_info        (CamelStore *store,
+						       CamelFolderInfo *fi,
+						       CamelException *ex);
+
 int              camel_store_folder_uri_equal         (CamelStore *store,
 						       const char *uri0,
 						       const char *uri1);
+GList *          camel_store_get_quota_resources      (CamelStore *store,
+						       CamelException *ex);
+
+CamelQuotaResource *camel_quota_resource_new          (const gchar *name,
+						       CamelQuotaAmount usage,
+						       CamelQuotaAmount limit);
+void                camel_quota_resource_free         (CamelQuotaResource *qr);
+CamelQuotaResource* camel_quota_resource_clone        (CamelQuotaResource *qr);
+
+void                camel_imap_quota_root_free        (CamelImapQuotaRoot *quota_root);
+int                 camel_imap_quota_root_get_worst_usage (CamelImapQuotaRoot *quota_root);
+CamelImapQuotaRoot *camel_imap_quota_root_clone        (CamelImapQuotaRoot *quota_root);
 
 #ifdef __cplusplus
 }
Index: camel/providers/imap/camel-imap-folder.c
===================================================================
RCS file: /cvs/gnome/evolution/camel/providers/imap/camel-imap-folder.c,v
retrieving revision 1.338
diff -u -p -r1.338 camel-imap-folder.c
--- camel/providers/imap/camel-imap-folder.c	20 Sep 2004 05:59:53 -0000	1.338
+++ camel/providers/imap/camel-imap-folder.c	7 Oct 2004 16:19:41 -0000
@@ -2807,3 +2807,87 @@ parse_fetch_response (CamelImapFolder *i
 	return data;
 }
 
+static void
+getquotaroot_cb (char *response_str,
+		 CamelImapQuotaRoot *result)
+{
+	g_assert (response_str);
+	g_assert (result);
+
+	g_message (response_str);
+
+	/* Expect lines of the form "* QUOTAROOT" and "* QUOTA": */
+	/*   
+   quota_list      ::= "(" #quota_resource ")"
+
+   quota_resource  ::= atom SP number SP number
+
+   quota_response  ::= "QUOTA" SP astring SP quota_list
+	*/
+
+	if (g_str_has_prefix(response_str, "* QUOTAROOT ")) {
+		/* FIXME: get quotaroot name */
+		if (result->name) {
+			g_free (result->name);
+		}
+		result->name = g_strdup ("fubar"); /* FIXME */
+	}
+
+	if (g_str_has_prefix(response_str, "* QUOTA ")) {
+		char *quota_name;		
+		if (imap_parse_rfc2087_quota_response (response_str+2,
+						       &quota_name,
+						       &result->resources)) {
+		}
+	}
+
+}
+
+CamelImapQuotaRoot *
+camel_imap_quota_root_new (CamelImapResponse *response)
+{
+	CamelImapQuotaRoot *result;
+
+	g_return_val_if_fail (response, NULL);
+
+	/* Expect a response of this form: */
+
+	/* 
+	   Result:     OK - getquota completed
+	   NO - getquota error: no such mailbox, permission denied
+	   BAD - command unknown or arguments invalid
+
+	   The GETQUOTAROOT command takes the name of a mailbox and returns the
+	   list of quota roots for the mailbox in an untagged QUOTAROOT
+	   response.  For each listed quota root, it also returns the quota
+	   root's resource usage and limits in an untagged QUOTA response.
+
+   Example:    C: A003 GETQUOTAROOT INBOX
+               S: * QUOTAROOT INBOX ""
+               S: * QUOTA "" (STORAGE 10 512)
+               S: A003 OK Getquota completed
+	*/
+
+	result = g_new0 (CamelImapQuotaRoot, 1);
+	
+	/* Handle the lines of the response: */
+	g_ptr_array_foreach (response->untagged,
+			     (GFunc)getquotaroot_cb,
+			     result);
+
+	return result;
+}
+
+CamelImapQuotaRoot *
+camel_imap_folder_get_quota_root (CamelImapFolder *imap_folder,
+				  CamelException *ex)
+{
+	CamelFolder *folder = CAMEL_FOLDER (imap_folder);
+	CamelImapStore *imap_store = CAMEL_IMAP_STORE (folder->parent_store);
+
+	return camel_imap_store_get_quota_root (imap_store,
+						folder->full_name,
+						ex);
+}
+
+
Index: camel/providers/imap/camel-imap-folder.h
===================================================================
RCS file: /cvs/gnome/evolution/camel/providers/imap/camel-imap-folder.h,v
retrieving revision 1.42
diff -u -p -r1.42 camel-imap-folder.h
--- camel/providers/imap/camel-imap-folder.h	14 Jan 2004 04:35:58 -0000	1.42
+++ camel/providers/imap/camel-imap-folder.h	7 Oct 2004 16:19:41 -0000
@@ -36,6 +36,7 @@ extern "C" {
 #include "camel-imap-types.h"
 #include <camel/camel-disco-folder.h>
 #include <camel/camel-folder-search.h>
+#include <camel/camel-store.h>
 
 #define CAMEL_IMAP_FOLDER_TYPE     (camel_imap_folder_get_type ())
 #define CAMEL_IMAP_FOLDER(obj)     (CAMEL_CHECK_CAST((obj), CAMEL_IMAP_FOLDER_TYPE, CamelImapFolder))
@@ -81,6 +82,11 @@ CamelStream *camel_imap_folder_fetch_dat
 					   const char *section_text,
 					   gboolean cache_only,
 					   CamelException *ex);
+
+CamelImapQuotaRoot *camel_imap_folder_get_quota_root (CamelImapFolder *imap_folder,
+						      CamelException *ex);
+
+CamelImapQuotaRoot *camel_imap_quota_root_new (CamelImapResponse *response);
 
 /* Standard Camel function */
 CamelType camel_imap_folder_get_type (void);
Index: camel/providers/imap/camel-imap-store.c
===================================================================
RCS file: /cvs/gnome/evolution/camel/providers/imap/camel-imap-store.c,v
retrieving revision 1.303
diff -u -p -r1.303 camel-imap-store.c
--- camel/providers/imap/camel-imap-store.c	27 Sep 2004 17:41:15 -0000	1.303
+++ camel/providers/imap/camel-imap-store.c	7 Oct 2004 16:19:41 -0000
@@ -73,6 +73,9 @@ static CamelDiscoStoreClass *parent_clas
 
 static char imap_tag_prefix = 'A';
 
+static void update_quota_info (CamelImapStore *imap_store,
+			       CamelFolderInfo *fi);
+
 static void construct (CamelService *service, CamelSession *session,
 		       CamelProvider *provider, CamelURL *url,
 		       CamelException *ex);
@@ -448,6 +451,7 @@ static struct {
 	{ "UIDPLUS",		IMAP_CAPABILITY_UIDPLUS },
 	{ "LITERAL+",		IMAP_CAPABILITY_LITERALPLUS },
 	{ "STARTTLS",           IMAP_CAPABILITY_STARTTLS },
+	{ "QUOTA",              IMAP_CAPABILITY_QUOTA },
 	{ NULL, 0 }
 };
 
@@ -2394,6 +2398,8 @@ parse_list_response_as_folder_info (Came
 	if (flags & CAMEL_IMAP_FOLDER_UNMARKED)
 		fi->unread = -1;
 
+	update_quota_info (imap_store, fi);
+
 	return fi;
 }
 
@@ -2904,6 +2910,32 @@ fail:
 	return NULL;
 }
 
+static void
+update_quota_info (CamelImapStore *imap_store,
+		   CamelFolderInfo *fi)
+{
+	/* Query and fill in quota info for this folder: */
+	char *folder_name;
+
+	CamelImapQuotaRoot *qr;
+
+	CAMEL_SERVICE_ASSERT_LOCKED (imap_store, connect_lock);
+
+	if (fi->quota_info) {
+		camel_imap_quota_root_free (fi->quota_info);
+		fi->quota_info = NULL;
+	}
+	
+	folder_name = camel_imap_store_summary_path_to_full (imap_store->summary, fi->full_name, imap_store->dir_sep);
+
+	qr = camel_imap_store_get_quota_root (imap_store, 
+					      folder_name,
+					      NULL);
+	g_free (folder_name);
+
+	fi->quota_info = qr;
+}
+
 static CamelFolderInfo *
 get_folder_info_online (CamelStore *store, const char *top, guint32 flags, CamelException *ex)
 {
@@ -2937,6 +2969,8 @@ get_folder_info_online (CamelStore *stor
 
 	d(dumpfi(tree));
 	camel_store_summary_save((CamelStoreSummary *)imap_store->summary);
+
+	update_quota_info (imap_store, tree);
 done:
 	CAMEL_SERVICE_UNLOCK(store, connect_lock);
 
@@ -3208,4 +3242,48 @@ camel_imap_store_readline (CamelImapStor
 	g_byte_array_free (ba, FALSE);
 	
 	return nread;
+}
+
+CamelImapQuotaRoot *
+camel_imap_store_get_quota_root (CamelImapStore *imap_store,
+				 const char *folder_name,
+				 CamelException *ex)
+{
+	g_return_val_if_fail (imap_store, NULL);
+	g_return_val_if_fail (folder_name, NULL);
+
+	CAMEL_SERVICE_LOCK (imap_store, connect_lock);
+
+	if (!imap_store->connected) {
+		CAMEL_SERVICE_UNLOCK (imap_store, connect_lock);
+		return NULL;
+	}
+
+	if (imap_store->capabilities & IMAP_CAPABILITY_QUOTA) {	
+		/* The server claimed to support RFC-2087: */
+		CamelImapResponse *response;
+
+		/* FIXME FIXME FIXME: locking issues? */
+		response = camel_imap_command (imap_store,
+					       NULL,
+					       ex,
+					       "GETQUOTAROOT \"%s\"", folder_name);
+
+		if (response) {
+			CamelImapQuotaRoot *qr = camel_imap_quota_root_new (response);
+
+			/* Free the resource, unlocking the store */
+			camel_imap_response_free (imap_store,
+						  response);	
+
+			CAMEL_SERVICE_UNLOCK (imap_store, connect_lock);
+
+			return qr;
+		}
+	}
+
+	CAMEL_SERVICE_UNLOCK (imap_store, connect_lock);
+
+	return NULL;
+
 }
Index: camel/providers/imap/camel-imap-store.h
===================================================================
RCS file: /cvs/gnome/evolution/camel/providers/imap/camel-imap-store.h,v
retrieving revision 1.60
diff -u -p -r1.60 camel-imap-store.h
--- camel/providers/imap/camel-imap-store.h	25 Mar 2004 23:02:40 -0000	1.60
+++ camel/providers/imap/camel-imap-store.h	7 Oct 2004 16:19:41 -0000
@@ -94,6 +94,7 @@ typedef enum {
 #define IMAP_CAPABILITY_STARTTLS                (1 << 6)
 #define IMAP_CAPABILITY_useful_lsub		(1 << 7)
 #define IMAP_CAPABILITY_utf8_search		(1 << 8)
+#define IMAP_CAPABILITY_QUOTA  		        (1 << 9)
 
 #define IMAP_PARAM_OVERRIDE_NAMESPACE		(1 << 0)
 #define IMAP_PARAM_CHECK_ALL			(1 << 1)
@@ -141,6 +142,10 @@ CamelType camel_imap_store_get_type (voi
 gboolean camel_imap_store_connected (CamelImapStore *store, CamelException *ex);
 
 ssize_t camel_imap_store_readline (CamelImapStore *store, char **dest, CamelException *ex);
+
+CamelImapQuotaRoot *camel_imap_store_get_quota_root (CamelImapStore *imap_store,
+						     const char *folder_name,
+						     CamelException *ex);
 
 #ifdef __cplusplus
 }
Index: camel/providers/imap/camel-imap-utils.c
===================================================================
RCS file: /cvs/gnome/evolution/camel/providers/imap/camel-imap-utils.c,v
retrieving revision 1.73
diff -u -p -r1.73 camel-imap-utils.c
--- camel/providers/imap/camel-imap-utils.c	15 Apr 2004 16:11:40 -0000	1.73
+++ camel/providers/imap/camel-imap-utils.c	7 Oct 2004 16:19:41 -0000
@@ -1263,3 +1263,132 @@ imap_mailbox_decode (const unsigned char
 	
 	return camel_utf7_utf8 (buf);
 }
+
+
+
+
+/* From RFC 2087: 
+   getquota        ::= "GETQUOTA" SP astring
+*/
+gboolean
+imap_parse_rfc2087_getquota (const char *in_str)
+{
+	g_assert_not_reached ();
+	return FALSE;
+}
+
+/* From RFC 2087: 
+   getquotaroot    ::= "GETQUOTAROOT" SP astring
+*/
+gboolean
+imap_parse_rfc2087_getquotaroot (const char *in_str)
+{
+	g_assert_not_reached ();
+	return FALSE;
+}
+
+/* From RFC 2087: 
+   quota_list      ::= "(" #quota_resource ")"
+*/
+gboolean
+imap_parse_rfc2087_quota_list (const char *in_str,
+			       GList **out_list_of_resource)
+{	
+	while (*in_str && *in_str!='(') {
+		in_str++;
+	}
+
+	if (*in_str!='(') return FALSE;
+	
+	in_str++;
+
+	if (!imap_parse_rfc2087_quota_resource (in_str, out_list_of_resource)) return FALSE;
+
+	while (*in_str && *in_str!=')') {
+		in_str++;
+	}
+
+	if (*in_str!=')') return FALSE;
+	
+	return TRUE;
+}
+
+/* From RFC 2087: 
+   quota_resource  ::= atom SP number SP number
+*/
+gboolean
+imap_parse_rfc2087_quota_resource (const char *in_str,
+				   GList **out_list_of_resource)
+{
+	size_t len;
+	char *name = imap_parse_string_generic (&in_str, &len, IMAP_ASTRING);
+	
+	const char *usage = imap_next_word (in_str);
+	const char *limit = imap_next_word (usage);
+
+	CamelQuotaResource *resource = camel_quota_resource_new (name, strtoul(usage, NULL, 10), strtoul(limit, NULL, 10));
+
+	*out_list_of_resource = g_list_append (*out_list_of_resource,
+					       resource);
+
+	g_free (name);
+
+	return TRUE;
+}
+
+/* From RFC 2087: 
+   quota_response  ::= "QUOTA" SP astring SP quota_list
+*/
+gboolean
+imap_parse_rfc2087_quota_response (const char *in_str,
+				   char **out_name,
+				   GList **out_list_of_resource)
+{
+	const char *curs = in_str+6;
+	size_t len;
+		
+	*out_name = imap_parse_string_generic (&curs, &len, IMAP_ASTRING);
+
+	return imap_parse_rfc2087_quota_list (curs, out_list_of_resource);
+}
+
+/* From RFC 2087: 
+   quotaroot_response ::= "QUOTAROOT" SP astring *(SP astring)
+*/
+gboolean
+imap_parse_rfc2087_quotaroot_response (const char *in_str)
+{
+	g_assert_not_reached ();
+	return FALSE;
+}
+
+/* From RFC 2087: 
+   setquota        ::= "SETQUOTA" SP astring SP setquota_list
+*/
+gboolean
+imap_parse_rfc2087_setquota (const char *in_str)
+{
+	g_assert_not_reached ();
+	return FALSE;
+}
+
+/* From RFC 2087: 
+   setquota_list   ::= "(" 0#setquota_resource ")"
+*/
+gboolean
+imap_parse_rfc2087_setquota_list (const char *in_str)
+{
+	g_assert_not_reached ();
+	return FALSE;
+}
+
+/* From RFC 2087: 
+   setquota_resource ::= atom SP number
+*/
+gboolean
+imap_parse_rfc2087_setquota_resource (const char *in_str)
+{
+	g_assert_not_reached ();
+	return FALSE;
+}
+
Index: camel/providers/imap/camel-imap-utils.h
===================================================================
RCS file: /cvs/gnome/evolution/camel/providers/imap/camel-imap-utils.h,v
retrieving revision 1.23
diff -u -p -r1.23 camel-imap-utils.h
--- camel/providers/imap/camel-imap-utils.h	15 Aug 2002 06:17:09 -0000	1.23
+++ camel/providers/imap/camel-imap-utils.h	7 Oct 2004 16:19:41 -0000
@@ -88,6 +88,30 @@ char *imap_namespace_concat (CamelImapSt
 char *imap_mailbox_encode (const unsigned char *in, size_t inlen);
 char *imap_mailbox_decode (const unsigned char *in, size_t inlen);
 
+/* RFC 2087 (IMAP quota) support: */
+gboolean
+imap_parse_rfc2087_getquota (const char *in_str);
+gboolean
+imap_parse_rfc2087_getquotaroot (const char *in_str);
+gboolean
+imap_parse_rfc2087_quota_list (const char *in_str,
+			       GList **out_list_of_resource);
+gboolean
+imap_parse_rfc2087_quota_resource (const char *in_str,
+				   GList **out_list_of_resource);
+gboolean
+imap_parse_rfc2087_quota_response (const char *in_str,
+				   char **out_name,
+				   GList **out_list_of_resource);
+gboolean
+imap_parse_rfc2087_quotaroot_response (const char *in_str);
+gboolean
+imap_parse_rfc2087_setquota (const char *in_str);
+gboolean
+imap_parse_rfc2087_setquota_list (const char *in_str);
+gboolean
+imap_parse_rfc2087_setquota_resource (const char *in_str);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: mail/em-folder-tree-model.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-folder-tree-model.c,v
retrieving revision 1.64
diff -u -p -r1.64 em-folder-tree-model.c
--- mail/em-folder-tree-model.c	24 Sep 2004 04:23:29 -0000	1.64
+++ mail/em-folder-tree-model.c	7 Oct 2004 16:19:43 -0000
@@ -69,6 +69,7 @@ static GType col_types[] = {
 	G_TYPE_UINT,     /* flags */
 	G_TYPE_BOOLEAN,  /* is a store node */
 	G_TYPE_BOOLEAN,  /* has not-yet-loaded subfolders */
+	G_TYPE_INT       /* percent of quota used (or EMFTM_QUOTA_UNAVAILABLE if not available) */
 };
 
 /* GObject virtual method overrides */
@@ -436,6 +438,7 @@ em_folder_tree_model_set_folder_info (EM
 	gboolean load = FALSE;
 	struct _CamelFolder *folder;
 	gboolean emitted = FALSE;
+	int quota_usage = CAMEL_QUOTA_UNAVAILABLE;
 	const char *name;
 
 	if (!fully_loaded)
@@ -465,9 +468,29 @@ em_folder_tree_model_set_folder_info (EM
 			
 			unread = total > 0 ? total : 0;
 		}
+
 		camel_object_unref(folder);
 	}
 
+
+	{
+		CamelImapQuotaRoot *quota_info = fi->quota_info;
+
+		if (quota_info) {
+			if (fi->parent) {
+				if (fi->parent->quota_info) {
+					if (0==strcmp (quota_info->name, fi->parent->quota_info->name)) {
+						/* Has the same quota root as parent; suppress output */
+						quota_usage = CAMEL_QUOTA_INHERIT_PARENT;
+					}
+				}
+			}
+			if (quota_usage != CAMEL_QUOTA_INHERIT_PARENT) {
+				quota_usage = camel_imap_quota_root_get_worst_usage (quota_info);
+			}
+		}
+	}
+
 	if (emft_is_special_local_folder(si->store, fi->full_name))
 		name = _(fi->name);
 	else
@@ -482,6 +505,7 @@ em_folder_tree_model_set_folder_info (EM
 			    COL_UINT_FLAGS, fi->flags,
 			    COL_BOOL_IS_STORE, FALSE,
 			    COL_BOOL_LOAD_SUBDIRS, load,
+			    COL_INT_QUOTA_USED, quota_usage,
 			    -1);
 	
 	if (load) {
@@ -495,6 +519,7 @@ em_folder_tree_model_set_folder_info (EM
 				    COL_BOOL_IS_STORE, FALSE,
 				    COL_STRING_URI, NULL,
 				    COL_UINT_UNREAD, 0,
+				    COL_INT_QUOTA_USED, CAMEL_QUOTA_UNAVAILABLE, 
 				    -1);
 		
 		path = gtk_tree_model_get_path ((GtkTreeModel *) model, iter);
@@ -764,7 +789,7 @@ em_folder_tree_model_add_store (EMFolder
 	uri = camel_url_to_string (((CamelService *) store)->url, CAMEL_URL_HIDE_ALL);
 	
 	account = mail_config_get_account_by_source_url (uri);
-	
+
 	/* add the store to the tree */
 	gtk_tree_store_append ((GtkTreeStore *) model, &iter, NULL);
 	gtk_tree_store_set ((GtkTreeStore *) model, &iter,
@@ -773,7 +798,9 @@ em_folder_tree_model_add_store (EMFolder
 			    COL_STRING_FULL_NAME, NULL,
 			    COL_BOOL_LOAD_SUBDIRS, TRUE,
 			    COL_BOOL_IS_STORE, TRUE,
-			    COL_STRING_URI, uri, -1);
+			    COL_STRING_URI, uri,
+			    COL_INT_QUOTA_USED, CAMEL_QUOTA_UNAVAILABLE,
+			    -1);
 	
 	path = gtk_tree_model_get_path ((GtkTreeModel *) model, &iter);
 	row = gtk_tree_row_reference_new ((GtkTreeModel *) model, path);
@@ -799,6 +826,7 @@ em_folder_tree_model_add_store (EMFolder
 			    COL_BOOL_IS_STORE, FALSE,
 			    COL_STRING_URI, NULL,
 			    COL_UINT_UNREAD, 0,
+			    COL_INT_QUOTA_USED, CAMEL_QUOTA_UNAVAILABLE, 
 			    -1);
 	
 	g_free (uri);
Index: mail/em-folder-tree-model.h
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-folder-tree-model.h,v
retrieving revision 1.20
diff -u -p -r1.20 em-folder-tree-model.h
--- mail/em-folder-tree-model.h	10 Jun 2004 22:08:41 -0000	1.20
+++ mail/em-folder-tree-model.h	7 Oct 2004 16:19:43 -0000
@@ -61,6 +61,8 @@ enum {
 	COL_BOOL_LOAD_SUBDIRS,    /* %TRUE only if the store/folder
 				   * has subfolders which have not yet
 				   * been added to the tree */
+
+	COL_INT_QUOTA_USED,      /* percentage of quota used, or CAMEL_QUOTA_UNAVAILABLE if no quota information available */
 	NUM_COLUMNS
 };
 
Index: mail/em-folder-tree.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/em-folder-tree.c,v
retrieving revision 1.131
diff -u -p -r1.131 em-folder-tree.c
--- mail/em-folder-tree.c	7 Oct 2004 01:32:09 -0000	1.131
+++ mail/em-folder-tree.c	7 Oct 2004 16:19:43 -0000
@@ -309,12 +309,15 @@ render_display_name (GtkTreeViewColumn *
 {
 	gboolean is_store, bold;
 	unsigned int unread;
+	int quota_used;
 	char *display;
 	char *name;
 	
 	gtk_tree_model_get (model, iter, COL_STRING_DISPLAY_NAME, &name,
 			    COL_BOOL_IS_STORE, &is_store,
-			    COL_UINT_UNREAD, &unread, -1);
+			    COL_UINT_UNREAD, &unread, 
+			    COL_INT_QUOTA_USED, &quota_used,
+			    -1);
 	
 	if (!(bold = is_store || unread)) {
 		if (gtk_tree_model_iter_has_child (model, iter))
@@ -324,8 +327,19 @@ render_display_name (GtkTreeViewColumn *
 	if (!is_store && unread) {
 		display = g_strdup_printf ("%s (%u)", name, unread);
 		g_free (name);
-	} else
+	} else {
 		display = name;
+	}
+
+	if (quota_used!=CAMEL_QUOTA_UNAVAILABLE) {
+
+		if (quota_used!=CAMEL_QUOTA_INHERIT_PARENT) {		
+			gchar *old_display = display;
+			/* Note to translators: this string will be used to display the name of an email folder, together with the percentage of the storage quota used appended; e.g. "INBOX (73% full)" */
+			display = g_strdup_printf (_("%s (%u%% full)"), display, quota_used);
+			g_free (old_display);
+		}
+	}
 	
 	g_object_set (renderer, "text", display,
 		      "weight", bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,


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