httpd-mbox-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Justin Erenkrantz <jus...@erenkrantz.com>
Subject Re: [PATCH] MIME Support & More
Date Fri, 11 Mar 2005 15:51:44 GMT
On Fri, Mar 11, 2005 at 12:26:56AM -0800, Paul Querna wrote:
> This is mostly a work in progress. I know there are several very very 
> bad parts in the patch, and I hope to smooth it over this weekend. 
> After saying that, I would love some feedback.

Excellent to see some patches.  Heh.  =)

> Index: mbox_parse.c
> ===================================================================
> --- mbox_parse.c	(revision 156118)
> +++ mbox_parse.c	(working copy)
> @@ -37,6 +37,8 @@
>  #include "apr_dbm.h"
>  #include "apr_hash.h"
>  #include "apr_date.h"
> +#include "apr_lib.h"
> +#include <assert.h>

No asserts please.  These are generally bad.  Just change that lone
assert statement to return APR_EGENERAL or something instead...

>  #define PUT_FIELD(r, table, name, db, keyBase) \
>      temp = apr_table_get(table, name); \
> @@ -45,14 +47,21 @@
>  
>  #define OPEN_DBM(r, db, flags, suffix, temp, status) \
>      temp = apr_pstrcat(r->pool, r->filename, suffix, NULL); \
> -    status = apr_dbm_open(&db, temp, flags, APR_OS_DEFAULT, r->pool );
> +    status = apr_dbm_open_ex(&db, "DB", temp, flags, APR_OS_DEFAULT, r->pool );

Is there a particular reason why we must use BDB now?  I'd prefer to remain
agnostic here.  The other issue is that this just currently uses whatever the
default DB is - so if you pass --with-dbm=gdbm or --with-dbm=db43 to
apr-util's configure, that will be the default.

> -#define MSGID_DBM_SUFFIX "msgdbm"
> -#define FROM_DBM_SUFFIX "fromdbm"
> -#define SUBJECT_DBM_SUFFIX "subjdbm"
> -#define DATE_DBM_SUFFIX "datedbm"
> -#define REFERENCE_DBM_SUFFIX "refdbm"
> +#define MSGID_DBM_SUFFIX ".msgsum"
>  
> +typedef struct mb_dbm_data {
> +    int location;
> +    mbox_cte_e cte;
> +    apr_time_t date;
> +    const char* from;
> +    const char* subject;
> +    const char* references;
> +    const char* content_type;
> +    const char* boundary;
> +} mb_dbm_data;
> +
>  /* Fills the MBOX_BUFF structure with data from the backing file descriptor.
>   * Will read up to MBOX_BUFF->maxlen-(remaining in buffer) bytes.
>   */
> @@ -589,29 +598,131 @@
>      if (!m->subject || !*m->subject)
>          m->subject = "[No Subject]";
>  
> -    /* FIXME: Note that apr_parsedate came from server/util.c, and
> -     * belongs in a new location in apr or apr-util.
> -     */
> -    m->date = apr_date_parse_rfc(m->raw_date);
> +    if (!m->content_type || !*m->content_type)
> +        m->content_type = "text/plain";
> +
>      m->str_date = (char*)apr_pcalloc(r->pool, APR_RFC822_DATE_LEN);
>      apr_rfc822_date(m->str_date, m->date);
>  
>      /* Parse the references into a table. */
>      parse_references(r, m);
> +}
>  
> -    /* Parse the date into an integer. */
> -    if (m->raw_location)
> +
> +#define fetch_cstring(where) \
> +    memcpy(&tlen, msgValue.dptr+pos, sizeof(tlen)); \
> +    pos += sizeof(tlen); \
> +    if (tlen == 0) { \
> +        where = NULL; \
> +    } \
> +    else { \
> +        where = apr_pmemdup(pool, msgValue.dptr+pos, tlen); \
> +        pos += tlen; \
> +    }

Can this macro become independent?  i.e. have no external dependencies...
It probably also needs a do while(0) loop for portability.

Same goes for the other multi-line macros...

