The previous patch didn't work if the webcit/ prefix wasn't used.

This patch covers the case when webcit/ prefix is not used.

Sam
Index: debian/control
===================================================================
--- debian/control	(revision 6707)
+++ debian/control	(working copy)
@@ -3,7 +3,7 @@
 Priority: optional
 Maintainer: Wilfried Goesgens <[EMAIL PROTECTED]>
 Build-Depends: debhelper (>= 4), po-debconf, libical-dev (>=0.30), gettext, locales,
- libcitadel-dev
+ libcitadel-dev, libtidy-dev, libxml-dev
 Standards-Version: 3.7.2
 
 Package: citadel-webcit
Index: webserver.c
===================================================================
--- webserver.c	(revision 6707)
+++ webserver.c	(working copy)
@@ -610,6 +610,11 @@
 		unlink(pid_file);
 	}
 	ShutDownLibCitadel ();
+#ifdef HAVE_XSLT
+	xsltCleanupGlobals();
+	xmlCleanupParser();
+	xmlCleanupThreads();
+#endif
 	exit(WEXITSTATUS(status));
 }
 
@@ -870,6 +875,11 @@
 		perror("chdir");
 	}
 	LoadIconDir(static_icon_dir);
+#ifdef HAVE_XSLT
+	xsltInit();
+	xsltRegisterAllExtras();
+	exsltRegisterAll();
+#endif
 	InitTemplateCache();
 
 	initialise_modules();
Index: configure.ac
===================================================================
--- configure.ac	(revision 6707)
+++ configure.ac	(working copy)
@@ -191,10 +191,42 @@
 	]
 )
 
+dnl Checks for libtidy
+PKG_CHECK_MODULES([TIDY], [libtidy], [
+	ok_libtidy=yes
+        LIBS="$TIDY_LIBS $LIBS"
+        CFLAGS="$TIDY_CFLAGS $LIBS"
+        AC_DEFINE(HAVE_TIDY,[],[whether we have libtidy])
+],[ dnl find libtidy the hard way
+	for location in /usr /usr/local/
+	do
+		for suffix in include include/tidy
+		do
+			test -r "$location/$suffix/tidy.h" && break
+		done
+		test -r "$location/$suffix/tidy.h" && break
+	done
+	if test -r "$location/$suffix/tidy.h"
+	then
+		LIBS="-ltidy $LIBS"
+		CFLAGS="-I$location/$suffix"
+        	AC_DEFINE(HAVE_TIDY,[],[whether we have libtidy])
+	fi
+]) 
 
+
+dnl Checks for libxslt AND libexslt (same package)
+PKG_CHECK_MODULES([XSLT], [libxslt >= 1.1 libexslt >= 0.8 libxml >= 1.8.4], [
+	ok_libxslt=yes
+        LIBS="$XSLT_LIBS $LIBS"
+        CFLAGS="$XSLT_CFLAGS $CFLAGS"
+        AC_DEFINE(HAVE_XSLT,[],[whether we have libxslt])
+])
+
 dnl Checks for the libical iCalendar library.
