mbox-expunge.c revision 007d354a674fb3ddf49db160cf050cf61270a1a0
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (C) 2002 Timo Sirainen */
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen#include "istream.h"
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen#include "ostream.h"
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen#include "mbox-index.h"
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen#include "mbox-storage.h"
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen#include "mbox-lock.h"
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen#include <fcntl.h>
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen#include <unistd.h>
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenbergerstatic int expunge_real(struct index_mailbox *ibox,
06ff2a72c39cb34cc6425f17fc82c5e93fef2018Timo Sirainen struct mail_index_record *rec, unsigned int seq,
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen struct istream *input, struct ostream *output,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int notify)
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen{
de76b960297406115cf6bae473f004c08174b16aTimo Sirainen uoff_t offset, hdr_size, body_size;
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen uoff_t end_offset, from_offset, copy_size, old_limit;
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen const unsigned char *data;
c519de264df14a9d525e2604671c332590ce54e3Timo Sirainen size_t size;
61530b48694398df42744204e35535dbe3f745c4Timo Sirainen int expunges, failed;
61530b48694398df42744204e35535dbe3f745c4Timo Sirainen
3ddbbe03fe74b3ee7b1dff4e08ec706d7880d052Timo Sirainen if (seq == 1)
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen end_offset = 0;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen else {
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen /* we need to find offset to beginning of From-line.
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen not the fastest way maybe, but easiest.. */
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen rec = ibox->index->lookup(ibox->index, seq-1);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (!mbox_mail_get_location(ibox->index, rec, &offset,
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen &hdr_size, &body_size))
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return FALSE;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen end_offset = offset + hdr_size + body_size;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2b3b0df76184799317584b596af8df5afec3ebddTimo Sirainen /* get back to the deleted record */
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen rec = ibox->index->next(ibox->index, rec);
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen }
6789ed17e7ca4021713507baf0dcf6979bb42e0cTimo Sirainen
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen old_limit = input->v_limit;
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen expunges = FALSE;
d244c6cadd5f077f5d0f1e00c3652d0108a2d908Timo Sirainen while (rec != NULL) {
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen if (!mbox_mail_get_location(ibox->index, rec, &offset,
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen &hdr_size, &body_size))
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen return FALSE;
5ce2084ada06ade9f44fc2914c34658e9a842dc1Timo Sirainen
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen from_offset = end_offset;
d244c6cadd5f077f5d0f1e00c3652d0108a2d908Timo Sirainen end_offset = offset + hdr_size + body_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen if (rec->msg_flags & MAIL_DELETED) {
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen if (!index_expunge_mail(ibox, rec, seq, notify))
d244c6cadd5f077f5d0f1e00c3652d0108a2d908Timo Sirainen return FALSE;
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen seq--;
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen
baf1148108b7d9739626b47cc57298c36929586aTimo Sirainen if (!expunges) {
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen /* first expunged record, seek to position
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen where we want to begin writing */
baf1148108b7d9739626b47cc57298c36929586aTimo Sirainen if (o_stream_seek(output, from_offset) < 0)
baf1148108b7d9739626b47cc57298c36929586aTimo Sirainen return FALSE;
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen expunges = TRUE;
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen }
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen } else if (expunges) {
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen /* seek to wanted input position, and copy
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen this messages */
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen i_assert(input->v_offset <= from_offset);
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen i_stream_skip(input, from_offset - input->v_offset);
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen if (output->offset == 0) {
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen /* we're writing to beginning of mbox, so we
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen don't want the [\r]\n there */
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen (void)i_stream_read_data(input, &data,
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch &size, 1);
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen if (size > 0 && data[0] == '\n')
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen i_stream_skip(input, 1);
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen else if (size > 1 && data[0] == '\r' &&
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen data[1] == '\n')
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen i_stream_skip(input, 2);
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen }
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen i_stream_set_read_limit(input, end_offset);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen failed = o_stream_send_istream(output, input) < 0;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen i_stream_set_read_limit(input, old_limit);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (failed || input->v_offset != end_offset)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return FALSE;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch rec = ibox->index->next(ibox->index, rec);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen seq++;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen i_stream_skip(input, end_offset - input->v_offset);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* copy the rest as well, should be only \n but someone might
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen as well just appended more data.. but if we've deleted all mail,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen don't write the only \n there. */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen copy_size = input->v_size - input->v_offset;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (output->offset == 0 && copy_size == 1)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return TRUE;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return o_stream_send_istream(output, input) >= 0;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen}
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenint mbox_expunge_locked(struct index_mailbox *ibox, int notify)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen{
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct mail_index_record *rec;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct istream *input;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct ostream *output;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen unsigned int seq;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen int failed;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (!index_expunge_seek_first(ibox, &seq, &rec))
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen return FALSE;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (rec == NULL) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen /* no deleted messages */
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen return TRUE;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen }
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen /* mbox must be already opened, synced and locked at this point.
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen we just want the istream. */
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen input = mbox_get_stream(ibox->index, 0, MAIL_LOCK_EXCLUSIVE);
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen if (input == NULL)
e82e363e7a6917f470412d629db6c5b1f5891a35Timo Sirainen return FALSE;
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen i_assert(ibox->index->mbox_sync_counter ==
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ibox->index->mbox_lock_counter);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen t_push();
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen output = o_stream_create_file(ibox->index->mbox_fd, data_stack_pool,
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen 4096, FALSE);
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen o_stream_set_blocking(output, 60000, NULL, NULL);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen failed = !expunge_real(ibox, rec, seq, input, output, notify);
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen if (failed && output->offset > 0) {
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen /* we moved some of the data. move the rest as well so there
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen won't be invalid holes in mbox file */
d10a370b2614712d9cb6a1dd8625f62a071b6377Timo Sirainen (void)o_stream_send_istream(output, input);
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen }
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen if (ftruncate(ibox->index->mbox_fd, (off_t)output->offset) < 0) {
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen mail_storage_set_error(ibox->box.storage, "ftruncate() failed "
88ea893b45d3ed8d68000921db9156c03cbe1b00Timo Sirainen "for mbox file %s: %m",
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen ibox->index->mailbox_path);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen failed = TRUE;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen }
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen o_stream_unref(output);
d6c5ceea8521b92d10e51a59da00c792f6140b1dTimo Sirainen t_pop();
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen return !failed;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen}
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen