Patch: Multipart support for reply/quote message



Here is another patch that is somewhat related to the ones for inclusion 
of messages. It updates the "quote message" logic so that it will do (at 
least nearly) the right thing for multi-part messages, i.e.:

1. Only parts marked as "inline" are quoted.
2. For multipart/mixed, the "best" part only is included. The part is 
selected according to RFC 1341. It changes the meaning of the "ignore 
html" preference a bit; when it's set, text/plain is preferred over 
text/html, but the HTML part will still be selected if a plain-text one 
isn't available.
3. An attempt is made to convert parts that can't be included directly to 
text/plain. Conversion is currently supported for HTML only. This part of 
the update has been submitted on 
http://bugzilla.gnome.org/show_bug.cgi?id=58915 earlier.

Note that the default (libmutt) content disposition had to be changed from 
DISPATTACH to DISPINLINE for 1) to behave correctly. Hopefully, that 
doesn't have any undesirable side effects. Also, the new version of 
preferred_part() included here should be called when displaying messages 
as well. I didn't include the necessary update in my patch because I've 
made a lot of unrelated changes to balsa-message.c

--
- Toralf
Index: libbalsa/mime.c
===================================================================
RCS file: /cvs/gnome/balsa/libbalsa/mime.c,v
retrieving revision 1.41
diff -u -b -r1.41 mime.c
--- libbalsa/mime.c	2001/09/23 18:04:48	1.41
+++ libbalsa/mime.c	2001/10/10 11:19:21
@@ -20,21 +20,216 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
  * 02111-1307, USA.
  */
+#include <stdlib.h>
 
 #include <string.h>
+#include <ctype.h>
 #include "config.h"
 
 #include "libbalsa.h"
 #include "mailbackend.h"
 
-/* FIXME: The content of this file could go to message.c */
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <libgnome/libgnome.h>
+
+
+
+
+#define HTML_TO_TEXT "lynx -force_html -dump file:%s > %s" /* *** FIXME: Set via ./configure */
 
-static GString *process_mime_multipart(LibBalsaMessage * message,
+
+static GString *
+process_mime_multipart(LibBalsaMessage * message,
                                        LibBalsaMessageBody * body,
-				       gchar * reply_prefix_str,
-				       gint llen, gboolean ignore_html,
-                                       gboolean flow);
+		       gchar * reply_prefix_str, gint llen,
+		       gboolean ignore_html, gboolean flow);
+
+/* FIXME: The content of this file could go to message.c */
+
+static gchar char_ref_char(const gchar *char_ref) 
+{
+    if(char_ref[1]=='#')
+	return (gchar )atoi(char_ref+1);
+    /* These are essential: */
+    else if(g_strcasecmp(char_ref, "lt")==0)
+	return '<';
+    else if(g_strcasecmp(char_ref, "gt")==0)
+	return '>';
+    else if(g_strcasecmp(char_ref, "amp")==0)
+	return '&';
+    else if(g_strcasecmp(char_ref, "nbsp")==0)
+	return ' ';
+    /* Some special characters I'm using a lot... */
+    else if(strcmp(char_ref, "aelig")==0)
+	return 'æ';
+    else if(strcmp(char_ref, "AElig")==0)
+	return 'Æ';
+    else if(strcmp(char_ref, "oslash")==0)
+	return 'ø';
+    else if(strcmp(char_ref, "Oslash")==0)
+	return 'Ø';
+    else if(strcmp(char_ref, "aring")==0)
+	return 'å';
+    else if(strcmp(char_ref, "Aring")==0)
+	return 'Å';
+    /* Return first character for the rest. Often makes sense because reference
+       is "<character base><info on accent etc.>" */
+    return char_ref[0];
+}
+
+static gchar *extract_tag(gchar **html, gchar end_sep)
+/* Pre : *html is at start of tag, i.e. points to '<' or similar
+   Post: *html points to position after end of tag. */
+{
+    gchar *end=strchr(*html, end_sep), *tag;
+
+    if(!end)			/* No '>', skip rest of string. */
+	end=*html+strlen(*html);
+    
+    tag=g_strndup(*html+1, end-*html-1);
+    *html=end+1;
+    
+    return tag;
+}
+
+static gchar *convert_html_internal(const gchar *html)
+/* Remarks: Fall-back used when no real HTML converter was configured, or 
+            call failed. */
+{
+    gchar *txt=calloc(strlen(html)+1, sizeof(gchar));
+
+    if(txt) {
+	gchar *c_html=(gchar *)html, *c_txt=txt;
+	gboolean skip=FALSE;
+
+	while(*c_html) {
+	    if(*c_html=='<') {
+		gchar *tag=extract_tag(&c_html, '>');
+		
+		if(g_strncasecmp(tag, "head", 4)==0) {
+		    skip=TRUE;
+		} else if(g_strncasecmp(tag, "body", 4)==0) {
+		    skip=FALSE;
+		} 
+
+		if(!skip) {
+		    if(g_strcasecmp(tag, "br")==0 || 
+		       g_strcasecmp(tag, "tr")==0) {
+			*c_txt++='\n';
+		    } else if(g_strcasecmp(tag, "p")==0 ||
+			      (tolower(tag[0])=='h' && isdigit(tag[1]))) {
+			*c_txt++='\n';
+			*c_txt++='\n';
+		    } else if(g_strcasecmp(tag, "li")==0) {
+			*c_txt++='\n';
+			*c_txt++='*';
+			*c_txt++='\t';
+		    } else if(g_strcasecmp(tag, "td")==0 || 
+			      g_strcasecmp(tag, "th")==0) {
+		     /* Note: Can't do a lot more than starting a new
+			      paragraph (more or less), since we are not
+			      able to interleave text from different cells. */
+			*c_txt++='\n';
+			*c_txt++='\n';
+			*c_txt++='\t';
+		    } else {
+			gchar *ref=strstr(tag, "href=");
+		    
+			if(!ref) { /* *** Want 'strcasestr' */
+			    ref=strstr(tag, "HREF=");
+			}
+		    
+			if(ref) {
+			    *c_txt++='[';
+			    ref+=5;
+			    
+			    while(*ref && *ref!=' ')
+				*c_txt++=*ref++;
+			    *c_txt++=']';
+			}
+		    }
+		}
+		g_free(tag);
+	    } else if(skip) {
+		c_html++;
+	    } else if(*c_html=='&') {
+		gchar *char_ref=extract_tag(&c_html, ';');
+
+		*c_txt++=char_ref_char(char_ref);
+		g_free(char_ref);
+	    } else if(*c_html=='\n' || *c_html==' ' || *c_html=='\t') {
+		gchar prev=*(c_txt-1);
+
+		c_html++;
+		
+		if(c_txt!=txt && prev!=' ' && prev!='\n' && prev!='\t')
+		    *c_txt++=' ';
+	    } else {
+		*c_txt++=*c_html++;
+	    }
+	}
+    }
+    return txt;
+}
 
