/* Copyright 2001-2005 The Apache Software Foundation or its licensors, as * applicable. * * Licensed 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. */ /* MIME parsing and structure management functions. */ #include "mod_mbox.h" /* Decode a multipart (or not) email. In order to support multiple * levels of MIME parts, this function is recursive. */ mbox_mime_message_t *mbox_mime_decode_multipart(apr_pool_t *p, char *body, char *ct, mbox_cte_e cte, char *boundary) { mbox_mime_message_t *mail; char *tmp = NULL, *k = NULL, *end_bound = NULL; char *headers_bound = NULL; /* Locate the end of part headers */ headers_bound = strstr(body, "\n\n"); if (!headers_bound) { return NULL; } /* If no Content-Type is provided, it means that we are parsing a sub-part of the multipart message. The Content-Type header should then be the first line of the part. If not, just ignore the sub-part. */ tmp = strstr(body, "Content-Type: "); if (!ct && (!tmp || tmp > headers_bound)) { return NULL; } mail = calloc(1, sizeof(mbox_mime_message_t)); /* If no Content-Type is given, we have to look for it. */ if (!ct) { tmp += strlen("Content-Type: "); k = strchr(tmp, ';'); /* Isolate the Content-Type string (between 'Content-Type: ' and ';' or end of line */ if (k && k < headers_bound) { *k = 0; } else { k = tmp; while (*k) { if (isspace(*k)) { *k = 0; break; } *k++; } } /* Copy the Content-Type and reset *k */ mail->content_type = apr_pstrdup(p, tmp); *k = ';'; /* If available, get MIME part name */ tmp = strstr(body, "name="); if (tmp && tmp < headers_bound) { tmp += strlen("name="); k = tmp; while (*k) { if (isspace(*k) || *k == ';') { *k = 0; break; } *k++; } /* Check for double quotes */ if ((*tmp == '"') && (tmp[strlen(tmp)-1] == '"')) { mail->content_name = apr_pstrndup(p, tmp+1, strlen(tmp) - 2); } else { mail->content_name = apr_pstrdup(p, tmp); } *k = ';'; } } else mail->content_type = ct; /* Now we have a Content-Type. Look for other useful header information */ /* Check Content-Disposition if the match is within the headers */ tmp = strstr(body, "Content-Disposition: "); if (tmp && tmp < headers_bound) { tmp += strlen("Content-Disposition: "); k = tmp; while (*k) { if (isspace(*k) || *k == ';') { *k = 0; break; } *k++; } /* Copy the Content-Disposition and reset *k */ mail->content_disposition = apr_pstrdup(p, tmp); *k = '\n'; } else { mail->content_disposition = apr_pstrdup(p, "inline"); } /* Check Content-Transfer-Encoding, if needed */ if (cte == CTE_NONE) { tmp = strstr(body, "Content-Transfer-Encoding: "); if (tmp && tmp < headers_bound) { tmp += strlen("Content-Transfer-Encoding: "); k = tmp; while (*k) { if (isspace(*k) || *k == ';') { *k = 0; break; } *k++; } /* Copy the Content-Disposition and reset *k */ mail->cte = mbox_parse_cte_header(tmp); *k = '\n'; } } else { mail->cte = cte; } /* Now we have all the headers we need. Start processing the body. If the Content-Type was given at call time, the body starts where it's given. Otherwise it's after the headers (first new empty line) */ if (ct) { mail->body = body; } else { mail->body = strstr(body, "\n\n") + 2; } /* If the mail is a multipart message, search for the boundary, and process its sub parts by recursive calls. */ if (strncmp(mail->content_type, "multipart/", strlen("multipart/")) == 0) { int end = 0, count = 0; char *search, *bound; /* If the boundary was not given, we must look for it in the headers */ if (!boundary) { tmp = strstr(body, "boundary=\""); if (!tmp) { return NULL; } tmp += strlen("boundary=\""); k = tmp; while (*k) { if (*k == '"') { *k = 0; break; } *k++; } mail->boundary = apr_pstrdup(p, tmp); *k = '"'; } /* Otherwise, the boundary is as given to us */ else { mail->boundary = boundary; } /* Now we have our boundary string. We must : look for it once (begining of MIME part) and then look for the end boundary : --boundary-- to mark the end of the MIME part */ /* The start boundary */ bound = strstr(mail->body, mail->boundary); if (!bound) { return NULL; } /* The end boudary */ end_bound = calloc(strlen(mail->boundary)+5, sizeof(char)); sprintf(end_bound, "--%s--", mail->boundary); tmp = strstr(mail->body, end_bound); if (!tmp) { return NULL; } *tmp = 0; /* Set the search begining to the line after the start boundary. */ search = bound + strlen(mail->boundary) + 1; /* While the MIME part is not finished, go through all sub parts */ while (!end) { char *inbound; inbound = strstr(search+strlen(mail->boundary), mail->boundary); if (inbound) { char *t = inbound - 2; *t = 0; } /* Allocate a new pointer for the sub part, and parse it. */ mail->sub = realloc(mail->sub, ++count * sizeof(struct mimemsg *)); mail->sub[count-1] = mbox_mime_decode_multipart(p, search, NULL, CTE_NONE, NULL); /* If the boudary is found again, it means we have another MIME part in the same multipart message. Set the new search begining to the line after this new start boundary */ if (inbound) { char *t = inbound - 2; *t = '-'; search = inbound + strlen(mail->boundary) + 1; if (mail->sub[count-1]) { mail->sub[count-1]->body_len = inbound - mail->sub[count-1]->body - 2; } } /* Otherwise, the MIME part is finished. */ else { mail->sub_count = count; end = 1; } } /* Finally reset the end-body pointer. */ // *tmp = '-'; } /* If the parsed body is not multipart or is a MIME part, the body length is the length of the body string (no surprise here). If it's a MIME part, its correct length will be set after the call to mbox_mime_decode_multipart just a dozen lines above. */ else { mail->body_len = strlen(mail->body); } return mail; } /* Decode a MIME part body, according to its CTE. */ char *mbox_mime_decode_body(apr_pool_t *p, mbox_cte_e cte, char *body, apr_size_t len) { char *new_body = apr_pstrndup(p, body, len); if (cte == CTE_BASE64) { len = mbox_cte_decode_b64(new_body); } else if (cte == CTE_QP) { len = mbox_cte_decode_qp(new_body); } new_body[len] = 0; return new_body; } /* This function returns the relevant MIME part from a message. For * the moment, it just returns the first text/ MIME part available. */ char *mbox_mime_get_body(apr_pool_t *p, mbox_mime_message_t *m) { int i; if (!m) { return NULL; } if (strncasecmp(m->content_type, "text/", strlen("text/")) == 0) { char *new_body; new_body = mbox_mime_decode_body(p, m->cte, m->body, m->body_len); m->body_len = mbox_cte_escape_html(p, new_body, m->body_len, &(m->body)); return apr_pstrndup(p, m->body, m->body_len); } if (!m->sub) { return NULL; } for (i=0 ; isub_count ; i++) { return mbox_mime_get_body(p, m->sub[i]); } return NULL; } /* Display an XHTML MIME structure */ void mbox_mime_display_static_structure(request_rec *r, mbox_mime_message_t *m, char *link) { int i; if (!m) { return; } ap_rputs("
  • ", r); if (m->body_len) { ap_rprintf(r, "", link); } if (m->content_name) { ap_rprintf(r, "%s", m->content_name); } else { ap_rprintf(r, "Unnamed %s", m->content_type); } if (m->body_len) { ap_rputs("", r); } ap_rprintf(r, " (%s, %s, %u bytes)
  • \n", m->content_disposition, mbox_cte_to_char(m->cte), m->body_len); if (!m->sub) { return; } for (i=0 ; isub_count ; i++) { ap_rputs("\n", r); } } /* Display an XML MIME structure */ void mbox_mime_display_xml_structure(request_rec *r, mbox_mime_message_t *m, char *link) { int i; if (!m) { return; } if (m->content_name) { ap_rprintf(r, "\n", m->content_name, m->content_disposition, mbox_cte_to_char(m->cte), m->body_len, link); } else { ap_rprintf(r, "\n", m->content_type, m->content_disposition, mbox_cte_to_char(m->cte), m->body_len, link); } if (!m->sub) { return; } ap_rputs("\n", r); for (i=0 ; isub_count ; i++) { if (link[strlen(link)-1] == '/') { link[strlen(link)-1] = 0; } mbox_mime_display_xml_structure(r, m->sub[i], apr_psprintf(r->pool, "%s/%d", link, i+1)); } ap_rputs("\n", r); }