> +
> +#define sstrlen(str) (str ? strlen(str) + 1 : 0)
> +static apr_status_t fetch_msgc(apr_pool_t* pool, apr_dbm_t* database, const char* key, mb_dbm_data* msgc)
> +{
> +    apr_datum_t msgKey, msgValue;
> +    char *temp;
> +    int status;
> +    int pos = 0;
> +    apr_uint16_t tlen = 0;
> +
> +    msgKey.dptr = (char*) key;
> +    msgKey.dsize = strlen(key) + 1;
> +
> +    status = apr_dbm_fetch(database, msgKey, &msgValue);
> +
> +    if (status != APR_SUCCESS || !msgValue.dptr || !msgValue.dsize)
>      {
> -        m->location = strtol(m->raw_location, &temp, 10);
> +        /* TODO: Error out. */
> +        return APR_EGENERAL;
> +    }
> +    
> +    memcpy(&msgc->location, msgValue.dptr+pos, sizeof(msgc->location));
> +    pos += sizeof(msgc->location);
>  
> -        /* FIXME: Come up with better error condition. */
> -        if (*temp != '\0')
> -           m->location = 0;
> +    memcpy(&msgc->date, msgValue.dptr+pos, sizeof(msgc->date));
> +    pos += sizeof(msgc->date);
> +
> +    memcpy(&msgc->cte, msgValue.dptr+pos, sizeof(msgc->cte));
> +    pos += sizeof(msgc->cte);
> +
> +    fetch_cstring(msgc->from);
> +    fetch_cstring(msgc->subject);
> +    fetch_cstring(msgc->references);
> +    fetch_cstring(msgc->content_type);
> +    fetch_cstring(msgc->boundary);
> +
> +    return APR_SUCCESS;
> +}
> +
> +#define store_cstring(source) \
> +    tlen = sstrlen(source); \
> +    memcpy(value+pos, &tlen, sizeof(tlen)); \
> +    pos += sizeof(tlen); \
> +    memcpy(value+pos, source, tlen); \
> +    pos += tlen;
> +
> +static apr_status_t store_msgc(apr_pool_t* pool, apr_dbm_t* database, const char* key, mb_dbm_data* msgc) 
> +{
> +    apr_datum_t msgKey, msgValue;
> +    int vlen;
> +    int pos = 0;
> +    apr_uint16_t tlen = 0;
> +    char* value;
> +
> +    if (!database || !key || !msgc)
> +        return APR_EGENERAL;  
> +   
> +    msgKey.dptr = (char*) key;
> +    /* We add one to the strlen to encompass the term null */
> +    msgKey.dsize = strlen(key) + 1;
> +    
> +    /* We store the entire structure in a single entry. */
> +    vlen = sizeof(msgc->location) + sizeof(msgc->date) + sizeof(msgc->cte) + \
> +           (sizeof(tlen) * 5) + \
> +           sstrlen(msgc->from) + \
> +           sstrlen(msgc->subject) + \
> +           sstrlen(msgc->references) + \
> +           sstrlen(msgc->content_type) + \
> +           sstrlen(msgc->boundary);
> +
> +    value = apr_palloc(pool, vlen);
> +
> +    memcpy(value+pos, &msgc->location, sizeof(msgc->location));
> +    pos += sizeof(msgc->location);
> +
> +    memcpy(value+pos, &msgc->date, sizeof(msgc->date));
> +    pos += sizeof(msgc->date);
> +
> +    memcpy(value+pos, &msgc->cte, sizeof(msgc->cte));
> +    pos += sizeof(msgc->cte);
> +    
> +    store_cstring(msgc->from);
> +    store_cstring(msgc->subject);
> +    store_cstring(msgc->references);
> +    store_cstring(msgc->content_type);
> +    store_cstring(msgc->boundary);
> +
> +    msgValue.dptr = (char*) value;
> +    msgValue.dsize = pos;
> +    assert(pos == vlen);
> +    return apr_dbm_store(database, msgKey, msgValue);
> +}
> +
> +/* This function is stolen from server/util.c, since we need to be able to run
> + * standalone, without the httpd core... sigh. */
> +void ex_ap_str_tolower(char *str)
> +{  
> +    while (*str) {
> +        *str = apr_tolower(*str);
> +        ++str;
>      }
> -    else
> -        m->location = 0;
>  }
>  
> +
>  /* This function will generate the appropriate DBMs for a given mbox file.
>   *
>   * Currently, there is a DBM generated for each specific header we wish
> @@ -621,7 +732,8 @@
>  {
>      apr_status_t status;
>      apr_table_t * table;
> -    apr_dbm_t *msgDB, *fromDB, *subjectDB, *dateDB, *refDB;
> +    apr_dbm_t *msgDB;
> +    apr_pool_t* tpool;
>  #ifdef APR_HAS_MMAP
>      apr_finfo_t fi;
>  #else
> @@ -630,6 +742,7 @@
>      MBOX_BUFF b;
>      long location;
>      const char *temp, *msgID;
> +    mb_dbm_data msgc;
>  
>  #ifdef APR_HAS_MMAP
>      status = apr_file_name_get(&temp, f);
> @@ -662,13 +775,10 @@
>      b.len = 0;
>  
>      OPEN_DBM(r, msgDB, APR_DBM_RWCREATE, MSGID_DBM_SUFFIX, temp, status);
> -    OPEN_DBM(r, fromDB, APR_DBM_RWCREATE, FROM_DBM_SUFFIX, temp, status);
> -    OPEN_DBM(r, subjectDB, APR_DBM_RWCREATE, SUBJECT_DBM_SUFFIX, temp, status);
> -    OPEN_DBM(r, dateDB, APR_DBM_RWCREATE, DATE_DBM_SUFFIX, temp, status);
> -    OPEN_DBM(r, refDB, APR_DBM_RWCREATE, REFERENCE_DBM_SUFFIX, temp, status);
>  
>      mbox_fillbuf(&b);
>  
> +    apr_pool_create(&tpool, r->pool);
>      /* When we reach the end of the file, b is NULL.  */
>      while (b.b)
>      {
> @@ -689,28 +799,82 @@
>              location = b.totalread - b.len + b.b - b.rb;
>  #endif
>              msgID = apr_table_get(table, "Message-ID");
> -            if (msgID)
> -            {
> -                put_field_int(msgDB, msgID, location, r->pool);
> -                PUT_FIELD(r, table, "From", fromDB, msgID);
> -                PUT_FIELD(r, table, "Subject", subjectDB, msgID);
> -                PUT_FIELD(r, table, "Date", dateDB, msgID);
> -                PUT_FIELD(r, table, "References", refDB, msgID);
> +            if (msgID) {
> +                msgc.location = location;
> +                msgc.from = apr_table_get(table, "From");
> +                msgc.subject = apr_table_get(table, "Subject");
> +                temp = apr_table_get(table, "Date");
> +                if (temp) {
> +                    msgc.date = apr_date_parse_rfc(temp);
> +                }
> +                else {
> +                    msgc.date = 0;
> +                }
> +                msgc.references = apr_table_get(table, "References");
> +
> +                temp = apr_table_get(table, "Content-Transfer-Encoding");
> +                if (temp) {
> +                    char* p = apr_pstrdup(tpool, temp);
> +                    msgc.cte = parse_cte_header(p);
> +                }
> +                else {
> +                    msgc.cte = CTE_NONE;
> +                }
> +
> +                temp = apr_table_get(table, "Content-Type");
> +                if (temp) {
> +                    char* p;
> +                    temp = apr_pstrdup(tpool, temp);
> +                    msgc.boundary = strcasestr(temp, "boundary=");
> +                    if (msgc.boundary) {
> +                        msgc.boundary += sizeof("boundary=") - 1;
> +                        if (msgc.boundary[0] == '"') {
> +                            ++msgc.boundary;
> +                            if (p = strstr(msgc.boundary, "\"")) {
> +                                *p = '\0';
> +                            }
> +                        }
> +                        else {
> +                            if (p = strstr(msgc.boundary, ";")) {
> +                                *p = '\0';
> +                            }
> +                        }
> +                    }
> +                    p = strstr(temp, ";");
> +                    if (p) {
> +                        *p = '\0';
> +                    }
> +                    msgc.content_type = temp;
> +                    /* Some old clients only sent 'text', 
> +                     * instead of 'text/plain'. Lets try to be nice to them */
> +                    if (!strcasecmp(msgc.content_type, "text")) {
> +                        msgc.content_type = "text/plain";
> +                    }
> +                    else {
> +                        /* Normalize the Content-Type */
> +                        ex_ap_str_tolower((char*)msgc.content_type);
> +                    }
> +                }
> +                else {
> +                    msgc.content_type = NULL;
> +                    msgc.boundary = NULL;
> +                }
> +
> +                store_msgc(tpool, msgDB, msgID, &msgc);
>              }
>          }
> -        else
> +        else {
>              skipLine(&b);
> +        }
> +        apr_pool_clear(tpool);
>      }
>  
> +    apr_pool_destroy(tpool);
>      apr_dbm_close(msgDB);
> -    apr_dbm_close(fromDB);
> -    apr_dbm_close(subjectDB);
> -    apr_dbm_close(dateDB);
> -    apr_dbm_close(refDB);
>  #ifdef APR_HAS_MMAP 
>      apr_mmap_delete(b.mm);
>  #else
> -/* If we aren't using MMAP, we relied on the open file passed in. */
> +    /* If we aren't using MMAP, we relied on the open file passed in. */
>  #endif
>      return OK;
>  }
> @@ -722,9 +886,11 @@
>  {
>      apr_status_t status;
>      MBOX_LIST *head, *keys;
> -    apr_dbm_t *msgDB, *fromDB, *subjectDB, *dateDB, *refDB;
> +    apr_dbm_t *msgDB;
>      apr_datum_t msgKey;
>      char *temp;
> +    mb_dbm_data msgc;
> +    apr_pool_t* tpool;
>      Message *curMsg;
>  
>      OPEN_DBM(r, msgDB, APR_DBM_READONLY, MSGID_DBM_SUFFIX, temp, status);
> @@ -732,16 +898,12 @@
>      if (status != APR_SUCCESS)
>          return NULL;
>  
> -    OPEN_DBM(r, fromDB, APR_DBM_READONLY, FROM_DBM_SUFFIX, temp, status);
> -    OPEN_DBM(r, subjectDB, APR_DBM_READONLY, SUBJECT_DBM_SUFFIX, temp, status);
> -    OPEN_DBM(r, dateDB, APR_DBM_READONLY, DATE_DBM_SUFFIX, temp, status);
> -    OPEN_DBM(r, refDB, APR_DBM_READONLY, REFERENCE_DBM_SUFFIX, temp, status);
> -
>      /* APR SDBM iteration is badly broken.  You can't skip around during
>       * an iteration.  Fixing this would be nice.
>       */
>      keys = 0;
>      head = 0;
> +    apr_pool_create(&tpool, r->pool);
>      status = apr_dbm_firstkey(msgDB, &msgKey);
>      while (msgKey.dptr != 0 && status == APR_SUCCESS)
>      {
> @@ -751,13 +913,21 @@
>          /* FIXME: When we evolve to MD5 hashes, switch this */
>          curMsg->msgID = apr_pstrndup(r->pool, msgKey.dptr, msgKey.dsize);
>  
> -        /* Load these fields from their respective DBMs */
> -        curMsg->from = get_field_value(r, fromDB, curMsg->msgID, NULL);
> -        curMsg->subject = get_field_value(r, subjectDB, curMsg->msgID, NULL);
> -        curMsg->raw_date = get_field_value(r, dateDB, curMsg->msgID, NULL);
> -        curMsg->raw_ref = get_field_value(r, refDB, curMsg->msgID, NULL);
> -        curMsg->raw_location = get_field_value(r, msgDB, curMsg->msgID, NULL);
> +        status = fetch_msgc(tpool, msgDB, curMsg->msgID, &msgc);
>  
> +        if (status != APR_SUCCESS)
> +            break;
> +
> +        curMsg->from = apr_pstrdup(r->pool, msgc.from);
> +        curMsg->subject = apr_pstrdup(r->pool, msgc.subject);
> +        curMsg->content_type = apr_pstrdup(r->pool, msgc.content_type);
> +        curMsg->boundary = apr_pstrdup(r->pool, msgc.boundary);
> +        curMsg->date = msgc.date;
> +        curMsg->raw_ref = apr_pstrdup(r->pool, msgc.references);
> +        curMsg->location = msgc.location;
> +        curMsg->cte = msgc.cte;
> +        apr_pool_clear(tpool);
> +
>          /* Normalize the message and perform tweaks on it */
>          normalize_message(r, curMsg);
>  
> @@ -767,11 +937,8 @@
>          status = apr_dbm_nextkey(msgDB, &msgKey);
>      }
>  
> +    apr_pool_destroy(tpool);
>      apr_dbm_close(msgDB);
> -    apr_dbm_close(fromDB);
> -    apr_dbm_close(subjectDB);
> -    apr_dbm_close(dateDB);
> -    apr_dbm_close(refDB);
>  
>      return head;
>  }
> @@ -783,10 +950,11 @@
>  Message* fetch_index(request_rec *r, apr_file_t *f, const char *msgID)
>  {
>      apr_status_t status;
> -    apr_dbm_t *msgDB, *fromDB, *subjectDB, *dateDB, *refDB;
> +    apr_dbm_t *msgDB;
>      apr_datum_t msgKey;
>      char *temp;
>      Message *curMsg = NULL;
> +    mb_dbm_data msgc;
>  
>      /* If the message ID passed in is blank. */
>      if (!msgID || *msgID == '\0')
> @@ -797,11 +965,6 @@
>      if (status != APR_SUCCESS)
>          return NULL;
>  
> -    OPEN_DBM(r, fromDB, APR_DBM_READONLY, FROM_DBM_SUFFIX, temp, status);
> -    OPEN_DBM(r, subjectDB, APR_DBM_READONLY, SUBJECT_DBM_SUFFIX, temp, status);
> -    OPEN_DBM(r, dateDB, APR_DBM_READONLY, DATE_DBM_SUFFIX, temp, status);
> -    OPEN_DBM(r, refDB, APR_DBM_READONLY, REFERENCE_DBM_SUFFIX, temp, status);
> -
>      msgKey.dptr = (char*)msgID;
>      /* We add one to the strlen to encompass the term null */
>      msgKey.dsize = strlen(msgID) + 1;
> @@ -812,21 +975,110 @@
>      /* FIXME: When we evolve to MD5 hashes, switch this */
>      curMsg->msgID = apr_pstrndup(r->pool, msgKey.dptr, msgKey.dsize);
>  
> -    /* Load these fields from their respective DBMs */
> -    curMsg->from = get_field_value(r, fromDB, curMsg->msgID, NULL);
> -    curMsg->subject = get_field_value(r, subjectDB, curMsg->msgID, NULL);
> -    curMsg->raw_date = get_field_value(r, dateDB, curMsg->msgID, NULL);
> -    curMsg->raw_ref = get_field_value(r, refDB, curMsg->msgID, NULL);
> -    curMsg->raw_location = get_field_value(r, msgDB, curMsg->msgID, NULL);
> +    status = fetch_msgc(r->pool, msgDB, curMsg->msgID, &msgc);
> +    if (status != APR_SUCCESS)
> +        return NULL;
>  
> +    curMsg->from = apr_pstrdup(r->pool, msgc.from);
> +    curMsg->subject = apr_pstrdup(r->pool, msgc.subject);
> +    curMsg->content_type = apr_pstrdup(r->pool, msgc.content_type);
> +    curMsg->boundary = apr_pstrdup(r->pool, msgc.boundary);
> +    curMsg->date = msgc.date;
> +    curMsg->raw_ref = apr_pstrdup(r->pool, msgc.references);
> +    curMsg->location = msgc.location;
> +    curMsg->cte = msgc.cte;
> +
>      /* Normalize the message and perform tweaks on it */
>      normalize_message(r, curMsg);
>  
>      apr_dbm_close(msgDB);
> -    apr_dbm_close(fromDB);
> -    apr_dbm_close(subjectDB);
> -    apr_dbm_close(dateDB);
> -    apr_dbm_close(refDB);
>  
>      return curMsg;
>  }
> +
> +
> +int mbox_msg_count(request_rec *r, char* path)
> +{
> +    apr_dbm_t *msgDB;
> +    int status;
> +    int count = 0;
> +    apr_datum_t msgKey;
> +    char* temp;
> +
> +    temp = apr_pstrcat(r->pool, r->filename, path, MSGID_DBM_SUFFIX, NULL);
> +
> +    status = apr_dbm_open_ex(&msgDB, "DB", temp, APR_DBM_READONLY,
> +                             APR_OS_DEFAULT, r->pool);
> +
> +    if (status != APR_SUCCESS)
> +        return 0;
> +
> +    /* FIXME: I think most DBMs have a faster method than
> +     *        iterating the keys..
> +     */
> +    status = apr_dbm_firstkey(msgDB, &msgKey);
> +    while (msgKey.dptr != 0 && status == APR_SUCCESS)
> +    {
> +        count++;
> +        status = apr_dbm_nextkey(msgDB, &msgKey);
> +    }
> +
> +    apr_dbm_close(msgDB);
> +
> +    return count;
> +}
> +
> +/**
> + * List of all C-T-E Types found on httpd-dev and FreeBSD-current:
> + *
> + * Content-Transfer-Encoding:      8bit
> + * Content-Transfer-Encoding:  7bit
> + * Content-Transfer-Encoding: 7BIT
> + * Content-Transfer-Encoding: 7Bit
> + * Content-Transfer-Encoding: 7bit
> + * Content-Transfer-Encoding: 8-bit
> + * Content-Transfer-Encoding: 8BIT
> + * Content-Transfer-Encoding: 8Bit
> + * Content-Transfer-Encoding: 8bit
> + * Content-Transfer-Encoding: BASE64
> + * Content-Transfer-Encoding: BINARY
> + * Content-Transfer-Encoding: Base64
> + * Content-Transfer-Encoding: QUOTED-PRINTABLE
> + * Content-Transfer-Encoding: Quoted-Printable
> + * Content-Transfer-Encoding: base64
> + * Content-Transfer-Encoding: binary
> + * Content-Transfer-Encoding: none
> + * Content-Transfer-Encoding: quoted-printable
> + * Content-Transfer-Encoding: x-uuencode
> + * Content-Transfer-Encoding:7bit
> + * Content-Transfer-Encoding:quoted-printable
> + *
> + * This is why we have RFCs.
> + */
> +
> +mbox_cte_e parse_cte_header(char* src) 
> +{
> +    ex_ap_str_tolower(src);
> +    if (strstr(src, "bi")) {
> +        if (strchr(src, '7')) {
> +            return CTE_7BIT;
> +        }
> +        else if (strchr(src, '8')) {
> +            return CTE_8BIT;
> +        }
> +        else if (strchr(src, 'y')) {
> +            return CTE_BINARY;
> +        }
> +    }
> +    else if (strchr(src, '6')) {
> +        return CTE_BASE64;
> +    }
> +    else if (strchr(src, 'q')) {
> +        return CTE_QP;
> +    }
> +    else if (strchr(src, 'u')) {
> +        return CTE_UUENCODE;
> +    }
> +
> +    return CTE_NONE;
> +}
> Index: build-dso
> ===================================================================
> --- build-dso	(revision 156118)
> +++ build-dso	(working copy)
> @@ -88,8 +88,9 @@
>  
>  build_helpers()
>  {
> +    $apxs_loc -c -p -o libcommon.a $common_files
>      for prg in $helper_programs; do
> -      $apxs_loc -c -p -o $prg $prg.c $common_files
> +      $apxs_loc -c -p -o $prg $prg.c .libs/libcommon.a
>        retval=$?
>        if test $retval -ne 0; then
>          exit $retval

=)

