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/08/28 16:28:03 UTC

svn commit: r263880 - in /httpd/mod_mbox/branches/httpd-mbox-if: STATUS data/archives.js data/style.css module-2.0/mbox_parse.h module-2.0/mod_mbox.h module-2.0/mod_mbox_file.c module-2.0/mod_mbox_mime.c module-2.0/mod_mbox_out.c

Author: maxime
Date: Sun Aug 28 07:27:54 2005
New Revision: 263880

URL: http://svn.apache.org/viewcvs?rev=263880&view=rev
Log:
MIME part viewing/downloading.

 * module-2.0/mod_mbox_file.c:
    (fetch_message): no longer decodes multipart. Just fills in the
new raw_msg and raw_body fields of the Message structure.

    (mbox_file_handler): '/raw' is now split from the other URIs
considered as outputting text/html. Raw message addresses are now
/raw/msgID.

 * module-2.0/mbox_parse.h:
    (struct Message_Struct): added new raw_msg and raw_body
fields. Some sort arragements (headers first).

 * module-2.0/mod_mbox.h:
    Added new functions prototypes.

 * module-2.0/mod_mbox_mime.c:
    (mbox_mime_decode_body): new function. decodes a part body
according to its CTE.

    (mbox_mime_get_body): changed to use the new body decoding
function.

    (mbox_mime_display_static_structure): only link to a part if its
size is not zero.

    (mbox_mime_display_xml_structure): added part key info to part
markups.

 * module-2.0/mod_mbox_out.c:
    (mbox_raw_message): now supports MIME part viewing/downloading.

    (mbox_static_message, mbox_xml_message): updated in order to
comply with made changes.

 * STATUS:
    Proper header parsing is now considered as a release showstopper.

 * data/archives.js:
    (indexLinks): do not propose links to the AJAX browser if user's
web browser does not even support XmlHttpRequest.

    (getMsgListHeader): only display page selector if needed.

    (getMessage, getMsgList): added loading boxes.

    (parseMimeStructure): now display links to MIME parts.

 * data/style.css:
    Opera compatibility.


Modified:
    httpd/mod_mbox/branches/httpd-mbox-if/STATUS
    httpd/mod_mbox/branches/httpd-mbox-if/data/archives.js
    httpd/mod_mbox/branches/httpd-mbox-if/data/style.css
    httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mbox_parse.h
    httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox.h
    httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_file.c
    httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_mime.c
    httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_out.c

Modified: httpd/mod_mbox/branches/httpd-mbox-if/STATUS
URL: http://svn.apache.org/viewcvs/httpd/mod_mbox/branches/httpd-mbox-if/STATUS?rev=263880&r1=263879&r2=263880&view=diff
==============================================================================
--- httpd/mod_mbox/branches/httpd-mbox-if/STATUS (original)
+++ httpd/mod_mbox/branches/httpd-mbox-if/STATUS Sun Aug 28 07:27:54 2005
@@ -8,7 +8,10 @@
 
 Release showstoppers:
 
-    * MIME part viewing / downloading
+    * Massive bug squashing.
+
+    * Subject RFC 2047 decoding. See function's comment
+      (mod_mbox_cte.c)
 
 Other known issues / ToDo:
 
@@ -24,9 +27,6 @@
       characters conversion with APR v0.x is a pain.
 
     * Better page selector (usefull for 10+ pages archives)
-
-    * Subject RFC 2047 decoding. See function's comment
-      (mod_mbox_cte.c)
 
 Wish list:
 

Modified: httpd/mod_mbox/branches/httpd-mbox-if/data/archives.js
URL: http://svn.apache.org/viewcvs/httpd/mod_mbox/branches/httpd-mbox-if/data/archives.js?rev=263880&r1=263879&r2=263880&view=diff
==============================================================================
--- httpd/mod_mbox/branches/httpd-mbox-if/data/archives.js (original)
+++ httpd/mod_mbox/branches/httpd-mbox-if/data/archives.js Sun Aug 28 07:27:54 2005
@@ -22,6 +22,13 @@
 function indexLinks ()
 {
   var i, entries;
+  var http = getHTTPObject ();
+
+  /* Do not change links if the browser does not support
+     XMLHttpRequest */
+  if (!http) {
+    return false;
+  }
 
   i = 0;
 
@@ -31,9 +38,8 @@
   /* Update span.links contents */
   while (entries[i]) {
     if (entries[i].className == 'links') {
-      entries[i].innerHTML = '<a href="' +
-	entries[i].getAttribute('id') +
-	'.mbox/browser">Browse</a>';
+      entries[i].innerHTML = '<a href="' + entries[i].getAttribute('id') +
+	'.mbox/browser" title="Dynamic browser">Browse</a>';
     }
     i++;
   }
@@ -153,7 +159,11 @@
   if (!sort) {
     sort = _sort;
   }
+  if (!page) {
+    page = _page;
+  }
 
+  _page = page;
   _sort = sort;
 
   /* The handler */
@@ -225,19 +235,23 @@
 
   /* Page selector */
   str += '<th class="pages">';
-  for (i = 0; i<pages ; i++) {
-    if (i) {
-      str += ' &middot; ';
-    }
 
-    if (i != current_page) {
-      str += '<a href="browser" onclick="javascript:getMsgList(\'' + _sort + '\', \'' +
-	i + '\'); return false;">' + (i+1) + '</a>';
-    }
-    else {
-      str += (i+1);
+  if (pages > 1) {
+    for (i = 0; i<pages ; i++) {
+      if (i) {
+	str += ' &middot; ';
+      }
+
+      if (i != current_page) {
+	str += '<a href="browser" onclick="javascript:getMsgList(\'' + _sort + '\', \'' +
+	  i + '\'); return false;">' + (i+1) + '</a>';
+      }
+      else {
+	str += (i+1);
+      }
     }
   }
+
   str += '</th>';
 
   str += '<th class="sort">';
@@ -408,10 +422,11 @@
 	var mime = message.getElementsByTagName('mime')[0].childNodes;
 
 	str += '<tr class="mime"><td class="left">Mime</td><td class="right"><ul>' +
-	parseMimeStructure (mime) + '</ul></td></tr>';
+	parseMimeStructure (id, mime) +
+	'</ul></td></tr>';
 
 	str += '<tr class="raw"><td class="left"></td><td class="right">' +
-	'<a href="' + _baseURI + _mbox + '/raw?' + id + '" target="_blank">View raw message</a></td></tr>';
+	'<a href="' + _baseURI + _mbox + '/raw/' + id + '" target="_blank">View raw message</a></td></tr>';
 
 	str += '</tbody>';
 	msgview.innerHTML = str;
@@ -420,12 +435,12 @@
 	msgview.style.top = document.getElementById('msglist').offsetHeight + 'px';
 	msgview.style.display = 'table';
 
-	/* Remove now useless loading message */
-	body.removeChild(document.getElementById('loading'));
-
 	/* And display the msgview */
 	body.appendChild(msgview);
 	document.documentElement.scrollTop = 0;
+
+	/* Remove now useless loading message */
+	body.removeChild(document.getElementById('loading'));
       }
     }
   }
@@ -436,34 +451,48 @@
   return true;
 }
 
-function parseMimeStructure (mime)
+function parseMimeStructure (id, mime)
 {
+  var count = 0;
   var i = 0;
   var str = '';
 
   while (mime[i]) {
-
     /* If the node is a MIME part, output its entry */
     if (mime[i].nodeName == "part") {
+      var length = parseInt(mime[i].getAttribute('length'));
+
       str += '<li>';
 
+      if (length) {
+	str += '<a href="' + _baseURI + _mbox + '/raw/' + id +
+	  mime[i].getAttribute('link') + '" target="_blank">';
+      }
+
       var name = mime[i].getAttribute('name');
       if (name) {
-	str += 'name';
+	str += name;
       }
       else {
 	str += 'Unnamed ' + mime[i].getAttribute('ct');
       }
 
+      if (length) {
+	str += '</a>';
+      }
+
       str += ' (' + mime[i].getAttribute('cd') + ', ' +
-	mime[i].getAttribute('cte') + ', ' +
-	mime[i].getAttribute('length') + ' bytes)</li>';
+	mime[i].getAttribute('cte') + ', ' + length + ' bytes)</li>';
+
+      count++;
     }
 
     /* Otherwise it's a MIME multipart, recurse */
     else if (mime[i].nodeName == "mime") {
-      str += '<ul>' + parseMimeStructure (mime[i].childNodes) + '</ul>';
+      str += '<ul>' +
+	parseMimeStructure (id, mime[i].childNodes) + '</ul>';
     }
+
     i++;
   }
 
@@ -521,6 +550,7 @@
 
 	str += '</tbody>';
 	boxlist.innerHTML = str;
+
 	body.appendChild(boxlist);
       }
     }

