[evolution-patches] The POP3 provider as a CamelDisco*



As per request, the diff that could make the POP3 code of Evolution a
CamelDisco*.


-- 
Philip Van Hoof, software developer
home: me at pvanhoof dot be 
gnome: pvanhoof at gnome dot org 
http://www.pvanhoof.be/blog



Index: camel-pop3-folder.h
===================================================================
--- camel-pop3-folder.h	(revision 7816)
+++ camel-pop3-folder.h	(working copy)
@@ -26,7 +26,8 @@
 #ifndef CAMEL_POP3_FOLDER_H
 #define CAMEL_POP3_FOLDER_H 1
 
-#include "camel-folder.h"
+#include <camel/camel-folder.h>
+#include <camel/camel-disco-folder.h>
 
 #define CAMEL_POP3_FOLDER_TYPE     (camel_pop3_folder_get_type ())
 #define CAMEL_POP3_FOLDER(obj)     (CAMEL_CHECK_CAST((obj), CAMEL_POP3_FOLDER_TYPE, CamelPOP3Folder))
@@ -47,7 +48,7 @@
 } CamelPOP3FolderInfo;
 
 typedef struct {
-	CamelFolder parent_object;
+	CamelDiscoFolder parent_object;
 
 	GPtrArray *uids;
 	GHashTable *uids_uid;	/* messageinfo by uid */
@@ -55,7 +56,7 @@
 } CamelPOP3Folder;
 
 typedef struct {
-	CamelFolderClass parent_class;
+	CamelDiscoFolderClass parent_class;
 
 	/* Virtual methods */	
 	
Index: camel-pop3-stream.h
===================================================================
--- camel-pop3-stream.h	(revision 7816)
+++ camel-pop3-stream.h	(working copy)
@@ -26,6 +26,13 @@
 
 #include <camel/camel-stream.h>
 
+#ifdef DEBUG
+#define pop3_debug	g_print 
+#else
+#define pop3_debug(o,...)	
+#endif
+
+
 #define CAMEL_POP3_STREAM(obj)         CAMEL_CHECK_CAST (obj, camel_pop3_stream_get_type (), CamelPOP3Stream)
 #define CAMEL_POP3_STREAM_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_pop3_stream_get_type (), CamelPOP3StreamClass)
 #define CAMEL_IS_POP3_STREAM(obj)      CAMEL_CHECK_TYPE (obj, camel_pop3_stream_get_type ())
Index: camel-pop3-store.h
===================================================================
--- camel-pop3-store.h	(revision 7816)
+++ camel-pop3-store.h	(working copy)
@@ -30,6 +30,7 @@
 #include <camel/camel-types.h>
 #include <camel/camel-store.h>
 #include "camel-pop3-engine.h"
+#include <camel/camel-disco-store.h>
 
 #define CAMEL_POP3_STORE_TYPE     (camel_pop3_store_get_type ())
 #define CAMEL_POP3_STORE(obj)     (CAMEL_CHECK_CAST((obj), CAMEL_POP3_STORE_TYPE, CamelPOP3Store))
@@ -39,25 +40,27 @@
 G_BEGIN_DECLS
 
 typedef struct {
-	CamelStore parent_object;
+	CamelDiscoStore parent_object;
 
 	CamelPOP3Engine *engine; /* pop processing engine */
 
 	struct _CamelDataCache *cache;
-	
 	guint delete_after;
+	gboolean immediate_delete_after;
+	gchar *storage_path, *base_url;
+	gboolean connected;
+	GStaticRecMutex *eng_lock;
+
 } CamelPOP3Store;
 
 
 
 typedef struct {
-	CamelStoreClass parent_class;
+	CamelDiscoStoreClass parent_class;
 
 } CamelPOP3StoreClass;
 
 
-/* public methods */
-void camel_pop3_store_expunge (CamelPOP3Store *store, CamelException *ex);
 
 /* support functions */
 enum { CAMEL_POP3_OK, CAMEL_POP3_ERR, CAMEL_POP3_FAIL };
Index: camel-pop3-engine.c
===================================================================
--- camel-pop3-engine.c	(revision 7816)
+++ camel-pop3-engine.c	(working copy)
@@ -60,6 +60,9 @@
 static void
 camel_pop3_engine_init(CamelPOP3Engine *pe, CamelPOP3EngineClass *peclass)
 {
+	pe->lock = g_new0 (GStaticRecMutex, 1);
+	g_static_rec_mutex_init (pe->lock);
+
 	e_dlist_init(&pe->active);
 	e_dlist_init(&pe->queue);
 	e_dlist_init(&pe->done);
@@ -71,12 +74,19 @@
 {
 	/* FIXME: Also flush/free any outstanding requests, etc */
 
+	g_static_rec_mutex_lock (pe->lock);
+
 	if (pe->stream)
 		camel_object_unref(pe->stream);
 	
 	g_list_free(pe->auth);
 	if (pe->apop)
 		g_free(pe->apop);
+
+	g_static_rec_mutex_unlock (pe->lock);
+
+	g_static_rec_mutex_free (pe->lock);
+	pe->lock = NULL;
 }
 
 CamelType
@@ -105,22 +115,29 @@
 	extern CamelServiceAuthType camel_pop3_apop_authtype;
 	unsigned char *line, *apop, *apopend;
 	unsigned int len;
-	
+
+
+	g_static_rec_mutex_lock (pe->lock);
+
 	/* first, read the greeting */
 	if (camel_pop3_stream_line (pe->stream, &line, &len) == -1
-	    || strncmp (line, "+OK", 3) != 0)
+	    || strncmp ((char *) line, "+OK", 3) != 0) {
+		g_static_rec_mutex_unlock (pe->lock);
 		return -1;
-	
-	if ((apop = strchr (line + 3, '<'))
-	    && (apopend = strchr (apop, '>'))) {
+	}
+
+	if ((apop = (unsigned char *) strchr ((char *) line + 3, '<'))
+	    && (apopend = (unsigned char *) strchr ((char *) apop, '>'))) {
 		apopend[1] = 0;
-		pe->apop = g_strdup (apop);
+		pe->apop = g_strdup ((gchar *) apop);
 		pe->capa = CAMEL_POP3_CAP_APOP;
 		pe->auth = g_list_append (pe->auth, &camel_pop3_apop_authtype);
 	}
 	
 	pe->auth = g_list_prepend (pe->auth, &camel_pop3_password_authtype);
-	
+
+	g_static_rec_mutex_unlock (pe->lock);
+
 	return 0;
 }
 
@@ -141,17 +158,21 @@
 
 	pe = (CamelPOP3Engine *)camel_object_new(camel_pop3_engine_get_type ());
 
+	g_static_rec_mutex_lock (pe->lock);
+
 	pe->stream = (CamelPOP3Stream *)camel_pop3_stream_new(source);
 	pe->state = CAMEL_POP3_ENGINE_AUTH;
 	pe->flags = flags;
 	
 	if (read_greeting (pe) == -1) {
+		g_static_rec_mutex_unlock (pe->lock);
 		camel_object_unref (pe);
 		return NULL;
 	}
-	
+
 	get_capabilities (pe);
-	
+	g_static_rec_mutex_unlock (pe->lock);
+
 	return pe;
 }
 
@@ -166,8 +187,10 @@
 camel_pop3_engine_reget_capabilities (CamelPOP3Engine *engine)
 {
 	g_return_if_fail (CAMEL_IS_POP3_ENGINE (engine));
-	
+
+	g_static_rec_mutex_lock (engine->lock);
 	get_capabilities (engine);
+	g_static_rec_mutex_unlock (engine->lock);
 }
 
 
@@ -193,19 +216,21 @@
 	int i;
 	CamelServiceAuthType *auth;
 
+	g_static_rec_mutex_lock (pe->lock);
+
 	dd(printf("cmd_capa\n"));
 
 	do {
 		ret = camel_pop3_stream_line(stream, &line, &len);
 		if (ret >= 0) {
-			if (strncmp(line, "SASL ", 5) == 0) {
+			if (strncmp((char *) line, "SASL ", 5) == 0) {
 				tok = line+5;
 				dd(printf("scanning tokens '%s'\n", tok));
 				while (tok) {
-					next = strchr(tok, ' ');
+					next = (unsigned char *) strchr((char *) tok, ' ');
 					if (next)
 						*next++ = 0;
-					auth = camel_sasl_authtype(tok);
+					auth = camel_sasl_authtype((const char *) tok);
 					if (auth) {
 						dd(printf("got auth type '%s'\n", tok));
 						pe->auth = g_list_prepend(pe->auth, auth);
@@ -216,19 +241,23 @@
 				}
 			} else {
 				for (i=0;i<sizeof(capa)/sizeof(capa[0]);i++) {
-					if (strcmp(capa[i].cap, line) == 0)
+					if (strcmp((char *) capa[i].cap, (char *) line) == 0)
 						pe->capa |= capa[i].flag;
 				}
 			}
 		}
 	} while (ret>0);
+
+	g_static_rec_mutex_unlock (pe->lock);
 }
 
 static void
 get_capabilities(CamelPOP3Engine *pe)
 {
 	CamelPOP3Command *pc;
-	
+
+	g_static_rec_mutex_lock (pe->lock);
+
 	if (!(pe->flags & CAMEL_POP3_ENGINE_DISABLE_EXTENSIONS)) {
 		pc = camel_pop3_engine_command_new(pe, CAMEL_POP3_COMMAND_MULTI, cmd_capa, NULL, "CAPA\r\n");
 		while (camel_pop3_engine_iterate(pe, pc) > 0)
@@ -247,34 +276,46 @@
 			camel_pop3_engine_command_free (pe, pc);
 		}
 	}
+
+	g_static_rec_mutex_unlock (pe->lock);
+
 }
 
 /* returns true if the command was sent, false if it was just queued */
 static int
 engine_command_queue(CamelPOP3Engine *pe, CamelPOP3Command *pc)
 {
+
+	g_static_rec_mutex_lock (pe->lock);
+
 	if (((pe->capa & CAMEL_POP3_CAP_PIPE) == 0 || (pe->sentlen + strlen(pc->data)) > CAMEL_POP3_SEND_LIMIT)
 	    && pe->current != NULL) {
 		e_dlist_addtail(&pe->queue, (EDListNode *)pc);
+		g_static_rec_mutex_unlock (pe->lock);
 		return FALSE;
-	} else {
-		/* ??? */
-		if (camel_stream_write((CamelStream *)pe->stream, pc->data, strlen(pc->data)) == -1) {
-			e_dlist_addtail(&pe->queue, (EDListNode *)pc);
-			return FALSE;
-		}
+	}
 
-		pe->sentlen += strlen(pc->data);
+	/* TNY TODO: Check online status here, else it will crash 
+		Correction, it will not crash but will emit a SIGPIPE */
 
-		pc->state = CAMEL_POP3_COMMAND_DISPATCHED;
+	if (camel_stream_write((CamelStream *)pe->stream, pc->data, strlen(pc->data)) == -1) {
+		e_dlist_addtail(&pe->queue, (EDListNode *)pc);
+		g_static_rec_mutex_unlock (pe->lock);
+		return FALSE;
+	}
 
-		if (pe->current == NULL)
-			pe->current = pc;
-		else
-			e_dlist_addtail(&pe->active, (EDListNode *)pc);
+	pe->sentlen += strlen(pc->data);
 
-		return TRUE;
-	}
+	pc->state = CAMEL_POP3_COMMAND_DISPATCHED;
+
+	if (pe->current == NULL)
+		pe->current = pc;
+	else
+		e_dlist_addtail(&pe->active, (EDListNode *)pc);
+
+	g_static_rec_mutex_unlock (pe->lock);
+
+	return TRUE;
 }
 
 /* returns -1 on error (sets errno), 0 when no work to do, or >0 if work remaining */
