<commit_before>/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "istream.h"
#include "hex-binary.h"
#include "index-mail.h"
#include "mbox-storage.h"
#include "mbox-file.h"
#include "mbox-lock.h"
#include "mbox-sync-private.h"
#include "istream-raw-mbox.h"
#include "istream-header-filter.h"

#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

static void mbox_prepare_resync(struct index_mail *mail)
{
	struct mbox_transaction_context *t =
		(struct mbox_transaction_context *)mail->trans;
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;

	if (mbox->mbox_lock_type == F_RDLCK) {
		if (mbox->mbox_lock_id == t->mbox_lock_id)
			t->mbox_lock_id = 0;
		(void)mbox_unlock(mbox, mbox->mbox_lock_id);
		i_assert(mbox->mbox_lock_type == F_UNLCK);
	}
}

static int mbox_mail_seek(struct index_mail *mail)
{
	struct mbox_transaction_context *t =
		(struct mbox_transaction_context *)mail->trans;
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;
	enum mbox_sync_flags sync_flags = 0;
	int ret, try;
	bool deleted;

	if (mail->mail.mail.expunged || mbox->syncing)
		return -1;

	if (mail->mail.mail.lookup_abort != MAIL_LOOKUP_ABORT_NEVER)
		return mail_set_aborted(&mail->mail.mail);

	if (mbox->mbox_stream != NULL &&
	    istream_raw_mbox_is_corrupted(mbox->mbox_stream)) {
		/* clear the corruption by forcing a full resync */
		sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC;
	}

	for (try = 0; try < 2; try++) {
		if ((sync_flags & MBOX_SYNC_FORCE_SYNC) != 0) {
			/* dirty offsets are broken. make sure we can sync. */
			mbox_prepare_resync(mail);
		}
		if (mbox->mbox_lock_type == F_UNLCK) {
			sync_flags |= MBOX_SYNC_LOCK_READING;
			if (mbox_sync(mbox, sync_flags) < 0)
				return -1;
			t->mbox_lock_id = mbox->mbox_lock_id;
			i_assert(t->mbox_lock_id != 0);

			/* refresh index file after mbox has been locked to
			   make sure we get only up-to-date mbox offsets. */
			if (mail_index_refresh(mbox->ibox.index) < 0) {
				mail_storage_set_index_error(&mbox->ibox);
				return -1;
			}

			i_assert(mbox->mbox_lock_type != F_UNLCK);
		} else if (t->mbox_lock_id == 0) {
			/* file is already locked by another transaction, but
			   we must keep it locked for the entire transaction,
			   so increase the lock counter. */
			if (mbox_lock(mbox, mbox->mbox_lock_type,
				      &t->mbox_lock_id) < 0)
				i_unreached();
		}

		if (mbox_file_open_stream(mbox) < 0)
			return -1;

		ret = mbox_file_seek(mbox, mail->trans->trans_view,
				     mail->mail.mail.seq, &deleted);
		if (ret > 0) {
			/* success */
			break;
		}
		if (ret < 0) {
			if (deleted)
				mail_set_expunged(&mail->mail.mail);
			return -1;
		}

		/* we'll need to re-sync it completely */
		sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC;
	}
	if (ret == 0) {
		mail_storage_set_critical(&mbox->storage->storage,
			"Losing sync for mail uid=%u in mbox file %s",
			mail->mail.mail.uid, mbox->ibox.box.path);
	}
	return 0;
}

static int mbox_mail_get_received_date(struct mail *_mail, time_t *date_r)
{
	struct index_mail *mail = (struct index_mail *)_mail;
	struct index_mail_data *data = &mail->data;
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;

	if (index_mail_get_received_date(_mail, date_r) == 0)
		return 0;

	if (mbox_mail_seek(mail) < 0)
		return -1;
	data->received_date =
		istream_raw_mbox_get_received_time(mbox->mbox_stream);
	if (data->received_date == (time_t)-1) {
		/* it's broken and conflicts with our "not found"
		   return value. change it. */
		data->received_date = 0;
	}

	*date_r = data->received_date;
	return 0;
}

static int mbox_mail_get_save_date(struct mail *_mail, time_t *date_r)
{
	struct index_mail *mail = (struct index_mail *)_mail;
	struct index_mail_data *data = &mail->data;

	if (index_mail_get_save_date(_mail, date_r) == 0)
		return 0;

	/* no way to know this. save the current time into cache and use
	   that from now on. this works only as long as the index files
	   are permanent */
	data->save_date = ioloop_time;
	*date_r = data->save_date;
	return 0;
}