+
+
+gchar *convert_html(const gchar *html)
+{
+#ifdef HTML_TO_TEXT
+    gchar html_file[PATH_MAX + 1], txt_file[PATH_MAX + 1];
+    gchar *txt=NULL;
+    FILE *fp;
+
+    libbalsa_lock_mutt();
+    mutt_mktemp(html_file);
+    mutt_mktemp(txt_file);
+    libbalsa_unlock_mutt();
+
+    fp=safe_fopen(html_file, "w");
+    
+    if(fp) {
+	gchar cmd[PATH_MAX + 1];
+	pid_t cmd_pid;
+
+	fprintf(fp, "%s", html);
+	fclose(fp);
+
+	snprintf(cmd, sizeof(cmd), HTML_TO_TEXT, html_file, txt_file);
+
+	/* Note: Should probably find gnome_ alternative to fork() + exec(),
+	         but this is at least better than system() (gnome_execute_shell
+		 is not what we want as it will start process in the background 
+	*/
+
+	if((cmd_pid=fork())==0) {
+	    gchar *shell=gnome_util_user_shell();
+
+	    execl(shell, shell, "-c", cmd, NULL);
+	    g_free(shell);	/* In case exec fails */
+	    _exit(1);
+	} else if(cmd_pid>0) {
+	    waitpid(cmd_pid, NULL, 0);
+	    fp=fopen(txt_file, "r");
+	    if(fp) {
+		libbalsa_readfile(fp, &txt);
+		fclose(fp);
+	    }
+	}
+    }
+    unlink(txt_file);
+    unlink(html_file);
+
+    if(txt)
+	return txt;
+
+#endif
+    return convert_html_internal(html);
+}
+
+
+
 /* process_mime_part:
    returns string representation of given message part.
    NOTE: may return NULL(!).
@@ -48,7 +243,10 @@
     size_t alloced;
     gchar *res = NULL;
     GString *reply = NULL;
+    gchar *content_type = libbalsa_message_body_get_content_type(body);
+    gboolean ishtml=(g_strcasecmp(content_type, "text/html") == 0);
 
+    
     switch (libbalsa_message_body_type(body)) {
     case LIBBALSA_MESSAGE_BODY_TYPE_OTHER:
     case LIBBALSA_MESSAGE_BODY_TYPE_AUDIO:
@@ -78,6 +276,16 @@
 	if (!res)
 	    break;
 
+	if(ishtml) {
+	    gchar *res_ascii=convert_html(res);
+	    
+	    if(res_ascii) {
+		g_free(res);
+		res=res_ascii;
+	    }
+	}
+	    
+
 	if (llen > 0) {
             if (flow && libbalsa_flowed_rfc2646(body)) {
                 /* we're making a `format=flowed' message, and the
@@ -126,6 +334,8 @@
 	g_free(res);
 	break;
     }
+    g_free(content_type);	
+
     return reply;
 }
 
@@ -137,10 +347,22 @@
 {
     LibBalsaMessageBody *part;
     GString *res = NULL, *s;
+    gchar *content_type = libbalsa_message_body_get_content_type(body);
+
+    fprintf(stderr, "process_mime_multipart\n");
 
+    if(g_strcasecmp(content_type, "multipart/alternative")==0) {
+	part = preferred_part(body->parts, ignore_html);
+	if(part)
+	    res = process_mime_part(message, part, reply_prefix_str, llen,
+				    ignore_html, flow);
+    } else {
     for (part = body->parts; part; part = part->next) {
+	    if(part->mutt_body->disposition==DISPINLINE)
 	s = process_mime_part(message, part, reply_prefix_str, llen,
                           ignore_html, flow);
+	    else
+		s = NULL;
 	if (!s)
 	    continue;
 	if (res) {
@@ -149,6 +371,9 @@
 	} else
 	    res = s;
     }
+    }
+
+    g_free(content_type);
     return res;
 }
 
@@ -173,4 +398,37 @@
     }
 
     return reply;
+}
+
+LibBalsaMessageBody*
+preferred_part(LibBalsaMessageBody *parts, gboolean ignore_html)
+/* Remarks: Try to find LAST suitable part, which should be considered the
+            best according to RFC 1341 */
+{
+    /* TODO: - Look for additional specific types, and have more flags 
+               to control their selection?
+             - Let user preferences or previous selections override order 
+	       (e.g. choose text/html part even when text/plain is included
+	       after it)? */
+    LibBalsaMessageBody *body;
+    gchar *content_type;
+    LibBalsaMessageBody *best_part=NULL, *last_part=NULL;
+
+    for(body=parts; body; body=body->next) {
+	content_type = libbalsa_message_body_get_content_type(body);
+	
+	if(g_strcasecmp(content_type, "text/plain")==0 ||
+	   !ignore_html && g_strcasecmp(content_type, "text/html")==0 ||
+	   !best_part && g_strncasecmp(content_type, "text/", 5)==0)
+	    best_part=body;
+	   
+	last_part=body;
+
+	g_free(content_type);
+    }
+    
+    if(!best_part)
+	best_part=last_part;
+    
+    return best_part;
 }