@@ -285,15 +326,19 @@
 	unsigned int len;
 	CamelPOP3Command *pc, *pw, *pn;
 
-	if (pcwait && pcwait->state >= CAMEL_POP3_COMMAND_OK)
+	g_static_rec_mutex_lock (pe->lock);
+
+	if (pcwait && pcwait->state >= CAMEL_POP3_COMMAND_OK) {
+		g_static_rec_mutex_unlock (pe->lock);
 		return 0;
+	}
 
 	pc = pe->current;
-	if (pc == NULL)
+	if (pc == NULL) {
+		g_static_rec_mutex_unlock (pe->lock);
 		return 0;
+	}
 
-	/* LOCK */
-
 	if (camel_pop3_stream_line(pe->stream, &pe->line, &pe->linelen) == -1)
 		goto ioerror;
 
@@ -309,9 +354,14 @@
 				pc->func(pe, pe->stream, pc->func_data);
 
 			/* Make sure we get all data before going back to command mode */
-			while (camel_pop3_stream_getd(pe->stream, &p, &len) > 0)
-				;
-			camel_pop3_stream_set_mode(pe->stream, CAMEL_POP3_STREAM_LINE);
+			if (!pe->partial_happening) 
+				while (camel_pop3_stream_getd(pe->stream, &p, &len) > 0)
+					;
+
+			if (!pe->partial_happening)
+				camel_pop3_stream_set_mode(pe->stream, CAMEL_POP3_STREAM_LINE);
+
+			pe->partial_happening = FALSE;
 		} else {
 			pc->state = CAMEL_POP3_COMMAND_OK;
 		}
@@ -326,16 +376,25 @@
 		break;
 	}
 
-	e_dlist_addtail(&pe->done, (EDListNode *)pc);
-	pe->sentlen -= strlen(pc->data);
+	if (pc)
+		e_dlist_addtail(&pe->done, (EDListNode *)pc);
+	else
+		g_warning ("Unexpected, pc == NULL");
 
+	if (pc && pc->data)
+		pe->sentlen -= strlen(pc->data);
+	else
+		g_warning ("Unexpected, pc == NULL");
+
 	/* Set next command */
 	pe->current = (CamelPOP3Command *)e_dlist_remhead(&pe->active);
 	
 	/* check the queue for sending any we can now send also */
 	pw = (CamelPOP3Command *)pe->queue.head;
 	pn = pw->next;
-	while (pn) {
+
+	while (pn) 
+	{
 		if (((pe->capa & CAMEL_POP3_CAP_PIPE) == 0 || (pe->sentlen + strlen(pw->data)) > CAMEL_POP3_SEND_LIMIT)
 		    && pe->current != NULL)
 			break;
@@ -357,12 +416,14 @@
 		pn = pn->next;
 	}
 
-	/* UNLOCK */
-
-	if (pcwait && pcwait->state >= CAMEL_POP3_COMMAND_OK)
+	if (pcwait && pcwait->state >= CAMEL_POP3_COMMAND_OK) {
+		g_static_rec_mutex_unlock (pe->lock);
 		return 0;
+	}
 
+	g_static_rec_mutex_unlock (pe->lock);
 	return pe->current==NULL?0:1;
+
 ioerror:
 	/* we assume all outstanding commands are gunna fail now */
 	while ( (pw = (CamelPOP3Command*)e_dlist_remhead(&pe->active)) ) {
@@ -381,6 +442,7 @@
 		pe->current = NULL;
 	}
 
+	g_static_rec_mutex_unlock (pe->lock);
 	return -1;
 }
 
@@ -408,8 +470,10 @@
 void
 camel_pop3_engine_command_free(CamelPOP3Engine *pe, CamelPOP3Command *pc)
 {
+	g_static_rec_mutex_lock (pe->lock);
 	if (pe->current != pc)
 		e_dlist_remove((EDListNode *)pc);
-	g_free(pc->data);
+	g_free(pc->data); pc->data = NULL;
 	g_free(pc);
+	g_static_rec_mutex_unlock (pe->lock);
 }
Index: camel-pop3-engine.h
===================================================================
--- camel-pop3-engine.h	(revision 7816)
+++ camel-pop3-engine.h	(working copy)
@@ -115,6 +115,10 @@
 	EDList done;		/* list of done commands, awaiting free */
 
 	CamelPOP3Command *current; /* currently busy (downloading) response */
+	void *store; gboolean partial_happening;
+	gint type; gint param;
+
+	GStaticRecMutex *lock;
 };
 
 struct _CamelPOP3EngineClass {
Index: camel-pop3-folder.c
===================================================================
--- camel-pop3-folder.c	(revision 7816)
+++ camel-pop3-folder.c	(working copy)
@@ -5,7 +5,13 @@
  * Authors:
  *   Dan Winship <danw ximian com>
  *   Michael Zucchi <notzed ximian com>
+ *   Philip Van Hoof <pvanhoof gnome org>
  *
+ * This is CamelPop3Store for camel-lite that implements CamelDiscoFolder and
+ * has support for CamelFolderSummary. Its implementation is significantly 
+ * different from Camel's upstream version (being used by Evolution): this
+ * version supports offline and online modes.
+ *
  * Copyright (C) 2002 Ximian, Inc. (www.ximian.com)
  *
  * This program is free software; you can redistribute it and/or 
@@ -27,6 +33,10 @@
 #include <config.h>
 #endif
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
@@ -39,70 +49,41 @@
 #include "camel-exception.h"
 #include "camel-mime-message.h"
 #include "camel-operation.h"
+
 #include "camel-pop3-folder.h"
 #include "camel-pop3-store.h"
 #include "camel-stream-filter.h"
+#include "camel-pop3-stream.h"
 #include "camel-stream-mem.h"
 
+#include "camel-disco-diary.h"
+
 #define d(x) 
 
 #define CF_CLASS(o) (CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(o)))
 static CamelFolderClass *parent_class;
+static CamelDiscoFolderClass *disco_folder_class = NULL;
 
 static void pop3_finalize (CamelObject *object);
 static void pop3_refresh_info (CamelFolder *folder, CamelException *ex);
 static void pop3_sync (CamelFolder *folder, gboolean expunge, CamelException *ex);
 static gint pop3_get_message_count (CamelFolder *folder);
 static GPtrArray *pop3_get_uids (CamelFolder *folder);
-static CamelMimeMessage *pop3_get_message (CamelFolder *folder, const char *uid, CamelException *ex);
+static CamelMimeMessage *pop3_get_message (CamelFolder *folder, const char *uid, CamelFolderReceiveType type, gint param, CamelException *ex);
 static gboolean pop3_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set);
+static CamelMimeMessage *pop3_get_top (CamelFolder *folder, const char *uid, CamelException *ex);
 
-static void
-camel_pop3_folder_class_init (CamelPOP3FolderClass *camel_pop3_folder_class)
-{
-	CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS(camel_pop3_folder_class);
-	
-	parent_class = CAMEL_FOLDER_CLASS(camel_folder_get_type());
-	
-	/* virtual method overload */
-	camel_folder_class->refresh_info = pop3_refresh_info;
-	camel_folder_class->sync = pop3_sync;
-	
-	camel_folder_class->get_message_count = pop3_get_message_count;
-	camel_folder_class->get_uids = pop3_get_uids;
-	camel_folder_class->free_uids = camel_folder_free_shallow;
-	
-	camel_folder_class->get_message = pop3_get_message;
-	camel_folder_class->set_message_flags = pop3_set_message_flags;
-}
 
-CamelType
-camel_pop3_folder_get_type (void)
+static void 
+destroy_lists (CamelPOP3Folder *pop3_folder)
 {
-	static CamelType camel_pop3_folder_type = CAMEL_INVALID_TYPE;
-	
-	if (!camel_pop3_folder_type) {
-		camel_pop3_folder_type = camel_type_register (CAMEL_FOLDER_TYPE, "CamelPOP3Folder",
-							      sizeof (CamelPOP3Folder),
-							      sizeof (CamelPOP3FolderClass),
-							      (CamelObjectClassInitFunc) camel_pop3_folder_class_init,
-							      NULL,
-							      NULL,
-							      (CamelObjectFinalizeFunc) pop3_finalize);
-	}
-	
-	return camel_pop3_folder_type;
-}
 
-static void
-pop3_finalize (CamelObject *object)
-{
-	CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (object);
-	CamelPOP3FolderInfo **fi = (CamelPOP3FolderInfo **)pop3_folder->uids->pdata;
-	CamelPOP3Store *pop3_store = (CamelPOP3Store *)((CamelFolder *)pop3_folder)->parent_store;
-	int i;
+	if (pop3_folder->uids != NULL) 
+	{
+		CamelPOP3FolderInfo **fi = (CamelPOP3FolderInfo **)pop3_folder->uids->pdata;
+		CamelPOP3Store *pop3_store = (CamelPOP3Store *)((CamelFolder *)pop3_folder)->parent_store;
+		int i;
 
-	if (pop3_folder->uids) {
 		for (i=0;i<pop3_folder->uids->len;i++,fi++) {
 			if (fi[0]->cmd) {
 				while (camel_pop3_engine_iterate(pop3_store->engine, fi[0]->cmd) > 0)
@@ -119,23 +100,70 @@
 	}
 }
 
+static void
+pop3_finalize (CamelObject *object)
+{
+	CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (object);
+
+	destroy_lists (pop3_folder);
+
+}
+
+static void
+camel_pop3_summary_set_extra_flags (CamelFolder *folder, CamelMessageInfoBase *mi)
+{
+	CamelPOP3Store *pop3_store = (CamelPOP3Store *)folder->parent_store;
+	camel_data_cache_set_flags (pop3_store->cache, "cache", mi);
+}
+
 CamelFolder *
 camel_pop3_folder_new (CamelStore *parent, CamelException *ex)
 {
 	CamelFolder *folder;
+	CamelPOP3Store *p3store = (CamelPOP3Store*) parent;
+	gchar *summary_file;
+	CamelPOP3Folder *pop3_folder;
 
 	d(printf("opening pop3 INBOX folder\n"));
 	
 	folder = CAMEL_FOLDER (camel_object_new (CAMEL_POP3_FOLDER_TYPE));
+	pop3_folder = CAMEL_POP3_FOLDER (folder);
+	pop3_folder->uids = NULL;
+
 	camel_folder_construct (folder, parent, "inbox", "inbox");
-	
+
+	summary_file = g_strdup_printf ("%s/summary.mmap", p3store->storage_path);
+	folder->summary = camel_folder_summary_new (folder);
+	folder->summary->set_extra_flags_func = camel_pop3_summary_set_extra_flags;
+	camel_folder_summary_set_build_content (folder->summary, TRUE);
+	camel_folder_summary_set_filename (folder->summary, summary_file);
+
+	if (camel_folder_summary_load (folder->summary) == -1) {
+		camel_folder_summary_clear (folder->summary);
+		camel_folder_summary_touch (folder->summary);
+		camel_folder_summary_save (folder->summary);
+		camel_folder_summary_load (folder->summary);
+	}
+
+	g_free (summary_file);
+
+
 	/* mt-ok, since we dont have the folder-lock for new() */
-	camel_folder_refresh_info (folder, ex);/* mt-ok */
+	/* camel_folder_refresh_info (folder, ex); 
 	if (camel_exception_is_set (ex)) {
 		camel_object_unref (CAMEL_OBJECT (folder));
 		folder = NULL;
+	}*/
+
+	if (!folder->summary) {
+		camel_object_unref (CAMEL_OBJECT (folder));
+		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+				      _("Could not load summary for INBOX"));
+		return NULL;
 	}
 	
+	folder->folder_flags |= CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY;
+
 	return folder;
 }
 
@@ -164,8 +192,8 @@
 		while (h) {
 			if (g_ascii_strcasecmp(h->name, "status") != 0
 			    && g_ascii_strcasecmp(h->name, "x-status") != 0) {
-				md5_update(&md5, h->name, strlen(h->name));
-				md5_update(&md5, h->value, strlen(h->value));
+				md5_update(&md5, (const guchar*) h->name, strlen(h->name));
+				md5_update(&md5, (const guchar*) h->value, strlen(h->value));
 			}
 			h = h->next;
 		}
@@ -174,7 +202,7 @@
 	}
 	camel_object_unref(mp);
 	md5_final(&md5, digest);
-	fi->uid = camel_base64_encode_simple(digest, 16);
+	fi->uid = camel_base64_encode_simple((const char*) digest, 16);
 
 	d(printf("building uid for id '%d' = '%s'\n", fi->id, fi->uid));
 }
