mbox-sync-parse.c revision a486ed03dce069ff60ab5a65d0ae24a1862f22fc
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher/* Copyright (C) 2004 Timo Sirainen */
ee359fe1384507fed6c2274e7bfe81d288de4542Stephen Gallagher/* MD5 header summing logic was pretty much copy&pasted from popa3d by
33396dc46ea52c18f47db1b5d590880806521005Sumit Bose Solar Designer */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#define IS_LWSP_LF(c) (IS_LWSP(c) || (c) == '\n')
87d3b47abba6a40fcf809c85a2b138bc1013d9c5Jakub Hrozek bool (*func)(struct mbox_sync_mail_context *ctx,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstruct mbox_flag_type mbox_status_flags[] = {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic void parse_trailing_whitespace(struct mbox_sync_mail_context *ctx,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* the value may contain newlines. we can't count whitespace before
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher and after it as a single contiguous whitespace block, as that may
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher get us into situation where removing whitespace goes eg.
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher " \n \n" -> " \n\n" which would then be treated as end of headers.
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce that could probably be avoided by being careful, but as newlines
f775337a7d4ca1c0be8eab683d0d753cbaee49e2Lukas Slebodnik should never be there (we don't generate them), it's not worth the
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek ctx->mail.offset = ctx->hdr_offset + str_len(ctx->header) + i;
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozekstatic enum mail_flags mbox_flag_find(struct mbox_flag_type *flags, char chr)
faa16fc9f0c9a02b26497e7cf148a92586144c08David Disseldorpstatic void parse_status_flags(struct mbox_sync_mail_context *ctx,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (i = 0; i < hdr->full_value_len; i++) {
d921c1eba437662437847279f251a0a5d8f70127Maximstatic bool parse_status(struct mbox_sync_mail_context *ctx,
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer parse_status_flags(ctx, hdr, mbox_status_flags);
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer ctx->hdr_pos[MBOX_HDR_STATUS] = str_len(ctx->header);
327127bb7fcc07f882209f029e14026de1b23c94Maximstatic bool parse_x_status(struct mbox_sync_mail_context *ctx,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher parse_status_flags(ctx, hdr, mbox_xstatus_flags);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->hdr_pos[MBOX_HDR_X_STATUS] = str_len(ctx->header);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic bool keyword_is_valid(const char *keyword)
eb2e21b764d03544d8161e9956d7f70b07b75f77Simo Sorce /* try to only prevent the most malicious looking keywords. */
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnikparse_imap_keywords_list(struct mbox_sync_mail_context *ctx,
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik struct message_header_line *hdr, size_t pos)
34c78b745eb349eef2b0f13ef2b722632aebe619Jan Cholasta /* read the keyword */
e07a94a66985b674c5df11ca466792902164c4e2George McCollister for (; pos < hdr->full_value_len; pos++) {
a9c287bda3fc2a1e12cef2135ade96945f11ad01Sumit Bose /* add it to index's keyword list if it's not there already */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher keyword = t_strndup(hdr->full_value + keyword_start,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (count != array_count(ctx->sync_ctx->mbox->ibox.keyword_names)) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* need to update this list */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic bool parse_x_imap_base(struct mbox_sync_mail_context *ctx,
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke /* Valid only in first message */
c3889e5a101a075defe533d81f5296d5e680f639Lukas Slebodnik /* <uid-validity> 10x<uid-last> */
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina for (i = 0, uid_validity = 0; i < hdr->full_value_len; i++) {
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9') {
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina uid_validity = uid_validity * 10 + (hdr->full_value[i] - '0');
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher for (uid_last = 0, j = 0; i < hdr->full_value_len; i++, j++) {
9dbdf62243f01f6aee41c2b5f2976c56da47f25dLukas Slebodnik if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9') {
574a1c20f114851071ae74112b34488c3d1aeeb3Ondrej Kos uid_last = uid_last * 10 + (hdr->full_value[i] - '0');
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek if (j != 10 ||
e6e26182d58c05d896f72f2925426658a6dc70b5Jakub Hrozek hdr->full_value_offset != ctx->hdr_offset + str_len(ctx->header)) {
e6e26182d58c05d896f72f2925426658a6dc70b5Jakub Hrozek /* uid-last field must be exactly 10 characters to make
9542512d7be40f2000298c86d3d2b728f4f0f65aStephen Gallagher rewriting it easier. also don't try to do this if some
e6e26182d58c05d896f72f2925426658a6dc70b5Jakub Hrozek headers have been removed */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->last_uid_value_start_pos = uid_last_pos;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->sync_ctx->next_uid-1 <= uid_last) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* new messages have been added since our last sync.
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher just update our internal next_uid. */
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek i_assert(ctx->sync_ctx->next_uid > ctx->sync_ctx->prev_msg_uid);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->sync_ctx->base_uid_validity == 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* first time parsing this (ie. we're not rewriting).
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek save the values. */
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek ctx->sync_ctx->base_uid_validity = uid_validity;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] = str_len(ctx->header);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic bool parse_x_imap(struct mbox_sync_mail_context *ctx,
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek /* this is the c-client style "FOLDER INTERNAL DATA" message.
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozekstatic bool parse_x_keywords(struct mbox_sync_mail_context *ctx,
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek const unsigned int *list;
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher if (array_is_created(&ctx->mail.keywords))
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik /* read keyword indexes to temporary array first */
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik for (pos = 0; pos < hdr->full_value_len; ) {
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik /* read the keyword string */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append_n(keyword, hdr->full_value + keyword_start,
72e60fd4eabcfbcdbfe01e8c38b94052bc6c2067Jakub Hrozek if (!mail_index_keyword_lookup(ctx->sync_ctx->mbox->ibox.index,
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik /* keyword wasn't found. that means the sent mail
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik originally contained X-Keywords header. Delete it. */
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik /* check that the keyword isn't already added there.
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik we don't want duplicates. */
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik for (i = 0; i < count; i++) {
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik /* once we know how many keywords there are, we can allocate the array
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik from mail_keyword_pool without wasting memory. */
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik array_append_array(&ctx->mail.keywords, &keyword_list);
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] = str_len(ctx->header);
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnikstatic bool parse_x_uid(struct mbox_sync_mail_context *ctx,
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik /* duplicate */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (i = 0; i < hdr->full_value_len; i++) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9')
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher value = value*10 + (hdr->full_value[i] - '0');
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* broken value */
77c0d1f6074059dafd2293f9c42ea0f9d60f8aadJakub Hrozek /* we're in mbox_sync_parse_match_mail().
77c0d1f6074059dafd2293f9c42ea0f9d60f8aadJakub Hrozek don't do any extra checks. */
e07a94a66985b674c5df11ca466792902164c4e2George McCollister if (ctx->seq == 1 && !ctx->seen_imapbase) {
bf01e8179cbb2be476805340636098deda7e1366Sumit Bose /* Don't bother allowing X-UID before X-IMAPbase
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bose header. c-client doesn't allow it either, and this
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bose way the UID doesn't have to be reset if X-IMAPbase
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bose header isn't what we expect it to be. */
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce /* X-UID is the next expected one. allow it because
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce we'd just use this UID anyway. X-IMAPbase header
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce still needs to be updated for this. */
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce /* UID is larger than expected. Don't allow it because
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce incoming mails can contain untrusted X-UID fields,
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce causing possibly DoS if the UIDs get large enough. */
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik /* broken - UIDs must be growing */
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik /* if we had multiple X-UID headers, we could have
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik uid_broken=TRUE here. */
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik if (ctx->sync_ctx->dest_first_mail && ctx->seq != 1) {
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik /* if we're expunging the first mail, delete this header since
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik otherwise X-IMAPbase header would be added after this, which
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik we don't like */
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik ctx->hdr_pos[MBOX_HDR_X_UID] = str_len(ctx->header);
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnikstatic bool parse_content_length(struct mbox_sync_mail_context *ctx,
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik /* duplicate */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (i = 0; i < hdr->full_value_len; i++) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (hdr->full_value[i] < '0' || hdr->full_value[i] > '9')
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher value = value*10 + (hdr->full_value[i] - '0');
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* broken value */
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovstatic struct mbox_sync_header_func header_funcs[] = {
8c294c1cd4d721818a59684cf7f2b36123f79163Stephen Gallagher { "Content-Length", parse_content_length },
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#define HEADER_FUNCS_COUNT (sizeof(header_funcs) / sizeof(*header_funcs))
a8d887323f83984679a7d9b827a70146656bb7b2Sumit Bosestatic int mbox_sync_bsearch_header_func_cmp(const void *p1, const void *p2)
e0c86d21388bffe2e3919e780780c40d96186abbJakub Hrozekvoid mbox_sync_parse_next_mail(struct istream *input,
2a9af1f71887f02935e2fb6ad5023afba5b6d43eSumit Bose struct mbox_sync_context *sync_ctx = ctx->sync_ctx;
for (i = 0; i < MBOX_HDR_COUNT; i++)
line_start_pos = 0;
const void *data;
int ret;
return TRUE;
&data) < 0) {