static int dbox_file_read_header(struct dbox_file *file) { const char *line; unsigned int hdr_size; int ret; i_stream_seek(file->input, 0); line = i_stream_read_next_line(file->input); if (line == NULL) { if (file->input->stream_errno == 0) { dbox_file_set_corrupted(file, "EOF while reading file header"); return 0; } dbox_file_set_syscall_error(file, "read()"); return -1; } hdr_size = file->input->v_offset; T_BEGIN { ret = dbox_file_parse_header(file, line) < 0 ? 0 : 1; } T_END; if (ret > 0) file->file_header_size = hdr_size; return ret; }
static int dbox_file_fix_write_stream(struct dbox_file *file, uoff_t start_offset, const char *temp_path, struct ostream *output) { struct dbox_message_header msg_hdr; uoff_t offset, msg_size, hdr_offset, body_offset; bool pre, write_header, have_guid; struct message_size body; struct istream *body_input; uint8_t guid_128[MAIL_GUID_128_SIZE]; int ret; i_stream_seek(file->input, 0); if (start_offset > 0) { /* copy the valid data */ if (stream_copy(file, output, temp_path, start_offset) < 0) return -1; } else { /* the file header is broken. recreate it */ if (dbox_file_header_write(file, output) < 0) { dbox_file_set_syscall_error(file, "write()"); return -1; } } while ((ret = dbox_file_find_next_magic(file, &offset, &pre)) > 0) { msg_size = offset - file->input->v_offset; if (msg_size < 256 && pre) { /* probably some garbage or some broken headers. we most likely don't miss anything by skipping over this data. */ i_stream_skip(file->input, msg_size); hdr_offset = file->input->v_offset; ret = dbox_file_read_mail_header(file, &msg_size); if (ret <= 0) { if (ret < 0) return -1; dbox_file_skip_broken_header(file); body_offset = file->input->v_offset; msg_size = (uoff_t)-1; } else { i_stream_skip(file->input, file->msg_header_size); body_offset = file->input->v_offset; i_stream_skip(file->input, msg_size); } ret = dbox_file_find_next_magic(file, &offset, &pre); if (ret <= 0) break; if (!pre && msg_size == offset - body_offset) { /* msg header ok, copy it */ i_stream_seek(file->input, hdr_offset); if (stream_copy(file, output, temp_path, file->msg_header_size) < 0) return -1; write_header = FALSE; } else { /* msg header is broken. write our own. */ i_stream_seek(file->input, body_offset); if (msg_size != (uoff_t)-1) { /* previous magic find might have skipped too much. seek back and make sure */ ret = dbox_file_find_next_magic(file, &offset, &pre); if (ret <= 0) break; } write_header = TRUE; msg_size = offset - body_offset; } } else { /* treat this data as a separate message. */ write_header = TRUE; body_offset = file->input->v_offset; } /* write msg header */ if (write_header) { dbox_msg_header_fill(&msg_hdr, msg_size); (void)o_stream_send(output, &msg_hdr, sizeof(msg_hdr)); } /* write msg body */ i_assert(file->input->v_offset == body_offset); if (stream_copy(file, output, temp_path, msg_size) < 0) return -1; i_assert(file->input->v_offset == offset); /* get message body size */ i_stream_seek(file->input, body_offset); body_input = i_stream_create_limit(file->input, msg_size); ret = message_get_body_size(body_input, &body, NULL); i_stream_unref(&body_input); if (ret < 0) { errno = output->stream_errno; mail_storage_set_critical(&file->storage->storage, "read(%s) failed: %m", file->cur_path); return -1; } /* write msg metadata. */ i_assert(file->input->v_offset == offset); ret = dbox_file_metadata_skip_header(file); if (ret < 0) return -1; o_stream_send_str(output, DBOX_MAGIC_POST); if (ret == 0) have_guid = FALSE; else dbox_file_copy_metadata(file, output, &have_guid); if (!have_guid) { mail_generate_guid_128(guid_128); o_stream_send_str(output, t_strdup_printf("%c%s\n", DBOX_METADATA_GUID, binary_to_hex(guid_128, sizeof(guid_128)))); } o_stream_send_str(output, t_strdup_printf("%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE, (unsigned long long)body.virtual_size)); o_stream_send_str(output, "\n"); if (output->stream_errno != 0) { errno = output->stream_errno; mail_storage_set_critical(&file->storage->storage, "write(%s) failed: %m", temp_path); return -1; } } return ret; }
static int dbox_file_find_next_magic(struct dbox_file *file, uoff_t *offset_r, bool *pre_r) { struct istream *input = file->input; struct str_find_context *pre_ctx, *post_ctx; uoff_t orig_offset, pre_offset, post_offset; const unsigned char *data; size_t size; int ret; *pre_r = FALSE; pre_ctx = str_find_init(default_pool, "\n"DBOX_MAGIC_PRE); post_ctx = str_find_init(default_pool, DBOX_MAGIC_POST); /* \n isn't part of the DBOX_MAGIC_PRE, but it always preceds it. assume that at this point we've already just read the \n. when scanning for it later we'll need to find the \n though. */ str_find_more(pre_ctx, (const unsigned char *)"\n", 1); orig_offset = input->v_offset; while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { pre_offset = (uoff_t)-1; if (str_find_more(pre_ctx, data, size)) { pre_offset = input->v_offset + str_find_get_match_end_pos(pre_ctx) - (strlen(DBOX_MAGIC_PRE) + 1); *pre_r = TRUE; } if (str_find_more(post_ctx, data, size)) { post_offset = input->v_offset + str_find_get_match_end_pos(post_ctx) - strlen(DBOX_MAGIC_POST); if (pre_offset == (uoff_t)-1 || post_offset < pre_offset) { pre_offset = post_offset; *pre_r = FALSE; } } if (pre_offset != (uoff_t)-1) { if (*pre_r) { /* LF isn't part of the magic */ pre_offset++; } *offset_r = pre_offset; break; } i_stream_skip(input, size); } if (ret <= 0) { i_assert(ret == -1); if (input->stream_errno != 0) dbox_file_set_syscall_error(file, "read()"); else { ret = 0; *offset_r = input->v_offset; } } i_stream_seek(input, orig_offset); str_find_deinit(&pre_ctx); str_find_deinit(&post_ctx); return ret; }