@@ -192,12 +220,12 @@
 	do {
 		ret = camel_pop3_stream_line(stream, &line, &len);
 		if (ret>=0) {
-			if (sscanf(line, "%u %u", &id, &size) == 2) {
-				fi = g_malloc0(sizeof(*fi));
+			if (sscanf((char *) line, "%u %u", &id, &size) == 2) {
+				fi = g_malloc0 (sizeof(*fi));
 				fi->size = size;
 				fi->id = id;
 				fi->index = ((CamelPOP3Folder *)folder)->uids->len;
-				if ((pop3_store->engine->capa & CAMEL_POP3_CAP_UIDL) == 0)
+				if ((pop3_store->engine && pop3_store->engine->capa & CAMEL_POP3_CAP_UIDL) == 0)
 					fi->cmd = camel_pop3_engine_command_new(pe, CAMEL_POP3_COMMAND_MULTI, cmd_builduid, fi, "TOP %u 0\r\n", id);
 				g_ptr_array_add(((CamelPOP3Folder *)folder)->uids, fi);
 				g_hash_table_insert(((CamelPOP3Folder *)folder)->uids_id, GINT_TO_POINTER(id), fi);
@@ -220,12 +248,12 @@
 	do {
 		ret = camel_pop3_stream_line(stream, &line, &len);
 		if (ret>=0) {
-			if (strlen(line) > 1024)
+			if (strlen((char*) line) > 1024)
 				line[1024] = 0;
-			if (sscanf(line, "%u %s", &id, uid) == 2) {
+			if (sscanf((char *) line, "%u %s", &id, uid) == 2) {
 				fi = g_hash_table_lookup(folder->uids_id, GINT_TO_POINTER(id));
 				if (fi) {
-					camel_operation_progress(NULL, (fi->index+1) * 100 / folder->uids->len);
+					camel_operation_progress(NULL, (fi->index+1) , folder->uids->len);
 					fi->uid = g_strdup(uid);
 					g_hash_table_insert(folder->uids_uid, fi->uid, fi);
 				} else {
@@ -242,23 +270,35 @@
 	CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (folder->parent_store);
 	CamelPOP3Folder *pop3_folder = (CamelPOP3Folder *) folder;
 	CamelPOP3Command *pcl, *pcu = NULL;
-	int i;
+	int i, hcnt = 0;
 
-	camel_operation_start (NULL, _("Retrieving POP summary"));
+	if (camel_disco_store_status (CAMEL_DISCO_STORE (pop3_store)) == CAMEL_DISCO_STORE_OFFLINE)
+		return;
 
+	g_static_rec_mutex_lock (pop3_store->eng_lock);
+	if (pop3_store->engine == NULL)
+	{
+		g_static_rec_mutex_unlock (pop3_store->eng_lock);
+		return;
+	}
+
+	destroy_lists (pop3_folder);
+
 	pop3_folder->uids = g_ptr_array_new ();
 	pop3_folder->uids_uid = g_hash_table_new(g_str_hash, g_str_equal);
 	/* only used during setup */
 	pop3_folder->uids_id = g_hash_table_new(NULL, NULL);
 
+	camel_operation_start (NULL, _("Fetching summary information for new messages in folder"));
+
+
 	pcl = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, cmd_list, folder, "LIST\r\n");
-	if (pop3_store->engine->capa & CAMEL_POP3_CAP_UIDL) {
+	if (pop3_store->engine->capa & CAMEL_POP3_CAP_UIDL)
 		pcu = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, cmd_uidl, folder, "UIDL\r\n");
-	}
-	while ((i = camel_pop3_engine_iterate(pop3_store->engine, NULL)) > 0)
-		;
+	while ((i = camel_pop3_engine_iterate(pop3_store->engine, NULL)) > 0);
 
-	if (i == -1) {
+	if (i == -1) 
+	{
 		if (errno == EINTR)
 			camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
 		else
@@ -270,12 +310,76 @@
 	/* TODO: check every id has a uid & commands returned OK too? */
 	
 	camel_pop3_engine_command_free(pop3_store->engine, pcl);
-	
+
+	/* Update the summary.mmap file */
+
+	camel_folder_summary_prepare_hash (folder->summary);
+
+	for (i=0;i<pop3_folder->uids->len;i++) 
+	{
+		CamelPOP3FolderInfo *fi = pop3_folder->uids->pdata[i];
+		CamelMessageInfoBase *mi = NULL;
+		CamelFolderChangeInfo *changes;
+
+		mi = (CamelMessageInfoBase*) camel_folder_summary_uid (folder->summary, fi->uid);
+		if (!mi)
+		{
+			CamelMimeMessage *msg = NULL;
+
+			if (pop3_store->engine && pop3_store->engine->capa & CAMEL_POP3_CAP_TOP) 
+				msg = pop3_get_top (folder, fi->uid, NULL);
+			else
+				msg = pop3_get_message (folder, fi->uid, CAMEL_FOLDER_RECEIVE_PARTIAL, -1, NULL);
+
+			if (msg) 
+			{
+				mi = (CamelMessageInfoBase*) camel_folder_summary_uid (folder->summary, fi->uid);
+				if (mi) {
+				    mi->size = (fi->size);
+				   /* TNY TODO: This is a hack! But else we need to parse 
+				    * BODYSTRUCTURE (and I'm lazy). It needs fixing though. */
+				    if (mi->size > 102400)
+					mi->flags |= CAMEL_MESSAGE_ATTACHMENTS;
+				    /* ... it does */
+				    camel_message_info_free (mi);
+				}
+
+				camel_object_unref (CAMEL_OBJECT (msg));
+
+				hcnt++;
+				if (hcnt > 1000)
+				{
+					/* Periodically save the summary (this reduces 
+					   memory usage too) */
+					camel_folder_summary_save (folder->summary);
+					hcnt = 0;
+				}
+
+				changes = camel_folder_change_info_new ();
+				camel_folder_change_info_add_uid (changes, fi->uid);
+				if (camel_folder_change_info_changed (changes))
+					camel_object_trigger_event (CAMEL_OBJECT (folder), "folder_changed", changes);
+				camel_folder_change_info_free (changes);
+
+			}
+
+		} else 
+			camel_message_info_free (mi);
+
+		camel_operation_progress (NULL, i , pop3_folder->uids->len);
+
+	}
+
+	camel_folder_summary_save (folder->summary);
+
+	camel_folder_summary_kill_hash (folder->summary);
+
 	if (pop3_store->engine->capa & CAMEL_POP3_CAP_UIDL) {
 		camel_pop3_engine_command_free(pop3_store->engine, pcu);
 	} else {
 		for (i=0;i<pop3_folder->uids->len;i++) {
 			CamelPOP3FolderInfo *fi = pop3_folder->uids->pdata[i];
+
 			if (fi->cmd) {
 				camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
 				fi->cmd = NULL;
@@ -287,8 +391,11 @@
 
 	/* dont need this anymore */
 	g_hash_table_destroy(pop3_folder->uids_id);
-	
+
 	camel_operation_end (NULL);
+
+	g_static_rec_mutex_unlock (pop3_store->eng_lock);
+
 	return;
 }
 
@@ -297,25 +404,32 @@
 {
 	CamelPOP3Folder *pop3_folder;
 	CamelPOP3Store *pop3_store;
-	int i;
-	CamelPOP3FolderInfo *fi;
+	int i, max; CamelPOP3FolderInfo *fi;
+	CamelMessageInfoBase *info;
 
 	pop3_folder = CAMEL_POP3_FOLDER (folder);
 	pop3_store = CAMEL_POP3_STORE (folder->parent_store);
 
+	if (camel_disco_store_status (CAMEL_DISCO_STORE (pop3_store)) == CAMEL_DISCO_STORE_OFFLINE)
+		return;
+
 	if(pop3_store->delete_after && !expunge)
 	{	
 		camel_operation_start(NULL, _("Expunging old messages"));
-		camel_pop3_delete_old(folder, pop3_store->delete_after,ex);	
-	}	
+		camel_pop3_delete_old(folder, pop3_store->delete_after,ex);
+	}
 
-	if (!expunge) {
+	if (!expunge)
 		return;
-	}	
-	
+
+	g_static_rec_mutex_lock (pop3_store->eng_lock);
+
 	camel_operation_start(NULL, _("Expunging deleted messages"));
-	
-	for (i = 0; i < pop3_folder->uids->len; i++) {
+
+	if (pop3_folder->uids)
+	{
+	  for (i = 0; i < pop3_folder->uids->len; i++) 
+	  {
 		fi = pop3_folder->uids->pdata[i];
 		/* busy already?  wait for that to finish first */
 		if (fi->cmd) {
@@ -326,20 +440,15 @@
 		}
 
 		if (fi->flags & CAMEL_MESSAGE_DELETED) {
-			fi->cmd = camel_pop3_engine_command_new(pop3_store->engine,
-								0,
-								NULL,
-								NULL,
-								"DELE %u\r\n",
-								fi->id);
+			fi->cmd = camel_pop3_engine_command_new(pop3_store->engine, 0, NULL, NULL, "DELE %u\r\n", fi->id);
 
 			/* also remove from cache */
 			if (pop3_store->cache && fi->uid)
 				camel_data_cache_remove(pop3_store->cache, "cache", fi->uid, NULL);
 		}
-	}
+	  }
 
-	for (i = 0; i < pop3_folder->uids->len; i++) {
+	  for (i = 0; i < pop3_folder->uids->len; i++) {
 		fi = pop3_folder->uids->pdata[i];
 		/* wait for delete commands to finish */
 		if (fi->cmd) {
@@ -348,64 +457,90 @@
 			camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
 			fi->cmd = NULL;
 		}
-		camel_operation_progress(NULL, (i+1) * 100 / pop3_folder->uids->len);
+		camel_operation_progress(NULL, (i+1) , pop3_folder->uids->len);
+	  }
 	}
 
+	max = camel_folder_summary_count (folder->summary);
+	for (i = 0; i < max; i++) 
+	{
+		if (!(info = (CamelMessageInfoBase*) camel_folder_summary_index (folder->summary, i)))
+			continue;
+
+		if (info->flags & CAMEL_MESSAGE_DELETED) 
+		{
+			struct _CamelPOP3Command *cmd;
+			cmd = camel_pop3_engine_command_new(pop3_store->engine, 0, NULL, NULL, "DELE %u\r\n", info->uid);
+			while (camel_pop3_engine_iterate(pop3_store->engine, cmd) > 0);
+			if (pop3_store->cache && info->uid)
+				camel_data_cache_remove(pop3_store->cache, "cache", info->uid, NULL);
+			camel_pop3_engine_command_free(pop3_store->engine, cmd);
+			camel_message_info_free((CamelMessageInfo *)info);
+		}
+
+	}
+
 	camel_operation_end(NULL);
 
-	camel_pop3_store_expunge (pop3_store, ex);
+	g_static_rec_mutex_unlock (pop3_store->eng_lock);
 }
 
+
 int
-camel_pop3_delete_old(CamelFolder *folder, int days_to_delete,	CamelException *ex)
+camel_pop3_delete_old(CamelFolder *folder, int days_to_delete, CamelException *ex)
 {
 	CamelPOP3Folder *pop3_folder;
 	CamelPOP3FolderInfo *fi;
 	int i;
 	CamelPOP3Store *pop3_store;
 	time_t temp;
-	CamelMimeMessage *message;
 
 	pop3_folder = CAMEL_POP3_FOLDER (folder);
-	pop3_store = CAMEL_POP3_STORE (CAMEL_FOLDER(pop3_folder)->parent_store);	
+	pop3_store = CAMEL_POP3_STORE (CAMEL_FOLDER(pop3_folder)->parent_store);
 	temp = time(&temp);
 
-	for (i = 0; i < pop3_folder->uids->len; i++) {
+	g_static_rec_mutex_lock (pop3_store->eng_lock);
+
+	for (i = 0; i < pop3_folder->uids->len; i++) 
+	{
+		CamelMimeMessage *message = NULL;
+
 		fi = pop3_folder->uids->pdata[i];
-	
-		message = pop3_get_message (folder, fi->uid, ex);
-		if(message) {
-			time_t message_time = message->date + message->date_offset;
-			double time_diff = difftime(temp,message_time);
-			int day_lag = time_diff/(60*60*24);
-			if( day_lag > days_to_delete)
+
+		if (pop3_store->cache && fi->uid && !camel_data_cache_is_partial(pop3_store->cache, "cache", fi->uid))
+			message = pop3_get_message (folder, fi->uid, CAMEL_FOLDER_RECEIVE_FULL, -1, ex);
+		else if (fi->uid)
+			message = pop3_get_message (folder, fi->uid, CAMEL_FOLDER_RECEIVE_PARTIAL, -1, ex);
+
+		time_t message_time = message->date + message->date_offset;
+
+		if (message) 
+		{
+		    double time_diff = difftime(temp,message_time);
+		    int day_lag = time_diff/(60*60*24);
+		    if (day_lag > days_to_delete)
+		    {
+			if (fi->cmd) 
 			{
-				if (fi->cmd) {
-					while (camel_pop3_engine_iterate(pop3_store->engine, fi->cmd) > 0) {
-						; /* do nothing - iterating until end */
-					}
-					
-					camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
-					fi->cmd = NULL;
-				}
-		
-				fi->cmd = camel_pop3_engine_command_new(pop3_store->engine, 
-									0,
-									NULL,
-									NULL,
-									"DELE %u\r\n",
-									fi->id);
-				/* also remove from cache */
-				if (pop3_store->cache && fi->uid) {
-					camel_data_cache_remove(pop3_store->cache, "cache", fi->uid, NULL);
-				}
-			}
-			/* free message - not used anymore */
-			camel_object_unref((CamelObject *)message);
+			    while (camel_pop3_engine_iterate(pop3_store->engine, fi->cmd) > 0)
+				    ;
+			    camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
+			    fi->cmd = NULL;
+		    	}
+
+		    	fi->cmd = camel_pop3_engine_command_new(pop3_store->engine, 0, NULL, NULL, "DELE %u\r\n", fi->id);
+			/* also remove from cache */
+		    	if (pop3_store->cache && fi->uid)
+			    camel_data_cache_remove(pop3_store->cache, "cache", fi->uid, NULL);
+		    }
+
+		    camel_object_unref (CAMEL_OBJECT (message));
 		}
+
 	}
 
-	for (i = 0; i < pop3_folder->uids->len; i++) {
+	for (i = 0; i < pop3_folder->uids->len; i++) 
+	{
 		fi = pop3_folder->uids->pdata[i];
 		/* wait for delete commands to finish */
 		if (fi->cmd) {
@@ -414,32 +549,36 @@
 			camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
 			fi->cmd = NULL;
 		}
-		camel_operation_progress(NULL, (i+1) * 100 / pop3_folder->uids->len);
+		camel_operation_progress(NULL, (i+1) , pop3_folder->uids->len);
 	}
 
 	camel_operation_end(NULL);
 
-	camel_pop3_store_expunge (pop3_store, ex);
+	/* camel_pop3_store_expunge (pop3_store, ex); */
 
+	g_static_rec_mutex_unlock (pop3_store->eng_lock);
+
 	return 0;
-	
+
 }
 
 static void
 cmd_tocache(CamelPOP3Engine *pe, CamelPOP3Stream *stream, void *data)
 {
+	CamelPOP3Store *tstore = (CamelPOP3Store *) pe->store;
 	CamelPOP3FolderInfo *fi = data;
 	char buffer[2048];
 	int w = 0, n;
 
 	/* What if it fails? */
-
 	/* We write an '*' to the start of the stream to say its not complete yet */
 	/* This should probably be part of the cache code */
+
 	if ((n = camel_stream_write(fi->stream, "*", 1)) == -1)
 		goto done;
 
-	while ((n = camel_stream_read((CamelStream *)stream, buffer, sizeof(buffer))) > 0) {
+	while ((n = camel_stream_read((CamelStream *)stream, buffer, sizeof(buffer))) > 0) 
+	{
 		n = camel_stream_write(fi->stream, buffer, n);
 		if (n == -1)
 			break;
@@ -448,13 +587,15 @@
 		if (w > fi->size)
 			w = fi->size;
 		if (fi->size != 0)
-			camel_operation_progress(NULL, (w * 100) / fi->size);
+			camel_operation_progress(NULL, w , fi->size);
 	}
 
 	/* it all worked, output a '#' to say we're a-ok */
 	if (n != -1) {
 		camel_stream_reset(fi->stream);
 		n = camel_stream_write(fi->stream, "#", 1);
+
+		camel_data_cache_set_partial (tstore->cache, "cache", fi->uid, FALSE);
 	}
 done:
 	if (n == -1) {
@@ -468,29 +609,161 @@
 	fi->stream = NULL;
 }
 
+
+static void
+cmd_tocache_partial (CamelPOP3Engine *pe, CamelPOP3Stream *stream, void *data)
+{
+	CamelPOP3Store *tstore = (CamelPOP3Store *) pe->store;
+	CamelPOP3FolderInfo *fi = data;
+	unsigned char *buffer;
+	int w = 0, n;
+	gchar *boundary = NULL;
+	gboolean occurred = FALSE, theend = FALSE;
+	unsigned int len;
+
+	/* We write an '*' to the start of the stream to say its not complete yet */
+	if ((n = camel_stream_write(fi->stream, "*", 1)) == -1)
+		goto done;
+
+
+	while (!theend && camel_pop3_stream_line (stream, &buffer, &len) > 0)
+	{
+		if (!buffer)
+			continue;
+
+		if (boundary == NULL)
+		{
+			   CamelContentType *ct = NULL;
+			   const char *bound=NULL;
+			   char *pstr = (char*)strcasestr ((const char *) buffer, "Content-Type:");
+
+			   if (pstr) 
+			   {
+				pstr = strchr (pstr, ':'); 
+				if (pstr) { pstr++;
+				ct = camel_content_type_decode(pstr); } 
+			   }
+
+			   if (ct) 
+			   { 
+				bound = camel_content_type_param(ct, "boundary");
+				if (bound && strlen (bound) > 0) 
+					boundary = g_strdup (bound);
+			   }
+		} else if (strstr ((const char*) buffer, (const char*) boundary))
+		{
+			if (occurred)
+			{
+				CamelException myex = CAMEL_EXCEPTION_INITIALISER;
+				camel_service_disconnect (CAMEL_SERVICE (tstore), FALSE, &myex);
+				camel_service_connect (CAMEL_SERVICE (tstore), &myex);
+				pe->partial_happening = TRUE;
+				theend = TRUE;
+			} 
+
+			occurred = TRUE;
+		}
+
+		if (!theend)
+		{
+		    n = camel_stream_write(fi->stream, (const char*) buffer, len);
+		    if (n == -1 || camel_stream_write(fi->stream, "\n", 1) == -1)
+			break;
+		    w += n+1;
+		} else if (boundary != NULL)
+		{
+		    gchar *nb = g_strdup_printf ("\n--%s\n", boundary);
+		    n = camel_stream_write(fi->stream, nb, strlen (nb));
+		    g_free (nb);
+		}
+
+		if (w > fi->size)
+			w = fi->size;
+		if (fi->size != 0)
+			camel_operation_progress(NULL, w , fi->size);
+	}
+
+	/* it all worked, output a '#' to say we're a-ok */
+	if (n != -1 || theend) {
+		camel_stream_reset(fi->stream);
+		n = camel_stream_write(fi->stream, "#", 1);
+		if (theend)
+			camel_data_cache_set_partial (tstore->cache, "cache", fi->uid, TRUE);
+		else
+ 			camel_data_cache_set_partial (tstore->cache, "cache", fi->uid, FALSE);
+
+	}
+done:
+	if (n == -1 && !theend) {
+		fi->err = errno;
+		g_warning("POP3 retrieval failed: %s", strerror(errno));
+	} else {
+		fi->err = 0;
+	}
+	
+	camel_object_unref((CamelObject *)fi->stream);
+	fi->stream = NULL;
+
+	if (boundary)
+		g_free (boundary);
+}
+
+
 static CamelMimeMessage *
-pop3_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
+pop3_get_message (CamelFolder *folder, const char *uid, CamelFolderReceiveType type, gint param, CamelException *ex)
 {
 	CamelMimeMessage *message = NULL;
 	CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (folder->parent_store);
 	CamelPOP3Folder *pop3_folder = (CamelPOP3Folder *)folder;
-	CamelPOP3Command *pcr;
+	CamelPOP3Command *pcr=NULL;
 	CamelPOP3FolderInfo *fi;
-	char buffer[1];
-	int i, last;
+	char buffer[1]; int i;
 	CamelStream *stream = NULL;
+	CamelFolderSummary *summary = folder->summary;
+	CamelMessageInfoBase *mi; gboolean im_certain=FALSE;
 
-	fi = g_hash_table_lookup(pop3_folder->uids_uid, uid);
+	g_static_rec_mutex_lock (pop3_store->eng_lock);
+
+	stream = camel_data_cache_get(pop3_store->cache, "cache", uid, NULL);
+	if (stream)
+	{
+		message = camel_mime_message_new ();
+		if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)message, stream) == -1) {
+			if (errno == EINTR)
+				camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
+			else
+				camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+						      _("Cannot get message %s: %s"),
+						      uid, g_strerror (errno));
+			camel_object_unref((CamelObject *)message);
+			message = NULL;
+		}
+
+		camel_object_unref (CAMEL_OBJECT (stream));
+
+		g_static_rec_mutex_unlock (pop3_store->eng_lock);
+
+		return message;
+	}
+
+	if (pop3_folder->uids_uid)
+		fi = g_hash_table_lookup(pop3_folder->uids_uid, uid);
+	else 
+		fi = NULL;
+
 	if (fi == NULL) {
 		camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
 				      _("No message with UID %s"), uid);
+
+		g_static_rec_mutex_unlock (pop3_store->eng_lock);
+
 		return NULL;
 	}
 
 	/* Sigh, most of the crap in this function is so that the cancel button
 	   returns the proper exception code.  Sigh. */
 
-	camel_operation_start_transient(NULL, _("Retrieving POP message %d"), fi->id);
+	camel_operation_start (NULL, _("Retrieving message"));
 
 	/* If we have an oustanding retrieve message running, wait for that to complete
 	   & then retrieve from cache, otherwise, start a new one, and similar */
@@ -518,8 +791,24 @@
 		}
 	}
 	
+	if (pop3_store->cache != NULL)
+	{
+		CamelException tex = CAMEL_EXCEPTION_INITIALISER;
+
+		if ((type & CAMEL_FOLDER_RECEIVE_FULL) && camel_data_cache_is_partial (pop3_store->cache, "cache", fi->uid))
+		{
+			camel_data_cache_remove (pop3_store->cache, "cache", fi->uid, &tex);
+			im_certain = TRUE;
+		} else if ((type & CAMEL_FOLDER_RECEIVE_PARTIAL || type & CAMEL_FOLDER_RECEIVE_SIZE_LIMITED) 
+			&& !camel_data_cache_is_partial (pop3_store->cache, "cache", fi->uid))
+		{
+			camel_data_cache_remove (pop3_store->cache, "cache", fi->uid, &tex);
+			im_certain = TRUE;
+		}
+	}
+
 	/* check to see if we have safely written flag set */
-	if (pop3_store->cache == NULL
+	if (im_certain || pop3_store->cache == NULL
 	    || (stream = camel_data_cache_get(pop3_store->cache, "cache", fi->uid, NULL)) == NULL
 	    || camel_stream_read(stream, buffer, 1) != 1
 	    || buffer[0] != '#') {
@@ -533,32 +822,20 @@
 		camel_object_ref((CamelObject *)stream);
 		fi->stream = stream;
 		fi->err = EIO;
-		pcr = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, cmd_tocache, fi, "RETR %u\r\n", fi->id);
 
-		/* Also initiate retrieval of some of the following messages, assume we'll be receiving them */
-		if (pop3_store->cache != NULL) {
-			/* This should keep track of the last one retrieved, also how many are still
-			   oustanding incase of random access on large folders */
-			i = fi->index+1;
-			last = MIN(i+10, pop3_folder->uids->len);
-			for (;i<last;i++) {
-				CamelPOP3FolderInfo *pfi = pop3_folder->uids->pdata[i];
-				
-				if (pfi->uid && pfi->cmd == NULL) {
-					pfi->stream = camel_data_cache_add(pop3_store->cache, "cache", pfi->uid, NULL);
-					if (pfi->stream) {
-						pfi->err = EIO;
-						pfi->cmd = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI,
-											 cmd_tocache, pfi, "RETR %u\r\n", pfi->id);
-					}
-				}
-			}
-		}
 
-		/* now wait for the first one to finish */
+		pop3_store->engine->type = type;
+		pop3_store->engine->param = param;
+
+		if (type & CAMEL_FOLDER_RECEIVE_FULL)
+			pcr = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, 
+				cmd_tocache, fi, "RETR %u\r\n", fi->id);
+		else if (type & CAMEL_FOLDER_RECEIVE_PARTIAL || type & CAMEL_FOLDER_RECEIVE_SIZE_LIMITED)
+			pcr = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, 
+				cmd_tocache_partial, fi, "RETR %u\r\n", fi->id);
+
 		while ((i = camel_pop3_engine_iterate(pop3_store->engine, pcr)) > 0)
 			;
-
 		if (i == -1)
 			fi->err = errno;
 
@@ -595,15 +872,184 @@
 					      uid, g_strerror (errno));
 		camel_object_unref((CamelObject *)message);
 		message = NULL;
+	} else {
+		if (type & CAMEL_FOLDER_RECEIVE_FULL && pop3_store->immediate_delete_after)
+		{
+			struct _CamelPOP3Command *cmd = NULL;
+			cmd = camel_pop3_engine_command_new(pop3_store->engine, 0, NULL, NULL, "DELE %u\r\n", uid);
+			while (camel_pop3_engine_iterate(pop3_store->engine, cmd) > 0);
+			camel_pop3_engine_command_free(pop3_store->engine, cmd);
+		}
 	}
+
+	mi = (CamelMessageInfoBase *) camel_folder_summary_uid (summary, uid);
+	if (mi) 
+		camel_folder_summary_remove (summary, (CamelMessageInfo *) mi);
+
+	mi = (CamelMessageInfoBase *) camel_folder_summary_info_new_from_message (summary, message);
+	if (mi->uid)
+		g_free (mi->uid);
+	mi->uid = g_strdup (uid);
+
+	camel_folder_summary_add (summary, (CamelMessageInfo *)mi);
+
 done:
 	camel_object_unref((CamelObject *)stream);
 fail:
 	camel_operation_end(NULL);
 
+	g_static_rec_mutex_unlock (pop3_store->eng_lock);
+
 	return message;
 }
 
+
+
+static CamelMimeMessage *
+pop3_get_top (CamelFolder *folder, const char *uid, CamelException *ex)
+{
+	CamelMimeMessage *message = NULL;
+	CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (folder->parent_store);
+	CamelPOP3Folder *pop3_folder = (CamelPOP3Folder *)folder;
+	CamelPOP3Command *pcr;
+	CamelPOP3FolderInfo *fi;
+	char buffer[1]; int i; 
+	CamelStream *stream = NULL, *old;
+	CamelFolderSummary *summary = folder->summary;
+	CamelMessageInfoBase *mi;
+
+	g_static_rec_mutex_lock (pop3_store->eng_lock);
+
+	fi = g_hash_table_lookup(pop3_folder->uids_uid, uid);
+
+	if (fi == NULL) {
+		camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
+				      _("No message with UID %s"), uid);
+
+		g_static_rec_mutex_unlock (pop3_store->eng_lock);
+
+		return NULL;
+	}
+
+	old = fi->stream;
+
+	/* Sigh, most of the crap in this function is so that the cancel button
+	   returns the proper exception code.  Sigh. */
+
+	camel_operation_start_transient(NULL, _("Retrieving POP message %d"), fi->id);
+
+	/* If we have an oustanding retrieve message running, wait for that to complete
+	   & then retrieve from cache, otherwise, start a new one, and similar */
+
+	if (fi->cmd != NULL) {
+		while ((i = camel_pop3_engine_iterate(pop3_store->engine, fi->cmd)) > 0)
+			;
+
+		if (i == -1)
+			fi->err = errno;
+
+		/* getting error code? */
+		/*g_assert (fi->cmd->state == CAMEL_POP3_COMMAND_DATA);*/
+		camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
+		fi->cmd = NULL;
+
+		if (fi->err != 0) {
+			if (fi->err == EINTR)
+				camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
+			else
+				camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+						      _("Cannot get message %s: %s"),
+						      uid, g_strerror (fi->err));
+			goto fail;
+		}
+	}
+	
+	/* check to see if we have safely written flag set */
+	if (pop3_store->cache == NULL
+	    || (stream = camel_data_cache_get(pop3_store->cache, "cache", fi->uid, NULL)) == NULL
+	    || camel_stream_read(stream, buffer, 1) != 1
+	    || buffer[0] != '#') {
+			
+
+		stream = camel_stream_mem_new();
+
+		/* the cmd_tocache thing unrefs it, therefore this is to keep it
+		   compatible with existing code */
+		camel_object_ref (CAMEL_OBJECT (stream));
+
+		fi->stream = stream;
+		fi->err = EIO;
+
+		/* TOP %s 1 only returns the headers of a message and the first
+		   line. Which is fine and to make sure broken POP servers also
+		   return something (in case TOP %s 0 would otherwise be 
+		   misinterpreted by the POP server) */
+
+		pcr = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, 
+			cmd_tocache, fi, "TOP %u 0\r\n", fi->id);
+
+		while ((i = camel_pop3_engine_iterate(pop3_store->engine, pcr)) > 0)
+			;
+		if (i == -1)
+			fi->err = errno;
+
+		camel_pop3_engine_command_free(pop3_store->engine, pcr);
+		camel_stream_reset(stream);
+
+		fi->stream = old;
+
+		/* Check to see we have safely written flag set */
+		if (fi->err != 0) {
+			if (fi->err == EINTR)
+				camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
+			else
+				camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+						      _("Cannot get message %s: %s"),
+						      uid, g_strerror (fi->err));
+			goto done;
+		}
+
+		if (camel_stream_read(stream, buffer, 1) != 1 || buffer[0] != '#') {
+			camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
+					     _("Cannot get message %s: %s"), uid, _("Unknown reason"));
+			goto done;
+		}
+	}
+
+	message = camel_mime_message_new ();
+	if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)message, stream) == -1) {
+		if (errno == EINTR)
+			camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
+		else
+			camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+					      _("Cannot get message %s: %s"),
+					      uid, g_strerror (errno));
+		camel_object_unref((CamelObject *)message);
+		message = NULL;
+	}
+
+	mi = (CamelMessageInfoBase *) camel_folder_summary_uid (summary, uid);
+	if (mi) 
+		camel_folder_summary_remove (summary, (CamelMessageInfo *) mi);
+
+	mi = (CamelMessageInfoBase *) camel_folder_summary_info_new_from_message (summary, message);
+
+	if (mi->uid)
+		g_free (mi->uid);
+	mi->uid = g_strdup(uid);
+
+	camel_folder_summary_add (summary, (CamelMessageInfo *)mi);
+
+done:
+	camel_object_unref((CamelObject *)stream);
+fail:
+	camel_operation_end(NULL);
+
+	g_static_rec_mutex_unlock (pop3_store->eng_lock);
+
+	return message;
+}
+
 static gboolean
 pop3_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
 {
@@ -621,6 +1067,8 @@
 		}
 	}
 
