static void mime_content_disposition(MIME_NODE *node, const HEADER_OPTS *header_info) { const char *cp = STR(node->buffer) + strlen(header_info->name) + 1; ssize_t tok_count; #define PARSE_CONTENT_DISPOSITION(state, ptr) \ header_token(state->token, MIME_MAX_TOKEN, \ state->token_buffer, ptr, RFC2045_TSPECIALS, ';') while ((tok_count = PARSE_CONTENT_DISPOSITION(node->state, &cp)) > 0) { if (tok_count < 3 || node->state->token[1].type != '=') continue; if (TOKEN_MATCH(node->state->token[0], "filename") && node->header_filename == NULL) { node->header_filename = acl_mystrdup(node->state->token[2].u.value); } else if (TOKEN_MATCH(node->state->token[0], "name") && node->header_name == NULL) { node->header_name = acl_mystrdup(node->state->token[2].u.value); } } }
/* * Read a word from a buffer and advance pointer. * This function knows about escapes and quotes. * * At end-of-line, buf[0] is set to '\0'. * Returns 0 or special token value. */ static FR_TOKEN getthing(const char **ptr, char *buf, int buflen, int tok, const FR_NAME_NUMBER *tokenlist) { char *s; const char *p; int quote, end = 0; unsigned int x; const FR_NAME_NUMBER*t; FR_TOKEN rcode; buf[0] = 0; /* Skip whitespace */ p = *ptr; while (*p && isspace((int) *p)) p++; if (*p == 0) { *ptr = p; return T_EOL; } /* * Might be a 1 or 2 character token. */ if (tok) for (t = tokenlist; t->name; t++) { if (TOKEN_MATCH(p, t->name)) { strcpy(buf, t->name); p += strlen(t->name); while (isspace((int) *p)) p++; *ptr = p; return (FR_TOKEN) t->number; } } /* Read word. */ quote = 0; if ((*p == '"') || (*p == '\'') || (*p == '`')) { quote = *p; end = 0; p++; } s = buf; while (*p && buflen-- > 1) { if (quote && (*p == '\\')) { p++; switch(*p) { case 'r': *s++ = '\r'; break; case 'n': *s++ = '\n'; break; case 't': *s++ = '\t'; break; case '\0': *s++ = '\\'; p--; /* force EOS */ break; default: if (*p >= '0' && *p <= '9' && sscanf(p, "%3o", &x) == 1) { *s++ = x; p += 2; } else *s++ = *p; break; } p++; continue; } if (quote && (*p == quote)) { end = 1; p++; break; } if (!quote) { if (isspace((int) *p)) break; if (tok) { for (t = tokenlist; t->name; t++) if (TOKEN_MATCH(p, t->name)) break; if (t->name != NULL) break; } } *s++ = *p++; } *s++ = 0; if (quote && !end) { fr_strerror_printf("Unterminated string"); return T_OP_INVALID; } /* Skip whitespace again. */ while (*p && isspace((int) *p)) p++; *ptr = p; /* we got SOME form of output string, even if it is empty */ switch (quote) { default: rcode = T_BARE_WORD; break; case '\'': rcode = T_SINGLE_QUOTED_STRING; break; case '"': rcode = T_DOUBLE_QUOTED_STRING; break; case '`': rcode = T_BACK_QUOTED_STRING; break; } return rcode; }
static void mime_content_type(MIME_NODE *node, const HEADER_OPTS *header_info) { const char *cp; ssize_t tok_count; MIME_STATE *state = node->state; #define PARSE_CONTENT_TYPE_HEADER(state, ptr) \ header_token(state->token, MIME_MAX_TOKEN, \ state->token_buffer, ptr, RFC2045_TSPECIALS, ';') cp = STR(node->buffer) + strlen(header_info->name) + 1; if ((tok_count = PARSE_CONTENT_TYPE_HEADER(state, &cp)) <= 0) { /* * other/whatever. */ node->ctype = MIME_CTYPE_OTHER; return; } /* tok_count > 0 */ /* * message/whatever body parts start with another block of message * headers that we may want to look at. The partial and external-body * subtypes cannot be subjected to 8-bit -> 7-bit conversion, so we * must properly recognize them. */ if (TOKEN_MATCH(state->token[0], "message")) { node->ctype = MIME_CTYPE_MESSAGE; node->stype = MIME_STYPE_OTHER; if (tok_count >= 3 && state->token[1].type == '/') { if (TOKEN_MATCH(state->token[2], "rfc822")) node->stype = MIME_STYPE_RFC822; else if (TOKEN_MATCH(state->token[2], "partial")) node->stype = MIME_STYPE_PARTIAL; else if (TOKEN_MATCH(state->token[2], "external-body")) node->stype = MIME_STYPE_EXTERN_BODY; } } /* * multipart/digest has default content type message/rfc822, * multipart/whatever has default content type text/plain. */ else if (TOKEN_MATCH(state->token[0], "multipart")) { node->ctype = MIME_CTYPE_MULTIPART; if (tok_count >= 3 && state->token[1].type == '/') { if (TOKEN_MATCH(state->token[2], "digest")) { node->ctype = MIME_CTYPE_MESSAGE; node->stype = MIME_STYPE_RFC822; } else if (TOKEN_MATCH(state->token[2], "alternative")) { node->stype = MIME_STYPE_ALTERNATIVE; } else if (TOKEN_MATCH(state->token[2], "related")) { node->stype = MIME_STYPE_RELATED; } else if (TOKEN_MATCH(state->token[2], "mixed")) { node->stype = MIME_STYPE_MIXED; } else { node->stype = MIME_STYPE_OTHER; } } else { node->ctype = MIME_CTYPE_TEXT; node->stype = MIME_STYPE_PLAIN; } /* * Yes, this is supposed to capture multiple boundary strings, * which are illegal and which could be used to hide content in * an implementation dependent manner. The code below allows us * to find embedded message headers as long as the sender uses * only one of these same-level boundary strings. * * Yes, this is supposed to ignore the boundary value type. */ while ((tok_count = PARSE_CONTENT_TYPE_HEADER(state, &cp)) >= 0) { if (tok_count < 3 || state->token[1].type != '=') continue; if (TOKEN_MATCH(state->token[0], "boundary")) { if (node->boundary == NULL) node->boundary = acl_vstring_alloc(256); /* 需要添加 "--" 做为分隔符的前导符 */ SCP(node->boundary, "--"); SCAT(node->boundary, state->token[2].u.value); break; } } } /* * text/whatever. Right now we don't really care if it is plain or * not, but we may want to recognize subtypes later, and then this * code can serve as an example. */ else if (TOKEN_MATCH(state->token[0], "text")) { node->ctype = MIME_CTYPE_TEXT; if (tok_count >= 3 && state->token[1].type == '/') { if (TOKEN_MATCH(state->token[2], "plain")) node->stype = MIME_STYPE_PLAIN; else if (TOKEN_MATCH(state->token[2], "html")) node->stype = MIME_STYPE_HTML; else node->stype = MIME_STYPE_OTHER; } else node->stype = MIME_STYPE_OTHER; while ((tok_count = PARSE_CONTENT_TYPE_HEADER(state, &cp)) >= 0) { if (tok_count < 3 || state->token[1].type != '=') continue; if (TOKEN_MATCH(state->token[0], "charset") && node->charset == NULL) { node->charset = acl_mystrdup(state->token[2].u.value); break; } } /* 如果没有字符集, 则缺省采用 gb2312 */ if (node->charset == NULL) node->charset = acl_mystrdup("gb2312"); } else if (TOKEN_MATCH(state->token[0], "image")) { node->ctype = MIME_CTYPE_IMAGE; if (tok_count >= 3 && state->token[1].type == '/') { if (TOKEN_MATCH(state->token[2], "jpeg")) node->stype = MIME_STYPE_JPEG; else if (TOKEN_MATCH(state->token[2], "gif")) node->stype = MIME_STYPE_GIF; else if (TOKEN_MATCH(state->token[2], "bmp")) node->stype = MIME_STYPE_BMP; else if (TOKEN_MATCH(state->token[2], "png")) node->stype = MIME_STYPE_PNG; else node->stype = MIME_STYPE_OTHER; } else node->stype = MIME_STYPE_OTHER; while ((tok_count = PARSE_CONTENT_TYPE_HEADER(state, &cp)) >= 0) { if (tok_count < 3 || state->token[1].type != '=') continue; if (TOKEN_MATCH(state->token[0], "name") && node->header_name == NULL) { node->header_name = acl_mystrdup(state->token[2].u.value); break; } } } else if (TOKEN_MATCH(state->token[0], "application")) { node->ctype = MIME_CTYPE_APPLICATION; if (tok_count >= 3 && state->token[1].type == '/') { if (TOKEN_MATCH(state->token[2], "octet-stream")) node->stype = MIME_STYPE_OCTET_STREAM; else node->stype = MIME_STYPE_OTHER; } while ((tok_count = PARSE_CONTENT_TYPE_HEADER(state, &cp)) >= 0) { if (tok_count < 3 || state->token[1].type != '=') continue; if (TOKEN_MATCH(state->token[0], "name") && node->header_name == NULL) { node->header_name = acl_mystrdup(state->token[2].u.value); break; } } } }
static void mime_state_content_type(MIME_STATE *state, HEADER_OPTS *header_info) { const char *cp; int tok_count; int def_ctype; int def_stype; #define TOKEN_MATCH(tok, text) \ ((tok).type == HEADER_TOK_TOKEN && strcasecmp((tok).u.value, (text)) == 0) #define RFC2045_TSPECIALS "()<>@,;:\\\"/[]?=" #define PARSE_CONTENT_TYPE_HEADER(state, ptr) \ header_token(state->token, MIME_MAX_TOKEN, \ state->token_buffer, ptr, RFC2045_TSPECIALS, ';') cp = STR(state->output_buffer) + strlen(header_info->name) + 1; if ((tok_count = PARSE_CONTENT_TYPE_HEADER(state, &cp)) > 0) { /* * text/whatever. Right now we don't really care if it is plain or * not, but we may want to recognize subtypes later, and then this * code can serve as an example. */ if (TOKEN_MATCH(state->token[0], "text")) { state->curr_ctype = MIME_CTYPE_TEXT; if (tok_count >= 3 && state->token[1].type == '/' && TOKEN_MATCH(state->token[2], "plain")) state->curr_stype = MIME_STYPE_PLAIN; else state->curr_stype = MIME_STYPE_OTHER; return; } /* * message/whatever body parts start with another block of message * headers that we may want to look at. The partial and external-body * subtypes cannot be subjected to 8-bit -> 7-bit conversion, so we * must properly recognize them. */ if (TOKEN_MATCH(state->token[0], "message")) { state->curr_ctype = MIME_CTYPE_MESSAGE; state->curr_stype = MIME_STYPE_OTHER; if (tok_count >= 3 && state->token[1].type == '/') { if (TOKEN_MATCH(state->token[2], "rfc822")) state->curr_stype = MIME_STYPE_RFC822; else if (TOKEN_MATCH(state->token[2], "partial")) state->curr_stype = MIME_STYPE_PARTIAL; else if (TOKEN_MATCH(state->token[2], "external-body")) state->curr_stype = MIME_STYPE_EXTERN_BODY; } return; } /* * multipart/digest has default content type message/rfc822, * multipart/whatever has default content type text/plain. */ if (TOKEN_MATCH(state->token[0], "multipart")) { state->curr_ctype = MIME_CTYPE_MULTIPART; if (tok_count >= 3 && state->token[1].type == '/' && TOKEN_MATCH(state->token[2], "digest")) { def_ctype = MIME_CTYPE_MESSAGE; def_stype = MIME_STYPE_RFC822; } else { def_ctype = MIME_CTYPE_TEXT; def_stype = MIME_STYPE_PLAIN; } /* * Yes, this is supposed to capture multiple boundary strings, * which are illegal and which could be used to hide content in * an implementation dependent manner. The code below allows us * to find embedded message headers as long as the sender uses * only one of these same-level boundary strings. * * Yes, this is supposed to ignore the boundary value type. */ while ((tok_count = PARSE_CONTENT_TYPE_HEADER(state, &cp)) >= 0) { if (tok_count >= 3 && TOKEN_MATCH(state->token[0], "boundary") && state->token[1].type == '=') { if (state->nesting_level > var_mime_maxdepth) { if (state->static_flags & MIME_OPT_REPORT_NESTING) REPORT_ERROR(state, MIME_ERR_NESTING, STR(state->output_buffer)); } else { mime_state_push(state, def_ctype, def_stype, state->token[2].u.value); } } } } return; } /* * other/whatever. */ else { state->curr_ctype = MIME_CTYPE_OTHER; return; } }