+dnl icalcomponent_as_ical_string_r,
 AC_CHECK_HEADER(libical/ical.h,
-	[AC_CHECK_LIB(ical, icalcomponent_as_ical_string_r,
+	[AC_CHECK_LIB(ical, icalcomponent_new,
 		[
 			LIBS="-lical $LIBS"
 		],
@@ -209,6 +241,10 @@
 )
 
 
+if test "x$ok_zlib" = xyes ; then
+        LIBS="-lz $LIBS"
+        AC_DEFINE(HAVE_ZLIB,[],[whether we have zlib])
+fi
 
 # The big search for OpenSSL
 if test "$with_ssl" != "no"; then
Index: tiny_mce/themes/advanced/image.htm
===================================================================
--- tiny_mce/themes/advanced/image.htm	(revision 6707)
+++ tiny_mce/themes/advanced/image.htm	(working copy)
@@ -5,6 +5,7 @@
 	<script language="javascript" type="text/javascript" src="../../utils/mctabs.js"></script>
 	<script language="javascript" type="text/javascript" src="../../utils/form_utils.js"></script>
 	<script language="javascript" type="text/javascript" src="jscripts/image.js"></script>
+	<script language="javascript" type="text/javascript" src="/webcit/postpart/image/images.js"></script>
 	<base target="_self" />
 </head>
 <body id="image" onload="tinyMCEPopup.executeOnLoad('init();');" style="display: none">
Index: html2html.c
===================================================================
--- html2html.c	(revision 6707)
+++ html2html.c	(working copy)
@@ -9,8 +9,25 @@
 /[EMAIL PROTECTED]/
 #include "webcit.h"
 #include "webserver.h"
+#ifdef HAVE_TIDY
+#include <tidy.h>
+#include <buffio.h>
+#endif
+#ifdef HAVE_XSLT
+#include <libxml/xmlmemory.h>
+#include <libxml/debugXML.h>
+#include <libxml/HTMLtree.h>
+#include <libxml/xmlIO.h>
+#include <libxml/xinclude.h>
+#include <libxml/catalog.h>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+#include "libexslt/exslt.h"
+#include <libexslt/exsltconfig.h>
+#endif
 
-
 /**
  * \brief	Strip surrounding single or double quotes from a string.
  *
@@ -73,18 +90,8 @@
 	}
 }
 
-
-
-/**
- * \brief Sanitize and enhance an HTML message for display.
- *        Also convert weird character sets to UTF-8 if necessary.
- *        Also fixup img src="cid:..." type inline images to fetch the image
- *
- * \param supplied_charset the input charset as declared in the MIME headers
- */
-void output_html(char *supplied_charset, int treat_as_wiki, int msgnum) {
-	char buf[SIZ];
-	char *msg;
+#ifndef HAVE_XSLT
+StrBuf * clean_html(char* msg, int treat_as_wiki, int msgnum) {
 	char *ptr;
 	char *msgstart;
 	char *msgend;
@@ -109,160 +116,6 @@
 	char *osav;                   /**< Saved pointer to output buffer       */
 #endif
 
-	safestrncpy(charset, supplied_charset, sizeof charset);
-	msg = strdup("");
-	sprintf(new_window, "<a target=\"%s\" href=", TARGET);
-
-	while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-		line_length = strlen(buf);
-		buffer_length = content_length + line_length + 2;
-		ptr = realloc(msg, buffer_length);
-		if (ptr == NULL) {
-			wprintf("<b>");
-			wprintf(_("realloc() error! couldn't get %d bytes: %s"),
-				buffer_length + 1,
-				strerror(errno));
-			wprintf("</b><br /><br />\n");
-			while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-				/** flush */
-			}
-			free(msg);
-			return;
-		}
-		msg = ptr;
-		strcpy(&msg[content_length], buf);
-		content_length += line_length;
-		strcpy(&msg[content_length], "\n");
-		content_length += 1;
-	}
-
-	/** Do a first pass to isolate the message body */
-	ptr = msg + 1;
-	msgstart = msg;
-	msgend = &msg[content_length];
-
-	while (ptr < msgend) {
-
-		/** Advance to next tag */
-		ptr = strchr(ptr, '<');
-		if ((ptr == NULL) || (ptr >= msgend)) break;
-		++ptr;
-		if ((ptr == NULL) || (ptr >= msgend)) break;
-
-		/**
-		 *  Look for META tags.  Some messages (particularly in
-		 *  Asian locales) illegally declare a message's character
-		 *  set in the HTML instead of in the MIME headers.  This
-		 *  is wrong but we have to work around it anyway.
-		 */
-		if (!strncasecmp(ptr, "META", 4)) {
-
-			char *meta_start;
-			char *meta_end;
-			int meta_length;
-			char *meta;
-			char *meta_http_equiv;
-			char *meta_content;
-			char *spaceptr;
-
-			meta_start = &ptr[4];
-			meta_end = strchr(ptr, '>');
-			if ((meta_end != NULL) && (meta_end <= msgend)) {
-				meta_length = meta_end - meta_start + 1;
-				meta = malloc(meta_length + 1);
-				safestrncpy(meta, meta_start, meta_length);
-				meta[meta_length] = 0;
-				striplt(meta);
-				if (!strncasecmp(meta, "HTTP-EQUIV=", 11)) {
-					meta_http_equiv = strdup(&meta[11]);
-					spaceptr = strchr(meta_http_equiv, ' ');
-					if (spaceptr != NULL) {
-						*spaceptr = 0;
-						meta_content = strdup(++spaceptr);
-						if (!strncasecmp(meta_content, "content=", 8)) {
-							strcpy(meta_content, &meta_content[8]);
-							stripquotes(meta_http_equiv);
-							stripquotes(meta_content);
-							extract_charset_from_meta(charset,
-								meta_http_equiv, meta_content);
-						}
-						free(meta_content);
-					}
-					free(meta_http_equiv);
-				}
-				free(meta);
-			}
-		}
-
-		/**
-		 * Any of these tags cause everything up to and including
-		 * the tag to be removed.
-		 */	
-		if ( (!strncasecmp(ptr, "HTML", 4))
-		   ||(!strncasecmp(ptr, "HEAD", 4))
-		   ||(!strncasecmp(ptr, "/HEAD", 5))
-		   ||(!strncasecmp(ptr, "BODY", 4)) ) {
-			ptr = strchr(ptr, '>');
-			if ((ptr == NULL) || (ptr >= msgend)) break;
-			++ptr;
-			if ((ptr == NULL) || (ptr >= msgend)) break;
-			msgstart = ptr;
-		}
-
-		/**
-		 * Any of these tags cause everything including and following
-		 * the tag to be removed.
-		 */
-		if ( (!strncasecmp(ptr, "/HTML", 5))
-		   ||(!strncasecmp(ptr, "/BODY", 5)) ) {
-			--ptr;
-			msgend = ptr;
-			strcpy(ptr, "");
-			
-		}
-
-		++ptr;
-	}
-	if (msgstart > msg) {
-		strcpy(msg, msgstart);
-	}
-
-	/** Convert foreign character sets to UTF-8 if necessary. */
-#ifdef HAVE_ICONV
-	if ( (strcasecmp(charset, "us-ascii"))
-	   && (strcasecmp(charset, "UTF-8"))
-	   && (strcasecmp(charset, ""))
-	) {
-		lprintf(9, "Converting %s to UTF-8\n", charset);
-		ic = ctdl_iconv_open("UTF-8", charset);
-		if (ic == (iconv_t)(-1) ) {
-			lprintf(5, "%s:%d iconv_open() failed: %s\n",
-				__FILE__, __LINE__, strerror(errno));
-		}
-	}
-	if (ic != (iconv_t)(-1) ) {
-		ibuf = msg;
-		ibuflen = content_length;
-		obuflen = content_length + (content_length / 2) ;
-		obuf = (char *) malloc(obuflen);
-		osav = obuf;
-		iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
-		content_length = content_length + (content_length / 2) - obuflen;
-		osav[content_length] = 0;
-		free(msg);
-		msg = osav;
-		iconv_close(ic);
-	}
-#endif
-
-	/**
-	 *	At this point, the message has been stripped down to
-	 *	only the content inside the <BODY></BODY> tags, and has
-	 *	been converted to UTF-8 if it was originally in a foreign
-	 *	character set.  The text is also guaranteed to be null
-	 *	terminated now.
-	 */
-
 	/** Now go through the message, parsing tags as necessary. */
 	converted_msg = NewStrBufPlain(NULL, content_length + 8192);
 	if (converted_msg == NULL) {
@@ -443,12 +296,294 @@
 		if (!strncasecmp(ptr, "</A>", 3)) --alevel;
 	}
 
+	return converted_msg;
+BAIL:
+	FreeStrBuf(&converted_msg);
+	return NULL;
+}
+
+#else
+
+StrBuf *clean_html(const char* input, int treat_as_wiki, int msgnum) {
+  xsltStylesheetPtr cur = NULL;
+  xmlDocPtr doc=NULL;
+  xmlDocPtr res = NULL;
+  const char *params[16 + 1];
+  char *_output=NULL;
+  StrBuf *output=NULL;
+  int len=0;
+  char _msgnum[10];
+
+  xmlSubstituteEntitiesDefault(1);
+  xmlLoadExtDtdDefaultValue = 0;
+#ifdef HAVE_TIDY
+  {
+    uint len=0;
+    char* xhtml=NULL;
+    TidyDoc tdoc = tidyCreate();                     // Initialize "document"
+
+    if (tdoc) {
+      /* my favourite generate xhtml spell is:
+       * tidy -f /dev/stderr -n --output-xhtml y --indent "auto" 
+       * --wrap "900" -q --force-output yes -asxhtml --add-xml-decl yes "$@"
+       */
+      tidyOptSetBool(tdoc, TidyXhtmlOut, yes); // --output-xhtml y | -asxhtml
+      tidyOptSetBool(tdoc, TidyForceOutput, yes); //--force-output yes
+      tidyOptSetInt(tdoc, TidyIndentContent, TidyAutoState); // --indent "auto"
+      tidyOptSetBool(tdoc, TidyXmlDecl, yes); //  --add-xml-decl yes
+      tidyOptSetBool(tdoc, TidyQuiet, yes); // -q
+      tidyOptSetBool(tdoc, TidyNumEntities, yes); // -n
+      tidySetInCharEncoding(tdoc, "utf8");
+      tidySetOutCharEncoding(tdoc, "utf8");
+      //--wrap "900" -f /dev/stderr -n
+
+      tidyParseString(tdoc, input);
+      tidyCleanAndRepair(tdoc);
+      tidySaveString(tdoc, NULL, &len);
+      xhtml=malloc(len+1);
+      if (xhtml) {
+        tidySaveString(tdoc, xhtml, &len);
+        tidyRelease(tdoc);
+        doc = htmlParseDoc((const xmlChar*)xhtml,"utf8");
+        free(xhtml);
+      } else {
+        tidyRelease(tdoc);
+      }
+    }
+  }
+#endif
+  /* also a fallback if tidy fails */
+  if (! doc) doc = htmlParseDoc((const xmlChar*)input, "utf8");
+  if (! doc) goto done;
+
+  /* Setup xslt arguments msgnum and treat_as_wiki */
+  snprintf(_msgnum, sizeof(_msgnum) -1, "%d", msgnum);
+  params[0]="msgnum";
+  params[1]=_msgnum;
+  params[2]="treat_as_wiki";
+  params[3]=treat_as_wiki?"yes":"no";
+  params[4]=NULL;
+
+  /* parse template. This should probably be done at startup */
+  cur = xsltParseStylesheetFile((const xmlChar *)WWWDIR "/static/clean_html.xslt");
+
+  res = xsltApplyStylesheet(cur, doc, params);
+  if (res) {
+
+    xsltSaveResultToString(&_output, &len, res, cur);
+  }
+
+  output=_NewConstStrBuf(_output, len);
+  if (! output) free(_output);
+
+done:
+  if (cur) xsltFreeStylesheet(cur);
+  if (res) xmlFreeDoc(res);
+  if (doc) xmlFreeDoc(doc);
+
+  return output;
+}
+
+#endif
+
+/**
+ * \brief Sanitize and enhance an HTML message for display.
+ *        Also convert weird character sets to UTF-8 if necessary.
+ *        Also fixup img src="cid:..." type inline images to fetch the image
+ *
+ * \param supplied_charset the input charset as declared in the MIME headers
+ */
+void output_html(char *supplied_charset, int treat_as_wiki, int msgnum) {
+	char buf[SIZ];
+	char *msg;
+	char *ptr;
+	char *msgstart;
+	char *msgend;
+	StrBuf *converted_msg;
+	int buffer_length = 1;
+	int line_length = 0;
+	int content_length = 0;
+	char new_window[SIZ];
+	int brak = 0;
+	int alevel = 0;
+	int scriptlevel = 0;
+	int script_start_pos = (-1);
+	int i;
+	int linklen;
+	char charset[128];
+#ifdef HAVE_ICONV
+	iconv_t ic = (iconv_t)(-1) ;
+	char *ibuf;                   /**< Buffer of characters to be converted */
+	char *obuf;                   /**< Buffer for converted characters      */
+	size_t ibuflen;               /**< Length of input buffer               */
+	size_t obuflen;               /**< Length of output buffer              */
+	char *osav;                   /**< Saved pointer to output buffer       */
+#endif
+
+	safestrncpy(charset, supplied_charset, sizeof charset);
+	msg = strdup("");
+	sprintf(new_window, "<a target=\"%s\" href=", TARGET);
+
+	while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+		line_length = strlen(buf);
+		buffer_length = content_length + line_length + 2;
+		ptr = realloc(msg, buffer_length);
+		if (ptr == NULL) {
+			wprintf("<b>");
+			wprintf(_("realloc() error! couldn't get %d bytes: %s"),
+				buffer_length + 1,
+				strerror(errno));
+			wprintf("</b><br /><br />\n");
+			while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+				/** flush */
+			}
+			free(msg);
+			return;
+		}
+		msg = ptr;
+		strcpy(&msg[content_length], buf);
+		content_length += line_length;
+		strcpy(&msg[content_length], "\n");
+		content_length += 1;
+	}
+
+	/** Do a first pass to isolate the message body */
+	ptr = msg + 1;
+	msgstart = msg;
+	msgend = &msg[content_length];
+
+	while (ptr < msgend) {
+
+		/** Advance to next tag */
+		ptr = strchr(ptr, '<');
+		if ((ptr == NULL) || (ptr >= msgend)) break;
+		++ptr;
+		if ((ptr == NULL) || (ptr >= msgend)) break;
+
+		/**
+		 *  Look for META tags.  Some messages (particularly in
+		 *  Asian locales) illegally declare a message's character
+		 *  set in the HTML instead of in the MIME headers.  This
+		 *  is wrong but we have to work around it anyway.
+		 */
+		if (!strncasecmp(ptr, "META", 4)) {
+
+			char *meta_start;
+			char *meta_end;
+			int meta_length;
+			char *meta;
+			char *meta_http_equiv;
+			char *meta_content;
+			char *spaceptr;
+
+			meta_start = &ptr[4];
+			meta_end = strchr(ptr, '>');
+			if ((meta_end != NULL) && (meta_end <= msgend)) {
+				meta_length = meta_end - meta_start + 1;
+				meta = malloc(meta_length + 1);
+				safestrncpy(meta, meta_start, meta_length);
+				meta[meta_length] = 0;
+				striplt(meta);
+				if (!strncasecmp(meta, "HTTP-EQUIV=", 11)) {
+					meta_http_equiv = strdup(&meta[11]);
+					spaceptr = strchr(meta_http_equiv, ' ');
+					if (spaceptr != NULL) {
+						*spaceptr = 0;
+						meta_content = strdup(++spaceptr);
+						if (!strncasecmp(meta_content, "content=", 8)) {
+							strcpy(meta_content, &meta_content[8]);
+							stripquotes(meta_http_equiv);
+							stripquotes(meta_content);
+							extract_charset_from_meta(charset,
+								meta_http_equiv, meta_content);
+						}
+						free(meta_content);
+					}
+					free(meta_http_equiv);
+				}
+				free(meta);
+			}
+		}
+
+		/**
+		 * Any of these tags cause everything up to and including
+		 * the tag to be removed.
+		 */	
+		if ( (!strncasecmp(ptr, "HTML", 4))
+		   ||(!strncasecmp(ptr, "HEAD", 4))
+		   ||(!strncasecmp(ptr, "/HEAD", 5))
+		   ||(!strncasecmp(ptr, "BODY", 4)) ) {
+			ptr = strchr(ptr, '>');
+			if ((ptr == NULL) || (ptr >= msgend)) break;
+			++ptr;
+			if ((ptr == NULL) || (ptr >= msgend)) break;
+			msgstart = ptr;
+		}
+
+		/**
+		 * Any of these tags cause everything including and following
+		 * the tag to be removed.
+		 */
+		if ( (!strncasecmp(ptr, "/HTML", 5))
+		   ||(!strncasecmp(ptr, "/BODY", 5)) ) {
+			--ptr;
+			msgend = ptr;
+			strcpy(ptr, "");
+			
+		}
+
+		++ptr;
+	}
+	if (msgstart > msg) {
+		strcpy(msg, msgstart);
+	}
+
+	/** Convert foreign character sets to UTF-8 if necessary. */
+#ifdef HAVE_ICONV
+	if ( (strcasecmp(charset, "us-ascii"))
+	   && (strcasecmp(charset, "UTF-8"))
+	   && (strcasecmp(charset, ""))
+	) {
+		lprintf(9, "Converting %s to UTF-8\n", charset);
+		ic = ctdl_iconv_open("UTF-8", charset);
+		if (ic == (iconv_t)(-1) ) {
+			lprintf(5, "%s:%d iconv_open() failed: %s\n",
+				__FILE__, __LINE__, strerror(errno));
+		}
+	}
+	if (ic != (iconv_t)(-1) ) {
+		ibuf = msg;
+		ibuflen = content_length;
+		obuflen = content_length + (content_length / 2) ;
+		obuf = (char *) malloc(obuflen);
+		osav = obuf;
+		iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
+		content_length = content_length + (content_length / 2) - obuflen;
+		osav[content_length] = 0;
+		free(msg);
+		msg = osav;
+		iconv_close(ic);
+	}
+#endif
+
+	/**
+	 *	At this point, the message has been stripped down to
+	 *	only the content inside the <BODY></BODY> tags, and has
+	 *	been converted to UTF-8 if it was originally in a foreign
+	 *	character set.  The text is also guaranteed to be null
+	 *	terminated now.
+	 */
+
+	converted_msg = clean_html(msg, treat_as_wiki, msgnum);
+
 	/**	uncomment these two lines to override conversion	*/
 	/**	memcpy(converted_msg, msg, content_length);		*/
 	/**	output_length = content_length;				*/
 
 	/** Output our big pile of markup */
-	StrBufAppendBuf(WC->WBuf, converted_msg, 0);
+	if (converted_msg) {
+		StrBufAppendBuf(WC->WBuf, converted_msg, 0);
+	}
 
 BAIL:	/** A little trailing vertical whitespace... */
 	wprintf("<br /><br />\n");
@@ -459,3 +594,4 @@
 }
 
 /[EMAIL PROTECTED]/
+
Index: sieve.c
===================================================================
--- sieve.c	(revision 6707)
+++ sieve.c	(working copy)
@@ -5,7 +5,7 @@
 #include "webcit.h"
 
 #define MAX_SCRIPTS	100
-#define MAX_RULES	25
+#define MAX_RULES	50
 #define RULES_SCRIPT	"__WebCit_Generated_Script__"
 
 
Index: webcit.c
===================================================================
--- webcit.c	(revision 6707)
+++ webcit.c	(working copy)
@@ -959,9 +959,34 @@
 void postpart(const char *partnum, const char *filename, int force_download)
 {
 	char content_type[256];
-	int num = atoi(partnum);
+	char *mime=NULL;
+	int num = strtol(partnum, &mime, 10);
 	struct wc_attachment *part = WC->first_attachment;
 
+	if (mime == partnum) {
+		int len = strlen(mime);
+		int count = 0;
+		/* Must be a mime type prefix, out put js list of parts */
+		output_headers(0, 0, 0, 0, 0, 0);
+		while(part) {
+			if (strncmp(mime, part->content_type, len)==0 && part->is_inline) {
+				if (! count) {
+					StrBufAppendBufPlain(WC->WBuf, "var tinyMCEImageList = new Array(\n", -1, 0);
+				} else {
+					StrBufAppendBufPlain(WC->WBuf, ", ", -1, 0);
+				}
+				StrBufAppendPrintf(WC->WBuf, "[\"%s\", \"/webcit/postpart/%d/%s\"]", part->filename, count, part->filename);
+				count++;
+			}
+			part=part->next;
+		}
+		if (count) {
+			StrBufAppendBufPlain(WC->WBuf, ");", -1, 0);
+		}
+		http_transmit_thing("application/x-javascript", 0);
+		return;
+	}
+
 	while(num && part) {
 		num--;
 		part=part->next;
Index: webcit.h
===================================================================
--- webcit.h	(revision 6707)
+++ webcit.h	(working copy)
@@ -388,6 +388,7 @@
 	char filename[SIZ];		   /* the filename hooked to this content ??? */
 	char *data;                /* the data pool; aka this content */
 	long lvalue;               /* if we put a long... */
+	int is_inline;		   /* if this will be multipart/related with the main message */
 };
 
 /*
Index: static/clean_html.xslt
===================================================================
--- static/clean_html.xslt	(revision 0)
+++ static/clean_html.xslt	(revision 0)
@@ -0,0 +1,182 @@
+<xsl:transform version="1.0"
+    xmlns="http://www.w3.org/1999/xhtml";
+    xmlns:xhtml="http://www.w3.org/1999/xhtml";
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+    xmlns:date="http://exslt.org/dates-and-times";
+    xmlns:str="http://exslt.org/strings";
+    xmlns:func="http://exslt.org/functions";
+    xmlns:exsl="http://exslt.org/common";
+    extension-element-prefixes="date str func exsl"
+    exclude-result-prefixes="xhtml"
+>
+  <xsl:namespace-alias stylesheet-prefix="#default" result-prefix="xhtml"/>
+<!--  <xsl:output method = "xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"; indent="yes" /> -->
+  <xsl:output method = "xml" indent="yes" omit-xml-declaration="yes"/>
+<!--  <xsl:strip-space elements="*" /> -->
+  <xsl:param name="msgnum" select="1"/>
+  <xsl:param name="treat_as_wiki">no</xsl:param>
+
+
+  <xsl:template match="/">
+    <div>
+    <!-- output only the body -->
+      <xsl:choose>
+        <xsl:when test="//node()[local-name()='body']">
+          <xsl:apply-templates select="//node()[local-name()='body']/node()"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:apply-templates select="node()"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </div>
+  </xsl:template>
+
+  <!-- Convert mailto links to webcit compose links -->
+  <xsl:template match="@href[starts-with(.,'mailto:')]">
+    <xsl:attribute name="{name()}">
+      <xsl:value-of select="concat('/webcit/display_enter?force_room=_MAIL_&amp;recp=',substring-after(.,'mailto:'))"/>
+    </xsl:attribute>
+  </xsl:template>
+
+  <!-- convert cid: links or src, to fetch the mime-part -->
+  <xsl:template match="@src[starts-with(.,'cid:')]|@href[starts-with(.,'cid:')]">
+    <xsl:attribute name="{name()}">
+      <xsl:value-of select="concat('/webcit/mimepart/',$msgnum,'/',substring-after(.,'cid:'),'/')"/>
+    </xsl:attribute>
+  </xsl:template>
+
+  <!-- remove all script linkes -->
+  <xsl:template match="node()[local-name()='script']"/>
+
+  <!-- remove all script handler attributes, for now defined as any attribute beginning with "on" -->
+  <xsl:template match="@onclick|@*[starts-with(.,'on')]" />
+
+  <!-- remove all target links from href tags -->
+  <xsl:template match="node()[local-name()='a']/@target"/>
+
+  <!-- add a target tag for all links except mailto: tags -->
+  <!-- TODO: and wiki tags and CERTAIN other non absolute tags -->
+  <xsl:template match="node()[local-name()='a' and @href and not(starts-with(@href,'mailto:'))]">
+    <xsl:copy>
+      <xsl:attribute name="target">_blank</xsl:attribute>
+      <xsl:apply-templates select="@*[local-name()!='target']|node()" />
+    </xsl:copy>
+  </xsl:template>
+  
+  <!-- enhance text by making <a>'s out of urls and email addresses -->
+  <xsl:template match="text()">
+    <xsl:for-each select="str:tokenalize(.)">
+      <xsl:choose>
+        <xsl:when test="starts-with(., 'http:')">
+          <a href="{.}" target="_blank"><xsl:value-of select="."/></a>
+        </xsl:when>
+        <xsl:when test="starts-with(., 'www.')">
+          <a href="http://{.}"; target="_blank"><xsl:value-of select="."/></a>
+        </xsl:when>
+        <xsl:when test="starts-with(., 'mailto:')">
+          <a href="{concat('/webcit/display_enter?force_room=_MAIL_&amp;recp=',substring-after(.,'mailto:'))}"><xsl:value-of select="."/></a>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="."/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:for-each>
+  </xsl:template>
+
+  <!-- default pass-through all other attributes -->
+  <xsl:template match="@*" name="fixup-text">
+    <xsl:copy/>
+  </xsl:template>
+
+  <!-- default pass-through all other tags -->
+  <xsl:template match="node()">
+    <xsl:copy>
+      <!-- with libxslt xsltproc we can't do @*|node() or the nodes may get processed before the attributes -->
+      <xsl:apply-templates select="@*"/>
+      <xsl:apply-templates select="node()"/>
+    </xsl:copy>
+  </xsl:template>
+
+<!-- str:tokenalize - like str:tokenize, but also returns the tokens 
+     based on str:tokenize by Jeni Tennison
+     from: http://www.exslt.org/str/functions/tokenize/str.tokenize.function.xsl -->
+
+<!-- <xsl:if test="not(function-available('str:tokenalize'))"> -->
+<func:function name="str:tokenalize">
+  <xsl:param name="string" select="''" />
+  <xsl:param name="delimiters" select="' &#x9;&#xA;'" />
+  <xsl:choose>
+    <xsl:when test="not($string)">
+      <func:result select="/.." />
+    </xsl:when>
+    <xsl:when test="not(function-available('exsl:node-set'))">
+
+      <xsl:message terminate="yes">
+        ERROR: EXSLT - Functions implementation of str:tokenalize relies on exsl:node-set().
+      </xsl:message>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="tokens">
+        <xsl:choose>
+          <xsl:when test="not($delimiters)">
+            <xsl:call-template name="str:_tokenalize-characters">
+              <xsl:with-param name="string" select="$string" />
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="str:_tokenalize-delimiters">
+              <xsl:with-param name="string" select="$string" />
+              <xsl:with-param name="delimiters" select="$delimiters" />
+            </xsl:call-template>
+          </xsl:otherwise>
+
+        </xsl:choose>
+      </xsl:variable>
+      <func:result select="exsl:node-set($tokens)/*" />
+    </xsl:otherwise>
+  </xsl:choose>
+</func:function>
+
+<xsl:template name="str:_tokenalize-characters">
+  <xsl:param name="string" />
+  <xsl:if test="$string">
+    <token><xsl:value-of select="substring($string, 1, 1)" /></token>
+    <xsl:call-template name="str:_tokenalize-characters">
+      <xsl:with-param name="string" select="substring($string, 2)" />
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="str:_tokenalize-delimiters">
+  <xsl:param name="string" />
+  <xsl:param name="delimiters" />
+
+  <xsl:variable name="delimiter" select="substring($delimiters, 1, 1)" />
+  <xsl:choose>
+    <xsl:when test="not($delimiter)">
+      <token><xsl:value-of select="$string" /></token>
+    </xsl:when>
+    <xsl:when test="contains($string, $delimiter)">
+      <xsl:if test="not(starts-with($string, $delimiter))">
+        <xsl:call-template name="str:_tokenalize-delimiters">
+          <xsl:with-param name="string" select="substring-before($string, $delimiter)" />
+          <xsl:with-param name="delimiters" select="substring($delimiters, 2)" />
+        </xsl:call-template>
+      </xsl:if>
+      <delimiter><xsl:value-of select="$delimiter"/></delimiter>
+      <xsl:call-template name="str:_tokenalize-delimiters">
+        <xsl:with-param name="string" select="substring-after($string, $delimiter)" />
+        <xsl:with-param name="delimiters" select="$delimiters" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="str:_tokenalize-delimiters">
+        <xsl:with-param name="string" select="$string" />
+        <xsl:with-param name="delimiters" select="substring($delimiters, 2)" />
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+<!-- </xsl:if> -->
+
+</xsl:transform>
Index: static/post_html.xslt
===================================================================
--- static/post_html.xslt	(revision 0)
+++ static/post_html.xslt	(revision 0)
@@ -0,0 +1,184 @@
+<xsl:transform version="1.0"
+    xmlns="http://www.w3.org/1999/xhtml";
+    xmlns:xhtml="http://www.w3.org/1999/xhtml";
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+    xmlns:date="http://exslt.org/dates-and-times";
+    xmlns:str="http://exslt.org/strings";
+    xmlns:func="http://exslt.org/functions";
+    xmlns:exsl="http://exslt.org/common";
+    extension-element-prefixes="date str func exsl"
+    exclude-result-prefixes="xhtml"
+>
+  <xsl:namespace-alias stylesheet-prefix="#default" result-prefix="xhtml"/>
+<!--  <xsl:output method = "xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"; indent="yes" /> -->
+  <xsl:output method = "xml" indent="yes" omit-xml-declaration="yes"/>
+<!--  <xsl:strip-space elements="*" /> -->
+  <xsl:param name="cid-prefix" select="'part.'"/>
+  <xsl:param name="msgnum" select="1"/>
+  <xsl:param name="treat_as_wiki">no</xsl:param>
+
+
+  <xsl:template match="/">
+    <div>
+    <!-- output only the body -->
+      <xsl:choose>
+        <xsl:when test="//node()[local-name()='body']">
+          <xsl:apply-templates select="//node()[local-name()='body']/node()"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:apply-templates select="node()"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </div>
+  </xsl:template>
+
+  <!-- Convert mailto links to webcit compose links -->
+  <xsl:template match="@href[starts-with(.,'mailto:')]">
+    <xsl:attribute name="{name()}">
+      <xsl:value-of select="concat('/webcit/display_enter?force_room=_MAIL_&amp;recp=',substring-after(.,'mailto:'))"/>
+    </xsl:attribute>
+  </xsl:template>
+
+  <!-- convert src relative url's to cid format -->
+  <xsl:template match="@src[starts-with(.,'/webcit/postpart/') or starts-with(.,'webcit/postpart/') or starts-with(., '/postpart/') or starts-with(., 'postpart/')]">
+    <xsl:variable name="part" select="substring-before(substring-after(., 'postpart/'),'/')"/>
+    <xsl:attribute name="{name()}">
+      <xsl:value-of select="concat('cid:',$cid-prefix,$part)"/>
+    </xsl:attribute>
+  </xsl:template>
+
+  <!-- remove all script linkes -->
+  <xsl:template match="node()[local-name()='script']"/>
+
+  <!-- remove all script handler attributes, for now defined as any attribute beginning with "on" -->
+  <xsl:template match="@onclick|@*[starts-with(.,'on')]" />
+
+  <!-- remove all target links from href tags -->
+  <xsl:template match="node()[local-name()='a']/@target"/>
+
+  <!-- add a target tag for all links except mailto: tags -->
+  <!-- TODO: and wiki tags and CERTAIN other non absolute tags -->
+  <xsl:template match="node()[local-name()='a' and @href and not(starts-with(@href,'mailto:'))]">
+    <xsl:copy>
+      <xsl:attribute name="target">_blank</xsl:attribute>
+      <xsl:apply-templates select="@*[local-name()!='target']|node()" />
+    </xsl:copy>
+  </xsl:template>
+  
+  <!-- enhance text by making <a>'s out of urls and email addresses -->
+  <xsl:template match="text()">
+    <xsl:for-each select="str:tokenalize(.)">
+      <xsl:choose>
+        <xsl:when test="starts-with(., 'http:')">
+          <a href="{.}" target="_blank"><xsl:value-of select="."/></a>
+        </xsl:when>
+        <xsl:when test="starts-with(., 'www.')">
+          <a href="http://{.}"; target="_blank"><xsl:value-of select="."/></a>
+        </xsl:when>
+        <xsl:when test="starts-with(., 'mailto:')">
+          <a href="{concat('/webcit/display_enter?force_room=_MAIL_&amp;recp=',substring-after(.,'mailto:'))}"><xsl:value-of select="."/></a>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="."/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:for-each>
+  </xsl:template>
+
+  <!-- default pass-through all other attributes -->
+  <xsl:template match="@*" name="fixup-text">
+    <xsl:copy/>
+  </xsl:template>
+
+  <!-- default pass-through all other tags -->
+  <xsl:template match="node()">
+    <xsl:copy>
+      <!-- with libxslt xsltproc we can't do @*|node() or the nodes may get processed before the attributes -->
+      <xsl:apply-templates select="@*"/>
+      <xsl:apply-templates select="node()"/>
+    </xsl:copy>
+  </xsl:template>
+
+<!-- str:tokenalize - like str:tokenize, but also returns the tokens 
+     based on str:tokenize by Jeni Tennison
+     from: http://www.exslt.org/str/functions/tokenize/str.tokenize.function.xsl -->
+
+<!-- <xsl:if test="not(function-available('str:tokenalize'))"> -->
+<func:function name="str:tokenalize">
+  <xsl:param name="string" select="''" />
+  <xsl:param name="delimiters" select="' &#x9;&#xA;'" />
+  <xsl:choose>
+    <xsl:when test="not($string)">
+      <func:result select="/.." />
+    </xsl:when>
+    <xsl:when test="not(function-available('exsl:node-set'))">
+
+      <xsl:message terminate="yes">
+        ERROR: EXSLT - Functions implementation of str:tokenalize relies on exsl:node-set().
+      </xsl:message>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:variable name="tokens">
+        <xsl:choose>
+          <xsl:when test="not($delimiters)">
+            <xsl:call-template name="str:_tokenalize-characters">
+              <xsl:with-param name="string" select="$string" />
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:call-template name="str:_tokenalize-delimiters">
+              <xsl:with-param name="string" select="$string" />
+              <xsl:with-param name="delimiters" select="$delimiters" />
+            </xsl:call-template>
+          </xsl:otherwise>
+
+        </xsl:choose>
+      </xsl:variable>
+      <func:result select="exsl:node-set($tokens)/*" />
+    </xsl:otherwise>
+  </xsl:choose>
+</func:function>
+
+<xsl:template name="str:_tokenalize-characters">
+  <xsl:param name="string" />
+  <xsl:if test="$string">
+    <token><xsl:value-of select="substring($string, 1, 1)" /></token>
+    <xsl:call-template name="str:_tokenalize-characters">
+      <xsl:with-param name="string" select="substring($string, 2)" />
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="str:_tokenalize-delimiters">
+  <xsl:param name="string" />
+  <xsl:param name="delimiters" />
+
+  <xsl:variable name="delimiter" select="substring($delimiters, 1, 1)" />
+  <xsl:choose>
+    <xsl:when test="not($delimiter)">
+      <token><xsl:value-of select="$string" /></token>
+    </xsl:when>
+    <xsl:when test="contains($string, $delimiter)">
+      <xsl:if test="not(starts-with($string, $delimiter))">
+        <xsl:call-template name="str:_tokenalize-delimiters">
+          <xsl:with-param name="string" select="substring-before($string, $delimiter)" />
+          <xsl:with-param name="delimiters" select="substring($delimiters, 2)" />
+        </xsl:call-template>
+      </xsl:if>
+      <delimiter><xsl:value-of select="$delimiter"/></delimiter>
+      <xsl:call-template name="str:_tokenalize-delimiters">
+        <xsl:with-param name="string" select="substring-after($string, $delimiter)" />
+        <xsl:with-param name="delimiters" select="$delimiters" />
+      </xsl:call-template>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:call-template name="str:_tokenalize-delimiters">
+        <xsl:with-param name="string" select="$string" />
+        <xsl:with-param name="delimiters" select="substring($delimiters, 2)" />
+      </xsl:call-template>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+<!-- </xsl:if> -->
+
+</xsl:transform>
Index: messages.c
===================================================================
--- messages.c	(revision 6707)
+++ messages.c	(working copy)
@@ -8,6 +8,20 @@
 #include "webcit.h"
 #include "webserver.h"
 #include "groupdav.h"
+#ifdef HAVE_XSLT
+#include <libxml/xmlmemory.h>
+#include <libxml/debugXML.h>
+#include <libxml/HTMLtree.h>
+#include <libxml/xmlIO.h>
+#include <libxml/xinclude.h>
+#include <libxml/catalog.h>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+#include "libexslt/exslt.h"
+#include <libexslt/exsltconfig.h>
+#endif
 
 #define SUBJ_COL_WIDTH_PCT		50	/**< Mailbox view column width */
 #define SENDER_COL_WIDTH_PCT		30	/**< Mailbox view column width */
@@ -2977,6 +2991,8 @@
 void post_mime_to_server(void) {
 	char top_boundary[SIZ];
 	char alt_boundary[SIZ];
+	char rel_boundary[SIZ];
+	char cid_prefix[SIZ];
 	int is_multipart = 0;
 	static int seq = 0;
 	struct wc_attachment *att;
@@ -2985,6 +3001,8 @@
 	size_t encoded_strlen;
 	char *txtmail = NULL;
 
+	sprintf(cid_prefix, "part.");
+
 	sprintf(top_boundary, "Citadel--Multipart--%s--%04x--%04x",
 		serv_info.serv_fqdn,
 		getpid(),
@@ -3027,19 +3045,91 @@
 
 	serv_printf("--%s", alt_boundary);
 
+	/* start multi-part related block */
+	sprintf(rel_boundary, "Citadel--Multipart--%s--%04x--%04x",
+		serv_info.serv_fqdn,
+		getpid(),
+		++seq
+	);
+	serv_printf("Content-type: multipart/related; "
+		"boundary=\"%s\"\n", rel_boundary);
+	serv_puts("");
+
+	serv_printf("--%s", rel_boundary);
 	serv_puts("Content-type: text/html; charset=utf-8");
 	serv_puts("Content-Transfer-Encoding: quoted-printable");
 	serv_puts("");
+
+#ifdef HAVE_XSLT
+	/* purify the html and fixup inline urls to cid: */
+	{
+		const char *params[16 + 1];
+		xmlDocPtr doc=NULL;
+		xmlDocPtr res=NULL;
+		xsltStylesheetPtr format=NULL;
+
+		doc = htmlParseDoc((const xmlChar*)bstr("msgtext"), "utf8");
+		format = xsltParseStylesheetFile((const xmlChar *)WWWDIR "/static/post_html.xslt");
+		params[0]="cid-prefix";
+		params[1]=cid_prefix;
+		params[0]=NULL;
+
+		res=xsltApplyStylesheet(format, doc, params);
+		if (res) {
+			char* output;
+			int len=0;
+			xsltSaveResultToString(&output, &len, res, format);
+			serv_puts("<html><body>\r\n");
+			text_to_server_qp(output);	/* Transmit message in quoted-printable encoding */
+			serv_puts("</body></html>\r\n");
+			if (output) free(output);
+		}
+
+		if (format) xsltFreeStylesheet(format);
+		if (res) xmlFreeDoc(res);
+		if (doc) xmlFreeDoc(doc);
+
+	}
+#else
 	serv_puts("<html><body>\r\n");
 	text_to_server_qp(bstr("msgtext"));	/* Transmit message in quoted-printable encoding */
 	serv_puts("</body></html>\r\n");
+#endif
 
+	/* Now output related inline attachments */
+	if (is_multipart) {
+		int count=0;
+
+		/* Add in the attachments; NOTE: we count++ ALL attachments */
+		for (att = WC->first_attachment; att!=NULL; count++, att=att->next) if (att->is_inline) {
+
+			encoded_length = ((att->length * 150) / 100);
+			encoded = malloc(encoded_length);
+			if (encoded == NULL) break;
+			encoded_strlen = CtdlEncodeBase64(encoded, att->data, att->length, 1);
+
+			serv_printf("--%s", rel_boundary);
+			serv_printf("Content-type: %s", att->content_type);
+			serv_printf("Content-ID: <%s%d>", cid_prefix, count);
+			serv_printf("Content-disposition: attachment; filename=\"%s\"", att->filename);
+			serv_puts("Content-transfer-encoding: base64");
+			serv_puts("");
+			serv_write(encoded, encoded_strlen);
+			serv_puts("");
+			serv_puts("");
+			free(encoded);
+		}
+	}
+
+	serv_printf("--%s--", rel_boundary);
+
 	serv_printf("--%s--", alt_boundary);
 	
 	if (is_multipart) {
+		int count=0;
 
-		/* Add in the attachments */
-		for (att = WC->first_attachment; att!=NULL; att=att->next) {
+		/* Add in the attachments; NOTE: we count++ ALL attachments */
+		for (att = WC->first_attachment; att!=NULL; count++, att=att->next) if (! att->is_inline) {
 
 			encoded_length = ((att->length * 150) / 100);
 			encoded = malloc(encoded_length);
@@ -3048,6 +3138,7 @@
 
 			serv_printf("--%s", top_boundary);
 			serv_printf("Content-type: %s", att->content_type);
+			serv_printf("Content-ID: <part.%d>", count, serv_info.serv_fqdn);
 			serv_printf("Content-disposition: attachment; filename=\"%s\"", att->filename);
 			serv_puts("Content-transfer-encoding: base64");
 			serv_puts("");
@@ -3134,6 +3225,8 @@
 		 * to the attachment struct.
 		 */
 		att->data = WCC->upload;
+		if (havebstr("insert_button")) 
+			att->is_inline=1;
 		WCC->upload_length = 0;
 		WCC->upload = NULL;
 		display_enter();
@@ -3143,7 +3236,7 @@
 	if (havebstr("cancel_button")) {
 		sprintf(WCC->ImportantMessage, 
 			_("Cancelled.  Message was not posted."));
-	} else if (havebstr("attach_button")) {
+	} else if (havebstr("attach_button") || havebstr("insert_button")) {
 		display_enter();
 		return;
 	} else if (lbstr("postseq") == dont_post) {
@@ -3679,9 +3772,11 @@
 	/** Now offer the ability to attach additional files... */
 	wprintf("&nbsp;&nbsp;&nbsp;");
 	wprintf(_("Attach file:"));
-	wprintf(" <input name=\"attachfile\" class=\"attachfile\" "
+	wprintf("<input name=\"attachfile\" class=\"attachfile\" "
 		"size=16 type=\"file\">\n&nbsp;&nbsp;"
 		"<input type=\"submit\" name=\"attach_button\" value=\"%s\">\n", _("Add"));
+	wprintf("&nbsp;&nbsp;"
+		"<input type=\"submit\" name=\"insert_button\" value=\"%s\">\n", _("Insert"));
 	wprintf("</div>");	/* End of "attachment buttons" div */
 
 

Reply via email to