You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ma...@apache.org on 2005/10/01 18:48:55 UTC

svn commit: r292999 [2/2] - in /httpd/mod_mbox/trunk: ./ data/ module-2.0/

Modified: httpd/mod_mbox/trunk/module-2.0/mod_mbox_mime.c
URL: http://svn.apache.org/viewcvs/httpd/mod_mbox/trunk/module-2.0/mod_mbox_mime.c?rev=292999&r1=292998&r2=292999&view=diff
==============================================================================
--- httpd/mod_mbox/trunk/module-2.0/mod_mbox_mime.c (original)
+++ httpd/mod_mbox/trunk/module-2.0/mod_mbox_mime.c Sat Oct  1 09:48:34 2005
@@ -14,228 +14,389 @@
 * limitations under the License.
 */
 
-/**
- * Output filters for decoding common Content-Encodings of E-Mail.  Currently
- * only quoted-printable and base64 are supported.
+/* MIME parsing and structure management functions.
  */
 
 #include "mod_mbox.h"
 
-/*
- * The char64 macro and `mime_decode_b64' routine are taken from
- * metamail 2.7, which is copyright (c) 1991 Bell Communications
- * Research, Inc. (Bellcore).  The following license applies to all
- * code below this point:
- *
- * Permission to use, copy, modify, and distribute this material
- * for any purpose and without fee is hereby granted, provided
- * that the above copyright notice and this permission notice
- * appear in all copies, and that the name of Bellcore not be
- * used in advertising or publicity pertaining to this
- * material without the specific, prior written permission
- * of an authorized representative of Bellcore.  BELLCORE
- * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
- * OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",
- * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+/* Decode a multipart (or not) email. In order to support multiple
+ * levels of MIME parts, this function is recursive.
  */
