/*
* mailspool.c:
* Berkeley mailspool handling.
*
* Note that this makes _no_ attempt to handle the awful SysVism of not
* quoting /^From / in body text and attempting to use Content-Length to
* figure out where messages start and end.
*
* See
* http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html
*
* This also, optionally, allows the metadata stored into mailspools (why,
* Washington University, why?) by PINE to be ignored. This means that those
* who use PINE locally and a POP3 client remotely will not find themselves
* continually downloading copies of "DON'T DELETE THIS MESSAGE -- ...".
*
* A further option allows caches of message offsets in a mailspool to be
* made, reducing the time needed to open a mailspool.
*
* Copyright (c) 2001 Chris Lightfoot. All rights reserved.
*
*/
#ifdef HAVE_CONFIG_H
#include "configuration.h"
#endif /* HAVE_CONFIG_H */
#ifdef MBOX_BSD
static const char rcsid[] = "$Id: mailspool.c,v 1.50 2003/11/06 01:19:27 chris Exp $";
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include "connection.h"
#include "locks.h"
#include "mailbox.h"
#include "md5.h"
#include "stringmap.h"
#include "util.h"
#ifdef MBOX_BSD_SAVE_INDICES
/* Stuff to support a metadata cache. */
int mailspool_save_indices;
char *mailspool_find_index(mailbox m);
int mailspool_save_index(mailbox m);
int mailspool_load_index(mailbox m);
#endif /* MBOX_BSD_SAVE_INDICES */
/* file_unlock FD FILENAME
* Unlock a mailspool file using the open FD and given FILENAME. Returns 1 on
* success or 0 on failure. */
int file_unlock(const int fd, const char *name) {
int r = 1;
#ifdef WITH_FCNTL_LOCKING
if (fcntl_unlock(fd) == -1) r = 0;
#endif
#ifdef WITH_FLOCK_LOCKING
if (flock_unlock(fd) == -1) r = 0;
#endif
#ifdef WITH_DOTFILE_LOCKING
if (name && dotfile_unlock(name) == -1) r = 0;
#endif
return r;
}
/* file_lock FD FILENAME
* Lock a mailspool file using the open FD and given FILENAME. Returns 1 on
* success or 0 on failure. This uses whatever locking strategies the user has
* selected with compile-time definitions. */
int file_lock(const int fd, const char *name) {
int l_fcntl, l_flock, l_dotfile;
l_fcntl = l_flock = l_dotfile = 0;
#ifdef WITH_FCNTL_LOCKING
if (fcntl_lock(fd) == -1) goto fail;
else l_fcntl = 1;
#endif
#ifdef WITH_FLOCK_LOCKING
if (flock_lock(fd) == -1) goto fail;
else l_flock = 1;
#endif
#ifdef WITH_DOTFILE_LOCKING
if (dotfile_lock(name) == -1) goto fail;
else l_dotfile = 1;
#endif
#ifdef WITH_CCLIENT_LOCKING
if (cclient_steal_lock(fd) == -1) goto fail;
#endif
return 1;
fail:
#ifdef WITH_FCNTL_LOCKING
if (l_fcntl) fcntl_unlock(fd);
#endif
#ifdef WITH_FLOCK_LOCKING
if (l_flock) flock_unlock(fd);
#endif
#ifdef WITH_DOTFILE_LOCKING
if (l_dotfile) dotfile_unlock(name);
#endif
return 0;
}
/* mailspool_make_indexpoint INDEXPOINT OFFSET LENGTH MESSAGELENGTH HASH
* Save in INDEXPOINT the OFFSET, header LENGTH, MESSAGELENGTH and 16-byte
* HASH of a message. */
void mailspool_make_indexpoint(struct indexpoint *x, const size_t offset, const size_t length, const size_t msglength, const unsigned char *hash) {
memset(x, 0, sizeof(struct indexpoint));
x->offset = offset;
x->length = length;
x->msglength = msglength;
if (hash) memcpy(x->hash, hash, 16);
}
/* mailspool_new_from_file FILENAME
* Open FILENAME, lock it, and form an index of the messages in it. */
mailbox mailspool_new_from_file(const char *filename) {
mailbox M, failM = NULL;
int i;
struct timeval tv1, tv2;
float f;
alloc_struct(_mailbox, M);
M->delete = mailspool_delete;
M->apply_changes = mailspool_apply_changes;
M->sendmessage = mailspool_sendmessage;
/* Allocate space for the index. */
M->index = (struct indexpoint*)xcalloc(32, sizeof(struct indexpoint));
M->size = 32;
if (stat(filename, &(M->st)) == -1) {
/* If the mailspool doesn't exist, fail silently, since this may be
* getting called from find_mailbox. */
if (errno == ENOENT) failM = MBOX_NOENT;
else log_print(LOG_ERR, "mailspool_new_from_file: stat(%s): %m", filename);
goto fail;
} else M->name = xstrdup(filename);
/* Naive locking strategy. */
for (i = 0; i < MAILSPOOL_LOCK_TRIES; ++i) {
M->fd = open(M->name, O_RDWR);
if (M->fd == -1) {
log_print(LOG_ERR, "mailspool_new_from_file: %m");
goto fail;
}
if (file_lock(M->fd, M->name)) break;
close(M->fd);
M->fd = -1;
sleep(MAILSPOOL_LOCK_WAIT);
}
if (M->fd == -1) {
log_print(LOG_ERR, _("mailspool_new_from_file: failed to lock %s: %m"), filename);
goto fail;
}
gettimeofday(&tv1, NULL);
/* Build index of mailspool. */
#ifdef MBOX_BSD_SAVE_INDICES
if (mailspool_save_indices) {
if (mailspool_load_index(M) == -1) {
log_print(LOG_ERR, _("mailspool_new_from_file: unable to index mailspool"));
goto fail;
}
} else
#endif
if (mailspool_build_index(M, NULL) == -1) {
log_print(LOG_ERR, _("mailspool_new_from_file: unable to index mailspool"));
goto fail;
}
gettimeofday(&tv2, NULL);
f = (float)(tv2.tv_sec - tv1.tv_sec) + 1e-6 * (float)(tv2.tv_usec - tv1.tv_usec);
log_print(LOG_DEBUG, _("mailspool_new_from_file: indexed mailspool %s (%d bytes) in %0.3fs"), filename, (int)M->st.st_size, f);
/* OK, now go through the mailspool and accumulate statistics. */
for (i = 0, M->totalsize = 0; i < M->num; ++i)
M->totalsize += M->index[i].msglength;
return M;
fail:
if (M) mailspool_delete(M);
return failM;
}
/* mailspool_delete MAILBOX
* Deletion specific to mailspools. */
void mailspool_delete(mailbox m) {
if (!m) return;
if (m->name) file_unlock(m->fd, m->name);
if (m->fd != -1) close(m->fd);
mailbox_delete(m);
}
/* memstr HAYSTACK HLEN NEEDLE NLEN
* Locate NEEDLE, of length NLEN, in HAYSTACK, of length HLEN, returning NULL
* if it is not found. Uses the Boyer-Moore search algorithm. Cf.
* http://www-igm.univ-mlv.fr/~lecroq/string/node14.html */
static unsigned char *memstr(const unsigned char *haystack, const size_t hlen,
const unsigned char *needle, const size_t nlen) {
int skip[256], k;
if (nlen == 0) return (char*)haystack;
/* Set up the finite state machine we use. */
for (k = 0; k < 256; ++k) skip[k] = nlen;
for (k = 0; k < nlen - 1; ++k) skip[needle[k]] = nlen - k - 1;
/* Do the search. */
for (k = nlen - 1; k < hlen; k += skip[haystack[k]]) {
int i, j;
for (j = nlen - 1, i = k; j >= 0 && haystack[i] == needle[j]; j--) i--;
if (j == -1) return (unsigned char*)(haystack + i + 1);
}
return NULL;
}
/* mailspool_build_index MAILBOX MEMORY
* Build an index of a mailspool in MAILBOX. Uses mmap(2) for speed; if MEMORY
* is non-NULL, it is assumed to point to a mapped region on the mailspool
* file. Assumes that mailspools use only '\n' to indicate EOL. Returns 0 on
* success or -1 on failure. */
int mailspool_build_index(mailbox M, char *filemem) {
char *p, *q;
size_t filelen, mappedlen = 0;
int first = 0;
if (!M || M->fd == -1) return -1;
filelen = M->st.st_size;
if (filelen < 16)
goto end;
mappedlen = getmaplength(filelen);
if (!filemem) {
if (MAP_FAILED == (filemem = mmap(0, mappedlen, PROT_READ, MAP_PRIVATE, M->fd, 0))) {
log_print(LOG_ERR, "mailspool_build_index(%s): mmap: %m", M->name);
return -1;
}
}
if (M->num > 0) {
/* Perhaps we are parsing the tail of the file, after reading a
* partial index? */
struct indexpoint *P = M->index + M->num - 1;
p = filemem + P->offset + P->msglength - 2;
first = M->num;
log_print(LOG_DEBUG, _("mailspool_build_index(%s): first %d messages indexed from cached metadata"), M->name, first);
} else
/* Nope, never seen this one before. */
p = filemem - 2;
/* Extract all From lines from file */
do {
p += 2;
q = (char*)memchr(p, '\n', filelen - (p - filemem));
if (q) {
size_t o, l;
struct indexpoint pt;
o = p - filemem;
l = q - p;
mailspool_make_indexpoint(&pt, o, l, 0, NULL);
mailbox_add_indexpoint(M, &pt);
p = memstr(q, filelen - (q - filemem), "\n\nFrom ", 7);
} else break;
} while (p && p < filemem + filelen);
if (first < M->num) {
struct indexpoint *t;
/* OK, we're done, figure out the lengths */
for (t = M->index; t < M->index + M->num - 1; ++t)
t->msglength = (t + 1)->offset - t->offset;
t->msglength = M->st.st_size - t->offset;
/* We generate "unique" IDs by hashing the first 512 or so bytes of the
* data in each message. Only do this for newly found messages. */
for (t = M->index; t < M->index + M->num; ++t) {
size_t n = 512;
if (n > t->msglength) n = t->msglength;
md5_digest((void*)(filemem + t->offset), n, t->hash);
}
}
#ifdef IGNORE_CCLIENT_METADATA
/* Optionally, check whether the first message in the mailspool is
* internal data used by c-client; such messages contain the following
* headers:
*
* Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
* X-IMAP: <some numbers>
*
* The metadata message is assumed to be the first one in the mailspool. */
if (M->num >= 1) {
struct indexpoint *P = M->index;
p = memstr(filemem + P->offset, P->msglength, "\n\n", 2);
if (p) {
const char hdr1[] = "\nX-IMAP: ", hdr2[] = "Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA\n";
if (memstr(filemem + P->offset, p - filemem, hdr1, strlen(hdr1)) && memstr(filemem + P->offset, p - filemem, hdr2, strlen(hdr2))) {
log_print(LOG_DEBUG, "mailspool_build_index(%s): skipping c-client metadata", M->name);
memmove((void*)M->index, (void*)(M->index + 1), sizeof(struct indexpoint) * (M->num - 1));
--M->num;
}
}
}
#endif /* IGNORE_CCLIENT_METADATA */
end:
if (filemem) munmap(filemem, mappedlen);
return 0;
}
/* mailspool_sendmessage MAILBOX CONNECTION I NLINES
* Front-end to connection_sendmessage in util.c. */
int mailspool_sendmessage(const mailbox M, connection c, const int i, int n) {
struct indexpoint *x;
if (!M || i < 0 || i >= M->num) {
/* Shouldn't happen. */
connection_sendresponse(c, 0, _("Unable to send that message"));
return -1;
}
x = M->index + i;
/* XXX I think that this should send msglength - 1; that is, it's sending
* the first \n of the `\n\nFrom '. */
return connection_sendmessage(c, M->fd, x->offset, x->length + 1, x->msglength, n);
}
/* mailspool_apply_changes MAILBOX
* Apply deletions to a mailspool by mapping it and copying it in blocks.
* Returns 1 on success or 0 on failure.
*
* This is messy. Apart from the special cases of all messages to be deleted,
* and no messages to be deleted, we need to cope with an arbitrary set of
* messages being marked. Rather than using a temporary file and copying the
* entire mailspool minus the marked messages, then unlinking the old one and
* renaming the new one in its place, we mmap(2) the whole file and do some
* memmove(3) magic to make the changes.
*
* Explanation: Clear sections represent sections not to be deleted, hatched
* sections are parts which will be.
*
* I, J and K represent messages in the mailspool index.
*
* +---+
* | |
* | |
* d --> +---+ <-- I beginning of
* |///| section to be deleted
* |///|
* d1 --> |///|
* |///|
* s --> +---+ <-- J end of section
* | |
* | |
* +---+ <-- K beginning of <-- I1
* |///| next section to be
* |///| deleted
* s1 --> +---+ <-- J1
* | |
* | |
* | |
* +---+ <-- K1
* |///|
* ...
*
* At this point, we can copy (K->offset - J->offset) bytes from J->offset
* (s) to I->offset in the file (d).
*
* Now, we find the next set of ranges (I1, J1, K1 on diagram), and can
* perform the next copy. s1 is J1->offset in the file, but d1 is
* d + (K->offset - J->offset), to take account of the hole we made.
*
* A special case occurs where the section to be deleted is at the end of the
* file, at which point we can just ftruncate(2). */
int mailspool_apply_changes(mailbox M) {
char *filemem, *s, *d;
size_t len;
struct indexpoint *I, *J, *K, *End;
if (!M || M->fd == -1) return 1;
if (M->numdeleted == 0)
/* No messages deleted, do nothing. */
return 1;
else if (M->numdeleted == M->num) {
/* All messages deleted, so just truncate file at the beginning of the
* first message. */
if (ftruncate(M->fd, M->index->offset) == -1) {
log_print(LOG_ERR, "mailspool_apply_changes(%s): ftruncate: %m", M->name);
return 0;
} else return 1;
}
/* We need to do something more complicated, so map the mailspool. */
len = getmaplength(M->st.st_size);
filemem = mmap(0, len, PROT_WRITE | PROT_READ, MAP_SHARED, M->fd, 0);
if (filemem == MAP_FAILED) {
log_print(LOG_ERR, "mailspool_apply_changes(%s): mmap: %m", M->name);
close(M->fd);
return 0;
}
I = M->index;
End = M->index + M->num;
d = filemem;
/* Find the first message to be deleted. */
while (I < End && !I->deleted) ++I;
if (I == End) {
if (munmap(filemem, len) == -1) log_print(LOG_ERR, "mailspool_sendmessage: munmap: %m");
log_print(LOG_ERR, _("mailspool_apply_changes(%s): inconsistency in mailspool data"), M->name);
return 0;
}
d = filemem + I->offset;
do {
/* Find the first non-deleted message after this block. */
J = I;
while (J < End && J->deleted) ++J;
if (J == End) break;
else {
/* Find the end of this chunk. */
size_t copylen = 0;
s = filemem + J->offset;
K = J;
while (K < End && !K->deleted) copylen += (K++)->msglength;
/* Not every machine has a working memmove(3) (allows overlapping
* memory areas). If yours doesn't, get a better one ;) */
memmove(d, s, copylen);
d += copylen;
}
I = K;
} while (I < End);
/* Truncate the very end. */
if (ftruncate(M->fd, d - filemem) == -1) {
log_print(LOG_ERR, "mailspool_apply_changes(%s): ftruncate: %m", M->name);
if (munmap(filemem, len) == -1) log_print(LOG_ERR, "mailspool_sendmessage: munmap: %m");
return 0;
}
/* Done, unmap the file. */
if (munmap(filemem, len) == -1) {
log_print(LOG_ERR, "mailspool_sendmessage: munmap: %m");
return 0;
}
#ifdef MBOX_BSD_SAVE_INDICES
if (mailspool_save_indices && !mailspool_save_index(M))
log_print(LOG_WARNING, _("mailspool_apply_changes(%s): unable to save mailspool index"), M->name);
#endif /* MBOX_BSD_SAVE_INDICES */
return 1;
}
#ifdef MBOX_BSD_SAVE_INDICES
/* Optionally, tpop3d can save indices of the offsets of messages within BSD
* mailspools. Obviously this is a win speed-wise, but it is a bit messy.
*
* The game is that we save (in a textual format, giving the nod to
* machine-independence for lunatics who believe in NFS-mounted mailspools)
* the offset, `From ' line length, message size and derived unique ID (MD5
* hash of beginning of message) for each message.
*
* The load routine can then check that the message offsets point to plausible
* messages (begin `From ' and have the right MD5 hash).
*
* We write out the index if we have parsed a mailspool without the benefit of
* an index; or after a mailspool is modified.
*
* Note that we don't rely on modification times for this, on the basis that
* some dumb pieces of software (I love you, PINE!) will modify them for some
* deranged reason probably known only to people who live up in that corner of
* the world among the giant redwoods, collapsing suspension bridges, and
* 1970s software empires. */
/* mailspool_find_index MAILBOX
* Find the name of a mailspool's index file, using the spec in the config
* file. */
extern stringmap config;
char *mailspool_find_index(mailbox m) {
char *path, *file, *escaped_name;
char *p, *q;
item *I;
char *subspec, *indexname;
struct sverr err;
I = stringmap_find(config, "mailspool-index");
if (!I) return NULL;
subspec = (char*)I->v;
/* First find out what name the index should have. We supply the user with
* the path, filename, and an escaped form of the full name; so,
*
* /var/spool/mail/fred -> name = /var/spool/mail/fred
* path = /var/spool/mail
* file = fred
* escaped_name = %2fvar%2fspool%2fmail%2ffred
*
* This allows you to say, for instance,
* mailspool-index: /var/spool/tpop3d/$(escaped_name)
* or
* mailspool-index: $(path)/.$(file).tpop3d-index
*
* In either case, the path in which the index is saved needs to have
* permissions which allow the user who owns the mailspool to write a new
* file to it. 1777 would be traditional. */
path = xstrdup(m->name);
p = strrchr(path, '/');
if (p) *p = 0;
file = xstrdup(p + 1);
escaped_name = xcalloc(strlen(m->name) * 3 + 2, 1);
/* Form HTTP-style escaped version of name. Only escape % and /, though. */
for (p = m->name, q = escaped_name; *p; ++p) {
if (strchr("/%", *p))
q += sprintf(q, "%%%02x", (int)*p);
else
*(q++) = *p;
}
indexname = substitute_variables(subspec, &err, 4, "name", m->name, "path", path, "file", file, "escaped_name", escaped_name);
if (!indexname) {
log_print(LOG_ERR, _("mailspool_find_index: %s near `%.16s'"), err.msg, subspec + err.offset);
goto fail;
}
fail:
if (path) xfree(path);
if (file) xfree(file);
if (escaped_name) xfree(escaped_name);
return indexname;
}
/* This is a signature we put at the top of our files to identify them later. */
char index_signature[] =
"# This is a mailspool index file, generated by tpop3d, version " TPOP3D_VERSION ".\n"
"# Its purpose is to speed up the parsing of mailspool files by the POP3\n"
"# server. If you delete this file, it will be automatically recreated by\n"
"# tpop3d. So don't do that.\n";
/* mailbox_save_index MAILBOX
* Save an index of a mailspool. Returns 1 on success or 0 on failure. Uses
* stdio, which is unfortunate but makes it a bit easier to write. The
* mailspool must be locked when this is called. Makes some attempt to avoid
* clobbering files via symlink attacks. */
int mailspool_save_index(mailbox m) {
char *indexfile;
int ret = 0;
int fd = -1;
FILE *fp = NULL;
int offset;
struct indexpoint *I, *End;
int a;
char buf[1024];
if (!m || m->fd == -1) return 1;
indexfile = mailspool_find_index(m);
if (!indexfile) return -1;
/* OK, now we need to save the thing. */
fd = open(indexfile, O_RDWR | O_CREAT, 0600); /* Ensure correct permissions. */
if (fd == -1) {
log_print(LOG_ERR, "mailspool_save_index(%s): %m", indexfile);
goto fail;
}
/* Now we need to make sure that there isn't some sort of childish symlink
* attack in progress. */
a = readlink(indexfile, buf, sizeof(buf));
if (a == 0) {
log_print(LOG_ERR, _("mailspool_save_index(%s): possible security problem: index file exists and is a symlink to `%s'"), indexfile, buf);
goto fail;
} else if (a == -1 && errno != EINVAL) {
log_print(LOG_ERR, _("mailspool_save_index(%s): possible security problem: index file exists, and is a symlink, but readlink failed: %m"), indexfile);
goto fail;
}
/* We're OK. Clobber the file and start writing to it. */
ftruncate(fd, 0);
fp = fdopen(fd, "wt");
if (!fp) {
log_print(LOG_ERR, "mailspool_save_index(%s): %m", indexfile);
goto fail;
}
/* Write a header to the file. */
fprintf(fp, "%s", index_signature);
/* Now we need to save data about all the messages in the mailspool. But
* note that some of them might have been deleted, so we rely on the
* message sizes rather than their offsets. */
if (m->numdeleted < m->num) {
/* There are some remaining messages. */
I = m->index;
End = m->index + m->num;
offset = I->offset; /* get first message offset to deal with cclient metadata etc. */
while (I < End) {
if (!I->deleted) {
fprintf(fp, "%08x %08x %08x %s\n", (unsigned int)offset, (unsigned int)I->length, (unsigned int)I->msglength, hex_digest(I->hash)); /* XXX error return? */
offset += I->msglength;
}
++I;
}
}
ret = 1;
fail:
if (indexfile) xfree(indexfile);
if (fp) fclose(fp);
else if (fd != -1) close(fd);
return ret;
}
/* mailspool_load_index MAILBOX
* Attempts to construct a mailspool index from a saved index file, if one
* exists and is of the correct format. We may find that we need to re-parse
* the tail of the file; this is done by calling into the `normal'
* mailspool_build_index. Returns 0 on success or -1 on failure. */
int mailspool_load_index(mailbox m) {
char *indexfile = NULL;
FILE *fp = NULL;
struct stat st;
int offset, length, msglength;
char hexdigest[33] = {0};
char sigbuf[sizeof(index_signature)];
char *filemem = NULL;
size_t mappedlen;
int num, r;
int index_missing = 0;
if (!m || m->fd == -1) goto fail;
indexfile = mailspool_find_index(m);
if (!indexfile) goto fail;
fp = fopen(indexfile, "rt");
if (!fp) {
if (errno == ENOENT)
index_missing = 1; /* create it at the end */
else
log_print(LOG_WARNING, "mailspool_load_index(%s): %m", indexfile);
goto fail;
}
/* Security. The file must have the correct permissions, and be owned by
* ourselves. */
if (fstat(fileno(fp), &st) == -1) {
log_print(LOG_ERR, "mailspool_load_index(%s): %m", indexfile);
goto fail;
} else if ((st.st_mode & 0777) != 0600 || st.st_uid != getuid()) {
log_print(LOG_ERR, _("mailspool_load_index(%s): possible security problem: index exists, but it has the wrong owner or file permissions"), indexfile);
log_print(LOG_ERR, _("mailspool_load_index(%s): owner is %d, should be %d; mode 0%03o, should be 0600"), indexfile,
(int)st.st_uid, (int)getuid(), st.st_mode & 0777);
goto fail;
}
/* OK, found an index file; let's try loading some data out of it. */
if (fread(sigbuf, 1, sizeof(sigbuf) - 1, fp) != sizeof(sigbuf) - 1 || memcmp(sigbuf, index_signature, sizeof(sigbuf) - 1) != 0) {
log_print(LOG_WARNING, _("mailspool_load_index(%s): index exists, but is of wrong format; ignoring"), indexfile);
goto fail;
}
/* Should now get a bunch of offset/hash lines. Stuff these into the
* mailbox object. Also mmap the real mailspool so we can check these. */
if (m->st.st_size < 16) goto fail;
mappedlen = getmaplength(m->st.st_size);
if (MAP_FAILED == (filemem = mmap(0, mappedlen, PROT_READ, MAP_PRIVATE, m->fd, 0))) {
log_print(LOG_ERR, "mailspool_load_index(%s): mmap: %m", m->name);
goto fail;
}
while (fscanf(fp, "%8x %8x %8x %32[0-9a-f]", &offset, &length, &msglength, hexdigest) == 4) {
struct indexpoint x;
size_t n = 512;
unsigned char realhash[16];
/* XXX check validity here. */
mailspool_make_indexpoint(&x, offset, length, msglength, NULL);
unhex_digest(hexdigest, x.hash);
if (x.offset + x.msglength > m->st.st_size || memcmp(filemem + x.offset, "From ", 5) != 0)
break;
if (n > x.msglength) n = x.msglength;
/* Compute MD5 */
md5_digest(filemem + x.offset, n, realhash);
/* No match; stop. */
if (memcmp(realhash, x.hash, 16) != 0) {
/* Get rid of any preceding record: we will have to re-index that
* one, too. */
if (m->num > 0)
--m->num;
break;
}
/* OK, this message seems to have been indexed correctly.... */
mailbox_add_indexpoint(m, &x);
}
if (!feof(fp)) {
log_print(LOG_WARNING, _("mailspool_load_index(%s): index exists, but has some stale or corrupt data"), indexfile);
goto fail;
}
r = 0;
/* That's it. Messages after this one (if any) must be indexed `properly'. */
fail:
if (fp) fclose(fp);
if (indexfile) xfree(indexfile);
/* Whatever happens, have a go at indexing the rest of the file. */
num = m->num;
r = mailspool_build_index(m, filemem);
if (m->num > num || index_missing) mailspool_save_index(m);
return r;
}
#endif /* MBOX_BSD_SAVE_INDICES */
#endif /* MBOX_BSD */
syntax highlighted by Code2HTML, v. 0.9.1