Re: Partial message retrieval, the design



This E-mail is all about throwing food to wolves

Yes, it implements the core of partial message retrieval for IMAP.

Not yet the jingle bells around it (there's still some work like
detecting whether the message was previously retrieved partial whereas
the user switched to full -- which means that it has to reretrieve the
full message).

Anyway, test this a little bit folks :)

Or join!? We can start a branch for example.


On Wed, 2007-01-03 at 18:40 +0100, Philip Van Hoof wrote:
> Hi folks,
> 
> This is a first design of the upcoming partial message retrieval feature
> 
>   o. http://tinymail.org/trac/tinymail/wiki/PartialMessageRetrieval
> 
> Two new documentation pages have been added. These are about the
> camel-lite internals. The first is going to be important when
> implementing the camel-lite part of the partial message retrieval
> feature (which is not really shown in the design, as that part will have
> to blend in the existing camel infrastructure).
> 
>   o. http://tinymail.org/trac/tinymail/wiki/CamelImapMessageCache
>   o. http://tinymail.org/trac/tinymail/wiki/CamelFolderSummaryMmap
> 
> In short will the camel-lite API, camel_folder_get_message, be changed
> to have an extra bool "full" to its parameters. If that bool is FALSE
> then it will only fetch the body of the E-mail from the service. Else it
> will retrieve the entire message content (including all attachments and
> other mime parts). Or ... at least that is the idea.
> 
> At the tinymail part no API will be changed, only added. The API that
> will be added is tny_folder_set/get_msg_receive_strategy. Two types will
> also be added in libtinymail-camel (but check the wiki page for more
> information, as decisions might change things .. whereas this E-mail
> can't change once I press the Send button).
> 
> Comments, thoughts and everything in between (except brick throwing at
> my head) is very welcome. If somebody is interested in cooperating or
> co-developing this feature, please don't hesitate to contact me or write
> about your ideas and intentions on this mailing list.
> 
> 
-- 
Philip Van Hoof, software developer
home: me at pvanhoof dot be 
gnome: pvanhoof at gnome dot org 
http://www.pvanhoof.be/blog



Index: libtinymail-camel/tny-camel-folder.c
===================================================================
--- libtinymail-camel/tny-camel-folder.c	(revision 1363)
+++ libtinymail-camel/tny-camel-folder.c	(working copy)
@@ -2318,7 +2318,7 @@
 	priv->cached_folder_type = TNY_FOLDER_TYPE_UNKNOWN;
 
 	priv->remove_strat = tny_camel_msg_remove_strategy_new ();
-	priv->receive_strat = tny_camel_full_msg_receive_strategy_new ();
+	priv->receive_strat = tny_camel_partial_msg_receive_strategy_new ();
 
 	return;
 }
Index: libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.c
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.c	(revision 1366)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.c	(working copy)
@@ -136,6 +136,7 @@
 static CamelObjectClass *parent_class;
 
 static GData *parse_fetch_response (CamelImapFolder *imap_folder, char *msg_att);
+static GData *parse_partial_fetch_response (CamelImapFolder *imap_folder, char *response, GData *data, gboolean last);
 
 #ifdef G_OS_WIN32
 /* The strtok() in Microsoft's C library is MT-safe (but still uses
@@ -1718,7 +1719,7 @@
 static CamelMimeMessage *get_message (CamelImapFolder *imap_folder,
 				      const char *uid,
 				      CamelMessageContentInfo *ci,
-				      CamelException *ex);
+				      gboolean full, CamelException *ex);
 
 struct _part_spec_stack {
 	struct _part_spec_stack *parent;
@@ -1839,7 +1840,8 @@
 			strcpy(spec, part_spec);
 		g_free(part_spec);
 		
-		stream = camel_imap_folder_fetch_data (imap_folder, uid, spec, FALSE, ex);
+		/* TNY TODO: partial message retrieval exception */
+		stream = camel_imap_folder_fetch_data (imap_folder, uid, spec, FALSE, TRUE, ex);
 		if (stream) {
 			ret = camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (body_mp), stream);
 			camel_object_unref (CAMEL_OBJECT (stream));
@@ -1876,7 +1878,8 @@
 		num = 1;
 		while (ci) {
 			sprintf (child_spec + speclen, "%d.MIME", num++);
-			stream = camel_imap_folder_fetch_data (imap_folder, uid, child_spec, FALSE, ex);
+			/* TNY TODO: partial message retrieval exception */
+			stream = camel_imap_folder_fetch_data (imap_folder, uid, child_spec, FALSE, TRUE, ex);
 			if (stream) {
 				int ret;
 				
@@ -1932,7 +1935,8 @@
 		
 		return (CamelDataWrapper *) body_mp;
 	} else if (camel_content_type_is (ci->type, "message", "rfc822")) {
-		content = (CamelDataWrapper *) get_message (imap_folder, uid, ci->childs, ex);
+		/* TNY TODO: partial message retrieval exception */
+		content = (CamelDataWrapper *) get_message (imap_folder, uid, ci->childs, TRUE, ex);
 		g_free (part_spec);
 		return content;
 	} else {
@@ -1956,7 +1960,7 @@
 static CamelMimeMessage *
 get_message (CamelImapFolder *imap_folder, const char *uid,
 	     CamelMessageContentInfo *ci,
-	     CamelException *ex)
+	     gboolean full, CamelException *ex)
 {
 	CamelImapStore *store = CAMEL_IMAP_STORE (CAMEL_FOLDER (imap_folder)->parent_store);
 	CamelDataWrapper *content;
@@ -1970,7 +1974,8 @@
 	section_text = g_strdup_printf ("%s%s%s", part_spec, *part_spec ? "." : "",
 					store->server_level >= IMAP_LEVEL_IMAP4REV1 ? "HEADER" : "0");
 
-	stream = camel_imap_folder_fetch_data (imap_folder, uid, section_text, FALSE, ex);
+	/* TNY: partial message retrieval */
+	stream = camel_imap_folder_fetch_data (imap_folder, uid, section_text, FALSE, full, ex);
 	g_free (section_text);
 	g_free(part_spec);
 	if (!stream)
@@ -2010,14 +2015,14 @@
 
 static CamelMimeMessage *
 get_message_simple (CamelImapFolder *imap_folder, const char *uid,
-		    CamelStream *stream, CamelException *ex)
+		    CamelStream *stream, gboolean full, CamelException *ex)
 {
 	CamelMimeMessage *msg;
 	int ret;
 	
 	if (!stream) {
 		stream = camel_imap_folder_fetch_data (imap_folder, uid, "",
-						       FALSE, ex);
+						       FALSE, full, ex);
 		if (!stream)
 			return NULL;
 	}
@@ -2076,8 +2081,8 @@
 
 	/* If its cached in full, just get it as is, this is only a shortcut,
 	   since we get stuff from the cache anyway.  It affects a busted connection though. */
-	if ( (stream = camel_imap_folder_fetch_data(imap_folder, uid, "", TRUE, NULL))
-	     && (msg = get_message_simple(imap_folder, uid, stream, ex)))
+	if ( (stream = camel_imap_folder_fetch_data(imap_folder, uid, "", TRUE, full, NULL))
+	     && (msg = get_message_simple(imap_folder, uid, stream, full, ex)))
 		goto done;
 
 	/* All this mess is so we silently retry a fetch if we fail with
@@ -2094,7 +2099,7 @@
 		    || mi->info.size < IMAP_SMALL_BODY_SIZE
 #endif
 		    || (!content_info_incomplete(mi->info.content) && !mi->info.content->childs)) {
-			msg = get_message_simple (imap_folder, uid, NULL, ex);
+			msg = get_message_simple (imap_folder, uid, NULL, full, ex);
 		} else {
 			if (content_info_incomplete (mi->info.content)) {
 				/* For larger messages, fetch the structure and build a message
@@ -2160,9 +2165,9 @@
 			 * let the mailer's "bad MIME" code handle it.
 			 */
 			if (content_info_incomplete (mi->info.content))
-				msg = get_message_simple (imap_folder, uid, NULL, ex);
+				msg = get_message_simple (imap_folder, uid, NULL, full, ex);
 			else
-				msg = get_message (imap_folder, uid, mi->info.content, ex);
+				msg = get_message (imap_folder, uid, mi->info.content, full, ex);
 		}
 	} while (msg == NULL
 		 && retry < 2
@@ -2184,7 +2189,8 @@
 	CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (disco_folder);
 	CamelStream *stream;
 
-	stream = camel_imap_folder_fetch_data (imap_folder, uid, "", FALSE, ex);
+	/* TNY TODO: partial message retrieval exception */
+	stream = camel_imap_folder_fetch_data (imap_folder, uid, "", FALSE, TRUE, ex);
 	if (stream)
 		camel_object_unref (CAMEL_OBJECT (stream));
 }
