Re: Partial message retrieval, the design



This version is much cleaner, doesn't crap the memory so much
(reallocating things that have already been allocated) and uses the
stream functionality of Camel in a much more optimised, less memory
using way.

I also tested this with Cyrus 2.1 (my usual is a braindeath Courier)
with IMAP4rev1 capabilities.

Note that Cyrus will not get the 1.HEADER right, it will be empty.

To get this right, in stead of 1.HEADER I should use the BODYSTRUCTURE
query and parse that.

This is overall a better technique, because it gives you a one-line
structure of the entire message. Including all info that you'll need.

For example in case 1.HEADER would fail, BODYSTRUCTURE wouldn't. Yet it
would give me all the info that I need.

imap_skip_list is an existing parser for this (for any skip-list, which
is what the BODYSTRUCTURE result looks like or is).

However, under the condition of the Camel MIME parser, this is currently
working. That's because that mime parser doesn't need the mime-part
headers underneath the boundary marker. I'm not sure if the actual
message, to be correct, needs these headers (I assume it does, else
can't the viewer know about the content-type nor about the encoding).

My plan is to add an enforced header to the cached message called

X-Tinymail-Retrieval-Mode: Partial or X-Tinymail-Retrieval-Mode: Full

.. and always write this one (so that if the on-imap message also had
it, it will be overwritten in the cache by the tinymail infrastructure,
so it will never confuse the code)

That header can then be used in later code to identify whether the
message is partially or fully retrieved. I can also simply count the
amount of parts in the CamelMessageInfo and compare it with the actual
parts in the final cached message. This would be more expensive to
perform, but probably less intrusive to implement.

More expensive because the code for returning the property would then
have to read the entire file. Whereas the code for when there would be a
header, would only have to stream the file until the header is found.

I can also write (or touch) a file like 1.PARTIAL or start keeping per
message information on the fs. I was thinking about someday cutting the
content info from the CamelMessageInfo (this means removing it from the
summary.mmap too).

The reason for that is that I think that when using the summary you only
have to know whether or not there are attachments. You don't need ALL
the mime-part information (right?). 

The thing is: Knowing whether or not there are attachments is a bit in a
bitfield.. whereas the content-info is at this moment a full 7% of all
of tinymail's memory.

But I wonder what people will need in the summary screen. Because, 7% is
a lot. That's something like 1 MB when displaying > 30,000 headers. For
just displaying those silly "this e-mail has attachments" icons.

While .. I already have a bitfield with one free bit where I could store
that yes/no flag.



ok, I'm making this E-mail too long.

Have fun looking at the partial message retrieval code :)


On Sat, 2007-01-06 at 18:33 +0100, Philip Van Hoof wrote:
> New version that fixes a few things
> 
> 
> On Sat, 2007-01-06 at 16:24 +0100, Philip Van Hoof wrote:
> > 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.
> > > 
> > > 
> > _______________________________________________
> > tinymail-devel-list mailing list
> > tinymail-devel-list gnome org
> > http://mail.gnome.org/mailman/listinfo/tinymail-devel-list
> _______________________________________________
> tinymail-devel-list mailing list
> tinymail-devel-list gnome org
> http://mail.gnome.org/mailman/listinfo/tinymail-devel-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-store.c
===================================================================
--- libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c	(revision 1363)
+++ libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c	(working copy)
@@ -672,6 +672,14 @@
 		force_imap4 = TRUE;
 	}
 	
+
+	/* Tinymail hack: always use IMAP4, not IMAP4rev1 (sorry) */
+
+	force_imap4 = TRUE;
+	store->braindamaged = TRUE;
+
+	/* end of hack :) */
+
 	g_free (buf);
 	
 	/* get the imap server capabilities */
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)
@@ -135,8 +135,11 @@
 
 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, const gchar *part_spec);
+
 #ifdef G_OS_WIN32
 /* The strtok() in Microsoft's C library is MT-safe (but still uses
  * only one buffer pointer per thread, but for the use of strtok_r()
@@ -1718,7 +1721,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 +1842,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 +1880,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 +1937,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 +1962,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 +1976,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 +2017,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 +2083,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 +2101,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 +2167,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 +2191,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));
 }
@@ -2682,19 +2690,23 @@
 	}
 }
 
+typedef struct {
+	const gchar *part_spec;
+	GData *data;
+} PartialRetrieveInfo;
 
 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;
-	char *found_uid;
-	int i;
+	GData *fetch_data=NULL;
+	char *found_uid = NULL, *boundary = NULL;
+	int i; gboolean free_fuid = FALSE;
 	
 	/* EXPUNGE responses have to modify the cache, which means
 	 * they have to grab the cache_lock while holding the
@@ -2708,7 +2720,8 @@
 	 */
 	CAMEL_IMAP_FOLDER_REC_LOCK (imap_folder, cache_lock);
 	stream = camel_imap_message_cache_get (imap_folder->cache, uid, section_text, ex);