+	/* TNY TODO: Sync to POP server and in the summary.mmap file */
+
 	return res;
 }
 
@@ -628,7 +1076,10 @@
 pop3_get_message_count (CamelFolder *folder)
 {
 	CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (folder);
-	
+
+	if (!pop3_folder->uids)
+		return 0;
+
 	return pop3_folder->uids->len;
 }
 
@@ -647,3 +1098,172 @@
 	
 	return uids;
 }
+
+
+static void
+pop3_sync_offline (CamelFolder *folder, CamelException *ex)
+{
+	camel_folder_summary_save (folder->summary);
+}
+
+static void
+pop3_sync_online (CamelFolder *folder, CamelException *ex)
+{
+	camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Not supported"));
+
+	pop3_sync_offline (folder, ex);
+
+	return;
+}
+
+static void
+pop3_expunge_uids_online (CamelFolder *folder, GPtrArray *uids, CamelException *ex)
+{
+	camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Not supported"));
+
+	return;
+}
+
+
+static int
+uid_compar (const void *va, const void *vb)
+{
+	const char **sa = (const char **)va, **sb = (const char **)vb;
+	unsigned long a, b;
+
+	a = strtoul (*sa, NULL, 10);
+	b = strtoul (*sb, NULL, 10);
+	if (a < b)
+		return -1;
+	else if (a == b)
+		return 0;
+	else
+		return 1;
+}
+
+static void
+pop3_expunge_uids_offline (CamelFolder *folder, GPtrArray *uids, CamelException *ex)
+{
+	CamelFolderChangeInfo *changes;
+	int i;
+	
+	qsort (uids->pdata, uids->len, sizeof (void *), uid_compar);
+	
+	changes = camel_folder_change_info_new ();
+	
+	for (i = 0; i < uids->len; i++) {
+		camel_folder_summary_remove_uid (folder->summary, uids->pdata[i]);
+		camel_folder_change_info_remove_uid (changes, uids->pdata[i]);
+		/* We intentionally don't remove it from the cache because
+		 * the cached data may be useful in replaying a COPY later.
+		 */
+	}
+	camel_folder_summary_save (folder->summary);
+
+	camel_disco_diary_log (CAMEL_DISCO_STORE (folder->parent_store)->diary,
+			       CAMEL_DISCO_DIARY_FOLDER_EXPUNGE, folder, uids);
+
+	camel_object_trigger_event (CAMEL_OBJECT (folder), "folder_changed", changes);
+	camel_folder_change_info_free (changes);
+
+	return;
+}
+
+static void
+pop3_expunge_uids_resyncing (CamelFolder *folder, GPtrArray *uids, CamelException *ex)
+{
+	camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Not supported"));
+
+	return;
+}
+
+
+static void
+pop3_append_offline (CamelFolder *folder, CamelMimeMessage *message,
+		     const CamelMessageInfo *info, char **appended_uid,
+		     CamelException *ex)
+{
+	camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Not supported"));
+
+	return;
+}
+
+static void
+pop3_transfer_offline (CamelFolder *source, GPtrArray *uids,
+		       CamelFolder *dest, GPtrArray **transferred_uids,
+		       gboolean delete_originals, CamelException *ex)
+{
+	camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Not supported"));
+	return;
+}
+
+static void
+pop3_cache_message (CamelDiscoFolder *disco_folder, const char *uid,
+		    CamelException *ex)
+{
+	CamelMimeMessage *msg = pop3_get_message (CAMEL_FOLDER (disco_folder), uid,
+		 CAMEL_FOLDER_RECEIVE_FULL, -1, ex);
+
+	if (msg) 
+		camel_object_unref (CAMEL_OBJECT (msg));
+
+}
+
+static void
+camel_pop3_folder_class_init (CamelPOP3FolderClass *camel_pop3_folder_class)
+{
+	CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS(camel_pop3_folder_class);
+	CamelDiscoFolderClass *camel_disco_folder_class = CAMEL_DISCO_FOLDER_CLASS (camel_pop3_folder_class);
+
+	disco_folder_class = CAMEL_DISCO_FOLDER_CLASS (camel_type_get_global_classfuncs (camel_disco_folder_get_type ()));
+
+	parent_class = CAMEL_FOLDER_CLASS(camel_folder_get_type());
+	
+	/* virtual method overload */
+	camel_folder_class->refresh_info = pop3_refresh_info;
+	camel_folder_class->sync = pop3_sync;
+	
+	camel_folder_class->get_message_count = pop3_get_message_count;
+	camel_folder_class->get_uids = pop3_get_uids;
+	camel_folder_class->free_uids = camel_folder_free_shallow;
+	camel_folder_class->get_message = pop3_get_message;
+	camel_folder_class->set_message_flags = pop3_set_message_flags;
+
+	camel_disco_folder_class->refresh_info_online = pop3_refresh_info;
+	camel_disco_folder_class->sync_online = pop3_sync_online;
+	camel_disco_folder_class->sync_offline = pop3_sync_offline;
+
+	camel_disco_folder_class->sync_resyncing = pop3_sync_offline;
+
+	camel_disco_folder_class->expunge_uids_online = pop3_expunge_uids_online;
+	camel_disco_folder_class->expunge_uids_offline = pop3_expunge_uids_offline;
+	camel_disco_folder_class->expunge_uids_resyncing = pop3_expunge_uids_resyncing;
+
+	camel_disco_folder_class->append_online = pop3_append_offline;
+	camel_disco_folder_class->append_offline = pop3_append_offline;
+	camel_disco_folder_class->append_resyncing = pop3_append_offline;
+
+	camel_disco_folder_class->transfer_online = pop3_transfer_offline;
+	camel_disco_folder_class->transfer_offline = pop3_transfer_offline;
+	camel_disco_folder_class->transfer_resyncing = pop3_transfer_offline;
+
+	camel_disco_folder_class->cache_message = pop3_cache_message;
+}
+
+CamelType
+camel_pop3_folder_get_type (void)
+{
+	static CamelType camel_pop3_folder_type = CAMEL_INVALID_TYPE;
+	
+	if (!camel_pop3_folder_type) {
+		camel_pop3_folder_type = camel_type_register (CAMEL_DISCO_FOLDER_TYPE, "CamelPOP3Folder",
+							      sizeof (CamelPOP3Folder),
+							      sizeof (CamelPOP3FolderClass),
+							      (CamelObjectClassInitFunc) camel_pop3_folder_class_init,
+							      NULL,
+							      NULL,
+							      (CamelObjectFinalizeFunc) pop3_finalize);
+	}
+	
+	return camel_pop3_folder_type;
+}
Index: camel-pop3-stream.c
===================================================================
--- camel-pop3-stream.c	(revision 7816)
+++ camel-pop3-stream.c	(working copy)
@@ -57,13 +57,13 @@
 		memcpy(is->buf, is->ptr, left);
 		is->end = is->buf + left;
 		is->ptr = is->buf;
