Re: [Evolution-hackers] New implementation of camel_imap_folder_fetch_data



So, after the lock fixes and a few other bugfixes, this is a new
implementation.

I left the partial-retrieval thingy in place. Just remove the "full"
parameter from the function and comment the else part of the "if (full)
{ } else { }" thingy to use it in a normal Camel.


static void 
handle_freeup (CamelImapStore *store, gint nread, CamelException *ex)
{
	if (nread <= 0) 
	{
		if (errno == EINTR)
			camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Operation cancelled"));
		else
			camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
					      _("Server unexpectedly disconnected: %s"),
					      g_strerror (errno));
		
		camel_service_disconnect (CAMEL_SERVICE (store), FALSE, NULL);
	}
}

CamelStream *
camel_imap_folder_fetch_data (CamelImapFolder *imap_folder, const char *uid,
			      const char *section_text, gboolean cache_only,
			      gboolean full, CamelException *ex)
{
	CamelFolder *folder = CAMEL_FOLDER (imap_folder);
	CamelImapStore *store = CAMEL_IMAP_STORE (folder->parent_store);
	CamelStream *stream;
	
	/* EXPUNGE responses have to modify the cache, which means
	 * they have to grab the cache_lock while holding the
	 * connect_lock.

	 * Because getting the service lock may cause MUCH unecessary
	 * delay when we already have the data locally, we do the
	 * locking separately.  This could cause a race
	 * getting the same data from the cache, but that is only
	 * an inefficiency, and bad luck.
	 */

	CAMEL_IMAP_FOLDER_REC_LOCK (imap_folder, cache_lock);
	stream = camel_imap_message_cache_get (imap_folder->cache, uid, section_text, ex);

	/* TNY TODO: if (full) Detect retrieval status (if partial refetch) */

	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);
	}
	CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
	
	if (stream || cache_only)
		return stream;

	camel_exception_clear(ex);

	CAMEL_IMAP_FOLDER_REC_LOCK (imap_folder, cache_lock);

	if (!camel_imap_store_connected(store, ex)) {
		camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
				     _("This message is not currently available"));
		CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
		return NULL;
	}
	
	camel_exception_clear (ex);

        stream = camel_imap_message_cache_insert (imap_folder->cache, 
			uid, full?section_text:"", "", 0, NULL);
	if (stream == NULL)
		stream = camel_stream_mem_new ();

	if (!stream)
		goto errorhander;

	if (full)
	{
	    gboolean first = TRUE, err=FALSE;
	    gchar line[512];
	    guint linenum = 0;
	    ssize_t nread; 
	    CamelStreamBuffer *server_stream = CAMEL_STREAM_BUFFER (store->istream);
	    gchar *tag;
	    guint taglen;
	    gboolean isnextdone = FALSE;

	    if (store->server_level < IMAP_LEVEL_IMAP4REV1 && !*section_text)
		    camel_imap_command_start (store, folder, ex,
			    "UID FETCH %s RFC822.PEEK",uid);
	    else
		    camel_imap_command_start (store, folder, ex,
			    "UID FETCH %s BODY.PEEK[%s]",uid, section_text);

	    tag = g_strdup_printf ("%c%.5u", store->tag_prefix, store->command-1);
	    taglen = strlen (tag);
	    store->command++;

	    while (nread = camel_stream_buffer_gets (server_stream, line, 512) > 0)
	    {

		    /* It might be the line before the last line */
		    if (line[0] == ')' && (line[1] == '\n' || (line[1] == '\r' && line[2] == '\n')))
		    {
			    isnextdone = TRUE;
			    continue;
		    }

		    /* It's the first line */
		    if (linenum == 0 && (line [0] != '*' || line[1] != ' '))
		    {
			    err=TRUE;
			    break;
		    } else if (linenum == 0) { linenum++; continue; }

		    /* It's the last line */
		    if (!strncmp (line, tag, taglen))
			    break;

		    camel_seekable_stream_seek (CAMEL_SEEKABLE_STREAM (stream), 0, CAMEL_STREAM_END);

		    if (isnextdone)
		    {
			    camel_stream_write (stream, ")\n", 2);
			    isnextdone = FALSE;
		    }

		    camel_stream_write (stream, line, strlen (line));

		    linenum++;
	    }

	    CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);

	    if (nread <= 0) 
		handle_freeup (store, nread, ex);
		
	    g_free (tag);

	    if (err)
		goto errorhander;

	    camel_stream_reset (stream);
		
	} else 
	{

	    /* Partial message retrieval feature gets the message like this: 
	       { HEADER boundary 1.HEADER 1 boundary } */

	    char *boundary = NULL;
	    int t = 0, boundary_len = 0;
	    const gchar *infos[2] = { "HEADER", /*"1.HEADER",*/ "1" };


	    for (t=0; t < 2; t++)
	    {
		gboolean first = TRUE, err=FALSE;
		gchar line[512];
		guint linenum = 0;
		ssize_t nread; 
		CamelStreamBuffer *server_stream = CAMEL_STREAM_BUFFER (store->istream);
		gchar *tag;
		guint taglen;
		gboolean isnextdone = FALSE;

		camel_imap_command_start (store, folder, ex,
			"UID FETCH %s BODY.PEEK[%s]", uid, infos[t]);

		tag = g_strdup_printf ("%c%.5u", store->tag_prefix, store->command-1);
		taglen = strlen (tag);

		store->command++;

		while (nread = camel_stream_buffer_gets (server_stream, line, 512) > 0)
		{

			/* It might be the line before the last line */
			if (line[0] == ')' && (line[1] == '\n' || (line[1] == '\r' && line[2] == '\n')))
			{
				isnextdone = TRUE;
				continue;
			}

			/* It's the first line */
			if (linenum == 0 && (line [0] != '*' || line[1] != ' '))
			{
				err=TRUE;
				break;
			} else if (linenum == 0) { linenum++; continue; }

			/* It's the last line */
			if (!strncmp (line, tag, taglen))
			{
				if ((t == 0 || t == 2) && boundary_len > 0)
				{
					camel_seekable_stream_seek (CAMEL_SEEKABLE_STREAM (stream), 0, CAMEL_STREAM_END);
					camel_stream_write (stream, "\n--", 3);
					camel_stream_write (stream, boundary, boundary_len);
					camel_stream_write (stream, "\n", 1);
				}
				break;
			}

			if (t == 0 && boundary_len == 0 && !boundary)
			{
			   CamelContentType *ct = NULL;
			   const char *bound=NULL;
			   char *pstr = (char*)strcasestr (line, "Content-Type:");

			   /* If it's the Content-Type line (TODO: use BODYSTRUCTURE for this) */

			   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_len = strlen (bound);
					if (boundary_len > 0) 
						boundary = g_strdup (bound);
					else boundary_len = 0;
				}
			   }
			}

			camel_seekable_stream_seek (CAMEL_SEEKABLE_STREAM (stream), 0, CAMEL_STREAM_END);

			if (isnextdone)
			{
				camel_stream_write (stream, ")\n", 2);
				isnextdone = FALSE;
			}

			camel_stream_write (stream, line, strlen (line));

			linenum++;
		}

		CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);

	        if (nread <= 0) 
		    handle_freeup (store, nread, ex);

		g_free (tag);

		if (err)
			goto errorhander;
	    
	    }

	    if (boundary_len > 0)
	   	 g_free (boundary);

	    camel_stream_reset (stream);		
	}

	CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);

	return stream;

errorhander:

	CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);

	camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
		    _("Could not find message body in FETCH response."));

	if (stream)
		camel_object_unref (CAMEL_OBJECT (stream));

	return NULL;
}


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







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