/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This file contains all output functions. */ #include "mod_mbox.h" /* Define local prototypes, even if functions are declared before they're called (avoid GCC warnings). */ void display_atom_entry(request_rec *r, Message *m, const char *mboxfile, apr_pool_t *pool, apr_file_t *f); void mbox_static_message_nav(request_rec *r, char **context, char *baseURI, char *msgID); char *mbox_months[12][2] = { {"Jan", "January"}, {"Feb", "February"}, {"Mar", "March"}, {"Apr", "April"}, {"May", "May"}, {"Jun", "June"}, {"Jul", "July"}, {"Aug", "August"}, {"Sep", "September"}, {"Oct", "October"}, {"Nov", "November"}, {"Dec", "December"} }; /* Display an ATOM feed entry from given message structure */ void display_atom_entry(request_rec *r, Message *m, const char *mboxfile, apr_pool_t *pool, apr_file_t *f) { char dstr[100]; apr_size_t dlen; apr_time_exp_t extime; char *uid; char *c; ap_rputs("\n", r); ap_rprintf(r, "%s\n", ESCAPE_OR_BLANK(pool, m->subject)); ap_rprintf(r, "%s\n", ESCAPE_OR_BLANK(pool, m->from)); ap_rprintf(r, "\n", ap_construct_url(r->pool, r->uri, r), mboxfile, URI_ESCAPE_OR_BLANK(pool, m->msgID)); uid = URI_ESCAPE_OR_BLANK(pool, m->msgID); c = uid; while (*c != '\0') { if (*c == '.') { *c = '-'; } *c++; } ap_rprintf(r, "urn:uuid:%s\n", uid); apr_time_exp_gmt(&extime, m->date); apr_strftime(dstr, &dlen, sizeof(dstr), "%G-%m-%dT%H:%M:%SZ", &extime); ap_rprintf(r, "%s\n", dstr); ap_rputs("\n" "
\n" "
\n", r);

    load_message(pool, f, m);
    /* Parse multipart information */
    m->mime_msg = mbox_mime_decode_multipart(pool, m->raw_body,
                                             m->content_type,
                                             m->cte, m->boundary);

    ap_rprintf(r, "%s",
               mbox_wrap_text(mbox_mime_get_body(pool, m->mime_msg)));

    ap_rputs("\n
\n
\n
\n", r); ap_rputs("
\n", r); } static int mbox2atom(request_rec *r, const char *mboxfile, mbox_cache_info *mli, int max) { apr_status_t rv; char *filename; char *origfilename; apr_file_t *f; MBOX_LIST *head; Message *m; int i; apr_pool_t *tpool; apr_pool_create(&tpool, r->pool); filename = apr_pstrcat(r->pool, r->filename, mboxfile, NULL); rv = apr_file_open(&f, filename, APR_READ, APR_OS_DEFAULT, r->pool); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "mod_mbox(mbox2atom): Can't open mbox '%s' for atom feed", filename); return 0; } origfilename = r->filename; r->filename = filename; head = mbox_load_index(r, f, NULL); /* Sort the list */ head = mbox_sort_list(head, MBOX_SORT_REVERSE_DATE); for (i = 0; i < max && head != NULL; i++) { m = (Message *) head->value; display_atom_entry(r, m, mboxfile, tpool, f); head = head->next; apr_pool_clear(tpool); } r->filename = origfilename; apr_pool_destroy(tpool); return i; } void mbox_atom_entries(request_rec *r, mbox_cache_info *mli) { mbox_file_t *fi; apr_array_header_t *files; int i, entries = 0; files = mbox_fetch_boxes_list(r, mli, r->filename); if (!files) { return; } fi = (mbox_file_t *) files->elts; for (i = 0; i < files->nelts && entries < MBOX_ATOM_NUM_ENTRIES; i++) { if (!fi[i].count) { continue; } entries += mbox2atom(r, fi[i].filename, mli, MBOX_ATOM_NUM_ENTRIES - entries); } } /* Outputs an XML list of available mailboxes */ apr_status_t mbox_xml_boxlist(request_rec *r) { apr_status_t rv = APR_SUCCESS; mbox_dir_cfg_t *conf; mbox_cache_info *mli; mbox_file_t *fi; apr_array_header_t *files; int i; char *path, *k; conf = ap_get_module_config(r->per_dir_config, &mbox_module); path = apr_pstrdup(r->pool, r->filename); k = strstr(path, ".mbox"); if (!k) { return HTTP_NOT_FOUND; } k = k - 6; *k = 0; /* Open mbox cache */ rv = mbox_cache_get(&mli, path, r->pool); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "mod_mbox(xml_boxlist): Can't open directory cache '%s' for index", r->filename); return HTTP_FORBIDDEN; } files = mbox_fetch_boxes_list(r, mli, path); if (!files) { return HTTP_FORBIDDEN; } ap_rputs("\n", r); ap_rputs("\n", r); fi = (mbox_file_t *) files->elts; for (i = 0; i < files->nelts; i++) { if (fi[i].count || !conf->hide_empty) { ap_rprintf(r, "\n", fi[i].filename, fi[i].count); } } ap_rputs("\n", r); return APR_SUCCESS; } /* Outputs a statix XHTML list of available mailboxes */ apr_status_t mbox_static_boxlist(request_rec *r) { apr_status_t rv = APR_SUCCESS; mbox_dir_cfg_t *conf; mbox_cache_info *mli; mbox_file_t *fi; apr_array_header_t *files; int i; char *base_path; char *path, *k; conf = ap_get_module_config(r->per_dir_config, &mbox_module); base_path = get_base_path(r); path = apr_pstrdup(r->pool, r->filename); k = strstr(path, ".mbox"); if (!k) { return HTTP_NOT_FOUND; } /* Roll back before the '/YYYYMM' part of the filename */ k = k - 7; *k = 0; /* Open mbox cache */ rv = mbox_cache_get(&mli, path, r->pool); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "mod_mbox(static_boxlist): Can't open directory cache '%s' for index", path); return HTTP_FORBIDDEN; } files = mbox_fetch_boxes_list(r, mli, path); if (!files) { return HTTP_FORBIDDEN; } ap_rputs(" \n", r); ap_rputs(" \n", r); ap_rputs(" \n", r); fi = (mbox_file_t *) files->elts; for (i = 0; i < files->nelts; i++) { if (fi[i].count || !conf->hide_empty) { if (strcmp(k + 1, fi[i].filename) == 0) { ap_rputs(" ", r); } else { ap_rputs(" ", r); } ap_rprintf(r, " \n", base_path, fi[i].filename, r->path_info, mbox_months[atoi (apr_pstrndup (r->pool, fi[i].filename + 4, 2)) - 1][1], fi[i].filename, mbox_months[atoi (apr_pstrndup (r->pool, fi[i].filename + 4, 2)) - 1][0], fi[i].filename, fi[i].count); ap_rputs(" \n", r); } } ap_rputs(" \n", r); ap_rputs("
Box list
" "%s %.4s%d
\n", r); return APR_SUCCESS; } /* Outputs an XHTML list of available mailboxes */ apr_status_t mbox_static_index_boxlist(request_rec *r, mbox_dir_cfg_t *conf, mbox_cache_info *mli) { mbox_file_t *fi; apr_array_header_t *files; int side = 0, year_hdr = 0, i; files = mbox_fetch_boxes_list(r, mli, r->filename); if (!files) { return HTTP_FORBIDDEN; } ap_rputs(" \n", r); fi = (mbox_file_t *) files->elts; for (i = 0; i < files->nelts; i++) { /* Only display an entry if it has messages or if we don't hide empty mailboxes */ if (fi[i].count || !conf->hide_empty) { if (!year_hdr) { if (!side) { ap_rputs(" \n", r); } else { ap_rputs(" \n\n", r); } year_hdr = 0; } } ap_rputs(" \n", r); ap_rputs("
\n", r); side = 1; } else { ap_rputs(" \n", r); side = 0; } ap_rputs(" \n", r); ap_rputs(" \n", r); ap_rprintf(r, " \n", fi[i].filename); ap_rputs(" \n", r); ap_rputs(" \n", r); year_hdr = 1; } ap_rputs(" \n", r); ap_rprintf(r, " \n", mbox_months[atoi(apr_pstrndup(r->pool, fi[i].filename + 4, 2)) - 1][0], fi[i].filename); ap_rprintf(r, " \n", fi[i].filename, fi[i].filename + 4, fi[i].filename, fi[i].filename + 4, fi[i].filename, fi[i].filename + 4, fi[i].filename, fi[i].filename + 4); ap_rprintf(r, " \n", fi[i].count); ap_rputs(" \n", r); } /* Year separation */ if ((fi[i + 1].filename && year_hdr) && (fi[i].filename[3] != fi[i + 1].filename[3])) { ap_rputs(" \n", r); ap_rputs("
Year %.4s
%s %.4s" "Thread" " · Date" " · Author%d
\n", r); if (side) { ap_rputs("
\n\n", r); if (side) { ap_rputs(" \n", r); } ap_rputs(" \n\n", r); return APR_SUCCESS; } /* Antispam protection */ static char *email_antispam(char *email) { char *tmp; int i, atsign = 0, ltsign = 0; tmp = strrchr(email, '@'); if (!tmp) { return email; } /* Before the '@' sign */ atsign = tmp - email - 1; tmp = strstr(email, "<"); if (tmp) { /* After the '<' */ ltsign = tmp - email + strlen("<") - 1; } /* Wipe out at most three chars preceding the '@' sign */ for (i = 0; i < 3; i++) { if ((atsign - i) > ltsign) { email[atsign - i] = '.'; } } return email; } /* Display an XHTML message list entry */ static void display_static_msglist_entry(request_rec *r, Message *m, int linked, int depth) { mbox_dir_cfg_t *conf; char *from; int i; conf = ap_get_module_config(r->per_dir_config, &mbox_module); from = ESCAPE_OR_BLANK(r->pool, m->str_from); from = mbox_cte_decode_header(r->pool, from); if (conf->antispam) { from = email_antispam(from); } /* Message author */ ap_rputs(" \n", r); if (linked) { ap_rprintf(r, " %s\n", from); } else { ap_rputs(" \n", r); } /* Subject, linked or not */ ap_rputs(" ", r); for (i = 0; i < depth; i++) { ap_rputs("  ", r); } if (linked) { ap_rprintf(r, "", URI_ESCAPE_OR_BLANK(r->pool, m->msgID)); } ap_rprintf(r, "%s", ESCAPE_OR_BLANK(r->pool, m->subject)); if (linked) { ap_rputs("", r); } ap_rputs(" \n", r); /* Message date */ if (linked) { ap_rprintf(r, " %s\n", ESCAPE_OR_BLANK(r->pool, m->str_date)); } else { ap_rputs(" \n", r); } ap_rputs(" \n", r); } /* Display an XML message list entry */ static void display_xml_msglist_entry(request_rec *r, Message *m, int linked, int depth) { mbox_dir_cfg_t *conf; char *from; conf = ap_get_module_config(r->per_dir_config, &mbox_module); from = ESCAPE_OR_BLANK(r->pool, m->str_from); from = mbox_cte_decode_header(r->pool, from); if (conf->antispam) { from = email_antispam(from); } ap_rprintf(r, " \n", linked, depth, ESCAPE_OR_BLANK(r->pool, m->msgID)); ap_rprintf(r, " \n", from); ap_rprintf(r, " \n", ESCAPE_OR_BLANK(r->pool, m->str_date)); ap_rprintf(r, " \n", ESCAPE_OR_BLANK(r->pool, m->subject)); ap_rprintf(r, " \n"); } /* Display a threaded message list for Container 'c' */ static void display_msglist_thread(request_rec *r, Container *c, int depth, int mode) { Message *m; int linked = 1; /* Under the rules of our threading tree, if we do not have a * message, we MUST have at least one child. Therefore, print * that child's subject when we don't have a message. */ if (c->message) { m = c->message; } else { m = c->child->message; linked = 0; } if (mode == MBOX_OUTPUT_STATIC) { display_static_msglist_entry(r, m, linked, depth); } else { display_xml_msglist_entry(r, m, linked, depth); } /* Display children : * Subject * +-> Re: Subject */ if (c->child) { display_msglist_thread(r, c->child, depth + 1, mode); } /* Display follow-ups : * Subject * +-> ... * | +-> ... * +-> Re: Subject */ if (depth && c->next) { display_msglist_thread(r, c->next, depth, mode); } } /* Display the XML index of the specified mbox file. */ apr_status_t mbox_xml_msglist(request_rec *r, apr_file_t *f, int sortFlags) { apr_finfo_t fi; mbox_dir_cfg_t *conf; MBOX_LIST *head; Message *m; Container *threads = NULL, *c; int current_page = 0; /* Current page number, starting at 0 */ int pages; /* Number of pages */ int count = 0; /* Message count */ int i = 0; conf = ap_get_module_config(r->per_dir_config, &mbox_module); /* Fetch page number if present. Otherwise, assume page #1 */ if (r->args && strcmp(r->args, "")) current_page = atoi(r->args); /* Load the index of messages from the DB into the MBOX_LIST */ head = mbox_load_index(r, f, &count); /* Compute the page count, depending on the sort flags */ if (sortFlags != MBOX_SORT_THREAD) { pages = count / DEFAULT_MSGS_PER_PAGE; if (count > pages * DEFAULT_MSGS_PER_PAGE) { pages++; } } else { threads = calculate_threads(r->pool, head); c = threads; count = 0; while (c) { c = c->next; count++; } pages = count / DEFAULT_THREADS_PER_PAGE; if (count > pages * DEFAULT_THREADS_PER_PAGE) { pages++; } } /* This index only changes when the .mbox file changes. */ apr_file_info_get(&fi, APR_FINFO_MTIME, f); r->mtime = fi.mtime; ap_set_last_modified(r); /* Send page header */ ap_rputs("\n", r); ap_rprintf(r, "\n", current_page, pages); /* For date and author sorts */ if (sortFlags != MBOX_SORT_THREAD) { /* Sort the list */ head = mbox_sort_list(head, sortFlags); /* Pass useless messages */ while (head && (i < current_page * DEFAULT_MSGS_PER_PAGE)) { head = head->next; i++; } /* Display current_page's messages */ while (head && (i < (current_page + 1) * DEFAULT_MSGS_PER_PAGE)) { m = (Message *) head->value; display_xml_msglist_entry(r, m, 1, 0); head = head->next; i++; } } /* For threaded view */ else { c = threads; /* Pass useless threads */ while (c && (i < current_page * DEFAULT_THREADS_PER_PAGE)) { c = c->next; i++; } /* Display current_page's threads */ while (c && (i < (current_page + 1) * DEFAULT_THREADS_PER_PAGE)) { display_msglist_thread(r, c, 0, MBOX_OUTPUT_AJAX); c = c->next; i++; } } ap_rputs("", r); return OK; } /* Display the page selector. * * FIXME: improve the algorithm in order to handle long pages list. */ static void mbox_static_msglist_page_selector(request_rec *r, char *baseURI, int pages, int current_page) { int i; /* If we don't have more than one page, the page selector is useless. Just return silently. */ if (pages <= 1) { return; } if (current_page) { ap_rprintf(r, "« Previous · ", baseURI, r->path_info, current_page - 1); } for (i = 0; i < pages; i++) { if (i != 0) { ap_rputs(" · ", r); } if (i != current_page) { ap_rprintf(r, "%d", baseURI, r->path_info, i, i + 1); } else { ap_rprintf(r, "%d", i + 1); } } if (current_page + 1 < pages) { ap_rprintf(r, " · Next »", baseURI, r->path_info, current_page + 1); } } static void mbox_static_msglist_nav(request_rec *r, char *baseURI, int pages, int current_page, int sortFlags) { ap_rputs (" Message list", r); ap_rputs("", r); mbox_static_msglist_page_selector(r, baseURI, pages, current_page); ap_rputs("", r); ap_rputs("", r); if (sortFlags == MBOX_SORT_THREAD) { ap_rprintf(r, "Thread · " "Author · " "Date", baseURI, baseURI); } else if (sortFlags == MBOX_SORT_AUTHOR) { ap_rprintf(r, "Thread · " "Author · " "Date", baseURI, baseURI); } else { ap_rprintf(r, "Thread · " "Author · " "Date", baseURI, baseURI); } ap_rputs("\n", r); } /* Display the XHTML index of the specified mbox file. */ apr_status_t mbox_static_msglist(request_rec *r, apr_file_t *f, int sortFlags) { apr_finfo_t fi; mbox_dir_cfg_t *conf; MBOX_LIST *head; Message *m; Container *threads = NULL, *c; int current_page = 0; /* Current page number, starting at 0 */ int pages; /* Total number of pages */ int count = 0; /* Message count */ int i = 0; char *baseURI; char *filename; char *month; char *year; conf = ap_get_module_config(r->per_dir_config, &mbox_module); baseURI = get_base_uri(r); /* Fetch page number if present. Otherwise, assume page #1 */ if (r->args && strcmp(r->args, "")) current_page = atoi(r->args); /* Load the index of messages from the DB into the MBOX_LIST */ head = mbox_load_index(r, f, &count); /* Compute the page count, depending on the sort flags */ if (sortFlags != MBOX_SORT_THREAD) { pages = count / DEFAULT_MSGS_PER_PAGE; if (count > pages * DEFAULT_MSGS_PER_PAGE) { pages++; } } else { threads = calculate_threads(r->pool, head); c = threads; count = 0; while (c) { c = c->next; count++; } pages = count / DEFAULT_THREADS_PER_PAGE; if (count > pages * DEFAULT_THREADS_PER_PAGE) { pages++; } } /* This index only changes when the .mbox file changes. */ apr_file_info_get(&fi, APR_FINFO_MTIME, f); r->mtime = fi.mtime; ap_set_last_modified(r); /* Determine the month and year of the list, if we can. */ filename = strrchr(r->filename, '/'); if (filename && apr_fnmatch("[0-9][0-9][0-9][0-9][0-9][0-9].mbox", filename + 1, 0) == APR_SUCCESS) { month = mbox_months[atoi(apr_pstrndup(r->pool, baseURI + (strlen(baseURI) - strlen(".mbox") - 2), 2)) - 1][1]; year = baseURI + (strlen(baseURI) - strlen(".mbox") - 6); } else { month = ""; year = ""; } /* Send page header */ ap_rputs("\n", r); ap_rputs("\n\n", r); ap_rputs("\n", r); ap_rputs(" \n", r); ap_rputs(" \n", r); ap_rprintf(r, " Mailing list archives: %s %.4s\n", month, year); if (conf->style_path) { ap_rprintf(r, " \n", conf->style_path); } ap_rputs(" \n\n", r); ap_rputs(" \n", r); ap_rprintf(r, "

Mailing list archives: %s %.4s

\n\n", month, year); ap_rputs("
\n", r); if (conf->root_path) { ap_rprintf(r, "" "Site index · ", conf->root_path); } ap_rprintf(r, "" "List index
", get_base_path(r)); ap_rputs(" \n", r); ap_rputs(" \n", r); mbox_static_msglist_nav(r, baseURI, pages, current_page, sortFlags); ap_rputs(" \n", r); ap_rputs(" \n", r); /* For date or author sorts */ if (sortFlags != MBOX_SORT_THREAD) { /* Sort the list */ head = mbox_sort_list(head, sortFlags); /* Pass useless messages */ while (head && (i < current_page * DEFAULT_MSGS_PER_PAGE)) { head = head->next; i++; } /* Display current_page's messages */ while (head && (i < (current_page + 1) * DEFAULT_MSGS_PER_PAGE)) { m = (Message *) head->value; display_static_msglist_entry(r, m, 1, 0); head = head->next; i++; } } /* For threaded view */ else { c = threads; /* Pass useless threads */ while (c && (i < current_page * DEFAULT_THREADS_PER_PAGE)) { c = c->next; i++; } /* Display current_page's threads */ while (c && (i < (current_page + 1) * DEFAULT_THREADS_PER_PAGE)) { display_msglist_thread(r, c, 0, MBOX_OUTPUT_STATIC); c = c->next; i++; } } ap_rputs(" \n", r); ap_rputs(" \n", r); mbox_static_msglist_nav(r, baseURI, pages, current_page, sortFlags); ap_rputs(" \n", r); ap_rputs("
\n", r); /* Display box list */ mbox_static_boxlist(r); ap_rputs(" \n", r); ap_rputs("", r); return OK; } /* Outputs the AJAX browser XHTML stub. */ apr_status_t mbox_ajax_browser(request_rec *r) { mbox_dir_cfg_t *conf; char *baseURI; conf = ap_get_module_config(r->per_dir_config, &mbox_module); baseURI = get_base_uri(r); ap_set_content_type(r, "text/html; charset=utf-8"); ap_rputs("\n", r); ap_rputs("\n\n", r); ap_rputs("\n", r); ap_rputs(" \n", r); ap_rputs(" \n", r); ap_rputs(" Mailing list archives\n", r); if (conf->style_path) { ap_rprintf(r, " \n", conf->style_path); } if (conf->script_path) { ap_rprintf(r, " \n", conf->script_path); } ap_rputs(" \n\n", r); ap_rprintf(r, " \n", baseURI); ap_rputs("

Mailing list archives

\n\n", r); ap_rputs("
\n", r); if (conf->root_path) { ap_rprintf(r, "" "Site index · ", conf->root_path); } ap_rprintf(r, "" "List index
", get_base_path(r)); /* Output a small notice if no MboxScriptPath configuration directive was specified. */ if (!conf->script_path) { ap_rputs("

You did not specified a script path, and the dynamic " "browser won't run without it. Check your server configuration.\n", r); } ap_rputs(" \n\n", r); return OK; } /* Display a raw mail from cache. No processing is done here. */ int mbox_raw_message(request_rec *r, apr_file_t *f) { int errstatus; mbox_mime_message_t *mime_part; Message *m; char *msgID, *part, *end; /* 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++; } ap_unescape_url(msgID); /* Fetch message */ m = fetch_message(r, f, msgID); if (!m) { return HTTP_NOT_FOUND; } if ((errstatus = ap_meets_conditions(r)) != OK) { r->status = errstatus; return r->status; } if (!m->raw_msg) { ap_set_content_type(r, "text/plain"); ap_rprintf(r, "%s", MBOX_FETCH_ERROR_STR); } /* 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; } /* Empty MIME part : we want only mail's body */ if (part && !*part) { apr_size_t len = m->body_end - m->body_start; ap_set_content_type(r, "text/plain"); ap_rprintf(r, "%s", mbox_mime_decode_body(r->pool, m->cte, m->raw_body, len, NULL)); 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 > 0 && (num <= mime_part->sub_count) && mime_part->sub[num - 1] && mime_part->sub[num - 1]->body != NULL) { mime_part = mime_part->sub[num - 1]; } else { return HTTP_NOT_FOUND; } } while (*part && end); 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); } if (mime_part->body_len > 0) { const char *pdata; apr_size_t ret_len; mime_part->body[mime_part->body_len] = 0; pdata = mbox_mime_decode_body(r->pool, mime_part->cte, mime_part->body, mime_part->body_len, &ret_len); if (pdata != NULL && ret_len) { ap_rwrite(pdata, ret_len, r); } } return OK; } void mbox_static_message_nav(request_rec *r, char **context, char *baseURI, char *msgID) { ap_rputs(" ", r); /* Date navigation */ if (context[0]) { ap_rprintf(r, "«", baseURI, URI_ESCAPE_OR_BLANK(r->pool, context[0])); } else { ap_rputs("«", r); } ap_rprintf(r, " Date ", baseURI); if (context[1]) { ap_rprintf(r, "»", baseURI, URI_ESCAPE_OR_BLANK(r->pool, context[1])); } else { ap_rputs("»", r); } ap_rputs(" · ", r); /* Thread navigation */ if (context[2]) { ap_rprintf(r, "«", baseURI, URI_ESCAPE_OR_BLANK(r->pool, context[2])); } else { ap_rputs("«", r); } ap_rprintf(r, " Thread ", baseURI); if (context[3]) { ap_rprintf(r, "»", baseURI, URI_ESCAPE_OR_BLANK(r->pool, context[3])); } else { ap_rputs("»", r); } ap_rputs("\n", r); } /* Display a static XHTML mail */ int mbox_static_message(request_rec *r, apr_file_t *f) { int errstatus; mbox_dir_cfg_t *conf; Message *m; 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, msgID); if (!m) { return HTTP_NOT_FOUND; } if ((errstatus = ap_meets_conditions(r)) != OK) { r->status = errstatus; return r->status; } /* Parse multipart information */ m->mime_msg = mbox_mime_decode_multipart(r->pool, m->raw_body, m->content_type, m->cte, m->boundary); ap_rputs("\n", r); ap_rputs("\n\n", r); ap_rputs("\n", r); ap_rputs(" \n", r); ap_rputs(" \n", r); ap_rprintf(r, " %s\n", ESCAPE_OR_BLANK(r->pool, m->subject)); if (conf->style_path) { ap_rprintf(r, " \n", conf->style_path); } ap_rputs(" \n\n", r); ap_rputs(" \n", r); ap_rputs("

Mailing list archives

\n\n", r); ap_rputs("
\n", r); if (conf->root_path) { ap_rprintf(r, "" "Site index · ", conf->root_path); } ap_rprintf(r, "" "List index
", get_base_path(r)); /* Display context message list */ from = ESCAPE_OR_BLANK(r->pool, m->from); from = mbox_cte_decode_header(r->pool, from); if (conf->antispam) { from = email_antispam(from); } ap_rputs(" \n", r); context = fetch_context_msgids(r, f, m->msgID); /* Top navigation */ ap_rputs(" \n" " \n" " \n", r); mbox_static_message_nav(r, context, baseURI, m->msgID); ap_rputs(" \n" " \n\n", r); /* Bottom navigation */ ap_rputs(" \n" " \n" " \n", r); mbox_static_message_nav(r, context, baseURI, m->msgID); ap_rputs(" \n" " \n\n", r); /* Headers */ ap_rputs(" \n", r); ap_rprintf(r, " \n" " \n" " \n" " \n", from); ap_rprintf(r, " \n" " \n" " \n" " \n", ESCAPE_OR_BLANK(r->pool, m->subject)); ap_rprintf(r, " \n" " \n" " \n" " \n", ESCAPE_OR_BLANK(r->pool, m->rfc822_date)); /* Message body */ ap_rputs(" \n", r); /* MIME structure */ ap_rputs(" \n" " \n" " \n\n", r); ap_rprintf(r, " \n" " \n" " \n" " \n", baseURI, URI_ESCAPE_OR_BLANK(r->pool, m->msgID)); ap_rputs(" \n", r); ap_rputs("
Message view
Top
From%s
Subject%s
Date%s
\n", r);
    ap_rprintf(r, "%s",
               mbox_wrap_text(mbox_mime_get_body(r->pool, m->mime_msg)));
    ap_rputs("
Mime\n
    \n", r); mbox_mime_display_static_structure(r, m->mime_msg, apr_psprintf(r->pool, "%s/raw/%s/", baseURI, m->msgID)); ap_rputs("
\n
View raw message
\n", r); ap_rputs(" \n", r); ap_rputs("\n", r); return OK; } /* Display an XML formatted mail */ apr_status_t mbox_xml_message(request_rec *r, apr_file_t *f) { mbox_dir_cfg_t *conf; Message *m; 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/). */ 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("\n", r); from = ESCAPE_OR_BLANK(r->pool, m->from); from = mbox_cte_decode_header(r->pool, from); if (conf->antispam) { from = email_antispam(from); } ap_rprintf(r, "\n" " \n" " \n" " \n" " pool, m->msgID), from, ESCAPE_OR_BLANK(r->pool, m->subject), ESCAPE_OR_BLANK(r->pool, m->rfc822_date)); ap_rprintf(r, "%s", mbox_wrap_text(mbox_mime_get_body(r->pool, m->mime_msg))); ap_rputs("]]>\n", r); ap_rputs(" \n", r); mbox_mime_display_xml_structure(r, m->mime_msg, apr_psprintf(r->pool, "/")); ap_rputs(" \n", r); ap_rputs("\n", r); return OK; }