bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen#include "lib.h"
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen#include "str.h"
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen#include "strescape.h"
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen#include "rfc822-parser.h"
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen/*
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen atext = ALPHA / DIGIT / ; Any character except controls,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen "!" / "#" / ; SP, and specials.
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen "$" / "%" / ; Used for atoms
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen "&" / "'" /
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen "*" / "+" /
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen "-" / "/" /
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen "=" / "?" /
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen "^" / "_" /
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen "`" / "{" /
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen "|" / "}" /
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen "~"
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen MIME:
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen or tspecials>
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen tspecials := "(" / ")" / "<" / ">" / "@" /
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen "," / ";" / ":" / "\" / <">
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen "/" / "[" / "]" / "?" / "="
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen So token is same as dot-atom, except stops also at '/', '?' and '='.
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen*/
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen/* atext chars are marked with 1, alpha and digits with 2,
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen atext-but-mime-tspecials with 4 */
5bbce06405dd5fc0d67411e48856953785f109f5Timo Sirainenunsigned char rfc822_atext_chars[256] = {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 4, /* 32-47 */
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 4, 0, 4, /* 48-63 */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 64-79 */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1, /* 80-95 */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 96-111 */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, /* 112-127 */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen};
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainenvoid rfc822_parser_init(struct rfc822_parser_context *ctx,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen const unsigned char *data, size_t size,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen string_t *last_comment)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen{
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(ctx);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data = data;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->end = data + size;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->last_comment = last_comment;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen}
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainenint rfc822_skip_comment(struct rfc822_parser_context *ctx)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen{
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen const unsigned char *start;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen int level = 1;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen i_assert(*ctx->data == '(');
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (ctx->last_comment != NULL)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_truncate(ctx->last_comment, 0);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen start = ++ctx->data;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen for (; ctx->data != ctx->end; ctx->data++) {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen switch (*ctx->data) {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen case '(':
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen level++;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen break;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen case ')':
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (--level == 0) {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (ctx->last_comment != NULL) {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_append_n(ctx->last_comment, start,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data - start);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data++;
f0339f522dc9c8e2e8a29ef9a3f937c431c6bd1bTimo Sirainen return ctx->data != ctx->end ? 1 : 0;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen break;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen case '\\':
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (ctx->last_comment != NULL) {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_append_n(ctx->last_comment, start,
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data - start);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen start = ctx->data + 1;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data++;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (ctx->data == ctx->end)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return -1;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen break;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen /* missing ')' */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return -1;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen}
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainenint rfc822_skip_lwsp(struct rfc822_parser_context *ctx)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen{
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen for (; ctx->data != ctx->end;) {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (*ctx->data == ' ' || *ctx->data == '\t' ||
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen *ctx->data == '\r' || *ctx->data == '\n') {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data++;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen continue;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (*ctx->data != '(')
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen break;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (rfc822_skip_comment(ctx) < 0)
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen return -1;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
f0339f522dc9c8e2e8a29ef9a3f937c431c6bd1bTimo Sirainen return ctx->data != ctx->end ? 1 : 0;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen}
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainenint rfc822_parse_atom(struct rfc822_parser_context *ctx, string_t *str)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen{
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen const unsigned char *start;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen /*
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen atom = [CFWS] 1*atext [CFWS]
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen atext =
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ; Any character except controls, SP, and specials.
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen */
f4b1d7e52e983ba3063584c8b0ef577d6946331cTimo Sirainen if (ctx->data == ctx->end || !IS_ATEXT(*ctx->data))
f4b1d7e52e983ba3063584c8b0ef577d6946331cTimo Sirainen return -1;
f4b1d7e52e983ba3063584c8b0ef577d6946331cTimo Sirainen
f4b1d7e52e983ba3063584c8b0ef577d6946331cTimo Sirainen for (start = ctx->data++; ctx->data != ctx->end; ctx->data++) {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (IS_ATEXT(*ctx->data))
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen continue;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_append_n(str, start, ctx->data - start);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return rfc822_skip_lwsp(ctx);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_append_n(str, start, ctx->data - start);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return 0;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen}
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainenint rfc822_parse_dot_atom(struct rfc822_parser_context *ctx, string_t *str)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen{
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen const unsigned char *start;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen int ret;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen /*
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen dot-atom = [CFWS] dot-atom-text [CFWS]
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen dot-atom-text = 1*atext *("." 1*atext)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen atext =
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ; Any character except controls, SP, and specials.
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen For RFC-822 compatibility allow LWSP around '.'
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen */
f4b1d7e52e983ba3063584c8b0ef577d6946331cTimo Sirainen if (ctx->data == ctx->end || !IS_ATEXT(*ctx->data))
f4b1d7e52e983ba3063584c8b0ef577d6946331cTimo Sirainen return -1;
f4b1d7e52e983ba3063584c8b0ef577d6946331cTimo Sirainen
f53915d18c75bb167cf4f86f7325baae7ea12ce9Timo Sirainen for (start = ctx->data++; ctx->data != ctx->end; ) {
f53915d18c75bb167cf4f86f7325baae7ea12ce9Timo Sirainen if (IS_ATEXT(*ctx->data)) {
f53915d18c75bb167cf4f86f7325baae7ea12ce9Timo Sirainen ctx->data++;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen continue;
f53915d18c75bb167cf4f86f7325baae7ea12ce9Timo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_append_n(str, start, ctx->data - start);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if ((ret = rfc822_skip_lwsp(ctx)) <= 0)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return ret;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (*ctx->data != '.')
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return 1;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data++;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_append_c(str, '.');
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if ((ret = rfc822_skip_lwsp(ctx)) <= 0)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return ret;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen start = ctx->data;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_append_n(str, start, ctx->data - start);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return 0;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen}
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainenint rfc822_parse_mime_token(struct rfc822_parser_context *ctx, string_t *str)
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen{
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen const unsigned char *start;
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen for (start = ctx->data; ctx->data != ctx->end; ctx->data++) {
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen if (IS_ATEXT_NON_TSPECIAL(*ctx->data) || *ctx->data == '.')
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen continue;
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen str_append_n(str, start, ctx->data - start);
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen return rfc822_skip_lwsp(ctx);
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen }
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen str_append_n(str, start, ctx->data - start);
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen return 0;
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen}
2de709376eddc50ec5fa470358bb57cf0a87bb1fTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainenint rfc822_parse_quoted_string(struct rfc822_parser_context *ctx, string_t *str)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen{
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen const unsigned char *start;
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen size_t len;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen i_assert(*ctx->data == '"');
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data++;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen for (start = ctx->data; ctx->data != ctx->end; ctx->data++) {
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen switch (*ctx->data) {
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen case '"':
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_append_n(str, start, ctx->data - start);
e8ef7a8c30741ad30c617d453b1c32d8da0111deTimo Sirainen ctx->data++;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return rfc822_skip_lwsp(ctx);
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen case '\n':
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen /* folding whitespace, remove the (CR)LF */
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen len = ctx->data - start;
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen if (len > 0 && start[len-1] == '\r')
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen len--;
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen str_append_n(str, start, len);
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen start = ctx->data + 1;
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen break;
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen case '\\':
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen ctx->data++;
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen if (ctx->data == ctx->end)
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen return -1;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
4c0c72a8b6791910bbc970eff0c75a91bdc38eb3Timo Sirainen str_append_n(str, start, ctx->data - start - 1);
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen start = ctx->data;
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen break;
0452398b245ac9643f6b47dfc450dc5bf7da51d0Timo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen /* missing '"' */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return -1;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen}
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainenstatic int
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainenrfc822_parse_atom_or_dot(struct rfc822_parser_context *ctx, string_t *str)
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen{
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen const unsigned char *start;
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen /*
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen atom = [CFWS] 1*atext [CFWS]
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen atext =
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen ; Any character except controls, SP, and specials.
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen The difference between this function and rfc822_parse_dot_atom()
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen is that this doesn't just silently skip over all the whitespace.
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen */
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen for (start = ctx->data; ctx->data != ctx->end; ctx->data++) {
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen if (IS_ATEXT(*ctx->data) || *ctx->data == '.')
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen continue;
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen str_append_n(str, start, ctx->data - start);
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen return rfc822_skip_lwsp(ctx);
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen }
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen str_append_n(str, start, ctx->data - start);
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen return 0;
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen}
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainenint rfc822_parse_phrase(struct rfc822_parser_context *ctx, string_t *str)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen{
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen int ret;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen /*
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen phrase = 1*word / obs-phrase
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen word = atom / quoted-string
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen obs-phrase = word *(word / "." / CFWS)
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen */
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen
48de9296c782084631b0a82e102d17e9f30060bdTimo Sirainen if (ctx->data == ctx->end)
48de9296c782084631b0a82e102d17e9f30060bdTimo Sirainen return 0;
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen if (*ctx->data == '.')
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen return -1;
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen for (;;) {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (*ctx->data == '"')
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ret = rfc822_parse_quoted_string(ctx, str);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen else
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen ret = rfc822_parse_atom_or_dot(ctx, str);
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (ret <= 0)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return ret;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen if (!IS_ATEXT(*ctx->data) && *ctx->data != '"'
d290217adc3d17ba82fd81f164da3c2b88489700Timo Sirainen && *ctx->data != '.')
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen break;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_append_c(str, ' ');
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return rfc822_skip_lwsp(ctx);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen}
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainenstatic int
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainenrfc822_parse_domain_literal(struct rfc822_parser_context *ctx, string_t *str)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen{
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen const unsigned char *start;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen /*
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen dcontent = dtext / quoted-pair
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen dtext = NO-WS-CTL / ; Non white space controls
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen %d33-90 / ; The rest of the US-ASCII
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen %d94-126 ; characters not including "[",
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ; "]", or "\"
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen i_assert(*ctx->data == '[');
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen for (start = ctx->data; ctx->data != ctx->end; ctx->data++) {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (*ctx->data == '\\') {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data++;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (ctx->data == ctx->end)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen break;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen } else if (*ctx->data == ']') {
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data++;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen str_append_n(str, start, ctx->data - start);
da27cf9a026b1c6708bdc0de1c02b83838e64bd8Timo Sirainen return rfc822_skip_lwsp(ctx);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen }
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen /* missing ']' */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return -1;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen}
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainenint rfc822_parse_domain(struct rfc822_parser_context *ctx, string_t *str)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen{
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen /*
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen domain = dot-atom / domain-literal / obs-domain
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen obs-domain = atom *("." atom)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen */
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen i_assert(*ctx->data == '@');
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen ctx->data++;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen if (rfc822_skip_lwsp(ctx) <= 0)
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen return -1;
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen
da27cf9a026b1c6708bdc0de1c02b83838e64bd8Timo Sirainen if (*ctx->data == '[')
da27cf9a026b1c6708bdc0de1c02b83838e64bd8Timo Sirainen return rfc822_parse_domain_literal(ctx, str);
da27cf9a026b1c6708bdc0de1c02b83838e64bd8Timo Sirainen else
da27cf9a026b1c6708bdc0de1c02b83838e64bd8Timo Sirainen return rfc822_parse_dot_atom(ctx, str);
a24665de9d5c773115a5918e60ed587aafe67d5cTimo Sirainen}
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainenint rfc822_parse_content_type(struct rfc822_parser_context *ctx, string_t *str)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen{
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (rfc822_skip_lwsp(ctx) <= 0)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return -1;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen /* get main type */
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (rfc822_parse_mime_token(ctx, str) <= 0)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return -1;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen /* skip over "/" */
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (*ctx->data != '/')
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return -1;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen ctx->data++;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (rfc822_skip_lwsp(ctx) <= 0)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return -1;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen str_append_c(str, '/');
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen /* get subtype */
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return rfc822_parse_mime_token(ctx, str);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen}
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainenint rfc822_parse_content_param(struct rfc822_parser_context *ctx,
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen const char **key_r, const char **value_r)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen{
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen string_t *tmp;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen size_t value_pos;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen int ret;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen /* .. := *(";" parameter)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen parameter := attribute "=" value
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen attribute := token
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen value := token / quoted-string
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen */
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen *key_r = NULL;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen *value_r = NULL;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (ctx->data == ctx->end)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return 0;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (*ctx->data != ';')
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return -1;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen ctx->data++;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (rfc822_skip_lwsp(ctx) <= 0)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return -1;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen tmp = t_str_new(64);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (rfc822_parse_mime_token(ctx, tmp) <= 0)
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return -1;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen str_append_c(tmp, '\0');
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen value_pos = str_len(tmp);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if (*ctx->data != '=')
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return -1;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen ctx->data++;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen if ((ret = rfc822_skip_lwsp(ctx)) <= 0) {
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen /* broken / no value */
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen } else if (*ctx->data == '"') {
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen ret = rfc822_parse_quoted_string(ctx, tmp);
a37b4f6dfcf753c0071ff0480b9510dce0d6f4aeTimo Sirainen } else if (ctx->data != ctx->end && *ctx->data == '=') {
a37b4f6dfcf753c0071ff0480b9510dce0d6f4aeTimo Sirainen /* workaround for broken input:
a37b4f6dfcf753c0071ff0480b9510dce0d6f4aeTimo Sirainen name==?utf-8?b?...?= */
a37b4f6dfcf753c0071ff0480b9510dce0d6f4aeTimo Sirainen while (ctx->data != ctx->end && *ctx->data != ';' &&
a37b4f6dfcf753c0071ff0480b9510dce0d6f4aeTimo Sirainen *ctx->data != ' ' && *ctx->data != '\t' &&
a37b4f6dfcf753c0071ff0480b9510dce0d6f4aeTimo Sirainen *ctx->data != '\r' && *ctx->data != '\n') {
a37b4f6dfcf753c0071ff0480b9510dce0d6f4aeTimo Sirainen str_append_c(tmp, *ctx->data);
a37b4f6dfcf753c0071ff0480b9510dce0d6f4aeTimo Sirainen ctx->data++;
a37b4f6dfcf753c0071ff0480b9510dce0d6f4aeTimo Sirainen }
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen } else {
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen ret = rfc822_parse_mime_token(ctx, tmp);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen }
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen *key_r = str_c(tmp);
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen *value_r = *key_r + value_pos;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen return ret < 0 ? -1 : 1;
687d1dee0e92229232aa8be416897b640df67d07Timo Sirainen}