mbox-save.c revision fddb56b3956545011dafec8d20b1222002a9df00
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (C) 2002 Timo Sirainen */
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "lib.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "hostpid.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "obuffer.h"
d5abbb932a0a598f002da39a8b3326643b1b5efcTimo Sirainen#include "write-full.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "mbox-index.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "mbox-lock.h"
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen#include "mbox-storage.h"
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <stdlib.h>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <unistd.h>
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen#include <fcntl.h>
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen#include <sys/stat.h>
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen#include <netdb.h>
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainenstatic char my_hostdomain[256] = "";
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainenstatic int write_error(MailStorage *storage, const char *mbox_path)
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (errno == ENOSPC)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen mail_storage_set_error(storage, "Not enough disk space");
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen else {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen mail_storage_set_critical(storage,
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen "Error writing to mbox file %s: %m", mbox_path);
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen }
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen return FALSE;
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen}
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenstatic int mbox_seek_to_end(MailStorage *storage, int fd,
356303df200c991580bd24041996a070ad08c05eTimo Sirainen const char *mbox_path, off_t *pos)
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen{
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen struct stat st;
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen char ch;
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (fstat(fd, &st) < 0) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen mail_storage_set_critical(storage,
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen "fstat() failed for mbox file %s: %m", mbox_path);
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen return FALSE;
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen }
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen *pos = st.st_size;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (st.st_size == 0)
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen return TRUE;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (lseek(fd, st.st_size-1, SEEK_SET) < 0) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen mail_storage_set_critical(storage,
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen "lseek() failed for mbox file %s: %m", mbox_path);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen return FALSE;
d5abbb932a0a598f002da39a8b3326643b1b5efcTimo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (read(fd, &ch, 1) != 1) {
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen mail_storage_set_critical(storage,
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen "read() failed for mbox file %s: %m", mbox_path);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return FALSE;
e2d97f20ae7301e2647f175fa072fc3cd2764b05Aki Tuomi }
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen if (ch != '\n') {
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen if (write_full(fd, "\n", 1) < 0)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return write_error(storage, mbox_path);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen *pos += 1;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen }
79fe1b28df44ba22b230326bee895583c1df5a28Stephan Bosch
79fe1b28df44ba22b230326bee895583c1df5a28Stephan Bosch return TRUE;
79fe1b28df44ba22b230326bee895583c1df5a28Stephan Bosch}
79fe1b28df44ba22b230326bee895583c1df5a28Stephan Bosch
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainenstatic int mbox_append_lf(MailStorage *storage, OBuffer *outbuf,
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen const char *mbox_path)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen{
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen if (o_buffer_send(outbuf, "\n", 1) < 0)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return write_error(storage, mbox_path);
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen return TRUE;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen}
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainenstatic int write_from_line(MailStorage *storage, OBuffer *outbuf,
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen const char *mbox_path, time_t internal_date)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen const char *sender, *line, *name;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen size_t len;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (*my_hostdomain == '\0') {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen struct hostent *hent;
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen
22535a9e685e29214082878e37a267157044618eTimo Sirainen hostpid_init();
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen hent = gethostbyname(my_hostname);
22535a9e685e29214082878e37a267157044618eTimo Sirainen
22535a9e685e29214082878e37a267157044618eTimo Sirainen name = hent != NULL ? hent->h_name : NULL;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (name == NULL) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen /* failed, use just the hostname */
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen name = my_hostname;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen strncpy(my_hostdomain, name, 255);
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen my_hostdomain[255] = '\0';
22535a9e685e29214082878e37a267157044618eTimo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen sender = t_strconcat(storage->user, "@", my_hostdomain, NULL);
e15f1d736c225c7ce6f3d08a37c1b2ae66b57c50Timo Sirainen
e15f1d736c225c7ce6f3d08a37c1b2ae66b57c50Timo Sirainen /* save in local timezone, no matter what it was given with */
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen line = mbox_from_create(sender, internal_date);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen len = strlen(line);
e15f1d736c225c7ce6f3d08a37c1b2ae66b57c50Timo Sirainen
e15f1d736c225c7ce6f3d08a37c1b2ae66b57c50Timo Sirainen if (o_buffer_send(outbuf, line, len) < 0)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen return write_error(storage, mbox_path);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen return TRUE;
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen}
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenstatic int write_flags(MailStorage *storage, OBuffer *outbuf,
6135260095e1704ed6edff9d00bdfc043c11429cTimo Sirainen const char *mbox_path,
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen MailFlags flags, const char *custom_flags[])
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen{
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen const char *str;
22535a9e685e29214082878e37a267157044618eTimo Sirainen unsigned int field;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen int i;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (flags == 0)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen return TRUE;
22535a9e685e29214082878e37a267157044618eTimo Sirainen
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen if (flags & MAIL_SEEN) {
22535a9e685e29214082878e37a267157044618eTimo Sirainen if (o_buffer_send(outbuf, "Status: R\n", 10) < 0)
22535a9e685e29214082878e37a267157044618eTimo Sirainen return write_error(storage, mbox_path);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen if (flags & (MAIL_ANSWERED|MAIL_DRAFT|MAIL_FLAGGED|MAIL_DELETED)) {
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen str = t_strconcat("X-Status: ",
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen (flags & MAIL_ANSWERED) ? "A" : "",
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen (flags & MAIL_DRAFT) ? "D" : "",
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen (flags & MAIL_FLAGGED) ? "F" : "",
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen (flags & MAIL_DELETED) ? "T" : "",
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen "\n", NULL);
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen if (o_buffer_send(outbuf, str, strlen(str)) < 0)
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen return write_error(storage, mbox_path);
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen }
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (flags & MAIL_CUSTOM_FLAGS_MASK) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (o_buffer_send(outbuf, "X-Keywords:", 11) < 0)
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen return write_error(storage, mbox_path);
849969f639a00eab26791db3cb1b66430420c0cdTimo Sirainen
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen field = 1 << MAIL_CUSTOM_FLAG_1_BIT;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++, field <<= 1) {
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen if ((flags & field) && custom_flags[i] != NULL) {
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen if (o_buffer_send(outbuf, " ", 1) < 0)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return write_error(storage, mbox_path);
c64e714193097f841691dcfa2902c270cb47bff8Timo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen if (o_buffer_send(outbuf, custom_flags[i],
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen strlen(custom_flags[i])) < 0)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return write_error(storage, mbox_path);
74674a53a72dab535c61f455b2246ef2797844eaTimo Sirainen }
74674a53a72dab535c61f455b2246ef2797844eaTimo Sirainen }
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen if (o_buffer_send(outbuf, "\n", 1) < 0)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return write_error(storage, mbox_path);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen }
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return TRUE;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen}
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainenint mbox_storage_save(Mailbox *box, MailFlags flags, const char *custom_flags[],
c64e714193097f841691dcfa2902c270cb47bff8Timo Sirainen time_t internal_date, int timezone_offset __attr_unused__,
c64e714193097f841691dcfa2902c270cb47bff8Timo Sirainen IBuffer *data, uoff_t data_size)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen{
dd93aba1901a457346990f49c54a738947dc7128Timo Sirainen IndexMailbox *ibox = (IndexMailbox *) box;
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen MailIndex *index;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen MailFlags real_flags;
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen const char *mbox_path;
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen OBuffer *outbuf;
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainen int failed;
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen off_t pos;
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen if (box->readonly) {
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen mail_storage_set_error(box->storage, "Mailbox is read-only");
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen return FALSE;
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen }
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen /* we don't need the real flags, easier to keep using our own.
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen they need to be checked/added though. */
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen real_flags = flags;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen if (!index_mailbox_fix_custom_flags(ibox, &real_flags, custom_flags))
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen return FALSE;
d5abbb932a0a598f002da39a8b3326643b1b5efcTimo Sirainen
d5abbb932a0a598f002da39a8b3326643b1b5efcTimo Sirainen if (!index_storage_sync_and_lock(ibox, FALSE, MAIL_LOCK_EXCLUSIVE))
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return FALSE;
d5abbb932a0a598f002da39a8b3326643b1b5efcTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen index = ibox->index;
mbox_path = index->mbox_path;
if (!mbox_seek_to_end(box->storage, index->mbox_fd, mbox_path, &pos))
failed = TRUE;
else {
failed = FALSE;
t_push();
outbuf = o_buffer_create_file(index->mbox_fd,
data_stack_pool, 4096,
0, FALSE);
o_buffer_set_blocking(outbuf, 60000, NULL, NULL);
if (!write_from_line(box->storage, outbuf, mbox_path,
internal_date) ||
!write_flags(box->storage, outbuf, mbox_path, flags,
custom_flags) ||
!index_storage_save(box->storage, mbox_path,
data, outbuf, data_size) ||
!mbox_append_lf(box->storage, outbuf, mbox_path)) {
/* failed, truncate file back to original size */
(void)ftruncate(index->mbox_fd, pos);
failed = TRUE;
}
o_buffer_unref(outbuf);
t_pop();
}
if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
return mail_storage_set_index_error(ibox);
return !failed;
}