-		left = camel_stream_read(is->source, is->end, CAMEL_POP3_STREAM_SIZE - (is->end - is->buf));
+		left = camel_stream_read(is->source, (char*) is->end, CAMEL_POP3_STREAM_SIZE - (is->end - is->buf));
 		if (left > 0) {
 			is->end += left;
 			is->end[0] = '\n';
 			return is->end - is->ptr;
 		} else {
-			dd(printf("POP3_STREAM_FILL(ERROR): '%s'\n", strerror (errno)));
+			pop3_debug ("POP3_STREAM_FILL(ERROR): '%s'\n", strerror (errno));
 			return -1;
 		}
 	}
@@ -105,7 +105,7 @@
 				is->ptr = p+3;
 				is->mode = CAMEL_POP3_STREAM_EOD;
 				is->state = 0;
-				dd(printf("POP3_STREAM_READ(%d):\n%.*s\n", (int)(o-buffer), (int)(o-buffer), buffer));
+				pop3_debug ("POP3_STREAM_READ(%d):\n%.*s\n", (int)(o-buffer), (int)(o-buffer), buffer);
 				return o-buffer;
 			}
 			p++;
@@ -138,7 +138,7 @@
 	is->ptr = p;
 	is->state = state;
 
-	dd(printf("POP3_STREAM_READ(%d):\n%.*s\n", (int)(o-buffer), (int)(o-buffer), buffer));
+	pop3_debug ("POP3_STREAM_READ(%d):\n%.*s\n", (int)(o-buffer), (int)(o-buffer), buffer);
 
 	return o-buffer;
 }