static bool
mbox_mail_get_md5_header(struct index_mail *mail, const char **value_r)
{
	static uint8_t empty_md5[16] =
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;
	const void *ext_data;

	if (mail->data.guid != NULL)
		return mail->data.guid;

	mail_index_lookup_ext(mail->trans->trans_view, mail->mail.mail.seq,
			      mbox->md5hdr_ext_idx, &ext_data, NULL);
	if (ext_data != NULL && memcmp(ext_data, empty_md5, 16) != 0) {
		mail->data.guid = p_strdup(mail->data_pool,
					   binary_to_hex(ext_data, 16));
		*value_r = mail->data.guid;
		return TRUE;
	} else {
		return FALSE;
	}
}

static int
mbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
		      const char **value_r)
{
	struct index_mail *mail = (struct index_mail *)_mail;
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;

	switch (field) {
	case MAIL_FETCH_FROM_ENVELOPE:
		if (mbox_mail_seek(mail) < 0)
			return -1;

		*value_r = istream_raw_mbox_get_sender(mbox->mbox_stream);
		return 0;
	case MAIL_FETCH_GUID:
	case MAIL_FETCH_HEADER_MD5:
		if (mbox_mail_get_md5_header(mail, value_r))
			return 0;

		/* i guess in theory the empty_md5 is valid and can happen,
		   but it's almost guaranteed that it means the MD5 sum is
		   missing. recalculate it. */
		mbox->mbox_save_md5 = TRUE;
                mbox_prepare_resync(mail);
		if (mbox_sync(mbox, MBOX_SYNC_FORCE_SYNC) < 0)
			return -1;

		if (!mbox_mail_get_md5_header(mail, value_r)) {
			i_error("mbox resyncing didn't save header MD5 values");
			return -1;
		}
		return 0;
	default:
		break;
	}

	return index_mail_get_special(_mail, field, value_r);
}

static bool
mbox_mail_get_next_offset(struct index_mail *mail, uoff_t *next_offset_r)
{
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;
	struct mail_index_view *view;
	const struct mail_index_header *hdr;
	uint32_t seq;
	int trailer_size;
	int ret = 1;

	hdr = mail_index_get_header(mail->trans->trans_view);
	if (mail->mail.mail.seq > hdr->messages_count) {
		/* we're appending a new message */
		return 0;<commit_msg>mbox: Minor error message improvement.<commit_after>/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "istream.h"
#include "hex-binary.h"
#include "index-mail.h"
#include "mbox-storage.h"
#include "mbox-file.h"
#include "mbox-lock.h"
#include "mbox-sync-private.h"
#include "istream-raw-mbox.h"
#include "istream-header-filter.h"

#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

static void mbox_prepare_resync(struct index_mail *mail)
{
	struct mbox_transaction_context *t =
		(struct mbox_transaction_context *)mail->trans;
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;

	if (mbox->mbox_lock_type == F_RDLCK) {
		if (mbox->mbox_lock_id == t->mbox_lock_id)
			t->mbox_lock_id = 0;
		(void)mbox_unlock(mbox, mbox->mbox_lock_id);
		i_assert(mbox->mbox_lock_type == F_UNLCK);
	}
}

static int mbox_mail_seek(struct index_mail *mail)
{
	struct mbox_transaction_context *t =
		(struct mbox_transaction_context *)mail->trans;
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;
	enum mbox_sync_flags sync_flags = 0;
	int ret, try;
	bool deleted;

	if (mail->mail.mail.expunged || mbox->syncing)
		return -1;

	if (mail->mail.mail.lookup_abort != MAIL_LOOKUP_ABORT_NEVER)
		return mail_set_aborted(&mail->mail.mail);

	if (mbox->mbox_stream != NULL &&
	    istream_raw_mbox_is_corrupted(mbox->mbox_stream)) {
		/* clear the corruption by forcing a full resync */
		sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC;
	}

	for (try = 0; try < 2; try++) {
		if ((sync_flags & MBOX_SYNC_FORCE_SYNC) != 0) {
			/* dirty offsets are broken. make sure we can sync. */
			mbox_prepare_resync(mail);
		}
		if (mbox->mbox_lock_type == F_UNLCK) {
			sync_flags |= MBOX_SYNC_LOCK_READING;
			if (mbox_sync(mbox, sync_flags) < 0)
				return -1;
			t->mbox_lock_id = mbox->mbox_lock_id;
			i_assert(t->mbox_lock_id != 0);

			/* refresh index file after mbox has been locked to
			   make sure we get only up-to-date mbox offsets. */
			if (mail_index_refresh(mbox->ibox.index) < 0) {
				mail_storage_set_index_error(&mbox->ibox);
				return -1;
			}

			i_assert(mbox->mbox_lock_type != F_UNLCK);
		} else if (t->mbox_lock_id == 0) {
			/* file is already locked by another transaction, but
			   we must keep it locked for the entire transaction,
			   so increase the lock counter. */
			if (mbox_lock(mbox, mbox->mbox_lock_type,
				      &t->mbox_lock_id) < 0)
				i_unreached();
		}

		if (mbox_file_open_stream(mbox) < 0)
			return -1;

		ret = mbox_file_seek(mbox, mail->trans->trans_view,
				     mail->mail.mail.seq, &deleted);
		if (ret > 0) {
			/* success */
			break;
		}
		if (ret < 0) {
			if (deleted)
				mail_set_expunged(&mail->mail.mail);
			return -1;
		}

		/* we'll need to re-sync it completely */
		sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC;
	}
	if (ret == 0) {
		mail_storage_set_critical(&mbox->storage->storage,
			"Losing sync for mail uid=%u in mbox file %s",
			mail->mail.mail.uid, mbox->ibox.box.path);
	}
	return 0;
}

static int mbox_mail_get_received_date(struct mail *_mail, time_t *date_r)
{
	struct index_mail *mail = (struct index_mail *)_mail;
	struct index_mail_data *data = &mail->data;
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;

	if (index_mail_get_received_date(_mail, date_r) == 0)
		return 0;

	if (mbox_mail_seek(mail) < 0)
		return -1;
	data->received_date =
		istream_raw_mbox_get_received_time(mbox->mbox_stream);
	if (data->received_date == (time_t)-1) {
		/* it's broken and conflicts with our "not found"
		   return value. change it. */
		data->received_date = 0;
	}

	*date_r = data->received_date;
	return 0;
}

static int mbox_mail_get_save_date(struct mail *_mail, time_t *date_r)
{
	struct index_mail *mail = (struct index_mail *)_mail;
	struct index_mail_data *data = &mail->data;

	if (index_mail_get_save_date(_mail, date_r) == 0)
		return 0;

	/* no way to know this. save the current time into cache and use
	   that from now on. this works only as long as the index files
	   are permanent */
	data->save_date = ioloop_time;
	*date_r = data->save_date;
	return 0;
}

static bool
mbox_mail_get_md5_header(struct index_mail *mail, const char **value_r)
{
	static uint8_t empty_md5[16] =
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;
	const void *ext_data;

	if (mail->data.guid != NULL)
		return mail->data.guid;

	mail_index_lookup_ext(mail->trans->trans_view, mail->mail.mail.seq,
			      mbox->md5hdr_ext_idx, &ext_data, NULL);
	if (ext_data != NULL && memcmp(ext_data, empty_md5, 16) != 0) {
		mail->data.guid = p_strdup(mail->data_pool,
					   binary_to_hex(ext_data, 16));
		*value_r = mail->data.guid;
		return TRUE;
	} else {
		return FALSE;
	}
}

static int
mbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
		      const char **value_r)
{
	struct index_mail *mail = (struct index_mail *)_mail;
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;

	switch (field) {
	case MAIL_FETCH_FROM_ENVELOPE:
		if (mbox_mail_seek(mail) < 0)
			return -1;

		*value_r = istream_raw_mbox_get_sender(mbox->mbox_stream);
		return 0;
	case MAIL_FETCH_GUID:
	case MAIL_FETCH_HEADER_MD5:
		if (mbox_mail_get_md5_header(mail, value_r))
			return 0;

		/* i guess in theory the empty_md5 is valid and can happen,
		   but it's almost guaranteed that it means the MD5 sum is
		   missing. recalculate it. */
		mbox->mbox_save_md5 = TRUE;
                mbox_prepare_resync(mail);
		if (mbox_sync(mbox, MBOX_SYNC_FORCE_SYNC) < 0)
			return -1;

		if (!mbox_mail_get_md5_header(mail, value_r)) {
			i_error("mbox %s resyncing didn't save header MD5 values",
				_mail->box->name);
			return -1;
		}
		return 0;
	default:
		break;
	}

	return index_mail_get_special(_mail, field, value_r);
}

static bool
mbox_mail_get_next_offset(struct index_mail *mail, uoff_t *next_offset_r)
{
	struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->ibox;
	struct mail_index_view *view;
	const struct mail_index_header *hdr;
	uint32_t seq;
	int trailer_size;
	int ret = 1;

	hdr = mail_index_get_header(mail->trans->trans_view);
	if (mail->mail.mail.seq > hdr->messages_count) {
		/* we're appending a new message */
		return 0;
{'fraction_non_alphanumeric': 0.12686804451510333, 'fraction_numerical': 0.013513513513513514, 'mean_word_length': 3.9924603174603175, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 139, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3938669', 'n_tokens_mistral': 5314, 'n_tokens_neox': 4821, 'n_words': 1157}