mbox-save.c revision a485d6b39bcdc27af6856b34bef465c312bbb66f
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek/* Copyright (C) 2002-2007 Timo Sirainen */
5a2cce34cf8843613b0b9dfde054b3d471dd5f3aPavel Březina uoff_t extra_hdr_offset, eoh_offset, eoh_input_offset;
5a2cce34cf8843613b0b9dfde054b3d471dd5f3aPavel Březinastatic void write_error(struct mbox_save_context *ctx, int error)
5a2cce34cf8843613b0b9dfde054b3d471dd5f3aPavel Březina mail_storage_set_error(STORAGE(ctx->mbox->storage),
5a2cce34cf8843613b0b9dfde054b3d471dd5f3aPavel Březina "Not enough disk space");
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int mbox_seek_to_end(struct mbox_save_context *ctx, uoff_t *offset)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return mbox_set_syscall_error(ctx->mbox, "fstat()");
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina return mbox_set_syscall_error(ctx->mbox, "lseek()");
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return mbox_set_syscall_error(ctx->mbox, "read()");
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int mbox_append_lf(struct mbox_save_context *ctx)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int write_from_line(struct mbox_save_context *ctx, time_t received_date,
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek /* failed, use just the hostname */
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina strocpy(my_hostdomain, name, sizeof(my_hostdomain));
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina t_strconcat(INDEX_STORAGE(ctx->mbox->storage)->user,
ed44814e0e7ff9f0ef7ffc98fab7d9542a7822dfPavel Březina /* save in local timezone, no matter what it was given with */
ed44814e0e7ff9f0ef7ffc98fab7d9542a7822dfPavel Březina line = mbox_from_create(from_envelope, received_date);
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if ((ret = o_stream_send_str(ctx->output, line)) < 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int mbox_write_content_length(struct mbox_save_context *ctx)
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek const char *str;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina /* we can't seek, don't set Content-Length */
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina /* write Content-Length headers */
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (o_stream_seek(ctx->output, ctx->extra_hdr_offset +
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina mbox_set_syscall_error(ctx->mbox, "o_stream_seek()");
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina } else if (o_stream_send(ctx->output, str, len) < 0) {
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (o_stream_seek(ctx->output, end_offset) < 0) {
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina mbox_set_syscall_error(ctx->mbox, "o_stream_seek()");
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic void mbox_save_init_sync(struct mbox_transaction_context *t)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek struct mbox_mailbox *mbox = (struct mbox_mailbox *)t->ictx.ibox;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina /* open a new view to get the header. this is required if we just
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina synced the mailbox so we can get updated next_uid. */
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina view = mail_index_view_open(mbox->ibox.index);
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic void status_flags_append(string_t *str, enum mail_flags flags,
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březinastatic void mbox_save_append_flag_headers(string_t *str, enum mail_flags flags)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek status_flags_append(str, flags, mbox_status_flags);
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina status_flags_append(str, flags, mbox_xstatus_flags);
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březinambox_save_append_keyword_headers(struct mbox_save_context *ctx,
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina sizeof("Content-Length: \n")-1 + MAX_INT_STRLEN];
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina const ARRAY_TYPE(keywords) *keyword_names_list;
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina const char *const *keyword_names;
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina keyword_names_list = mail_index_get_keywords(ctx->mbox->ibox.index);
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina keyword_names = array_get(keyword_names_list, &keyword_names_count);
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina count = keywords == NULL ? 0 : keywords->count;
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina for (i = 0; i < count; i++) {
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina i_assert(keywords->idx[i] < keyword_names_count);
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina str_append(ctx->headers, keyword_names[keywords->idx[i]]);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek str_append_n(ctx->headers, space, sizeof(space));
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březinambox_save_init_file(struct mbox_save_context *ctx,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek struct mbox_transaction_context *t, bool want_mail)
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina mail_storage_set_error(STORAGE(ctx->mbox->storage),
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek "Read-only mbox");
fb4e4c4eb6a6dc732370584f70d23dd4a2c5c7b6Pavel Březina /* first appended mail in this transaction */
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina /* FIXME: we shouldn't fail here. it's just
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina a locking issue that should be possible to
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina "Can't copy mails inside same mailbox");
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina if (mbox_lock(mbox, F_WRLCK, &t->mbox_lock_id) <= 0)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* assign UIDs only if mbox doesn't require syncing */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (mbox_seek_to_end(ctx, &ctx->append_offset) < 0)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek ctx->output = o_stream_create_file(mbox->mbox_fd, default_pool,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* we'll need to assign UID for the mail immediately. */
e7b5b99e5a5d276f32039c4fb8b21ba51bdb1537Pavel Březinastatic void save_header_callback(struct message_header_line *hdr,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* we can't allow From_-lines in headers. there's no
2ce00e0d3896bb42db169d1e79553a81ca837a22Simo Sorce legitimate reason for allowing them in any case,
2ce00e0d3896bb42db169d1e79553a81ca837a22Simo Sorce so just drop them. */
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina if ((hdr == NULL && ctx->eoh_input_offset == (uoff_t)-1) ||
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březinastatic void mbox_save_x_delivery_id(struct mbox_save_context *ctx)
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina buf = buffer_create_dynamic(pool_datastack_create(), 256);
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina buffer_append(buf, &ioloop_time, sizeof(ioloop_time));
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina randbuf = buffer_append_space_unsafe(buf, MBOX_DELIVERY_ID_RAND_BYTES);
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina random_fill_weak(randbuf, MBOX_DELIVERY_ID_RAND_BYTES);
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina md5_get_digest(buf->data, buf->used, md5_result);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek base64_encode(md5_result, sizeof(md5_result), ctx->headers);
e7b5b99e5a5d276f32039c4fb8b21ba51bdb1537Pavel Březinaint mbox_save_init(struct mailbox_transaction_context *_t,
e7b5b99e5a5d276f32039c4fb8b21ba51bdb1537Pavel Březina enum mail_flags flags, struct mail_keywords *keywords,
e7b5b99e5a5d276f32039c4fb8b21ba51bdb1537Pavel Březina time_t received_date, int timezone_offset __attr_unused__,
5d72a91a37273c8c874640906fd2f7a70e606812Simo Sorce const char *from_envelope, struct istream *input,
e7b5b99e5a5d276f32039c4fb8b21ba51bdb1537Pavel Březina struct mail *dest_mail, struct mail_save_context **ctx_r)
e7b5b99e5a5d276f32039c4fb8b21ba51bdb1537Pavel Březina struct mbox_mailbox *mbox = (struct mbox_mailbox *)t->ictx.ibox;
e7b5b99e5a5d276f32039c4fb8b21ba51bdb1537Pavel Březina i_assert((t->ictx.flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
e7b5b99e5a5d276f32039c4fb8b21ba51bdb1537Pavel Březina /* FIXME: we could write timezone_offset to From-line.. */
e7b5b99e5a5d276f32039c4fb8b21ba51bdb1537Pavel Březina ctx = t->save_ctx = i_new(struct mbox_save_context, 1);
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina if (mbox_save_init_file(ctx, t, dest_mail != NULL) < 0) {
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina save_flags = (flags & ~MAIL_RECENT) | MAIL_RECENT;
69e7d6649b58c66675ef38084868fc5356c5a240Jakub Hrozek /* writing the first mail. Insert X-IMAPbase as well. */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek str_printfa(ctx->headers, "X-IMAPbase: %u %010u\n",
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* we're using MD5 sums to generate POP3 UIDLs.
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek clients don't like it much if there are duplicates,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek so make sure that there can't be any by appending
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek our own X-Delivery-ID header. */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek str_printfa(ctx->headers, "X-UID: %u\n", ctx->next_uid);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek mail_index_append(ctx->trans, ctx->next_uid, &ctx->seq);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek mail_index_update_keywords(ctx->trans, ctx->seq,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* parse and cache the mail headers as we read it */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina index_mail_parse_header_init((struct index_mail *)dest_mail,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina mbox_save_append_flag_headers(ctx->headers, save_flags);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina mbox_save_append_keyword_headers(ctx, keywords);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (write_from_line(ctx, received_date, from_envelope) < 0)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina o_stream_create_crlf(default_pool, ctx->output) :
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina o_stream_create_lf(default_pool, ctx->output);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinaint mbox_save_continue(struct mail_save_context *_ctx)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina const unsigned char *data;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* writing body */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina while ((ret = i_stream_read(ctx->input)) != -1) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (o_stream_send(ctx->body_output, data, size) < 0) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* if mail doesn't end with LF, we'll do that.
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina otherwise some mbox parsers don't like the result.
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina this makes it impossible to save a mail that doesn't
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina end with LF though. */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (o_stream_send(ctx->body_output, "\n", 1) < 0) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina while ((ret = i_stream_read(ctx->input)) != -1) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina ctx->input->v_offset + size >= ctx->eoh_input_offset) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* found end of headers. write the rest of them. */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina size = ctx->eoh_input_offset - ctx->input->v_offset;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (o_stream_send(ctx->output, data, size) < 0) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (o_stream_send(ctx->output, data, size) < 0) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (o_stream_send(ctx->output, "\n", 1) < 0) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina mbox_md5_finish(ctx->mbox_md5_ctx, hdr_md5_sum);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* append our own headers and ending empty line */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (o_stream_send(ctx->output, str_data(ctx->headers),
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* write body */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina return ctx->input->eof && size == 0 ? 0 : mbox_save_continue(_ctx);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinaint mbox_save_finish(struct mail_save_context *_ctx)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina if (ctx->failed && ctx->mail_offset != (uoff_t)-1) {
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina /* saving this mail failed - truncate back to beginning of it */
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina if (ftruncate(ctx->mbox->mbox_fd, (off_t)ctx->mail_offset) < 0)
9675bccabff4e79d224f64611ad9ff3e073b488eSimo Sorce mbox_set_syscall_error(ctx->mbox, "ftruncate()");
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březinavoid mbox_save_cancel(struct mail_save_context *_ctx)
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březinastatic void mbox_transaction_save_deinit(struct mbox_save_context *ctx)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekint mbox_transaction_save_commit(struct mbox_save_context *ctx)
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březina &ctx->next_uid, sizeof(ctx->next_uid), FALSE);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek offsetof(struct mail_index_header, sync_stamp),
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina if (!ctx->synced && ctx->mbox->mbox_fd != -1 &&
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina !ctx->mbox->mbox_writeonly && !ctx->mbox->ibox.fsync_disable) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek mbox_set_syscall_error(ctx->mbox, "fdatasync()");
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekvoid mbox_transaction_save_rollback(struct mbox_save_context *ctx)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (ctx->append_offset != (uoff_t)-1 && mbox->mbox_fd != -1) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* failed, truncate file back to original size.
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek output stream needs to be flushed before truncating
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek so unref() won't write anything. */