+mbox_mime_message_t *mbox_mime_decode_multipart(apr_pool_t *p, char *body,
+						char *ct, mbox_cte_e cte,
+						char *boundary)
+{
+    mbox_mime_message_t *mail;
+    char *tmp = NULL, *k = NULL, *end_bound = NULL;
+    char *headers_bound = NULL;
+
+    /* Locate the end of part headers */
+    headers_bound = strstr(body, "\n\n");
+    if (!headers_bound) {
+      return NULL;
+    }
 
-static char index_64[128] = {
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
-    52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
-    -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
-    15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-    -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
-    41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
-};
+    /* If no Content-Type is provided, it means that we are parsing a
+       sub-part of the multipart message. The Content-Type header
+       should then be the first line of the part. If not, just ignore
+       the sub-part. */
+    tmp = strstr(body, "Content-Type: ");
+    if (!ct && (!tmp || tmp > headers_bound)) {
+        return NULL;
+    }
 
-#define char64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
+    mail = calloc(1, sizeof(mbox_mime_message_t));
 
-apr_size_t mbox_mime_decode_b64(char *src)
-{
-    char *dst;
-    int c1, c2, c3, c4;
-    int newline = 1, DataDone = 0;
-    apr_size_t len = 0;
-    dst = src;
-
-    while ((c1 = *src++) != '\0') {
-        if (isspace(c1)) {
-            if (c1 == '\n') {
-                newline = 1;
-            } else {
-                newline = 0;
-            }
-            continue;
-        }
-
-        if (DataDone) {
-            continue;
-        }
-
-        newline = 0;
-
-        do {
-            c2 = *src++;
-        } while (c2 != '\0' && isspace(c2));
-
-        do {
-            c3 = *src++;
-        } while (c3 != '\0' && isspace(c3));
-
-        do {
-            c4 = *src++;
-        } while (c4 != '\0' && isspace(c4));
-
-        if (c2 == '\0' || c3 == '\0' || c4 == '\0') {
-            /* Premature EOF. Should return an Error? */
-            return len;
-        }
-
-        if (c1 == '=' || c2 == '=') {
-            DataDone=1;
-            continue;
-        }
-
-        c1 = char64(c1);
-        c2 = char64(c2);
-        *dst++ = (c1<<2) | ((c2&0x30)>>4);
-        len++;
-
-        if (c3 == '=') {
-            DataDone = 1;
-        }
-        else {
-            c3 = char64(c3);
-            *dst++ = ((c2&0XF) << 4) | ((c3&0x3C) >> 2);
-            len++;
-
-            if (c4 == '=') {
-                DataDone = 1;
-            }
-            else {
-                c4 = char64(c4);
-                *dst++ = ((c3&0x03) <<6) | c4;
-                len++;
-            }
-        }
+    /* If no Content-Type is given, we have to look for it. */
+    if (!ct) {
+	tmp += strlen("Content-Type: ");
+	k = strchr(tmp, ';');
+
+	/* Isolate the Content-Type string (between 'Content-Type: '
+	   and ';' or end of line */
+	if (k && k < headers_bound) {
+	    *k = 0;
+	}
+	else {
+	    k = tmp;
+	    while (*k) {
+	        if (isspace(*k)) {
+		    *k = 0;
+		    break;
+		}
+		*k++;
+	    }
+	}
+
+	/* Copy the Content-Type and reset *k */
+	mail->content_type = apr_pstrdup(p, tmp);
+	*k = ';';
+
+	/* If available, get MIME part name */
+	tmp = strstr(body, "name=");
+	if (tmp && tmp < headers_bound) {
+	    tmp += strlen("name=");
+	    k = tmp;
+
+	    while (*k) {
+	        if (isspace(*k) || *k == ';') {
+		    *k = 0;
+		    break;
+		}
+		*k++;
+	    }
+
+	    /* Check for double quotes */
+	    if ((*tmp == '"') && (tmp[strlen(tmp)-1] == '"')) {
+	        mail->content_name = apr_pstrndup(p, tmp+1, strlen(tmp) - 2);
+	    }
+	    else {
+	        mail->content_name = apr_pstrdup(p, tmp);
+	    }
+
+	    *k = ';';
+	}
     }
+    else
+      mail->content_type = ct;
 
-    *dst = '\0';
-    return len;
-}
+    /* Now we have a Content-Type. Look for other useful header information */
 
-static int hex2dec_char(char ch)
-{
-    if (isdigit(ch)) {
-        return ch-'0';
+    /* Check Content-Disposition if the match is within the headers */
+    tmp = strstr(body, "Content-Disposition: ");
+    if (tmp && tmp < headers_bound) {
+	tmp += strlen("Content-Disposition: ");
+	k = tmp;
+
+	while (*k) {
+	    if (isspace(*k) || *k == ';') {
+		*k = 0;
+		break;
+	    }
+	    *k++;
+	}
+
+	/* Copy the Content-Disposition and reset *k */
+	mail->content_disposition = apr_pstrdup(p, tmp);
+	*k = '\n';
     }
-    else if (isupper(ch)) {
-        return ch-'A'+10;
+    else {
+        mail->content_disposition = apr_pstrdup(p, "inline");
     }
+
+    /* Check Content-Transfer-Encoding, if needed */
+    if (cte == CTE_NONE)
+      {
+	  tmp = strstr(body, "Content-Transfer-Encoding: ");
+	  if (tmp && tmp < headers_bound) {
+	      tmp += strlen("Content-Transfer-Encoding: ");
+	      k = tmp;
+
+	      while (*k) {
+		  if (isspace(*k) || *k == ';') {
+		      *k = 0;
+		      break;
+		  }
+		  *k++;
+	      }
+
+	      /* Copy the Content-Disposition and reset *k */
+	      mail->cte = mbox_parse_cte_header(tmp);
+	      *k = '\n';
+	  }
+      }
     else {
-        return ch-'a'+10;
+      mail->cte = cte;
+    }
+
+    /* Now we have all the headers we need. Start processing the
+       body. If the Content-Type was given at call time, the body
+       starts where it's given. Otherwise it's after the headers
+       (first new empty line) */
+
+    if (ct) {
+        mail->body = body;
     }
+    else {
+        mail->body = strstr(body, "\n\n") + 2;
+    }
+
+    /* If the mail is a multipart message, search for the boundary,
+       and process its sub parts by recursive calls. */
+    if (strncmp(mail->content_type, "multipart/", strlen("multipart/")) == 0) {
+	int end = 0, count = 0;
+	char *search, *bound;
+
+	/* If the boundary was not given, we must look for it in the headers */
+	if (!boundary) {
+	    tmp = strstr(body, "boundary=\"");
+	    if (!tmp) {
+		return NULL;
+	    }
+
+	    tmp += strlen("boundary=\"");
+	    k = tmp;
+
+	    while (*k) {
+		if (*k == '"') {
+		    *k = 0;
+		    break;
+		}
+		*k++;
+	    }
+
+	    mail->boundary = apr_pstrdup(p, tmp);
+	    *k = '"';
+	}
+
+	/* Otherwise, the boundary is as given to us */
+	else {
+	    mail->boundary = boundary;
+	}
+
+	/* Now we have our boundary string. We must : look for it once
+	 (begining of MIME part) and then look for the end boundary :
+	 --boundary-- to mark the end of the MIME part */
+
+	/* The start boundary */
+	bound = strstr(mail->body, mail->boundary);
+	if (!bound) {
+	    return NULL;
+	}
+
+	/* The end boudary */
+	end_bound = calloc(strlen(mail->boundary)+5, sizeof(char));
+	sprintf(end_bound, "--%s--", mail->boundary);
+	tmp = strstr(mail->body, end_bound);
+	if (!tmp) {
+	    return NULL;
+	}
+	*tmp = 0;
+
+	/* Set the search begining to the line after the start boundary. */
+	search = bound + strlen(mail->boundary) + 1;
+
+	/* While the MIME part is not finished, go through all sub parts */
+	while (!end) {
+	    char *inbound;
+
+	    inbound = strstr(search+strlen(mail->boundary), mail->boundary);
+	    if (inbound) {
+		char *t = inbound - 2;
+		*t = 0;
+	    }
+
+	    /* Allocate a new pointer for the sub part, and parse it. */
+	    mail->sub = realloc(mail->sub, ++count * sizeof(struct mimemsg *));
+	    mail->sub[count-1] = mbox_mime_decode_multipart(p, search, NULL, CTE_NONE, NULL);
+
+	    /* If the boudary is found again, it means we have another
+	       MIME part in the same multipart message. Set the new
+	       search begining to the line after this new start
+	       boundary */
+	    if (inbound) {
+		char *t = inbound - 2;
+		*t = '-';
+
+		search = inbound + strlen(mail->boundary) + 1;
+
+		if (mail->sub[count-1]) {
+		    mail->sub[count-1]->body_len = inbound - mail->sub[count-1]->body - 2;
+		}
+	    }
+
+	    /* Otherwise, the MIME part is finished. */
+	    else {
+		mail->sub_count = count;
+		end = 1;
+	    }
+	}
+
+	/* Finally reset the end-body pointer. */
+	//	*tmp = '-';
+      }
+
+    /* If the parsed body is not multipart or is a MIME part, the body
+       length is the length of the body string (no surprise here). If
+       it's a MIME part, its correct length will be set after the call
+       to mbox_mime_decode_multipart just a dozen lines above. */
+    else {
+        mail->body_len = strlen(mail->body);
+    }
+
+    return mail;
 }
 
-/* mime_decode_qp: convert the preamble of MSG from a quoted-printable
-*	encoding to raw text.
-*/
-apr_size_t mbox_mime_decode_qp(char* p)
+/* Decode a MIME part body, according to its CTE. */
+char *mbox_mime_decode_body(apr_pool_t *p, mbox_cte_e cte, char *body, apr_size_t len)
 {
-    unsigned char *src, *dst;
-    apr_size_t len = 0;
+    char *new_body = apr_pstrndup(p, body, len);
 
-    dst = src = p;
-    while (*src != '\0') {
-        if (*src == '=') {
-            if (*++src == '\n') {
-                ++src;
-                continue;
-            }
-            else {
-                int hi, lo;
-                hi = hex2dec_char(*src++);
-                lo = hex2dec_char(*src);
-                *dst = hi*16 + lo;
-            }
-        }
-        else {
-            *dst = *src;
-        }
-        ++dst, ++src;
-        len++;
+    if (cte == CTE_BASE64) {
+        len = mbox_cte_decode_b64(new_body);
+    }
+    else if (cte == CTE_QP) {
+	len = mbox_cte_decode_qp(new_body);
     }
 
-    return len;
+    new_body[len] = 0;
+    return new_body;
 }
 
-/** End metamail 2.7 code **/
 
-apr_status_t mbox_cte_filter(ap_filter_t *f, apr_bucket_brigade *bb,
-                             mbox_cte_e cte, int noescape)
+/* This function returns the relevant MIME part from a message. For
+ * the moment, it just returns the first text/ MIME part available.
+ */
+char *mbox_mime_get_body(apr_pool_t *p, mbox_mime_message_t *m)
 {
-    apr_status_t rv = 0;
-    apr_size_t len;
-    apr_off_t off;
-    int seen_eos = 0;
-    apr_bucket* eos;
-    apr_bucket* e;
-    char* p;
+    int i;
+
+    if (!m) {
+        return NULL;
+    }
+
+    if (strncasecmp(m->content_type, "text/", strlen("text/")) == 0) {
+        char *new_body;
+
+	new_body = mbox_mime_decode_body(p, m->cte, m->body, m->body_len);
+	m->body_len = mbox_cte_escape_html(p, new_body, m->body_len, &(m->body));
+
+	return apr_pstrndup(p, m->body, m->body_len);
+    }
 
-    eos = APR_BRIGADE_LAST(bb);
+    if (!m->sub) {
+        return NULL;
+    }
 
-    if (APR_BUCKET_IS_EOS(eos)) {
-        seen_eos = 1;
+    for (i=0 ; i<m->sub_count ; i++) {
+        return mbox_mime_get_body(p, m->sub[i]);
     }
 
-    apr_brigade_length(bb, 1, &off);
+    return NULL;
+}
 
-    /* FIXME: Make the C-T Encoding streamy/bucket based, instead of
-     *        using pflatten
-     */
-    rv = apr_brigade_pflatten(bb, &p, &len, f->r->pool);
+/* Display an XHTML MIME structure */
+void mbox_mime_display_static_structure(request_rec *r, mbox_mime_message_t *m,
+					char *link)
+{
+    int i;
 
-    if (rv == APR_SUCCESS) {
+    if (!m) {
+        return;
+    }
 
-        p[len] = '\0';
+    ap_rputs("<li>", r);
 
-        if (cte == CTE_BASE64) {
-            len = mbox_mime_decode_b64(p);
-        }
-        else if (cte == CTE_QP) {
-            len = mbox_mime_decode_qp(p);
-        }
+    if (m->body_len) {
+        ap_rprintf(r, "<a href=\"%s\">", link);
+    }
 
-        if (noescape) {
-            e = apr_bucket_pool_create(p, len, f->r->pool,
-                                       f->c->bucket_alloc);
-        }
-        else {
-            e = mbox_bucket_escape_html(f, f->r->pool, p, len);
-        }
+    if (m->content_name) {
+        ap_rprintf(r, "%s", m->content_name);
+    }
+    else {
+        ap_rprintf(r, "Unnamed %s", m->content_type);
+    }
 
-        apr_brigade_cleanup(bb);
+    if (m->body_len) {
+        ap_rputs("</a>", r);
+    }
 
-        if (seen_eos) {
-            eos = apr_bucket_eos_create(f->c->bucket_alloc);
-            APR_BRIGADE_INSERT_HEAD(bb, eos);
-        }
+    ap_rprintf(r, " (%s, %s, %u bytes)</li>\n", m->content_disposition,
+	       mbox_cte_to_char(m->cte), m->body_len);
 
-        APR_BRIGADE_INSERT_HEAD(bb, e);
+    if (!m->sub) {
+        return;
     }
 
-    return ap_pass_brigade(f->next, bb);
-}
+    for (i=0 ; i<m->sub_count ; i++) {
+        ap_rputs("<ul>\n", r);
 
-apr_status_t mbox_base64_filter(ap_filter_t *f, apr_bucket_brigade *bb)
-{
-    return mbox_cte_filter(f, bb, CTE_BASE64, 0);
+	if (link[strlen(link)-1] == '/') {
+	    link[strlen(link)-1] = 0;
+	}
+
+	mbox_mime_display_static_structure(r, m->sub[i],
+					   apr_psprintf(r->pool, "%s/%d", link, i+1));
+	ap_rputs("</ul>\n", r);
+    }
 }
 
-apr_status_t mbox_qp_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+/* Display an XML MIME structure */
+void mbox_mime_display_xml_structure(request_rec *r, mbox_mime_message_t *m,
+				     char *link)
 {
-    return mbox_cte_filter(f, bb, CTE_QP, 0);
-}
+    int i;
+
+    if (!m) {
+        return;
+    }
+
+    if (m->content_name) {
+        ap_rprintf(r, "<part name=\"%s\" cd=\"%s\" cte=\"%s\" "
+		   "length=\"%u\" link=\"%s\" />\n",
+		   m->content_name, m->content_disposition,
+		   mbox_cte_to_char(m->cte), m->body_len, link);
+    }
+    else {
+        ap_rprintf(r, "<part ct=\"%s\" cd=\"%s\" cte=\"%s\" "
+		   "length=\"%u\" link=\"%s\" />\n",
+		   m->content_type, m->content_disposition,
+		   mbox_cte_to_char(m->cte), m->body_len, link);
+    }
+
+    if (!m->sub) {
+        return;
+    }
 
+    ap_rputs("<mime>\n", r);
+    for (i=0 ; i<m->sub_count ; i++) {
+	if (link[strlen(link)-1] == '/') {
+	    link[strlen(link)-1] = 0;
+	}
+
+        mbox_mime_display_xml_structure(r, m->sub[i],
+					apr_psprintf(r->pool, "%s/%d", link, i+1));
+    }
+    ap_rputs("</mime>\n", r);
+}

Modified: httpd/mod_mbox/trunk/module-2.0/mod_mbox_search.c
URL: http://svn.apache.org/viewcvs/httpd/mod_mbox/trunk/module-2.0/mod_mbox_search.c?rev=292999&r1=292998&r2=292999&view=diff
==============================================================================
--- httpd/mod_mbox/trunk/module-2.0/mod_mbox_search.c (original)
+++ httpd/mod_mbox/trunk/module-2.0/mod_mbox_search.c Sat Oct  1 09:48:34 2005
@@ -61,7 +61,7 @@
 
 int mbox_search_handler(request_rec *r)
 {
-    dir_cfg *conf;
+    mbox_dir_cfg_t *conf;
 
     if (strcmp(r->handler, "mbox-search")) {
         return DECLINED;