@@ -147,12 +147,13 @@
 stream_write(CamelStream *stream, const char *buffer, size_t n)
 {
 	CamelPOP3Stream *is = (CamelPOP3Stream *)stream;
-	
-	if (strncmp (buffer, "PASS ", 5) != 0)
-		dd(printf("POP3_STREAM_WRITE(%d):\n%.*s\n", (int)n, (int)n, buffer));
-	else
-		dd(printf("POP3_STREAM_WRITE(%d):\nPASS xxxxxxxx\n", (int)n));
-	
+
+	if (strncmp (buffer, "PASS ", 5) != 0) {
+		pop3_debug("POP3_STREAM_WRITE(%d):\n%.*s\n", (int)n, (int)n, buffer);
+	} else {
+		pop3_debug("POP3_STREAM_WRITE(%d):\nPASS xxxxxxxx\n", (int)n);
+	}
+
 	return camel_stream_write(is->source, buffer, n);
 }
 
Index: camel-pop3-store.c
===================================================================
--- camel-pop3-store.c	(revision 7816)
+++ camel-pop3-store.c	(working copy)
@@ -5,7 +5,12 @@
  * Authors:
  *   Dan Winship <danw ximian com>
  *   Michael Zucchi <notzed ximian com>
+ *   Philip Van Hoof <pvanhoof gnome org>
  *
+ * This is CamelPop3Store for camel-lite that implements CamelDiscoStore. Its 
+ * implementation is significantly different from Camel's upstream version 
+ * (being used by Evolution): this version supports offline and online modes.
+ * 
  * Copyright (C) 2000-2002 Ximian, Inc. (www.ximian.com)
  *
  * This program is free software; you can redistribute it and/or 
@@ -27,18 +32,28 @@
 #include <config.h>
 #endif
 
+#include <sys/types.h>
+#include <ctype.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <dirent.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <stdio.h>
 #include <sys/types.h>
-#include <ctype.h>
 
-#include <glib/gi18n-lib.h>
+#include "camel-file-utils.h"
 
-#include <libedataserver/md5-utils.h>
-
 #include "camel-data-cache.h"
 #include "camel-exception.h"
 #include "camel-net-utils.h"
@@ -52,15 +67,21 @@
 #include "camel-tcp-stream-raw.h"
 #include "camel-tcp-stream.h"
 #include "camel-url.h"
+#include "camel/camel-string-utils.h"
 
 #ifdef HAVE_SSL
 #include "camel-tcp-stream-ssl.h"
 #endif
+#include "camel-disco-diary.h"
 
+#include <libedataserver/md5-utils.h>
+
 /* Specified in RFC 1939 */
 #define POP3_PORT "110"
 #define POP3S_PORT "995"
 
+#define _(o) o
+
 static CamelStoreClass *parent_class = NULL;
 
 static void finalize (CamelObject *object);
@@ -74,68 +95,156 @@
 
 static CamelFolder *get_trash  (CamelStore *store, CamelException *ex);
 
-static void
-camel_pop3_store_class_init (CamelPOP3StoreClass *camel_pop3_store_class)
+
+
+static void 
+pop3_delete_cache  (CamelStore *store)
 {
-	CamelServiceClass *camel_service_class =
-		CAMEL_SERVICE_CLASS (camel_pop3_store_class);
-	CamelStoreClass *camel_store_class =
-		CAMEL_STORE_CLASS (camel_pop3_store_class);
+	CamelPOP3Store *pop3_store = (CamelPOP3Store *) store;
+	gchar *folder_dir = pop3_store->storage_path;
+	camel_rm (folder_dir);
+}
 
-	parent_class = CAMEL_STORE_CLASS (camel_type_get_global_classfuncs (camel_store_get_type ()));
-	
-	/* virtual method overload */
-	camel_service_class->query_auth_types = query_auth_types;
-	camel_service_class->connect = pop3_connect;
-	camel_service_class->disconnect = pop3_disconnect;
 
-	camel_store_class->get_folder = get_folder;
-	camel_store_class->get_trash = get_trash;
+static gboolean
+pop3_can_work_offline (CamelDiscoStore *disco_store)
+{
+	return TRUE;
 }
 
+static gboolean
+pop3_connect_offline (CamelService *service, CamelException *ex)
+{
+	CamelPOP3Store *store = CAMEL_POP3_STORE (service);
+	store->connected = !camel_exception_is_set (ex);
+	return store->connected;
 
+}
 
-static void
-camel_pop3_store_init (gpointer object, gpointer klass)
+static gboolean
+pop3_connect_online (CamelService *service, CamelException *ex)
 {
-	;
+	return pop3_connect (service, ex);
 }
 
-CamelType
-camel_pop3_store_get_type (void)
+
+static gboolean
+pop3_disconnect_offline (CamelService *service, gboolean clean, CamelException *ex)
 {
-	static CamelType camel_pop3_store_type = CAMEL_INVALID_TYPE;
+	return TRUE;
+}
 