Modified: httpd/mod_mbox/branches/httpd-mbox-if/data/style.css
URL: http://svn.apache.org/viewcvs/httpd/mod_mbox/branches/httpd-mbox-if/data/style.css?rev=263880&r1=263879&r2=263880&view=diff
==============================================================================
--- httpd/mod_mbox/branches/httpd-mbox-if/data/style.css (original)
+++ httpd/mod_mbox/branches/httpd-mbox-if/data/style.css Sun Aug 28 07:27:54 2005
@@ -15,8 +15,8 @@
 
 h1
 {
-  font-size: xx-large;
-  margin: 0.5em 2%;
+  font-size: x-large;
+  margin: 1em 2% 0.5em 2%;
   padding: 0;
 }
 
@@ -68,6 +68,7 @@
 
 table thead th a, table tfoot th a { color: white; }
 table td { padding: 0.2em 0.5em; }
+table th a { text-decoration: underline; }
 
 table#grid
 {
@@ -140,8 +141,10 @@
 {
   position: absolute;
   top: 6em;
-  margin: 0 2% 2em auto;
-  width: 82%;
+  right: 2%;
+  left: 14%;
+  margin: 0 0 2em 0;
+  width: 84%;
 }
 
 table#msglist thead th.title { text-align: left; white-space: nowrap; }
@@ -172,7 +175,9 @@
 {
   position: absolute;
   top: 6em;
-  margin: 0 2% 2em 2%;
+  right: 2%;
+  left: 2%;
+  margin: 0 0 2em 0;
   width: 96%;
 }
 
@@ -180,8 +185,10 @@
 {
   position: absolute;
   top: 0;
-  margin: 5em 2% 2em auto;
-  width: 82%;
+  right: 2%;
+  left: 14%;
+  margin: 5em 0 2em 0;
+  width: 84%;
   display: none;
 }
 

Modified: httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mbox_parse.h
URL: http://svn.apache.org/viewcvs/httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mbox_parse.h?rev=263880&r1=263879&r2=263880&view=diff
==============================================================================
--- httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mbox_parse.h (original)
+++ httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mbox_parse.h Sun Aug 28 07:27:54 2005
@@ -128,14 +128,18 @@
 struct Message_Struct
 {
     ID msgID;
+
     char *from;
     char *str_from;
+
     char *subject;
+
+    apr_time_t date;
+    char *str_date;
+
     char *content_type;
     char *boundary;
     mbox_cte_e cte;
-    apr_time_t date;
-    char *str_date;
 
     apr_table_t *references;
     char *raw_ref;
@@ -143,6 +147,9 @@
     apr_off_t msg_start;
     apr_off_t body_start;
     apr_off_t body_end;
+
+    char *raw_msg;
+    char *raw_body;
 
     mbox_mime_message_t *mime_msg;
 };

Modified: httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox.h
URL: http://svn.apache.org/viewcvs/httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox.h?rev=263880&r1=263879&r2=263880&view=diff
==============================================================================
--- httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox.h (original)
+++ httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox.h Sun Aug 28 07:27:54 2005
@@ -127,10 +127,12 @@
 mbox_mime_message_t *mbox_mime_decode_multipart(apr_pool_t *p, char *body,
 						char *ct, mbox_cte_e cte,
 						char *boundary);
+char *mbox_mime_decode_body(apr_pool_t *p, mbox_mime_message_t *m);
 char *mbox_mime_get_body(apr_pool_t *p, mbox_mime_message_t *m);
 void mbox_mime_display_static_structure(request_rec *r, mbox_mime_message_t *m,
 					char *link);
-void mbox_mime_display_xml_structure(request_rec *r, mbox_mime_message_t *m);
+void mbox_mime_display_xml_structure(request_rec *r, mbox_mime_message_t *m,
+				     char *link);
 
 /* Utility functions */
 char *mbox_wrap_text(char *str);

Modified: httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_file.c
URL: http://svn.apache.org/viewcvs/httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_file.c?rev=263880&r1=263879&r2=263880&view=diff
==============================================================================
--- httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_file.c (original)
+++ httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_file.c Sun Aug 28 07:27:54 2005
@@ -21,7 +21,6 @@
 {
     apr_size_t len = 0;
     Message *m;
-    char *body;
 
     /* Fetch message from mbox backend */
     m = mbox_fetch_index(r, f, msgID);
@@ -32,22 +31,20 @@
     r->mtime = m->date;
     ap_set_last_modified(r);
 
-    /* Fetch message body (from body_start to body_end) */
-    if (apr_file_seek(f, APR_SET, &m->body_start) != APR_SUCCESS) {
+    /* Fetch message (from msg_start to body_end) */
+    if (apr_file_seek(f, APR_SET, &m->msg_start) != APR_SUCCESS) {
         return NULL;
     }
 
-    len = m->body_end - m->body_start;
-    body = apr_palloc(r->pool, len+1);
+    len = m->body_end - m->msg_start;
+    m->raw_msg = apr_palloc(r->pool, len+1);
 
-    if (apr_file_read(f, body, &len) != APR_SUCCESS) {
+    if (apr_file_read(f, m->raw_msg, &len) != APR_SUCCESS) {
         return NULL;
     }
 
-    body[len] = '\0';
-
-    m->mime_msg = mbox_mime_decode_multipart(r->pool, body, m->content_type,
-					     m->cte, m->boundary);
+    m->raw_msg[len] = '\0';
+    m->raw_body = m->raw_msg + (m->body_start - m->msg_start);
 
     return m;
 }
@@ -273,6 +270,8 @@
 	else
 	    status = mbox_xml_message(r, f);
     }
+    else if (strncmp(r->path_info, "/raw", 4) == 0)
+      status = mbox_raw_message(r, f);
     else {
 	/* Set content type */
 	r->content_type = "text/html";
@@ -290,8 +289,6 @@
 	else if (strcmp(r->path_info, "/date") == 0)
             status = mbox_static_msglist(r, f, MBOX_SORT_DATE);
 
-        else if (strcmp(r->path_info, "/raw") == 0)
-            status = mbox_raw_message(r, f);
         else
             status = mbox_static_message(r, f);
     }

Modified: httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_mime.c
URL: http://svn.apache.org/viewcvs/httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_mime.c?rev=263880&r1=263879&r2=263880&view=diff
==============================================================================
--- httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_mime.c (original)
+++ httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_mime.c Sun Aug 28 07:27:54 2005
@@ -256,6 +256,31 @@
     return mail;
 }
 
+/* Decode a MIME part body, according to its CTE. */
+char *mbox_mime_decode_body(apr_pool_t *p, mbox_mime_message_t *m)
+{
+    apr_size_t len = m->body_len;
+    char *new_body = NULL;
+
+    if (m->cte == CTE_BASE64) {
+        new_body = apr_pstrndup(p, m->body, m->body_len);
+	len = mbox_cte_decode_b64(new_body);
+
+	new_body[len] = 0;
+	m->body = new_body;
+    }
+    else if (m->cte == CTE_QP) {
+        new_body = apr_pstrndup(p, m->body, m->body_len);
+	len = mbox_cte_decode_qp(m->body);
+
+	new_body[len] = 0;
+	m->body = new_body;
+    }
+
+    return m->body;
+}
+
+
 /* This function returns the relevant MIME part from a message. For
  * the moment, it just returns the first text/ MIME part available.
  */
@@ -268,22 +293,7 @@
     }
 
     if (strncmp(m->content_type, "text/", strlen("text/")) == 0) {
-	char *new_body = NULL;
-
-        if (m->cte == CTE_BASE64) {
-	    new_body = apr_pstrndup(p, m->body, m->body_len);
-            m->body_len = mbox_cte_decode_b64(new_body);
-
-	    m->body = new_body;
-	    m->body[m->body_len] = 0;
-	}
-	else if (m->cte == CTE_QP) {
-  	    new_body = apr_pstrndup(p, m->body, m->body_len);
-            m->body_len = mbox_cte_decode_qp(m->body);
-
-	    m->body = new_body;
-	    m->body[m->body_len] = 0;
-	}
+        char *new_body = mbox_mime_decode_body(p, m);
 
 	m->body_len = mbox_cte_escape_html(p, m->body, m->body_len, &new_body);
 	m->body = new_body;
@@ -312,7 +322,11 @@
         return;
     }
 