@@ -2686,13 +2692,13 @@
 CamelStream *
 camel_imap_folder_fetch_data (CamelImapFolder *imap_folder, const char *uid,
 			      const char *section_text, gboolean cache_only,
-			      CamelException *ex)
+			      gboolean full, CamelException *ex)
 {
 	CamelFolder *folder = CAMEL_FOLDER (imap_folder);
 	CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
 	CamelImapResponse *response;
 	CamelStream *stream;
-	GData *fetch_data;
+	GData *fetch_data=NULL;
 	char *found_uid;
 	int i;
 	
@@ -2731,36 +2737,96 @@
 	}
 	
 	camel_exception_clear (ex);
-	if (store->server_level < IMAP_LEVEL_IMAP4REV1 && !*section_text) {
-		response = camel_imap_command (store, folder, ex,
-					       "UID FETCH %s RFC822.PEEK",
-					       uid);
+
+	if (full)
+	{
+g_print ("FULL\n");
+	    if (store->server_level < IMAP_LEVEL_IMAP4REV1 && !*section_text) {
+		    response = camel_imap_command (store, folder, ex,
+						   "UID FETCH %s RFC822.PEEK",
+						   uid);
+	    } else {
+		    response = camel_imap_command (store, folder, ex,
+						   "UID FETCH %s BODY.PEEK[%s]",
+						   uid, section_text);
+	    }
+	    /* We won't need the connect_lock again after this. */
+	    CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
+	    
+	    if (!response) {
+		    CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
+		    return NULL;
+	    }
+	    
+	    for (i = 0; i < response->untagged->len; i++) {
+		    fetch_data = parse_fetch_response (imap_folder, response->untagged->pdata[i]);
+		    found_uid = g_datalist_get_data (&fetch_data, "UID");
+		    stream = g_datalist_get_data (&fetch_data, "BODY_PART_STREAM");
+		    if (found_uid && stream && !strcmp (uid, found_uid))
+			    break;
+		    
+		    g_datalist_clear (&fetch_data);
+		    stream = NULL;
+	    }
+	    camel_imap_response_free (store, response);
 	} else {
-		response = camel_imap_command (store, folder, ex,
-					       "UID FETCH %s BODY.PEEK[%s]",
-					       uid, section_text);
+
+ 	    /* Partial message retrieval feature */
+
+g_print ("PARTIAL\n");
+
+	    stream = NULL;
+	    response = camel_imap_command (store, folder, ex,
+			   "UID FETCH %s BODY.PEEK[HEADER]", uid);
+	    if (!response) {
+		CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
+		goto errorh;
+	    }
+	    
+	    for (i = 0; i < response->untagged->len; i++)
+		    fetch_data = parse_partial_fetch_response (imap_folder, response->untagged->pdata[i], fetch_data, FALSE);
+	    camel_imap_response_free (store, response);
+
+	    response = camel_imap_command (store, folder, ex,
+			   "UID FETCH %s BODY.PEEK[1.HEADER]", uid);
+	    if (!response) {
+		CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
+		goto errorh;
+	    }
+
+	    for (i = 0; i < response->untagged->len; i++) 
+		    fetch_data = parse_partial_fetch_response (imap_folder, response->untagged->pdata[i], fetch_data, FALSE);
+	    camel_imap_response_free (store, response);
+
+	    response = camel_imap_command (store, folder, ex,
+			   "UID FETCH %s BODY.PEEK[1]", uid);	    
+	    if (!response) {
+		CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
+		goto errorh;
+	    }
+
+	    CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
+
+	    for (i = 0; i < response->untagged->len; i++) {
+		    fetch_data = parse_partial_fetch_response (imap_folder, response->untagged->pdata[i], fetch_data, TRUE);
+		    found_uid = g_datalist_get_data (&fetch_data, "UID");
+		    stream = g_datalist_get_data (&fetch_data, "BODY_PART_STREAM");
+		    if (found_uid && stream && !strcmp (uid, found_uid))
+			    break;
+		    
+		    g_datalist_clear (&fetch_data);
+		    stream = NULL;
+	    }
+	    camel_imap_response_free (store, response);
+
 	}
-	/* We won't need the connect_lock again after this. */
-	CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
-	
-	if (!response) {
-		CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
-		return NULL;
-	}
-	
-	for (i = 0; i < response->untagged->len; i++) {
-		fetch_data = parse_fetch_response (imap_folder, response->untagged->pdata[i]);
-		found_uid = g_datalist_get_data (&fetch_data, "UID");
-		stream = g_datalist_get_data (&fetch_data, "BODY_PART_STREAM");
-		if (found_uid && stream && !strcmp (uid, found_uid))
-			break;
-		
-		g_datalist_clear (&fetch_data);
-		stream = NULL;
-	}
-	camel_imap_response_free (store, response);
+
+errorh:
+
 	CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
-	if (!stream) {
+
+	if (!stream) 
+	{
 		camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
 				      _("Could not find message body in FETCH response."));
 	} else {
@@ -2771,7 +2837,223 @@
 	return stream;
 }
 
+
+
 static GData *
+parse_partial_fetch_response (CamelImapFolder *imap_folder, char *response, GData *data, gboolean last)
+{
+	char *start, *part_spec = NULL, *body = NULL, *uid = NULL, *idate = NULL;
+	gboolean cache_header = TRUE, header = FALSE;
+	size_t body_len = 0;
+
+	char *esequence, *ebodypartspec, *ebodypartdata;
+	char *eintdate, *euid;
+	int eflags = 0, erfc822size = 0, ebodypartlen = 0;
+
+	esequence = g_datalist_get_data (&data, "SEQUENCE");
+	eflags = GPOINTER_TO_UINT (g_datalist_get_data (&data, "FLAGS"));
+	erfc822size = GPOINTER_TO_UINT (g_datalist_get_data (&data, "RFC822.SIZE"));
+	esequence = g_datalist_get_data (&data, "SEQUENCE");
+	eintdate = g_datalist_get_data (&data, "INTERNALDATE");
+	euid = g_datalist_get_data (&data, "UID");
+	ebodypartspec = g_datalist_get_data (&data, "BODY_PART_SPEC");
+	ebodypartdata = g_datalist_get_data (&data, "BODY_PART_DATA");
+	ebodypartlen = GPOINTER_TO_INT (g_datalist_get_data (&data, "BODY_PART_LEN"));
+
+
+	if (*response != '(') {
+		long seq;
+		
+		if (*response != '*' || *(response + 1) != ' ')
+			return NULL;
+		seq = strtol (response + 2, &response, 10);
+		if (seq == 0)
+			return NULL;
+		if (g_ascii_strncasecmp (response, " FETCH (", 8) != 0)
+			return NULL;
+		response += 7;
+		
+		if (!esequence)
+			g_datalist_set_data (&data, "SEQUENCE", GINT_TO_POINTER (seq));
+	}
+	
+	do {
+		/* Skip the initial '(' or the ' ' between elements */
+		response++;
+		
+		if (!g_ascii_strncasecmp (response, "FLAGS ", 6)) {
+			guint32 flags;
+			
+			response += 6;
+			/* FIXME user flags */
+			flags = imap_parse_flag_list (&response);
+			
+			g_datalist_set_data (&data, "FLAGS", GUINT_TO_POINTER (flags | eflags));
+		} else if (!g_ascii_strncasecmp (response, "RFC822.SIZE ", 12)) {
+			unsigned long size;
+			
+			response += 12;
+			size = strtoul (response, &response, 10);
+			g_datalist_set_data (&data, "RFC822.SIZE", GUINT_TO_POINTER (size + erfc822size));
+		} else if (!g_ascii_strncasecmp (response, "BODY[", 5) ||
+			   !g_ascii_strncasecmp (response, "RFC822 ", 7)) {
+			char *p;
+			char *boundary = NULL;
+			char *nbody;
+			int nlen = 0, blen = 0;
+
+			if (*response == 'B') {
+				response += 5;
+				
+				/* HEADER], HEADER.FIELDS (...)], or 0] */
+				if (!g_ascii_strncasecmp (response, "HEADER", 6)) {
+					header = TRUE;
+					if (!g_ascii_strncasecmp (response + 6, ".FIELDS", 7))
+						cache_header = FALSE;
+				} else if (!g_ascii_strncasecmp (response, "0]", 2))
+					header = TRUE;
+				
+				p = strchr (response, ']');
+				if (!p || *(p + 1) != ' ')
+					break;
+				
+				if (cache_header)
+					part_spec = g_strndup (response, p - response);
+				else
+					part_spec = g_strdup ("HEADER.FIELDS");
+				
+				response = p + 2;
+			} else {
+				part_spec = g_strdup ("");
+				response += 7;
+				
+				if (!g_ascii_strncasecmp (response, "HEADER", 6))
+					header = TRUE;
+			}
+			
+			body = imap_parse_nstring ((const char **) &response, &body_len);
+			if (!response) {
+				g_free (part_spec);
+				break;
+			}
+			
+			if (!body)
+				body = g_strdup ("");
+
+		        boundary = g_datalist_get_data (&data, "BOUNDARY");
+
+			if (!boundary)
+			{
+			   CamelContentType *ct = NULL;
+			   const char *bound=NULL;
+			   char *pstr = (char*)strcasestr (body, "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) {
+					boundary = g_strdup (bound);
+					blen = strlen (bound);
+					g_datalist_set_data_full (&data, "BOUNDARY", boundary, g_free);
+				}
+			   }
+			} else
+				blen = strlen (boundary);
+
+			if (!ebodypartspec) { /* Only the first is cool for below */
+			    g_datalist_set_data_full (&data, "BODY_PART_SPEC", part_spec, g_free);
+			    ebodypartspec = part_spec;
+			}
+
+			if (ebodypartdata)
+			{
+			    if (blen > 0)
+			    {
+				
+				    nbody = g_strdup_printf ("%s\n--%s\n%s", ebodypartdata, boundary, body);
+				    nlen = ebodypartlen + body_len + blen + 4;
+				    g_datalist_set_data (&data, "BODY_PART_LEN", GINT_TO_POINTER (nlen));
+			    } else {
+				    nbody = g_strdup_printf ("%s%s", ebodypartdata, body);
+				    nlen = ebodypartlen + body_len;
+				    g_datalist_set_data (&data, "BODY_PART_LEN", GINT_TO_POINTER (nlen));
+			    }
+			    g_free (body); body = nbody;
+			    body_len = nlen;
+			    g_datalist_set_data_full (&data, "BODY_PART_DATA", nbody, g_free);
+			} else {
+			    g_datalist_set_data_full (&data, "BODY_PART_DATA", body, g_free);
+			    g_datalist_set_data (&data, "BODY_PART_LEN", GINT_TO_POINTER (body_len));
+			}
+
+		} else if (!g_ascii_strncasecmp (response, "BODY ", 5) ||
+			   !g_ascii_strncasecmp (response, "BODYSTRUCTURE ", 14)) {
+			response = strchr (response, ' ') + 1;
+			start = response;
+			imap_skip_list ((const char **) &response);
+			g_datalist_set_data_full (&data, "BODY", g_strndup (start, response - start), g_free);
+		} else if (!g_ascii_strncasecmp (response, "UID ", 4)) {
+			int len;
+			
+			len = strcspn (response + 4, " )");
+			uid = g_strndup (response + 4, len);
+			g_datalist_set_data_full (&data, "UID", uid, g_free);
+			response += 4 + len;
+		} else if (!g_ascii_strncasecmp (response, "INTERNALDATE ", 13)) {
+			int len;
+			
+			response += 13;
+			if (*response == '"') {
+				response++;
+				len = strcspn (response, "\"");
+				idate = g_strndup (response, len);
+				g_datalist_set_data_full (&data, "INTERNALDATE", idate, g_free);
+				response += len + 1;
+			}
+		} else {
+			g_warning ("Unexpected FETCH response from server: (%s", response);
+			break;
+		}
+	} while (response && *response != ')');
+	
+	if (!response || *response != ')') {
+		g_datalist_clear (&data);
+		return NULL;
+	}
+	
+	if (last && uid && body) {
+		CamelStream *stream;
+		
+		if (header && !cache_header) {
+			stream = camel_stream_mem_new_with_buffer (body, body_len);
+		} else {
+			char *pspec = ebodypartspec;
+			if (!strcmp (ebodypartspec, "HEADER"))
+				pspec = "";
+
+			CAMEL_IMAP_FOLDER_REC_LOCK (imap_folder, cache_lock);
+			stream = camel_imap_message_cache_insert (imap_folder->cache,
+								  uid, pspec,
+								  body, body_len, NULL);
+			CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
+			if (stream == NULL)
+				stream = camel_stream_mem_new_with_buffer (body, body_len);
+		}
+		
+		if (stream)
+			g_datalist_set_data_full (&data, "BODY_PART_STREAM", stream,
+						  (GDestroyNotify) camel_object_unref);
+	}
+	
+	return data;
+}
+
+static GData *
 parse_fetch_response (CamelImapFolder *imap_folder, char *response)
 {
 	GData *data = NULL;
Index: libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.h
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.h	(revision 1363)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-folder.h	(working copy)
@@ -80,7 +80,7 @@
 					   const char *uid,
 					   const char *section_text,
 					   gboolean cache_only,
-					   CamelException *ex);
+					   gboolean full, CamelException *ex);
 
 /* Standard Camel function */
 CamelType camel_imap_folder_get_type (void);
Index: libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-wrapper.c
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-wrapper.c	(revision 1363)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-wrapper.c	(working copy)
@@ -141,9 +141,10 @@
 	if (data_wrapper->offline) {
 		CamelStream *datastream;
 		
+		/* TNY TODO: partial message retrieval exception */
 		datastream = camel_imap_folder_fetch_data (
 			imap_wrapper->folder, imap_wrapper->uid,
-			imap_wrapper->part_spec, FALSE, NULL);
+			imap_wrapper->part_spec, FALSE, TRUE, NULL);
 		if (!datastream) {
 			CAMEL_IMAP_WRAPPER_UNLOCK (imap_wrapper, lock);
 #ifdef ENETUNREACH
@@ -188,8 +189,9 @@
 	imap_wrapper->part = part;
 
 	/* Try the cache. */
+	/* TNY TODO: Partial message retrieval exception */
 	stream = camel_imap_folder_fetch_data (imap_folder, uid, part_spec,
-					       TRUE, NULL);
+					       TRUE, TRUE, NULL);
 	if (stream) {
 		imap_wrapper_hydrate (imap_wrapper, stream);
 		camel_object_unref (stream);


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