index-save.c revision 087e7957fb95f619d86db2d3d8c0664937cea419
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (C) 2002 Timo Sirainen */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "lib.h"
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen#include "istream.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "ostream.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "write-full.h"
7af70f7646264a2f52b361f9ca78f08681acc4e2Stephan Bosch#include "index-storage.h"
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include <stdlib.h>
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include <unistd.h>
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct save_header_context {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct mail_storage *storage;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *path;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct ostream *output;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch write_func_t *write_func;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch header_callback_t *header_callback;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch void *context;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch int failed;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch};
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int write_with_crlf(struct ostream *output, const unsigned char *data,
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen size_t size)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch size_t i, start;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch i_assert(size > 0 && size <= SSIZE_T_MAX);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch start = 0;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch for (i = 0; i < size; i++) {
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch if (data[i] == '\n' && (i == 0 || data[i-1] != '\r')) {
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch /* missing CR */
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if (o_stream_send(output, data + start, i - start) < 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (o_stream_send(output, "\r", 1) < 0)
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch /* \n is written next time */
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch start = i;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch /* if last char is \r, leave it to buffer */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (data[size-1] == '\r')
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch size--;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (o_stream_send(output, data + start, size - start) < 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return size;
da300472555d9afdb0bcb767456f731cf5c2f6aaStephan Bosch}
f9d2a1f21ad65262bc630f0834d7eead06a1bac3Timo Sirainen
f9d2a1f21ad65262bc630f0834d7eead06a1bac3Timo Sirainenstatic int write_with_lf(struct ostream *output, const unsigned char *data,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch size_t size)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch size_t i, start;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(size > 0 && size <= SSIZE_T_MAX);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6dad0888fcec8372f230941c70d8940b8c203b32Stephan Bosch start = 0;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch for (i = 0; i < size; i++) {
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch if (data[i] == '\n' && i > 0 && data[i-1] == '\r') {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* \r\n - skip \r */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (o_stream_send(output, data + start,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i - start - 1) < 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen /* \n is written next time */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch start = i;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* if last char is \r, leave it to buffer */
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen if (data[size-1] == '\r')
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch size--;
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (o_stream_send(output, data + start, size - start) < 0)
1c2f122ae93d3316f6746f255f6659b510527cc8Stephan Bosch return -1;
1c2f122ae93d3316f6746f255f6659b510527cc8Stephan Bosch
1c2f122ae93d3316f6746f255f6659b510527cc8Stephan Bosch return size;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic void set_write_error(struct mail_storage *storage,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct ostream *output, const char *path)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch errno = output->stream_errno;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen if (errno == ENOSPC)
1ec26e0b70ac7f8a4e3dfbc59aa77f572651d5aeStephan Bosch mail_storage_set_error(storage, "Not enough disk space");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch else {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch mail_storage_set_critical(storage,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Can't write to file %s: %m", path);
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch }
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen}
1bc12a53ddc6696bb209fb79d7cc66262d2ea621Timo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic void save_header_callback(struct message_part *part __attr_unused__,
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen const unsigned char *name, size_t name_len,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const unsigned char *value, size_t value_len,
1ec26e0b70ac7f8a4e3dfbc59aa77f572651d5aeStephan Bosch void *context)
6a90041707f1290c8970a3bacb0f8f928aeaaba6Stephan Bosch{
1ec26e0b70ac7f8a4e3dfbc59aa77f572651d5aeStephan Bosch struct save_header_context *ctx = context;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen int ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (ctx->failed)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ret = ctx->header_callback(name, name_len, ctx->write_func,
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen ctx->context);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (ret <= 0) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (ret < 0)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen ctx->failed = TRUE;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen return;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (name_len == 0) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen name = "\n"; value_len = 1;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen } else {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (value[value_len] == '\r')
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen value_len++;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen i_assert(value[value_len] == '\n');
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen value_len += (size_t) (value-name) + 1;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen if (ctx->write_func(ctx->output, name, value_len) < 0) {
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen set_write_error(ctx->storage, ctx->output, ctx->path);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen ctx->failed = TRUE;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen}
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainenint index_storage_save(struct mail_storage *storage, const char *path,
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen struct istream *input, struct ostream *output,
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen header_callback_t *header_callback, void *context)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen{
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen int (*write_func)(struct ostream *, const unsigned char *, size_t);
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen const unsigned char *data;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen size_t size;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ssize_t ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch int failed;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch write_func = getenv("MAIL_SAVE_CRLF") ? write_with_crlf : write_with_lf;
b72c3363092b73cab1da2de4a9d75592e7d8fd6bTimo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (header_callback != NULL) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct save_header_context ctx;
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch memset(&ctx, 0, sizeof(ctx));
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch ctx.storage = storage;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ctx.output = output;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ctx.path = path;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ctx.write_func = write_func;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ctx.header_callback = header_callback;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ctx.context = context;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch message_parse_header(NULL, input, NULL,
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch save_header_callback, &ctx);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (ctx.failed)
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen return FALSE;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch failed = FALSE;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch for (;;) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch data = i_stream_get_data(input, &size);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (!failed) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ret = write_func(output, data, size);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (ret < 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch set_write_error(storage, output, path);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch failed = TRUE;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch } else {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch size = ret;
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch i_stream_skip(input, size);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch ret = i_stream_read(input);
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (ret < 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch errno = input->stream_errno;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (errno == 0) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* EOF */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch if (input->v_offset != input->v_limit) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch /* too early */
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch mail_storage_set_error(storage,
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch "Unexpected EOF");
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch failed = TRUE;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch break;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch } else if (errno == EAGAIN) {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch mail_storage_set_error(storage,
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch "Timeout while waiting for input");
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch } else {
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch mail_storage_set_critical(storage,
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch "Error reading mail from client: %m");
d08e49550aa890e71a5f10b7de43347ec44473acTimo Sirainen }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch failed = TRUE;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch break;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch }
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch return !failed;
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch}
208dcaf62332b80b220c8c66e776f7cc0c39253bStephan Bosch