/* mairix - message index builder and finder for maildir folders. ********************************************************************** * Copyright (C) Richard P. Curnow 2003,2004,2005,2006,2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * ********************************************************************** */ #include #include #include #include #include #include #include #include #include #include "mairix.h" #include "from.h" #include "fromcheck.h" #include "md5.h" struct extant_mbox {/*{{{*/ char *full_path; time_t mtime; size_t size; int db_index; /* + stuff to store positions etc of individual messages. */ }; /*}}}*/ static int compare_extant_mboxen(const void *a, const void *b)/*{{{*/ { const struct extant_mbox *aa = (const struct extant_mbox *) a; const struct extant_mbox *bb = (const struct extant_mbox *) b; return strcmp(aa->full_path, bb->full_path); } /*}}}*/ static int lookup_extant_mbox(struct extant_mbox *sorted_mboxen, int n_extant, char *key)/*{{{*/ { /* Implement bisection search */ int l, h, m, r; l = 0, h = n_extant; m = -1; while (h > l) { m = (h + l) >> 1; /* Should only get called on 'file' type messages - TBC */ r = strcmp(sorted_mboxen[m].full_path, key); if (r == 0) break; if (l == m) return -1; if (r > 0) h = m; else l = m; } return m; } /*}}}*/ static void append_new_mboxen_to_db(struct database *db, struct extant_mbox *extant_mboxen, int n_extant)/*{{{*/ { int N, n_reqd; int i, j; for (i=N=0; in_mboxen + N; if (n_reqd > db->max_mboxen) { db->max_mboxen = n_reqd; db->mboxen = grow_array(struct mbox, n_reqd, db->mboxen); } /* Init new entries. */ for (j=0, i=db->n_mboxen; jmboxen[i].path = new_string(extant_mboxen[j].full_path); db->mboxen[i].current_mtime = extant_mboxen[j].mtime; db->mboxen[i].current_size = extant_mboxen[j].size; db->mboxen[i].file_mtime = 0; db->mboxen[i].file_size = 0; db->mboxen[i].n_msgs = 0; db->mboxen[i].n_old_msgs_valid = 0; db->mboxen[i].max_msgs = 0; db->mboxen[i].start = NULL; db->mboxen[i].len = NULL; db->mboxen[i].check_all = NULL; i++; } } db->n_mboxen = n_reqd; } /*}}}*/ void compute_checksum(const char *data, size_t len, checksum_t *csum)/*{{{*/ { MD5_CTX md5; MD5Init(&md5); MD5Update(&md5, (unsigned char *) data, len); MD5Final(&md5); memcpy(csum, md5.digest, sizeof(md5.digest)); return; } /*}}}*/ static int message_is_intact(struct mbox *mb, int idx, char *va, size_t len)/*{{{*/ { /* TODO : later, look at whether to optimise this in some way, e.g. by doing an initial check on just the first 1k of a message, this will detect failures much faster at the cost of extra storage. */ if (mb->start[idx] + mb->len[idx] > len) { /* Message overruns the end of the file - can't possibly be intact. */ return 0; } else { checksum_t csum; compute_checksum(va + mb->start[idx], mb->len[idx], &csum); if (!memcmp(mb->check_all[idx], &csum, sizeof(checksum_t))) { return 1; } else { return 0; } } return 0; } /*}}}*/ static int find_number_intact(struct mbox *mb, char *va, size_t len)/*{{{*/ { /* Pick up the common obvious case first - where new messages have been appended to the end of the mbox */ if (mb->n_msgs == 0) { return 0; } else if (message_is_intact(mb, mb->n_msgs - 1, va, len)) { return mb->n_msgs; /* The lot */ } else if (!message_is_intact(mb, 0, va, len)) { return 0; /* None of them. */ } else { /* Looks like a deletion has occurred earlier in the file => binary chop search to find the last message that's still valid. Assume that everything below a valid message is still valid itself (possibly dangerous assumption, time will tell.) */ int l, m, h; l = 0; h = mb->n_msgs; /* Loop invariant : always, mesasage[l] is intact, message[h] isn't. */ while (l < h) { m = (h + l) >> 1; if (m==l) break; if (message_is_intact(mb, m, va, len)) { l = m; } else { h = m; } } /* By loop invariant, message[l] is the highest valid one. */ return (l + 1); } } /*}}}*/ static int fromtab_inited = 0; static signed char fromtab[256]; static void init_fromtab(void)/*{{{*/ { memset(fromtab, 0xff, 256); fromtab[(int)(unsigned char)'\n'] = ~(1<<0); fromtab[(int)(unsigned char)'F'] = ~(1<<1); fromtab[(int)(unsigned char)'r'] = ~(1<<2); fromtab[(int)(unsigned char)'o'] = ~(1<<3); fromtab[(int)(unsigned char)'m'] = ~(1<<4); fromtab[(int)(unsigned char)' '] = ~(1<<5); } /*}}}*/ /* REAL CHECKING : need to see if the line looks like this: * From [ ]