> Index: mbox_parse.h
> ===================================================================
> --- mbox_parse.h	(revision 156118)
> +++ mbox_parse.h	(working copy)
> @@ -78,6 +78,21 @@
>      void * value;
>  };
>  
> +/*
> + * All possible Content-Transfer-Encodings.
> + */
> +typedef enum {
> +    CTE_NONE = 0,
> +    CTE_7BIT = 1,
> +    CTE_8BIT = 2,
> +    CTE_UUENCODE = 3,
> +    CTE_BINARY = 4,
> +    CTE_QP = 5,
> +    CTE_BASE64 = 6,
> +} mbox_cte_e;
> +
> +mbox_cte_e parse_cte_header(char* src);
> +

Could we pick a slightly more descriptive acronym that mbox_cte_e?  It
works, but it's a little ambiguous until you know what it means.  =)

>  /* The following is based on Jamie Zawinski's description of the Netscape 3.x
>   * threading algorithm at <http://www.jwz.org/doc/threading.html>.
>   */
> @@ -98,13 +113,14 @@
>      char * from;
>      char * str_from;
>      char * subject;
> +    char * content_type;
> +    char * boundary;
>      apr_time_t date;
>      char * str_date;
> -    char * raw_date;
>      apr_table_t *references;
>      char * raw_ref;
>      apr_off_t location;
> -    char * raw_location;
> +    mbox_cte_e cte;
>  };
>  
>  /* The threading information about a message. */
> @@ -146,4 +162,7 @@
>   */
>  Message* fetch_index(request_rec *r, apr_file_t * f, const char * msgID);
>  
> +/* Get A total message count for a file. */
> +int mbox_msg_count(request_rec *r, char* path);

Get a total..

> +
>  #endif
> Index: mod_mbox.c
> ===================================================================
> --- mod_mbox.c	(revision 156118)
> +++ mod_mbox.c	(working copy)
> @@ -75,6 +75,10 @@
>  
>  #define MBOX_OUT_MSG_FILTER "mbox-out-message-filter"
>  #define MBOX_OUT_INDEX_FILTER "mbox-out-index-filter"
> +#define MBOX_BASE64_FILTER "mbox-out-base64-filter"
> +#define MBOX_QP_FILTER "mbox-out-quoted-printable-filter"
> +#define MBOX_MPART_FILTER "mbox-out-multipart-filter"
> +#define MBOX_HTML_FILTER "mbox-out-html-filter"
>  
>  #define MBOX_PREV 0
>  #define MBOX_NEXT 1
> @@ -87,6 +91,10 @@
>      int sent;
>  } mbox_filter_ctx;
>  
> +typedef struct dir_cfg {
> +    int enabled;
> +} dir_cfg;
> +
>  /* Declare ourselves so the configuration routines can find and know us.
>   * We'll fill it in at the end of the module.
>   */
> @@ -382,6 +390,464 @@
>      return HTTP_NOT_FOUND;
>  }
>  
> +static apr_status_t handle_line(request_rec* r, int len, char* line) 
> +{
> +
> +    if (len > 0) {
> +        ap_rputs(line, r);
> +    }
> +
> +    ap_rputc(LF, r);
> +
> +    return APR_SUCCESS;
> +}
> +
> +/*
> + * 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.
> + */
> +
> +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
> +};
> +
> +#define char64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
> +
> +apr_size_t
> +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));

Funky tabs?

> +      if (c2 == '\0' || c3 == '\0' || c4 == '\0')
> +	{
> +          /* Premature EOF. Should return an Error? */
> +	  return;
> +        }
> +      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++;
> +            }
> +        }
> +    }
> +  *dst = '\0';
> +  return len;
> +}
> +
> +int
> +hex2dec_char(ch)
> +     char ch;
> +{
> +  if (isdigit(ch))
> +    return ch-'0';
> +  else if (isupper(ch))
> +    return ch-'A'+10;
> +  else
> +    return ch-'a'+10;
> +}
> +
> +/* mime_decode_qp: convert the preamble of MSG from a quoted-printable
> + *	encoding to raw text.
> + */
> +apr_size_t
> +mime_decode_qp (char* p)
> +{
> +  unsigned char *src, *dst;
> +  apr_size_t len = 0;
> +
> +  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;
> +              len++;
> +	    }
> +	}
> +      else
> +	*dst = *src;
> +      ++dst, ++src;
> +      len++;
> +    }
> +}
> +
> +/** End metamail 2.7 code **/

Note that we'll probably need to add a comment in NOTICE wrt to this
chunk.

> +
> +static const char* cte_e_to_char(mbox_cte_e cte)
> +{
> +    switch(cte) {
> +    case CTE_NONE:
> +        return "None";
> +    case CTE_7BIT:
> +        return "7-Bit";
> +    case CTE_8BIT:
> +        return "8-Bit";
> +    case CTE_UUENCODE:
> +        return "uuencode";
> +    case CTE_BINARY:
> +        return "Binary";
> +    case CTE_QP:
> +        return "Quoted Printable";
> +    case CTE_BASE64:
> +        return "Base64";
> +    default:
> +        return "Unknown CTE";
> +    }
> +}
> +
> +static apr_status_t cte_filter(ap_filter_t *f, apr_bucket_brigade *bb, 
> +                    mbox_cte_e cte, int noescape)
> +{
> +    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;
> +
> +    eos = APR_BRIGADE_LAST(bb);
> +
> +    if (APR_BUCKET_IS_EOS(eos)) {
> +        seen_eos = 1;
> +    }
> +
> +    apr_brigade_length(bb, 1, &off);
> +
> +    /* FIXME: This could be done without a pflatten. */
> +    rv = apr_brigade_pflatten(bb, &p, &len, f->r->pool);
> +
> +    if (rv == APR_SUCCESS) {
> +
> +        p[len] = '\0';
> +
> +        if (cte == CTE_BASE64) {
> +            len = mime_decode_b64(p);
> +        }
> +        else if (cte == CTE_QP) {
> +            len = mime_decode_qp(p);
> +        }
> +
> +        if (noescape) {
> +            e = apr_bucket_pool_create(p, len, f->r->pool,
> +                                       f->c->bucket_alloc);
> +        }
> +        else {
> +            p = ap_escape_html(f->r->pool, p);
> +
> +            e = apr_bucket_pool_create(p, strlen(p), f->r->pool,
> +                                       f->c->bucket_alloc);
> +        }
> +
> +        apr_brigade_cleanup(bb);
> +
> +        if (seen_eos) {
> +            eos = apr_bucket_eos_create(f->c->bucket_alloc);
> +            APR_BRIGADE_INSERT_HEAD(bb, eos);
> +        }
> +
> +        APR_BRIGADE_INSERT_HEAD(bb, e);
> +    }
> +
> +    return ap_pass_brigade(f->next, bb);
> +}
> +
> +static apr_status_t mbox_base64_filter(ap_filter_t *f, apr_bucket_brigade *bb)
> +{
> +    return cte_filter(f, bb, CTE_BASE64, 0);
> +}
> +
> +static apr_status_t mbox_qp_filter(ap_filter_t *f, apr_bucket_brigade *bb)
> +{
> +    return cte_filter(f, bb, CTE_QP, 0);
> +}
> +
> +typedef struct
> +{
> +    int get_part;
> +    int mp_count;
> +    int status;
> +    Message *m;
> +    char* bound;
> +    apr_bucket_brigade *bb;
> +    apr_bucket_brigade *tbb;
> +    char buf[HUGE_STRING_LEN+1];
> +} mbox_mpartf_ctx;
> +
> +static apr_status_t mbox_mpart_filter(ap_filter_t *f, apr_bucket_brigade *bb)
> +{
> +    mbox_mpartf_ctx* ctx = f->ctx;
> +    int seen_eos = 0;
> +    apr_bucket* e;
> +    apr_bucket* eos;
> +    apr_size_t len;
> +    char* d;
> +    char* ct;
> +    mbox_cte_e ccte = CTE_NONE;
> +    apr_status_t rv = APR_SUCCESS;
> +
> +    eos = APR_BRIGADE_LAST(bb);
> +
> +    if (APR_BUCKET_IS_EOS(eos)) {
> +        seen_eos = 1;
> +    }
> +
> +    do {
> +        apr_brigade_cleanup(ctx->bb);
> +
> +        /* FIXME: The Brigade could end in the middle of a line. */
> +        apr_brigade_split_line(ctx->bb, bb, APR_BLOCK_READ, HUGE_STRING_LEN);
> +
> +        if (APR_BRIGADE_EMPTY(ctx->bb)) {
> +            /* End of the Source Brigade.. */
> +            if (seen_eos) {
> +                eos = apr_bucket_eos_create(f->c->bucket_alloc);
> +                APR_BRIGADE_INSERT_TAIL(ctx->bb, eos);
> +                rv = ap_pass_brigade(f->next, ctx->bb);
> +            }
> +
> +            break;
> +        }
> +
> +        len = HUGE_STRING_LEN;
> +
> +        apr_brigade_flatten(ctx->bb, ctx->buf, &len);
> +
> +        ctx->buf[len+1] = '\0';
> +
> +        /* FIXME: We Don't care about meta buckets? */
> +        apr_brigade_cleanup(ctx->bb);
> +
> +        if (ctx->status == 1) {
> +            d = strstr(ctx->buf, ctx->bound);
> +            if (d) {
> +                char* tmp = d + strlen(ctx->bound);
> +                /* Check for the end of the entire multipart email. */
> +                if (strlen(tmp) >= 2 && tmp[0] == '-' && tmp[1] == '-') {
> +                    ctx->status = 0;
> +                }
> +                else {
> +                    /* Goto the next line, and look for a content type */
> +                    ctx->status = 2;
> +                    ct = "multipart/broken";
> +                }
> +
> +                if (ctx->get_part != 0 && ctx->mp_count == ctx->get_part) {
> +                    ap_set_content_type(f->r, apr_pstrdup(f->r->pool, ct));
> +                    rv = cte_filter(f, ctx->tbb, ccte, 1);
> +                }
> +                else if (ctx->get_part == 0){
> +                    rv = cte_filter(f, ctx->tbb, ccte, 0);
> +                }
> +                apr_brigade_cleanup(ctx->tbb);
> +                /* Reset C-T-E */
> +                ccte = CTE_NONE;
> +                continue;
> +            }
> +            else {
> +                if (ctx->get_part != 0 && ctx->mp_count != ctx->get_part) {
> +                    continue;
> +                }
> +                else if (ctx->get_part != 0  && ctx->get_part == ctx->mp_count) {
> +                    /* this is the correct section! */
> +                }
> +                else if (strcmp(ct, "text/plain") != 0 && 
> +                    strcmp(ct, "text/x-patch") != 0) {
> +                    continue;
> +                }
> +
> +                e = apr_bucket_heap_create(ctx->buf, len, 
> +                                           NULL, f->c->bucket_alloc);
> +                APR_BRIGADE_INSERT_TAIL(ctx->tbb, e);
> +                continue;
> +            }
> +        }
> +        else if (ctx->status == 2) {
> +            if (len == 1 && strcmp(ctx->buf, "\n")) {
> +                if (!strcmp(ct, "multipart/broken")) {
> +                    /* Unable to find a Content Type header. */
> +                    ctx->status = 1;
> +                    ctx->mp_count++;
> +                    continue;
> +                }
> +                else if (!strcmp(ct, "text/plain")) {
> +                    ctx->status = 1;
> +                    ctx->mp_count++;
> +                    continue;
> +                }
> +                else {
> +                    if (ctx->get_part) {
> +                        ctx->status = 1;
> +                        ctx->mp_count++;
> +                        continue;
> +                    }
> +
> +                    d = apr_psprintf(f->r->pool,
> +                                   "<hr/>Attachment "
> +                                   "<a href='%s/%d'>#%d</a> (%s) (%s)<hr/>", 
> +                                   f->r->uri, ctx->mp_count+1, ctx->mp_count, 
> +                                   ct, cte_e_to_char(ccte));
> +
> +                    e = apr_bucket_pool_create(d, strlen(d), 
> +                                            f->r->pool, f->c->bucket_alloc);
> +                    APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
> +                    ctx->status = 1;
> +                    ctx->mp_count++;
> +                    rv = ap_pass_brigade(f->next, ctx->bb);
> +                    continue;
> +                }
> +            }
> +            else {
> +                /* FIXME: Handle Content-Disposition */
> +                if (!strncasecmp(ctx->buf, "Content-Type: ",
> +                                 strlen("Content-Type: "))) {
> +                    char* tmp = ctx->buf + strlen("Content-Type: ");
> +                    char* p = strstr(tmp, ";");
> +                    if (p) {
> +                        *p = '\0';
> +                        /* FIXME: Handle the name= param. */
> +                    }
> +                    else {
> +                        p = tmp;
> +                        while(*p != '\0') {
> +                            if (isspace(*p)) {
> +                                *p = '\0';
> +                                break;
> +                            }
> +                            *p++;
> +                        }
> +                    }
> +                    ct = apr_pstrdup(f->r->pool, tmp);
> +                    continue;
> +                }
> +
> +                if (!strncasecmp(ctx->buf, "Content-Transfer-Encoding:", 
> +                                 strlen("Content-Transfer-Encoding:"))) {
> +
> +                    ccte = parse_cte_header(ctx->buf);
> +                    continue;
> +                }
> +            }
> +        }
> +    } while(rv == APR_SUCCESS);
> +
> +    return rv;
> +}
> +
> +static apr_status_t mbox_html_filter(ap_filter_t *f, 
> +    apr_bucket_brigade *bb)
> +{
> +    const char *buf = 0;
> +    apr_size_t bytes = 0;
> +    char* p;
> +    apr_bucket *b;
> +    apr_bucket *e;
> +    apr_bucket_brigade *nbb = f->ctx;
> +    p = ap_escape_html(f->r->pool, p);
> +
> +    if (!f->ctx) {
> +        nbb = f->ctx = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
> +    }
> +    else {
> +        apr_brigade_destroy(nbb);
> +    }
> +
> +    for (b = APR_BRIGADE_FIRST(bb);
> +         b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
> +        if (APR_BUCKET_IS_METADATA(b)) {
> +            apr_bucket_copy(b, &e);
> +            APR_BRIGADE_INSERT_TAIL(nbb, e);
> +        }
> +        else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
> +                 == APR_SUCCESS) {
> +
> +            /* FIXME: create a pool to reuse */
> +
> +            p = ap_escape_html(f->r->pool,
> +                               apr_pstrmemdup(f->r->pool, buf, bytes));
> +            e = apr_bucket_pool_create(p, strlen(p), 
> +                                       f->r->pool, f->c->bucket_alloc);
> +            APR_BRIGADE_INSERT_TAIL(nbb, e);
> +        }
> +    }
> +    apr_brigade_destroy(bb);
> +
> +    return ap_pass_brigade(f->next, nbb);
> +}
> +
>  /* This function will take the message id from the virtual namespace, and
>   * print out the cached headers of the message and the complete body
>   * of the message.
> @@ -392,11 +858,16 @@
>  {
>      MBOX_BUFF b;
>      char *msgID;
> -    char buf[HUGE_STRING_LEN+1], line[HUGE_STRING_LEN+1];
> -    int len;
> +    char *bound;
> +    char buf[HUGE_STRING_LEN+1];
> +    char line[HUGE_STRING_LEN+1]; 
> +    int len = 0;
> +    int multipart = 0;
>      apr_status_t status;
>      mbox_filter_ctx *ctx;
> +    mbox_mpartf_ctx* mctx;
>      Message *m;
> +    mbox_cte_e ccte = CTE_NONE;
>  
>      /* msgID should be the part of the URI that Apache could not resolve
>       * on its own.  Grab it and skip over the expected /.
> @@ -404,6 +875,20 @@
>      msgID = r->path_info;
>      msgID++;
>  
> +    bound = strrchr(msgID, '/');
> +    if (bound) {
> +        *bound = '\0'; 
> +        *bound++;
> +        len = atoi(bound);
> +        /* We don't support mime messages with more than 32 parts */
> +        if (len < 1 || len > 32) {
> +            len = 0;
> +        }
> +    }
> +    else {
> +        len = 0;
> +    }
> +
>      m = fetch_index(r, f, msgID);
>  
>      if (!m)
> @@ -411,6 +896,16 @@
>  
>      status = apr_file_seek(f, APR_SET, &m->location);
>  
> +    if (!strncmp(m->content_type,"multipart/", strlen("multipart/"))) {
> +        multipart = 1;
> +        if (m->boundary) {
> +            bound = apr_pstrcat(r->pool, "--", m->boundary, NULL);
> +        }
> +        else {
> +            bound = NULL;
> +        }
> +    }
> +
>      buf[0] = '\0';
>      b.sb = b.rb = b.b = buf;
>      b.fd = f;
> @@ -419,10 +914,41 @@
>      b.totalread = 0;
>      mbox_fillbuf(&b);
>  
> -    ctx = (mbox_filter_ctx*) apr_pcalloc(r->pool, sizeof(mbox_filter_ctx));
> -    ctx->m = m;
> -    ap_add_output_filter(MBOX_OUT_MSG_FILTER, ctx, r, r->connection);
> +    /* If the entire message is Base64 or Q-P, it cannot be a multipart. */
> +    if (m->cte == CTE_BASE64) {
> +        ap_add_output_filter(MBOX_BASE64_FILTER, NULL, r, r->connection);
> +    }
> +    else if (m->cte == CTE_QP) {
> +        ap_add_output_filter(MBOX_QP_FILTER, NULL, r, r->connection);
> +    }
> +    else if (multipart) {
>  
> +        mctx = apr_palloc(r->pool, sizeof(mbox_mpartf_ctx));
> +        mctx->m = m;
> +        mctx->get_part = len;
> +        mctx->tbb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
> +        mctx->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
> +        mctx->status = multipart;
> +        mctx->bound = bound;
> +        mctx->buf[HUGE_STRING_LEN] = '\0';
> +        mctx->mp_count = 0;
> +        ap_add_output_filter(MBOX_MPART_FILTER, mctx, r, r->connection);
> +    }
> +    else {
> +        /* Just Escape the HTML */
> +        ap_add_output_filter(MBOX_HTML_FILTER, NULL, r, r->connection);
> +    }
> +
> +    if (!(multipart && mctx->get_part != 0)) {
> +        ctx = (mbox_filter_ctx*) apr_pcalloc(r->pool, sizeof(mbox_filter_ctx));
> +        ctx->m = m;
> +        ap_add_output_filter(MBOX_OUT_MSG_FILTER, ctx, r, r->connection);
> +    }
> +/*
> +    if ((r->proto_num >= 1001) && !r->main && !r->prev)
> +        r->chunked = 1;
> +*/
> +
>      /* When we reach the end of the file, b.b is NULL.  */
>      while (b.b)
>      {
> @@ -431,36 +957,19 @@
>          
>          if (!b.b || 
>              (line[0] == 'F' && line[1] == 'r' && 
> -            line[2] == 'o' && line[3] == 'm' && line[4] == ' '))
> +            line[2] == 'o' && line[3] == 'm' && line[4] == ' ')) {
>              break;
> -        else
> -        {
> -            if (len < 0)
> -                return HTTP_INTERNAL_SERVER_ERROR;
> -
> -            if (len > 0)
> -            {
> -                /* This could and should easily be moved to a filter. */
> -                if (line[0] == '>' )
> -                {
> -                    /* Hide sendmail funniness by removing the > */
> -                    if (line[1] == 'F' && line[2] == 'r' && 
> -                        line[3] == 'o' && line[4] == 'm' && line[5] == ' ')
> -                        memmove(line, line + 1, len - 1);
> -                    else
> -                        ap_rputs("<I>", r);
> -                }
> -                    
> -                ap_rputs(ap_escape_html(r->pool, line), r);
> -
> -                if (line[0] == '>')
> -                    ap_rputs("</I>", r);
> +        }
> +        else {
> +            if (len < 0) {
> +                /* FIXME: Log This Condition! */
> +                status = HTTP_INTERNAL_SERVER_ERROR;
> +                break;
>              }
>  
> -            ap_rputc(LF, r);
> +            handle_line(r, len, line);
>          }
>      }
> -
>      return status;
>  }
>  
> @@ -496,6 +1005,8 @@
>  
>      if (!ctx->sent)
>      {
> +        apr_table_unset(f->r->headers_out, "Content-Length");
> +
>          header = apr_psprintf(f->r->pool,
>              DOCTYPE_HTML_4_0T 
>              "<HTML>\n<HEAD>\n<TITLE>%s</TITLE>\n</HEAD>\n<BODY>"
> @@ -570,6 +1081,8 @@
>  
>      if (!ctx->sent)
>      {
> +        apr_table_unset(f->r->headers_out, "Content-Length");
> +
>          temp = URI_ESCAPE_OR_BLANK(f->r->pool, m->msgID);
>          header = apr_psprintf(f->r->pool,
>              DOCTYPE_HTML_4_0T 
> @@ -580,6 +1093,9 @@
>              "<STRONG>From:</STRONG> %s<BR>\n"
>              "<STRONG>Subject:</STRONG> %s<BR>\n" 
>              "<STRONG>Date:</STRONG> %s<BR>\n"
> +            "<STRONG>Content-Type:</STRONG> %s<BR>\n"
> +            "<STRONG>Boundary:</STRONG> %s<BR>\n"
> +            "<STRONG>C-T-E:</STRONG> %s<BR>\n"
>              "<A HREF=\"%s/prev?%s\">Prev</A> "
>              "<A HREF=\"%s/next?%s\">Next</A> "
>              "<A HREF=\"%s/prev-thread?%s\">Prev by Thread</A> "

Ideally, I'd like to see these be conditional.  If they aren't present,
they wouldn't appear.

> @@ -590,6 +1106,9 @@
>              ESCAPE_OR_BLANK(f->r->pool, m->from),
>              ESCAPE_OR_BLANK(f->r->pool, m->subject),
>              ESCAPE_OR_BLANK(f->r->pool, m->str_date),
> +            ESCAPE_OR_BLANK(f->r->pool, m->content_type),
> +            ESCAPE_OR_BLANK(f->r->pool, m->boundary),
> +            cte_e_to_char(m->cte),
>              ctx->baseURI, temp,
>              ctx->baseURI, temp,
>              ctx->baseURI, temp,
> @@ -721,25 +1240,180 @@
>      return status; 
>  }
>  
> +static int show_index_file_info(request_rec *r, char *path) 
> +{
> +    int count = mbox_msg_count(r, path);
> +    ap_rprintf(r, "<tr><td>%.2s/%.4s</td><td>"
> +               "<a href=\"%s/threads.html\">Threads</a> "
> +               "<a href=\"%s/index.html\">Date</a> "
> +               "<a href=\"%s/authors.html\">Authors</a></td>"
> +               "<td>%d</td></tr>\n",
> +               path+4, path, path, path, path, count);
> +    return OK;
> +}
> +
> +static int file_alphasort(const void *fn1, const void *fn2)
> +{
> +    /* reverse order */
> +    return strcmp(*(char**)fn2, *(char**)fn1);
> +}
> +
> +static int generate_mbox_index(request_rec *r) 
> +{

This is pretty cool.  =)

> +   apr_status_t rv = APR_SUCCESS;
> +   char* file;
> +   apr_dir_t *dir;
> +   apr_finfo_t finfo;
> +   apr_array_header_t* files;
> +   int i;
> +
> +   ap_set_content_type(r, "text/html; charset=utf-8");
> +
> +    rv = apr_dir_open(&dir, r->filename, r->pool);
> +    if (rv != APR_SUCCESS) {
> +        return rv;
> +    }
> +
> +    files = apr_array_make(r->pool, 0, sizeof(char *));
> +
> +    while (apr_dir_read(&finfo, APR_FINFO_NAME, dir) == APR_SUCCESS) {
> +        if (apr_fnmatch("*.mbox", finfo.name, 0) == APR_SUCCESS) {
> +               *(const char **)apr_array_push(files) = \
> +                        apr_pstrdup(r->pool, finfo.name);
> +        }
> +    }
> +
> +    apr_dir_close(dir);
> +
> +    if (files->nelts != 0) {
> +        qsort((void *) files->elts, files->nelts,
> +              sizeof(char *), file_alphasort);
> +    }
> +
> +    ap_rputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
> +              DOCTYPE_XHTML_1_0T
> +              "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n"
> +              "<head>\n<title>", r);
> +    ap_rputs(r->filename, r);
> +    ap_rputs("</title>\n</head>\n"
> +             "<body\n bgcolor=\"#FFFFFF\" text=\"#000000\" "
> +             "link=\"#0000FF\" vlink=\"#000080\" alink=\"#FF0000\">\n"
> +             "<h2>", r);
> +    if (files->nelts != 0) {
> +        /**
> +         * The First File in the list should be the newest. 
> +         * Use this to determine the List Name.
> +         */
> +        file = ((char**)files->elts)[0];
> +        ap_rputs(file, r);
> +    }
> +    ap_rputs("</h2>\n<table width=\"100%\">\n", r);
> +    ap_rputs("<tr><th align=\"left\" width=\"15%\">Date</th>"
> +             "<th align=\"left\" width=\"85%\">Sorted by</th><th>Messages</th></tr>", r);
> +
> +    if (files->nelts != 0) {
> +
> +        for (i = 0; i < files->nelts; i++) {
> +               file = ((char**)files->elts)[i];
> +               show_index_file_info(r, file);
> +               if (i+1 < files->nelts) {
> +                   if(((char**)files->elts)[i][3] != ((char**)files->elts)[i+1][3]) {
> +                       ap_rputs("<tr><td colspan='3'><hr/></td></tr>", r);
> +                   }
> +               }
> +        }
> +    }
> +    else {
> +        ap_rputs("<tr><td colspan=\"2\">"
> +                 "No messages have been archived for this list."
> +                 "</td></tr>", r);
> +    }
> +
> +    ap_rputs("</table>\n</body>\n</html>", r);
> +
> +    return OK;
> +}
> +
> +static int mboxindex_handler(request_rec *r)
> +{
> +    dir_cfg *conf;
> +
> +    if (strcmp(r->handler, DIR_MAGIC_TYPE)) {
> +        return DECLINED;
> +    }
> +
> +    conf = ap_get_module_config(r->per_dir_config, &mbox_module);
> +
> +    if (!conf->enabled) {
> +        return DECLINED;
> +    }
> +    
> +    return generate_mbox_index(r);
> +}
> +
>  static void mbox_register_hooks(apr_pool_t *p)
>  {
>      ap_hook_handler(mbox_handler, NULL, NULL, APR_HOOK_MIDDLE);
> +    ap_hook_handler(mboxindex_handler, NULL, NULL, APR_HOOK_FIRST);
>  
> -    ap_register_output_filter(MBOX_OUT_INDEX_FILTER, mbox_out_index_filter,  
> +    ap_register_output_filter(MBOX_OUT_INDEX_FILTER, mbox_out_index_filter,
>          NULL,
>          AP_FTYPE_RESOURCE);
> -    ap_register_output_filter(MBOX_OUT_MSG_FILTER, mbox_out_message_filter, 
> +    ap_register_output_filter(MBOX_OUT_MSG_FILTER, mbox_out_message_filter,
>          NULL,
>          AP_FTYPE_RESOURCE);
> +    ap_register_output_filter(MBOX_MPART_FILTER, mbox_mpart_filter,
> +        NULL,
> +        AP_FTYPE_RESOURCE);
> +    ap_register_output_filter(MBOX_BASE64_FILTER, mbox_base64_filter,
> +        NULL,
> +        AP_FTYPE_RESOURCE);
> +    ap_register_output_filter(MBOX_QP_FILTER, mbox_qp_filter,
> +        NULL,
> +        AP_FTYPE_RESOURCE);
> +    ap_register_output_filter(MBOX_HTML_FILTER, mbox_html_filter,
> +        NULL,
> +        AP_FTYPE_RESOURCE);
>  }
>  
> +static void *mbox_create_dir_config(apr_pool_t * p, char *x)
> +{
> +    dir_cfg *conf = apr_pcalloc(p, sizeof(dir_cfg));
> +
> +    conf->enabled = 0;
> +
> +    return conf;
> +}
> +
> +static void *mbox_merge_dir_config(apr_pool_t *p, void *basev, void *addv)
> +{
> +    dir_cfg *from = basev;
> +    dir_cfg *merge = addv;
> +    dir_cfg *to = apr_palloc(p, sizeof(dir_cfg));
> +
> +    if (merge->enabled == 1) {
> +        to->enabled = 1;
> +    }
> +    else {
> +        to->enabled = from->enabled;
> +    }
> +    return to;
> +}
> +
> +static const command_rec mbox_cmds[] ={
> +    AP_INIT_FLAG("mboxindex", ap_set_flag_slot,
> +                 (void *)APR_OFFSETOF(dir_cfg, enabled), OR_INDEXES,
> +		 "Enable mod_mbox to create directory listings of .mbox files."),
> +    {NULL}
> +};
> +
>  module mbox_module =
>  {
>      STANDARD20_MODULE_STUFF,
> -    NULL,                      /* per-directory config creator */
> -    NULL,                      /* dir config merger */
> +    mbox_create_dir_config,    /* per-directory config creator */
> +    mbox_merge_dir_config,     /* dir config merger */
>      NULL,                      /* server config creator */
>      NULL,                      /* server config merger */
> -    NULL,                      /* command table */
> +    mbox_cmds,                 /* command table */
>      mbox_register_hooks        /* set up other request processing hooks */
>  };

Looks fine.

You should have commit privs to the tree (by virtue of PMC membership).  So
whenever you feel comfortable, I'd say that you should commit what you
have and we can then fix up in the tree.  No need to be sending
large emails to the list.  =)

Thanks!  -- justin

Mime
View raw message