-	if (!stream && (!strcmp (section_text, "HEADER") || !strcmp (section_text, "0"))) {
+	if (!stream && (!strcmp (section_text, "HEADER") || !strcmp (section_text, "0"))) 
+	{
 		camel_exception_clear (ex);
 		stream = camel_imap_message_cache_get (imap_folder->cache, uid, "", ex);
 	}
@@ -2731,44 +2744,249 @@
 	}
 	
 	camel_exception_clear (ex);
-	if (store->server_level < IMAP_LEVEL_IMAP4REV1 && !*section_text) {
+
+	if (full)
+	{
+	    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 
+	{
+
+ 	    /* Partial message retrieval feature 
+	       { HEADER boundary 1.HEADER 1 boundary } */
+
+	    int t = 0, boundary_len = 0;
+	    PartialRetrieveInfo infos[3] = {
+		{ "HEADER", NULL },
+		{ "1.HEADER", NULL }, 
+		{ "1", NULL },
+	    };
+
+/* TODO: It's better to use BODYSTRUCTURE to get 1.HEADER (Cyrus doesn't support 1.HEADER, Courier does)
+ * * 1 FETCH (BODYSTRUCTURE (("TEXT" "PLAIN" NIL NIL NIL "7BIT" 144 9 NIL NIL NIL)("IMAGE" "PNG" ("NAME" "drawing.png") NIL NIL "BASE64" 1005522 NIL ("ATTACHMENT" ("FILENAME" "drawing.png")) NIL) "MIXED" ("BOUNDARY" "=-7wX60rl1FvrW91cX0wLC") NIL NIL))
+ */
+	    for (t=0; t < 3; t++)
+	    {
 		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) {
+			   "UID FETCH %s BODY.PEEK[%s]", uid, infos[t].part_spec);
+
+		if (!response)
+		    goto errorh;
+
+		for (i = 0; i < response->untagged->len; i++)
+		    infos[t].data = parse_partial_fetch_response (imap_folder, response->untagged->pdata[0], infos[t].data, infos[t].part_spec);
+		camel_imap_response_free (store, response);
+
+		if (!boundary) {
+			boundary = g_strdup (g_datalist_get_data (&(infos[t].data), "BOUNDARY"));
+			if (boundary) boundary_len = strlen (boundary);
+		}
+		if (!found_uid) {
+			found_uid = g_strdup (g_datalist_get_data (&(infos[t].data), "UID"));
+			free_fuid = TRUE;
+		}
+	    }
+
+		
+	    CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
+
+	    stream = NULL;
+
+	    if (found_uid)
+	    {
+		CAMEL_IMAP_FOLDER_REC_LOCK (imap_folder, cache_lock);
+
+		for (t=0; t < 3; t++)
+		{
+		    gchar *data = g_datalist_get_data (&(infos[t].data), "PART_BODY");
+		    size_t len = GPOINTER_TO_INT (g_datalist_get_data (&(infos[t].data), "PART_LEN"));
+
+		    if (!stream)
+		    {
+			    stream = camel_imap_message_cache_insert (imap_folder->cache, 
+				found_uid, "", (len>0&&data)?data:"", len, NULL);
+			    if (stream == NULL)
+				    stream = camel_stream_mem_new_with_buffer ((len>0&&data)?data:"", len);
+			    if (stream == NULL) { /* wtf? */
+				int x=0;
+				for (x=0; x < 3; x++)
+				   g_datalist_clear (&(infos[x].data)); 
+				goto errorh; 
+			    }
+		    } else 
+		    {
+			    camel_seekable_stream_seek (CAMEL_SEEKABLE_STREAM (stream), 0, CAMEL_STREAM_END);
+
+			    if (boundary && !strcmp (infos[t].part_spec, "1.HEADER")) 
+			    {
+				    camel_stream_write (stream, "\n--", 3);
+				    camel_stream_write (stream, boundary, boundary_len);
+				    camel_stream_write (stream, "\n", 1);
+			    }
+			    camel_stream_write (stream, data, len);
+			    if (boundary && !strcmp (infos[t].part_spec, "1"))
+			    {
+				    camel_stream_write (stream, "\n--", 3);
+				    camel_stream_write (stream, boundary, boundary_len);
+				    camel_stream_write (stream, "\n", 1);
+			    }
+		    }
+
+		    g_datalist_clear (&(infos[t].data));
+		}
+
+		if (stream)
+			camel_stream_reset (stream);
+
 		CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
-		return NULL;
+	    } else {
+		int x=0;
+		for (x=0; x < 3; x++) 
+			g_datalist_clear (&(infos[x].data)); 
+		goto errorh;
+	    }
+
 	}
-	
-	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;
+
+errorh:
+
+	CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
+
+	if (!stream) camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+		    _("Could not find message body in FETCH response."));
+
+	if (free_fuid)
+		g_free (found_uid);
+
+	if (boundary)
+		g_free (boundary);
+
+	return stream;
+}
+
+
+
+static GData *
+parse_partial_fetch_response (CamelImapFolder *imap_folder, char *response, GData *data, const gchar *part_spec)
+{
+	if (*response != '(') 
+	{
+		long seq;
 		
-		g_datalist_clear (&fetch_data);
-		stream = NULL;
+		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;		
 	}
-	camel_imap_response_free (store, response);
-	CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
-	if (!stream) {
-		camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
-				      _("Could not find message body in FETCH response."));
-	} else {
-		camel_object_ref (CAMEL_OBJECT (stream));
-		g_datalist_clear (&fetch_data);
+
+	do {
+		/* Skip the initial '(' or the ' ' between elements */
+		response++;
+		
+		if (!g_ascii_strncasecmp (response, "FLAGS ", 6))			
+			response += 6;
+		else if (!g_ascii_strncasecmp (response, "RFC822.SIZE ", 12)) 
+		{ /* REMOVE THIS? */
+			unsigned long size;
+			response += 12;
+			size = strtoul (response, &response, 10);
+			g_datalist_set_data (&data, "RFC822.SIZE", GUINT_TO_POINTER (size));
+		} else if (!g_ascii_strncasecmp (response, "BODY[", 5) ||
+			   !g_ascii_strncasecmp (response, "RFC822 ", 7)) 
+		{
+			char *body = NULL, *p = NULL, *boundary = NULL;
+			size_t body_len = 0;
+
+			if (*response == 'B') 
+			{
+				response += 5;
+				p = strchr (response, ']');
+				if (!p || *(p + 1) != ' ')
+					break;
+				response = p + 2;
+			} else 
+				response += 7;
+			
+			body = imap_parse_nstring ((const char **) &response, &body_len);
+
+			if (!response) break;
+			if (!body) body = g_strdup ("");
+
+			if (!strcmp (part_spec, "HEADER"))
+			{
+			   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);
+					g_datalist_set_data_full (&data, "BOUNDARY", boundary, g_free);
+				}
+			   }
+			}
+
+		  	g_datalist_set_data_full (&data, "PART_BODY", body, g_free);
+			g_datalist_set_data (&data, "PART_LEN", GINT_TO_POINTER (body_len));
+
+
+		} else if (!g_ascii_strncasecmp (response, "UID ", 4)) 
+		{
+			int len = strcspn (response + 4, " )");
+			char *uid = g_strndup (response + 4, len);
+			g_datalist_set_data_full (&data, "UID", uid, g_free);
+			response += 4 + len;
+		} else {
+			g_warning ("Unexpected FETCH response from server: (%s", response);
+			break;
+		}
+	} while (response && *response != ')');
+	
+	if (!response || *response != ')') 
+	{
+		g_warning ("Unexpected FETCH response from server: (%s", response);
+		g_datalist_clear (&data);
+		return NULL;
 	}
 	
-	return stream;
+	return data;
 }
 
 static GData *
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);
Index: libtinymailui-gtk/tny-gtk-msg-view.c
===================================================================
--- libtinymailui-gtk/tny-gtk-msg-view.c	(revision 1363)
+++ libtinymailui-gtk/tny-gtk-msg-view.c	(working copy)
@@ -502,8 +502,12 @@
 		gboolean displayed = tny_gtk_msg_view_display_part (self, part, desc);
 
 		g_object_unref (G_OBJECT (part));
-		if (alternatives && displayed)
-			break;
+
+		/* TNY TODO: partial message retrieval: temporarily disabled 
+			alternatives detection */
+
+		/* if (alternatives && displayed)
+			break; */
 		tny_iterator_next (iterator);
 	}
 


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