mbox-sync-parse.c revision 6825360d446542046757b06064282301c4c6b27c
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (C) 2004 Timo Sirainen */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde/* MD5 header summing logic was pretty much copy&pasted from popa3d by
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen Solar Designer */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen#define IS_LWSP_LF(c) (IS_LWSP(c) || (c) == '\n')
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde bool (*func)(struct mbox_sync_mail_context *ctx,
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainenstatic void parse_trailing_whitespace(struct mbox_sync_mail_context *ctx,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* the value may contain newlines. we can't count whitespace before
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde and after it as a single contiguous whitespace block, as that may
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde get us into situation where removing whitespace goes eg.
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen " \n \n" -> " \n\n" which would then be treated as end of headers.
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen that could probably be avoided by being careful, but as newlines
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen should never be there (we don't generate them), it's not worth the
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen ctx->mail.offset = ctx->hdr_offset + str_len(ctx->header) + i;
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic enum mail_flags mbox_flag_find(struct mbox_flag_type *flags, char chr)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestatic void parse_status_flags(struct mbox_sync_mail_context *ctx,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen mbox_flag_find(flags_list, hdr->full_value[i]);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic bool parse_status(struct mbox_sync_mail_context *ctx,
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen parse_status_flags(ctx, hdr, mbox_status_flags);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde ctx->hdr_pos[MBOX_HDR_STATUS] = str_len(ctx->header);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestatic bool parse_x_status(struct mbox_sync_mail_context *ctx,
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde parse_status_flags(ctx, hdr, mbox_xstatus_flags);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde ctx->hdr_pos[MBOX_HDR_X_STATUS] = str_len(ctx->header);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic bool keyword_is_valid(const char *keyword)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* try to only prevent the most malicious looking keywords. */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainenparse_imap_keywords_list(struct mbox_sync_mail_context *ctx,
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen /* read the keyword */
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen /* add it to index's keyword list if it's not there already */
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen keyword = t_strndup(hdr->full_value + keyword_start,
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen ctx->sync_ctx->mbox->ibox.index, keyword, &idx);
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen if (count != array_count(ctx->sync_ctx->mbox->ibox.keyword_names)) {
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen /* need to update this list */
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainenstatic bool parse_x_imap_base(struct mbox_sync_mail_context *ctx,
2ba63f475f74b2aa87f9fd9e28a6c5738deb0878Timo Sirainen /* Valid only in first message */
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen /* <uid-validity> 10x<uid-last> */
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen for (i = 0, uid_validity = 0; i < hdr->full_value_len; i++) {
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9') {
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen uid_validity = uid_validity * 10 + (hdr->full_value[i] - '0');
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen for (uid_last = 0, j = 0; i < hdr->full_value_len; i++, j++) {
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9') {
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen uid_last = uid_last * 10 + (hdr->full_value[i] - '0');
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen if (j != 10 ||
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen hdr->full_value_offset != ctx->hdr_offset + str_len(ctx->header)) {
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen /* uid-last field must be exactly 10 characters to make
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen rewriting it easier. also don't try to do this if some
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen headers have been removed */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde /* first time parsing this (ie. we're not rewriting).
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen save the values. */
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen ctx->sync_ctx->base_uid_validity = uid_validity;
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde /* new messages have been added since our last sync.
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde just update our internal next_uid. */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* we need to rewrite the next-uid */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde i_assert(ctx->sync_ctx->next_uid > ctx->sync_ctx->prev_msg_uid);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] = str_len(ctx->header);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildestatic bool parse_x_imap(struct mbox_sync_mail_context *ctx,
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen /* this is the c-client style "FOLDER INTERNAL DATA" message.
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic bool parse_x_keywords(struct mbox_sync_mail_context *ctx,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen const unsigned int *list;
9bb91f1dbf7cf8cfbd2df7784101df98d59fb46dTimo Sirainen /* read keyword indexes to temporary array first */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde /* read the keyword string */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen str_append_n(keyword, hdr->full_value + keyword_start,
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (!mail_index_keyword_lookup(ctx->sync_ctx->mbox->ibox.index,
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen /* keyword wasn't found. that means the sent mail
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen originally contained X-Keywords header. Delete it. */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* check that the keyword isn't already added there.
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen we don't want duplicates. */
d99107ddf4d9bccb710994482daf65276a9d6321Timo Sirainen for (i = 0; i < count; i++) {
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* once we know how many keywords there are, we can allocate the array
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen from mail_keyword_pool without wasting memory. */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen array_append_array(&ctx->mail.keywords, &keyword_list);
c5a6a6565be93224fc26522eda855b0990f256e8Timo Sirainen ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] = str_len(ctx->header);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic bool parse_x_uid(struct mbox_sync_mail_context *ctx,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* duplicate */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9')
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* broken value */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* we're in mbox_sync_parse_match_mail().
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen don't do any extra checks. */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* Don't bother allowing X-UID before X-IMAPbase
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde header. c-client doesn't allow it either, and this
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen way the UID doesn't have to be reset if X-IMAPbase
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen header isn't what we expect it to be. */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* X-UID is the next expected one. allow it because
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen we'd just use this UID anyway. X-IMAPbase header
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen still needs to be updated for this. */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* UID is larger than expected. Don't allow it because
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen incoming mails can contain untrusted X-UID fields,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen causing possibly DoS if the UIDs get large enough. */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen /* broken - UIDs must be growing */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen /* if we had multiple X-UID headers, we could have
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen uid_broken=TRUE here. */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (ctx->sync_ctx->dest_first_mail && ctx->seq != 1) {
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen /* if we're expunging the first mail, delete this header since
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen otherwise X-IMAPbase header would be added after this, which
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen we don't like */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen ctx->hdr_pos[MBOX_HDR_X_UID] = str_len(ctx->header);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenstatic bool parse_content_length(struct mbox_sync_mail_context *ctx,
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* duplicate */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9')
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen value = value*10 + (hdr->full_value[i] - '0');
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* broken value */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenstatic struct mbox_sync_header_func header_funcs[] = {
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen#define HEADER_FUNCS_COUNT (sizeof(header_funcs) / sizeof(*header_funcs))
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainenstatic int mbox_sync_bsearch_header_func_cmp(const void *p1, const void *p2)
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen const struct mbox_sync_header_func *func = p2;
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainenvoid mbox_sync_parse_next_mail(struct istream *input,
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen struct mbox_sync_context *sync_ctx = ctx->sync_ctx;
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen ctx->mail.flags = MAIL_RECENT; /* default to having recent flag */
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen for (i = 0; i < MBOX_HDR_COUNT; i++)
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen hdr_ctx = message_parse_header_init(input, NULL, 0);
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen while ((ret = message_parse_header_next(hdr_ctx, &hdr)) > 0) {
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen str_append_n(ctx->header, hdr->middle, hdr->middle_len);
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen /* this header is broken, remove it */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen mbox_md5_finish(mbox_md5_ctx, ctx->hdr_md5_sum);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen (ctx->seq > 1 && sync_ctx->dest_first_mail)) {
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* missing X-IMAPbase */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* figure out a new UIDVALIDITY for us. */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainenbool mbox_sync_parse_match_mail(struct mbox_mailbox *mbox,
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* we only wish to be sure that this mail actually is what we expect
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen it to be. If there's X-UID header and it matches our UID, we use it.
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen Otherwise it could mean that the X-UID header is invalid and it's
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen just not yet been rewritten. In that case use MD5 sum, if it
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen hdr_ctx = message_parse_header_init(mbox->mbox_stream, NULL, 0);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen while ((ret = message_parse_header_next(hdr_ctx, &hdr)) > 0) {
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen mbox_md5_finish(mbox_md5_ctx, ctx.hdr_md5_sum);
return TRUE;