Index: libbalsa/mime.h
===================================================================
RCS file: /cvs/gnome/balsa/libbalsa/mime.h,v
retrieving revision 1.16
diff -u -b -r1.16 mime.h
--- libbalsa/mime.h	2001/09/23 18:04:48	1.16
+++ libbalsa/mime.h	2001/10/10 11:19:21
@@ -31,8 +31,14 @@
 			   LibBalsaMessageBody * body,
 			   gchar * reply_prefix_str, gint llen,
 			   gboolean ignore_html, gboolean flow);
+
 GString *content2reply(LibBalsaMessage * message,
 		       gchar * reply_prefix_str, gint llen,
 		       gboolean ignore_html, gboolean flow);	/* arp */
+
+gchar *convert_html(const gchar *html);
+
+LibBalsaMessageBody*
+preferred_part(LibBalsaMessageBody *parts, gboolean ignore_html);
 
 #endif				/* __LIBBALSA_MIME_H__ */
Index: libmutt/lib.c
===================================================================
RCS file: /cvs/gnome/balsa/libmutt/lib.c,v
retrieving revision 1.16
diff -u -b -r1.16 lib.c
--- libmutt/lib.c	2001/05/26 09:13:21	1.16
+++ libmutt/lib.c	2001/10/10 11:19:22
@@ -40,7 +40,7 @@
 {
   BODY *p = (BODY *) safe_calloc (1, sizeof (BODY));
     
-  p->disposition = DISPATTACH;
+  p->disposition = DISPINLINE;
   p->use_disp = 1;
   return (p);
 }


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