-    ap_rprintf(r, "<li><a href=\"%s\">", link);
+    ap_rputs("<li>", r);
+
+    if (m->body_len) {
+        ap_rprintf(r, "<a href=\"%s\">", link);
+    }
 
     if (m->content_name) {
         ap_rprintf(r, "%s", m->content_name);
@@ -321,7 +335,11 @@
         ap_rprintf(r, "Unnamed %s", m->content_type);
     }
 
-    ap_rprintf(r, "</a> (%s, %s, %u bytes)</li>\n", m->content_disposition,
+    if (m->body_len) {
+        ap_rputs("</a>", r);
+    }
+
+    ap_rprintf(r, " (%s, %s, %u bytes)</li>\n", m->content_disposition,
 	       mbox_cte_to_char(m->cte), m->body_len);
 
     if (!m->sub) {
@@ -337,7 +355,8 @@
 }
 
 /* Display an XML MIME structure */
-void mbox_mime_display_xml_structure(request_rec *r, mbox_mime_message_t *m)
+void mbox_mime_display_xml_structure(request_rec *r, mbox_mime_message_t *m,
+				     char *link)
 {
     int i;
 
@@ -346,14 +365,16 @@
     }
 
     if (m->content_name) {
-        ap_rprintf(r, "<part name=\"%s\" cd=\"%s\" cte=\"%s\" length=\"%u\" />\n",
+        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);
+		   mbox_cte_to_char(m->cte), m->body_len, link);
     }
     else {
-        ap_rprintf(r, "<part ct=\"%s\" cd=\"%s\" cte=\"%s\" length=\"%u\" />\n",
+        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);
+		   mbox_cte_to_char(m->cte), m->body_len, link);
     }
 
     if (!m->sub) {
@@ -362,7 +383,8 @@
 
     ap_rputs("<mime>\n", r);
     for (i=0 ; i<m->sub_count ; i++) {
-        mbox_mime_display_xml_structure(r, m->sub[i]);
+        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/branches/httpd-mbox-if/module-2.0/mod_mbox_out.c
URL: http://svn.apache.org/viewcvs/httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_out.c?rev=263880&r1=263879&r2=263880&view=diff
==============================================================================
--- httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_out.c (original)
+++ httpd/mod_mbox/branches/httpd-mbox-if/module-2.0/mod_mbox_out.c Sun Aug 28 07:27:54 2005
@@ -740,39 +740,78 @@
 /* Display a raw mail from cache. No processing is done here. */
 apr_status_t mbox_raw_message(request_rec *r, apr_file_t *f)
 {
-    apr_size_t len = 0;
-
+    mbox_mime_message_t *mime_part;
     Message *m;
 
-    char *msgID, *body;
+    char *msgID, *part, *end;
 
-    /* Fetch message ID */
-    if (!r->args)
-        return HTTP_NOT_FOUND;
+    /* Fetch message ID (offset is 5 : '/raw') and eventual MIME part
+       number. */
+    msgID = r->path_info+5;
+
+    part = strchr(msgID, '/');
+    if (part) {
+        *part = 0;
+	part++;
+    }
 
-    msgID = r->args;
     ap_unescape_url(msgID);
 
     /* Fetch message */
-    m = mbox_fetch_index(r, f, msgID);
+    m = fetch_message(r, f, msgID);
     if (!m) {
         return HTTP_NOT_FOUND;
     }
 
-    /* Fetch message contents (from msg_start to body_end) */
-    if (apr_file_seek(f, APR_SET, &m->msg_start) != APR_SUCCESS)
-        return HTTP_NOT_FOUND;
+    /* No MIME part specified : output whole message and return. */
+    if (!part) {
+        ap_set_content_type(r, "text/plain");
+	ap_rprintf(r, "%s", m->raw_msg);
+	return OK;
+    }
+
+    /* First, parse the MIME structure, and look for the correct
+       subpart */
+    m->mime_msg = mbox_mime_decode_multipart(r->pool, m->raw_body,
+					     m->content_type,
+					     m->cte, m->boundary);
+
+    mime_part = m->mime_msg;
+
+    do {
+        int num;
+
+	end = strchr(part, '/');
+	if (end) {
+	    *end = 0;
+	    num = atoi(part);
+	    *end = '/';
+
+	    part = end + 1;
+	}
+	else {
+	    num = atoi(part);
+	}
+
+	if (mime_part &&
+	    (num <= mime_part->sub_count) &&
+	    mime_part->sub[num - 1]) {
+	    mime_part = mime_part->sub[num - 1];
+	}
+	else {
+	    return HTTP_NOT_FOUND;
+	}
+    } while (*part && end);
 
-    len = m->body_end - m->msg_start;
-    body = apr_palloc(r->pool, len+1);
-
-    if (apr_file_read(f, body, &len) != APR_SUCCESS)
-        return HTTP_INTERNAL_SERVER_ERROR;
-
-    body[len] = '\0';
+    if (strncmp(mime_part->content_type, "multipart/", 10) == 0) {
+        ap_set_content_type(r, "text/plain");
+    }
+    else {
+        ap_set_content_type(r, mime_part->content_type);
+    }
 
-    ap_set_content_type(r, "text/plain");
-    ap_rprintf(r, "%s", body);
+    mime_part->body[mime_part->body_len] = 0;
+    ap_rprintf(r, "%s", mbox_mime_decode_body(r->pool, mime_part));
 
     return OK;
 }
@@ -839,20 +878,25 @@
     mbox_dir_cfg_t *conf;
     Message *m;
 
-    char *baseURI, *from, **context;
+    char *baseURI, *from, **context, *msgID;
 
     conf = ap_get_module_config(r->per_dir_config, &mbox_module);
     baseURI = get_base_uri(r);
 
+    msgID = r->path_info+1;
+    ap_unescape_url(msgID);
+
     /* msgID should be the part of the URI that Apache could not resolve
-     * on its own.  Grab it and skip over the expected /.
-     */
-    m = fetch_message(r, f, r->path_info+1);
+     * on its own.  Grab it and skip over the expected /. */
+    m = fetch_message(r, f, msgID);
     if (!m) {
         return HTTP_NOT_FOUND;
     }
 
-    /* FIXME: handle attachment viewing/downloading from here ? */
+    /* Parse multipart information */
+    m->mime_msg = mbox_mime_decode_multipart(r->pool, m->raw_body,
+					     m->content_type,
+					     m->cte, m->boundary);
 
     ap_rputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", r);
     ap_rputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n", r);
@@ -927,13 +971,13 @@
 	     "    <td class=\"left\">Mime</td>\n"
 	     "    <td class=\"right\">\n<ul>\n", r);
     mbox_mime_display_static_structure(r, m->mime_msg,
-				       apr_psprintf(r->pool, "%s/%s",
+				       apr_psprintf(r->pool, "%s/raw/%s",
 						    baseURI, m->msgID));
     ap_rputs("</ul>\n</td>\n</tr>\n", r);
 
     ap_rprintf(r, "   <tr class=\"raw\">\n"
 	       "    <td class=\"left\"></td>\n"
-	       "    <td class=\"right\"><a href=\"%s/raw?%s\">View raw message</a></td>\n"
+	       "    <td class=\"right\"><a href=\"%s/raw/%s\">View raw message</a></td>\n"
 	       "   </tr>\n", baseURI, URI_ESCAPE_OR_BLANK(r->pool, m->msgID));
 
     ap_rputs("   </tbody>\n", r);
@@ -951,17 +995,25 @@
     mbox_dir_cfg_t *conf;
     Message *m;
 
-    char *baseURI, *from;
+    char *baseURI, *from, *msgID;
 
     conf = ap_get_module_config(r->per_dir_config, &mbox_module);
     baseURI = get_base_uri(r);
 
     /* Here, we skip 6 chars (/ajax/). */
-    m = fetch_message(r, f, r->path_info+6);
+    msgID = r->path_info+6;
+    ap_unescape_url(msgID);
+
+    m = fetch_message(r, f, msgID);
     if (!m) {
         return HTTP_NOT_FOUND;
     }
 
+    /* Parse multipart information */
+    m->mime_msg = mbox_mime_decode_multipart(r->pool, m->raw_body,
+					     m->content_type,
+					     m->cte, m->boundary);
+
     ap_rputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", r);
 
     from = ESCAPE_OR_BLANK(r->pool, m->from);
@@ -983,7 +1035,7 @@
     ap_rprintf(r, "%s", mbox_wrap_text(mbox_mime_get_body(r->pool, m->mime_msg)));
     ap_rputs("]]></contents>\n", r);
     ap_rputs(" <mime>\n", r);
-    mbox_mime_display_xml_structure(r, m->mime_msg);
+    mbox_mime_display_xml_structure(r, m->mime_msg, "");
     ap_rputs(" </mime>\n", r);
     ap_rputs("</mail>\n", r);