-	if (!camel_pop3_store_type) {
-		camel_pop3_store_type = camel_type_register (CAMEL_STORE_TYPE,
-							     "CamelPOP3Store",
-							     sizeof (CamelPOP3Store),
-							     sizeof (CamelPOP3StoreClass),
-							     (CamelObjectClassInitFunc) camel_pop3_store_class_init,
-							     NULL,
-							     (CamelObjectInitFunc) camel_pop3_store_init,
-							     finalize);
+static gboolean
+pop3_disconnect_online (CamelService *service, gboolean clean, CamelException *ex)
+{
+
+	CamelPOP3Store *store = CAMEL_POP3_STORE (service);
+	
+	if (clean) {
+		CamelPOP3Command *pc;
+		
+		pc = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "QUIT\r\n");
+		while (camel_pop3_engine_iterate(store->engine, NULL) > 0)
+			;
+		camel_pop3_engine_command_free(store->engine, pc);
 	}
 
-	return camel_pop3_store_type;
+	g_static_rec_mutex_lock (store->eng_lock);
+	camel_object_unref((CamelObject *)store->engine);
+	store->engine = NULL;
+	g_static_rec_mutex_unlock (store->eng_lock);
+
+	return TRUE;
 }
 
-static void
-finalize (CamelObject *object)
+static CamelFolder *
+pop3_get_folder_online (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex)
 {
-	CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (object);
+	return get_folder (store, folder_name, flags, ex);
+}
 
-	/* force disconnect so we dont have it run later, after we've cleaned up some stuff */
-	/* SIGH */
 
-	camel_service_disconnect((CamelService *)pop3_store, TRUE, NULL);
+static CamelFolderInfo *
+pop3_build_folder_info(CamelPOP3Store *store, const char *folder_name)
+{
+	const char *name;
+	CamelFolderInfo *fi;
+	gint msize;
+	gchar *folder_dir = store->storage_path;
+	gchar *spath;
+	FILE *f;
 
-	if (pop3_store->engine)
-		camel_object_unref((CamelObject *)pop3_store->engine);
-	if (pop3_store->cache)
-		camel_object_unref((CamelObject *)pop3_store->cache);
+	fi = camel_folder_info_new ();
+
+	fi->full_name = g_strdup(folder_name);
+	fi->unread = 0;
+	fi->total = 0;
+
+	camel_du (folder_dir, &msize);
+
+	spath = g_strdup_printf ("%s/summary.mmap", folder_dir);
+	f = fopen (spath, "r");
+	g_free (spath);
+	if (f) {
+		gint tsize = ((sizeof (guint32) * 5) + sizeof (time_t));
+		char *buffer = malloc (tsize), *ptr;
+		guint32 version, a;
+		a = fread (buffer, 1, tsize, f);
+		if (a == tsize) 
+		{
+			ptr = buffer;
+			version = g_ntohl(get_unaligned_u32(ptr));
+			ptr += 16;
+			fi->total = g_ntohl(get_unaligned_u32(ptr));
+			ptr += 4;
+			if (version < 0x100 && version >= 13)
+				fi->unread = g_ntohl(get_unaligned_u32(ptr));
+		}
+		g_free (buffer);
+		fclose (f);
+	} 
+
+	fi->local_size = (guint) msize;
+
+	fi->uri = g_strdup ("");
+	name = strrchr (fi->full_name, '/');
+	if (name == NULL)
+		name = fi->full_name;
+	else
+		name++;
+	if (!g_ascii_strcasecmp (fi->full_name, "INBOX"))
+		fi->name = g_strdup ((const gchar *) _("Inbox"));
+	else
+		fi->name = g_strdup (name);
+
+	return fi;
 }
 
+static CamelFolder *
+pop3_get_folder_offline (CamelStore *store, const char *folder_name,
+		    guint32 flags, CamelException *ex)
+{
+	return get_folder (store, folder_name, flags, ex);
+}
+
+
+static CamelFolderInfo *
+pop3_get_folder_info_offline (CamelStore *store, const char *top, guint32 flags, CamelException *ex)
+{
+	return pop3_build_folder_info (CAMEL_POP3_STORE (store), "INBOX");
+}
+
+static CamelFolderInfo *
+pop3_get_folder_info_online (CamelStore *store, const char *top, guint32 flags, CamelException *ex)
+{
+	CamelFolderInfo *info =  pop3_get_folder_info_offline (store, top, flags, ex);
+
+	/* TODO: get read and unread count into info->unread & info->total */
+
+	return info;
+}
+
+
 enum {
 	MODE_CLEAR,
 	MODE_SSL,
@@ -148,7 +257,7 @@
 #endif
 
 static gboolean
-connect_to_server (CamelService *service, struct addrinfo *ai, int ssl_mode, CamelException *ex)
+connect_to_server (CamelService *service, struct addrinfo *ai, int ssl_mode, int must_tls, CamelException *ex)
 {
 	CamelPOP3Store *store = CAMEL_POP3_STORE (service);
 	CamelStream *tcp_stream;
@@ -156,128 +265,151 @@
 	guint32 flags = 0;
 	int clean_quit = TRUE;
 	int ret;
-	const gchar *delete_days;
-	
-	if (ssl_mode != MODE_CLEAR) {
+	gchar *delete_days;
+
+	store->connected = FALSE;
+
+	if (ssl_mode != MODE_CLEAR) 
+	{
+
 #ifdef HAVE_SSL
-		if (ssl_mode == MODE_TLS) {
+		if (ssl_mode == MODE_TLS)
 			tcp_stream = camel_tcp_stream_ssl_new_raw (service->session, service->url->host, STARTTLS_FLAGS);
-		} else {
+		else 
 			tcp_stream = camel_tcp_stream_ssl_new (service->session, service->url->host, SSL_PORT_FLAGS);
-		}
 #else
+
 		camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-				      _("Could not connect to %s: %s"),
-				      service->url->host, _("SSL unavailable"));
-		
+				_("Could not connect to %s: %s"),
+				service->url->host, _("SSL unavailable"));
+
 		return FALSE;
+
 #endif /* HAVE_SSL */
-	} else {
+	} else 
 		tcp_stream = camel_tcp_stream_raw_new ();
-	}
-	
-	if ((ret = camel_tcp_stream_connect ((CamelTcpStream *) tcp_stream, ai)) == -1) {
+
+	if ((ret = camel_tcp_stream_connect ((CamelTcpStream *) tcp_stream, ai)) == -1) 
+	{
 		if (errno == EINTR)
 			camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
-					     _("Connection canceled"));
+				_("Connection canceled"));
 		else
 			camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-					      _("Could not connect to %s: %s"),
-					      service->url->host,
-					      g_strerror (errno));
+				_("Could not connect to %s: %s"),
+				service->url->host,
+				g_strerror (errno));
 		
 		camel_object_unref (tcp_stream);
 		
 		return FALSE;
 	}
-	
+
 	/* parent class connect initialization */
-	if (CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex) == FALSE) {
+	/*if (CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex) == FALSE) {
 		camel_object_unref (tcp_stream);
 		return FALSE;
-	}
-	
+	}*/
+
 	if (camel_url_get_param (service->url, "disable_extensions"))
 		flags |= CAMEL_POP3_ENGINE_DISABLE_EXTENSIONS;
-	
-	if ((delete_days = camel_url_get_param(service->url,"delete_after"))) 
+
+	if ((delete_days = (gchar *) camel_url_get_param(service->url,"delete_after"))) 
 		store->delete_after =  atoi(delete_days);
-	
+
+	g_static_rec_mutex_lock (store->eng_lock);
 	if (!(store->engine = camel_pop3_engine_new (tcp_stream, flags))) {
 		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
-				      _("Failed to read a valid greeting from POP server %s"),
-				      service->url->host);
+			_("Failed to read a valid greeting from POP server %s"),
+			service->url->host);
 		camel_object_unref (tcp_stream);
+		g_static_rec_mutex_unlock (store->eng_lock);
 		return FALSE;
 	}
-	
-	if (ssl_mode != MODE_TLS) {
+	store->engine->store = store;
+	store->engine->partial_happening = FALSE;
+	g_static_rec_mutex_unlock (store->eng_lock);
+
+	if (!must_tls && (ssl_mode != MODE_TLS)) 
+	{
 		camel_object_unref (tcp_stream);
+		store->connected = TRUE;
 		return TRUE;
 	}
-	
+
 #ifdef HAVE_SSL
-	if (!(store->engine->capa & CAMEL_POP3_CAP_STLS)) {
+
+	if (!(store->engine->capa & CAMEL_POP3_CAP_STLS)) 
+	{
 		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
-				      _("Failed to connect to POP server %s in secure mode: %s"),
-				      service->url->host, _("STLS not supported by server"));
+			_("Failed to connect to POP server %s in secure mode: %s"),
+			service->url->host, _("STLS not supported by server"));
 		goto stls_exception;
 	}
-	
+
 	/* as soon as we send a STLS command, all hope is lost of a clean QUIT if problems arise */
 	clean_quit = FALSE;
-	
+
 	pc = camel_pop3_engine_command_new (store->engine, 0, NULL, NULL, "STLS\r\n");
 	while (camel_pop3_engine_iterate (store->engine, NULL) > 0)
 		;
-	
+
 	ret = pc->state == CAMEL_POP3_COMMAND_OK;
 	camel_pop3_engine_command_free (store->engine, pc);
-	
-	if (ret == FALSE) {
+
+	if (ret == FALSE) 
+	{
 		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
-				      _("Failed to connect to POP server %s in secure mode: %s"),
-				      service->url->host, store->engine->line);
+			_("Failed to connect to POP server %s in secure mode: %s"),
+			service->url->host, store->engine->line);
 		goto stls_exception;
 	}
-	
+
 	/* Okay, now toggle SSL/TLS mode */
 	ret = camel_tcp_stream_ssl_enable_ssl (CAMEL_TCP_STREAM_SSL (tcp_stream));
-	
+
 	if (ret == -1) {
 		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 				      _("Failed to connect to POP server %s in secure mode: %s"),
 				      service->url->host, _("TLS negotiations failed"));
 		goto stls_exception;
 	}
+
 #else
 	camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
-			      _("Failed to connect to POP server %s in secure mode: %s"),
-			      service->url->host, _("TLS is not available in this build"));
+		_("Failed to connect to POP server %s in secure mode: %s"),
+		service->url->host, _("TLS is not available in this build"));
+
 	goto stls_exception;
 #endif /* HAVE_SSL */
-	
+
 	camel_object_unref (tcp_stream);
-	
+
 	/* rfc2595, section 4 states that after a successful STLS
-           command, the client MUST discard prior CAPA responses */
+	   command, the client MUST discard prior CAPA responses */
+
 	camel_pop3_engine_reget_capabilities (store->engine);
-	
+	store->connected = TRUE;
+
 	return TRUE;
-	
+
  stls_exception:
-	if (clean_quit) {
+	if (clean_quit) 
+	{
 		/* try to disconnect cleanly */
 		pc = camel_pop3_engine_command_new (store->engine, 0, NULL, NULL, "QUIT\r\n");
 		while (camel_pop3_engine_iterate (store->engine, NULL) > 0)
 			;
 		camel_pop3_engine_command_free (store->engine, pc);
 	}
-	
+
+	g_static_rec_mutex_lock (store->eng_lock);
 	camel_object_unref (CAMEL_OBJECT (store->engine));
 	camel_object_unref (CAMEL_OBJECT (tcp_stream));
 	store->engine = NULL;
-	
+	store->connected = FALSE;
+	g_static_rec_mutex_unlock (store->eng_lock);
+
 	return FALSE;
 }
 
@@ -286,12 +418,14 @@
 	char *serv;
 	char *port;
 	int mode;
+	int must_tls;
 } ssl_options[] = {
-	{ "",              "pop3s", POP3S_PORT, MODE_SSL   },  /* really old (1.x) */
-	{ "always",        "pop3s", POP3S_PORT, MODE_SSL   },
-	{ "when-possible", "pop3",  POP3_PORT,  MODE_TLS   },
-	{ "never",         "pop3",  POP3_PORT,  MODE_CLEAR },
-	{ NULL,            "pop3",  POP3_PORT,  MODE_CLEAR },
+	{ "",              "pop3s", POP3S_PORT, MODE_SSL, 0   },  /* really old (1.x) */
+	{ "wrapped",       "pop3s", POP3S_PORT, MODE_SSL, 0   },
+	{ "tls",           "pop3",  POP3_PORT,  MODE_TLS, 1   },
+	{ "when-possible", "pop3",  POP3_PORT,  MODE_TLS, 0   },
+	{ "never",         "pop3",  POP3_PORT,  MODE_CLEAR, 0 },
+	{ NULL,            "pop3",  POP3_PORT,  MODE_CLEAR, 0 },
 };
 
 static gboolean
@@ -299,7 +433,7 @@
 {
 	struct addrinfo hints, *ai;
 	const char *ssl_mode;
-	int mode, ret, i;
+	int mode, ret, i, must_tls=0;
 	char *serv;
 	const char *port;
 
@@ -310,10 +444,12 @@
 		mode = ssl_options[i].mode;
 		serv = ssl_options[i].serv;
 		port = ssl_options[i].port;
+		must_tls = ssl_options[i].must_tls;
 	} else {
 		mode = MODE_CLEAR;
 		serv = "pop3";
 		port = POP3S_PORT;
+		must_tls = 0;
 	}
 	
 	if (service->url->port) {
@@ -334,7 +470,7 @@
 	if (ai == NULL)
 		return FALSE;
 	
-	ret = connect_to_server (service, ai, mode, ex);
+	ret = connect_to_server (service, ai, mode, must_tls, ex);
 	
 	camel_freeaddrinfo (ai);
 	
@@ -366,28 +502,7 @@
 	return types;
 }
 
-/**
- * camel_pop3_store_expunge:
- * @store: the store
- * @ex: a CamelException
- *
- * Expunge messages from the store. This will result in the connection
- * being closed, which may cause later commands to fail if they can't
- * reconnect.
- **/
-void
-camel_pop3_store_expunge (CamelPOP3Store *store, CamelException *ex)
-{
-	CamelPOP3Command *pc;
 
-	pc = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "QUIT\r\n");
-	while (camel_pop3_engine_iterate(store->engine, NULL) > 0)
-		;
-	camel_pop3_engine_command_free(store->engine, pc);
-
-	camel_service_disconnect (CAMEL_SERVICE (store), FALSE, ex);
-}
-
 static int
 try_sasl(CamelPOP3Store *store, const char *mech, CamelException *ex)
 {
@@ -412,9 +527,9 @@
 	while (1) {
 		if (camel_pop3_stream_line(stream, &line, &len) == -1)
 			goto ioerror;
-		if (strncmp(line, "+OK", 3) == 0)
+		if (strncmp((char *) line, "+OK", 3) == 0)
 			break;
-		if (strncmp(line, "-ERR", 4) == 0) {
+		if (strncmp((char *) line, "-ERR", 4) == 0) {
 			camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
 					      _("SASL `%s' Login failed for POP server %s: %s"),
 					      mech, CAMEL_SERVICE (store)->url->host, line);
@@ -422,9 +537,9 @@
 		}
 		/* If we dont get continuation, or the sasl object's run out of work, or we dont get a challenge,
 		   its a protocol error, so fail, and try reset the server */
-		if (strncmp(line, "+ ", 2) != 0
+		if (strncmp((char *) line, "+ ", 2) != 0
 		    || camel_sasl_authenticated(sasl)
-		    || (resp = camel_sasl_challenge_base64(sasl, line+2, ex)) == NULL) {
+		    || (resp = (unsigned char *) camel_sasl_challenge_base64(sasl, (const char *) line+2, ex)) == NULL) {
 			camel_stream_printf((CamelStream *)stream, "*\r\n");
 			camel_pop3_stream_line(stream, &line, &len);
 			camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
@@ -444,7 +559,7 @@
 	
  ioerror:
 	if (errno == EINTR) {
-		camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled"));
+		camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, "Canceled");
 	} else {
 		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 				      _("Failed to authenticate on POP server %s: %s"),
@@ -540,7 +655,7 @@
 	
 	if (status == -1) {
 		if (errno == EINTR) {
-			camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled"));
+			camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, "Canceled");
 		} else {
 			camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 					      _("Unable to connect to POP server %s.\n"
@@ -579,22 +694,7 @@
 	int status;
 	
 	session = camel_service_get_session (service);
-	
-	if (store->cache == NULL) {
-		char *root;
 
-		root = camel_session_get_storage_path (session, service, ex);
-		if (root) {
-			store->cache = camel_data_cache_new(root, 0, ex);
-			g_free(root);
-			if (store->cache) {
-				/* Default cache expiry - 1 week or not visited in a day */
-				camel_data_cache_set_expire_age(store->cache, 60*60*24*7);
-				camel_data_cache_set_expire_access(store->cache, 60*60*24);
-			}
-		}
-	}
-	
 	if (!connect_to_server_wrapper (service, ex))
 		return FALSE;
 	
@@ -610,6 +710,7 @@
 			service->url->passwd = NULL;
 			reprompt = TRUE;
 			camel_exception_clear (ex);
+			sleep (5); /* For example Cyrus-POPd dislikes hammering */
 		} else
 			break;
 	}
@@ -641,13 +742,12 @@
 			;
 		camel_pop3_engine_command_free(store->engine, pc);
 	}
-	
-	if (!CAMEL_SERVICE_CLASS (parent_class)->disconnect (service, clean, ex))
-		return FALSE;
-	
+
+	g_static_rec_mutex_lock (store->eng_lock);
 	camel_object_unref((CamelObject *)store->engine);
 	store->engine = NULL;
-	
+	g_static_rec_mutex_unlock (store->eng_lock);
+
 	return TRUE;
 }
 
@@ -668,3 +768,134 @@
 	/* no-op */
 	return NULL;
 }
+
+
+static void
+finalize (CamelObject *object)
+{
+	CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (object);
+
+	g_static_rec_mutex_lock (pop3_store->eng_lock);
+	if (pop3_store->engine)
+		camel_object_unref((CamelObject *)pop3_store->engine);
+	pop3_store->engine = NULL;
+	g_static_rec_mutex_unlock (pop3_store->eng_lock);
+
+	if (pop3_store->cache)
+		camel_object_unref((CamelObject *)pop3_store->cache);
+	pop3_store->cache = NULL;
+	if (pop3_store->storage_path)
+		g_free (pop3_store->storage_path);
+	pop3_store->storage_path = NULL;
+
+	g_static_rec_mutex_free (pop3_store->eng_lock);
+	pop3_store->eng_lock = NULL;
+
+}
+
+
+
+static void
+pop3_construct (CamelService *service, CamelSession *session,
+	   CamelProvider *provider, CamelURL *url,
+	   CamelException *ex)
+{
+	CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (service);
+	CamelDiscoStore *disco_store = CAMEL_DISCO_STORE (service);
+	char *path;
+
+	CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
+	if (camel_exception_is_set (ex))
+		return;
+
+	pop3_store->storage_path = camel_session_get_storage_path (session, service, ex);
+	if (!pop3_store->storage_path)
+		return;
+
+	pop3_store->cache = camel_data_cache_new(pop3_store->storage_path, 0, ex);
+	if (pop3_store->cache) {
+		/* Default cache expiry - 1 week or not visited in a day */
+		camel_data_cache_set_expire_age (pop3_store->cache, 60*60*24*7);
+		camel_data_cache_set_expire_access (pop3_store->cache, 60*60*24);
+	}
+
+	pop3_store->base_url = camel_url_to_string (service->url, (CAMEL_URL_HIDE_PASSWORD |
+								   CAMEL_URL_HIDE_PARAMS |
+								   CAMEL_URL_HIDE_AUTH));
+
+	/* setup journal*/
+	path = g_strdup_printf ("%s/journal", pop3_store->storage_path);
+	disco_store->diary = camel_disco_diary_new (disco_store, path, ex);
+	g_free (path);
+
+}
+
+static void
+camel_pop3_store_class_init (CamelPOP3StoreClass *camel_pop3_store_class)
+{
+	CamelServiceClass *camel_service_class =
+		CAMEL_SERVICE_CLASS (camel_pop3_store_class);
+	CamelStoreClass *camel_store_class =
+		CAMEL_STORE_CLASS (camel_pop3_store_class);
+	CamelDiscoStoreClass *camel_disco_store_class =
+		CAMEL_DISCO_STORE_CLASS (camel_pop3_store_class);
+
+	parent_class = CAMEL_STORE_CLASS (camel_type_get_global_classfuncs (camel_disco_store_get_type ()));
+
+	/* virtual method overload */
+	camel_service_class->construct = pop3_construct;
+	camel_service_class->query_auth_types = query_auth_types;
+
+	/* camel_service_class->connect = pop3_connect; */
+	/* camel_service_class->disconnect = pop3_disconnect; */
+
+	camel_store_class->get_folder = get_folder;
+	camel_store_class->get_trash = get_trash;
+
+	camel_store_class->delete_cache = pop3_delete_cache;
+
+	camel_disco_store_class->can_work_offline = pop3_can_work_offline;
+	camel_disco_store_class->connect_online = pop3_connect_online;
+	camel_disco_store_class->connect_offline = pop3_connect_offline;
+	camel_disco_store_class->disconnect_online = pop3_disconnect_online;
+	camel_disco_store_class->disconnect_offline = pop3_disconnect_offline;
+	camel_disco_store_class->get_folder_online = pop3_get_folder_online;
+	camel_disco_store_class->get_folder_offline = pop3_get_folder_offline;
+	camel_disco_store_class->get_folder_resyncing = pop3_get_folder_online;
+	camel_disco_store_class->get_folder_info_online = pop3_get_folder_info_online;
+	camel_disco_store_class->get_folder_info_offline = pop3_get_folder_info_offline;
+	camel_disco_store_class->get_folder_info_resyncing = pop3_get_folder_info_online;
+}
+
+
+
+static void
+camel_pop3_store_init (gpointer object, gpointer klass)
+{
+	CamelPOP3Store *store = (CamelPOP3Store *) object;
+
+	store->immediate_delete_after = FALSE;
+	store->eng_lock = g_new0 (GStaticRecMutex, 1);
+	g_static_rec_mutex_init (store->eng_lock);
+
+	return;
+}
+
+CamelType
+camel_pop3_store_get_type (void)
+{
+	static CamelType camel_pop3_store_type = CAMEL_INVALID_TYPE;
+
+	if (!camel_pop3_store_type) {
+		camel_pop3_store_type = camel_type_register (CAMEL_DISCO_STORE_TYPE,
+							     "CamelPOP3Store",
+							     sizeof (CamelPOP3Store),
+							     sizeof (CamelPOP3StoreClass),
+							     (CamelObjectClassInitFunc) camel_pop3_store_class_init,
+							     NULL,
+							     (CamelObjectInitFunc) camel_pop3_store_init,
+							     finalize);
+	}
+
+	return camel_pop3_store_type;
+}


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