/* * Copyright (C) 2000 Helix Code Inc. * * Authors: Michael Zucchi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "camel-mbox-summary.h" #include #include #include #define d(x) #define CAMEL_MBOX_SUMMARY_VERSION 2 static int safe_write(int fd, char *buffer, size_t towrite); static void camel_mbox_summary_add(CamelMboxSummary *s, CamelMboxMessageInfo *info); /* Disk file format? message uid message-block date: date received? subject: (unicode encoded) from: (unicode encoded) to: (unicode) content-block content-block content-type: ; params; content-id: content-description: content-transfer-encoding: message-start: header-size: body-size: message-block multipart-block */ /* pah, i dont care, its almost no code and it works, dont need a glist */ struct _node { struct _node *next; }; static struct _node * my_list_append(struct _node **list, struct _node *n) { struct _node *ln = (struct _node *)list; while (ln->next) ln = ln->next; n->next = 0; ln->next = n; return n; } static int my_list_size(struct _node **list) { int len = 0; struct _node *ln = (struct _node *)list; while (ln->next) { ln = ln->next; len++; } return len; } /* low-level io functions */ static int encode_int (FILE *out, gint32 value) { int i; for (i=28;i>0;i-=7) { if (value >= (1<>i) & 0x7f; if (fputc(c, out) == -1) return -1; } } return fputc(value | 0x80, out); } static gint32 decode_int (FILE *in) { gint32 value=0, v; /* until we get the last byte, keep decoding 7 bits at a time */ while ( ((v = fgetc(in)) & 0x80) == 0 && v!=EOF) { value |= v; value <<= 7; } value |= (v&0x7f); return value; } static int encode_fixed_int (FILE *out, gint32 value) { guint32 save; save = htonl(value); return fwrite(&save, sizeof(save), 1, out); } static gint32 decode_fixed_int (FILE *out) { guint32 save; if (fread(&save, sizeof(save), 1, out) != -1) { return ntohl(save); } else { return -1; } } /* should be sorted, for binary search */ /* This is a tokenisation mechanism for strings written to the summary - to save space. This list can have at most 31 words. */ static char * tokens[] = { "7bit", "8bit", "alternative", "application", "base64", "boundary", "charset", "filename", "html", "image", "iso-8859-1", "iso-8859-8", "message", "mixed", "multipart", "name", "octet-stream", "parallel", "plain", "quoted-printable", "rfc822", "text", "us-ascii", /* 23 words */ }; #define tokens_len (sizeof(tokens)/sizeof(tokens[0])) /* baiscally ... 0 = null 1-tokens_len == tokens[id-1] >=32 string, length = n-32 */ static int encode_string (FILE *out, char *str) { if (str == NULL) { return encode_int(out, 0); } else { int len = strlen(str); int i, token=-1; if (len <= 16) { char lower[32]; for (i=0;i 10240) { g_warning("Got broken string header length: %d bytes", len); ret = NULL; } else { len -= 32; ret = g_malloc(len+1); if (fread(ret, len, 1, in) == -1) { g_free(ret); return NULL; } ret[len]=0; } return ret; } /* allocation functions */ static void body_part_dump(CamelMboxMessageContentInfo *bs, int depth) { CamelMboxMessageContentInfo *c; char *prefix; if (bs == NULL) return; prefix = alloca(depth*2+1); memset(prefix, ' ', depth*2); prefix[depth*2]=0; printf("%scontent-range: %d %d %d\n", prefix, (int)bs->pos, (int)bs->bodypos, (int)bs->endpos); printf("%scontent-type: %s/%s\n", prefix, bs->info.type?bs->info.type->type:"?", bs->info.type?bs->info.type->subtype:"?"); printf("%scontent-id: %s\n", prefix, bs->info.id); printf("%scontent-description: %s\n", prefix, bs->info.description); printf("%scontent-transfer-encoding: %s\n", prefix, bs->info.encoding); c = (CamelMboxMessageContentInfo *)bs->info.childs; while (c) { printf("%s -- \n", prefix); body_part_dump(c, depth+1); c = (CamelMboxMessageContentInfo *)c->info.next; } } static void message_struct_dump(CamelMboxMessageInfo *ms) { char *tmp; if (ms == NULL) { printf("Empty message?\n"); return; } printf("Subject: %s\n", ms->info.subject); printf("From: %s\n", ms->info.from); printf("To: %s\n", ms->info.to); tmp = header_format_date(ms->info.date_sent, 0); printf("Date: %s\n", tmp); g_free(tmp); tmp = header_format_date(ms->info.date_received, 0); printf("Date-Received: %s\n", tmp); g_free(tmp); printf("UID: %08x-%04x\n", atoi(ms->info.uid), ms->info.flags); printf(" -- content ->\n"); body_part_dump((CamelMboxMessageContentInfo *)ms->info.content, 1); } static CamelMboxMessageContentInfo * body_part_new(CamelMimeParser *mp, CamelMboxMessageContentInfo *parent, int start, int body) { CamelMboxMessageContentInfo *bs; bs = g_malloc0(sizeof(*bs)); bs->info.parent = (CamelMessageContentInfo *)parent; bs->info.type = camel_mime_parser_content_type(mp); header_content_type_ref(bs->info.type); bs->info.id = header_msgid_decode(camel_mime_parser_header(mp, "content-id", NULL)); bs->info.description = header_decode_string(camel_mime_parser_header(mp, "content-description", NULL)); bs->info.encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL)); /* not sure what to set here? */ bs->pos = start; bs->bodypos = body; bs->endpos = -1; if (parent) my_list_append((struct _node **)&parent->info.childs, (struct _node *)bs); return bs; } static CamelMboxMessageInfo * message_struct_new(CamelMimeParser *mp, CamelMboxMessageContentInfo *parent, int start, int body, off_t xev_offset) { CamelMboxMessageInfo *ms; ms = g_malloc0(sizeof(*ms)); /* FIXME: what about cc, sender vs from? */ ms->info.subject = g_strdup(camel_mime_parser_header(mp, "subject", NULL)); ms->info.from = g_strdup(camel_mime_parser_header(mp, "from", NULL)); ms->info.to = g_strdup(camel_mime_parser_header(mp, "to", NULL)); ms->info.date_sent = header_decode_date(camel_mime_parser_header(mp, "date", NULL), NULL); ms->info.date_received = 0; ms->info.content = (CamelMessageContentInfo *)body_part_new(mp, parent, start, body); ms->xev_offset = xev_offset; return ms; } static void body_part_free(CamelMboxMessageContentInfo *bs) { CamelMboxMessageContentInfo *c, *cn; c = (CamelMboxMessageContentInfo *)bs->info.childs; while (c) { cn = (CamelMboxMessageContentInfo *)c->info.next; body_part_free(c); c = cn; } g_free(bs->info.id); g_free(bs->info.description); g_free(bs->info.encoding); header_content_type_unref(bs->info.type); g_free(bs); } static void message_struct_free(CamelMboxMessageInfo *ms) { g_free(ms->info.subject); g_free(ms->info.to); g_free(ms->info.from); body_part_free((CamelMboxMessageContentInfo *)ms->info.content); g_free(ms); } /* IO functions */ static CamelMboxMessageContentInfo * body_part_load(FILE *in) { CamelMboxMessageContentInfo *bs = NULL, *c; struct _header_content_type *ct; char *type; char *subtype; int i, count; d(printf("got content-block\n")); bs = g_malloc0(sizeof(*bs)); bs->pos = decode_int(in); bs->bodypos = bs->pos + decode_int(in); bs->endpos = bs->pos + decode_int(in); /* do content type */ d(printf("got content-type\n")); type = decode_string(in); subtype = decode_string(in); ct = header_content_type_new(type, subtype); bs->info.type = ct; count = decode_int(in); d(printf("getting %d params\n", count)); for (i=0;iinfo.id = decode_string(in); d(printf("got content-description\n")); bs->info.description = decode_string(in); d(printf("got content-encoding\n")); bs->info.encoding = decode_string(in); count = decode_int(in); d(printf("got children, %d\n", count)); for (i=0;iinfo.childs, (struct _node *)c); c->info.parent = (CamelMessageContentInfo *)bs; } else { printf("Cannot load child\n"); } } return bs; } static int body_part_save(FILE *out, CamelMboxMessageContentInfo *bs) { CamelMboxMessageContentInfo *c, *cn; struct _header_content_type *ct; struct _header_param *hp; encode_int(out, bs->pos); encode_int(out, bs->bodypos - bs->pos); encode_int(out, bs->endpos - bs->pos); ct = bs->info.type; if (ct) { encode_string(out, ct->type); encode_string(out, ct->subtype); encode_int(out, my_list_size((struct _node **)&ct->params)); hp = ct->params; while (hp) { encode_string(out, hp->name); encode_string(out, hp->value); hp = hp->next; } } else { encode_string(out, NULL); encode_string(out, NULL); encode_int(out, 0); } encode_string(out, bs->info.id); encode_string(out, bs->info.description); encode_string(out, bs->info.encoding); encode_int(out, my_list_size((struct _node **)&bs->info.childs)); c = (CamelMboxMessageContentInfo *)bs->info.childs; while (c) { cn = (CamelMboxMessageContentInfo *)c->info.next; body_part_save(out, c); c = cn; } return 0; } static CamelMboxMessageInfo * message_struct_load(FILE *in) { CamelMboxMessageInfo *ms; ms = g_malloc0(sizeof(*ms)); ms->info.uid = g_strdup_printf("%u", decode_int(in)); ms->info.flags = decode_int(in); ms->info.date_sent = decode_int(in); ms->info.date_received = decode_int(in); ms->xev_offset = decode_int(in); ms->info.subject = decode_string(in); ms->info.from = decode_string(in); ms->info.to = decode_string(in); ms->info.content = (CamelMessageContentInfo *)body_part_load(in); return ms; } static int message_struct_save(FILE *out, CamelMboxMessageInfo *ms) { encode_int(out, strtoul(ms->info.uid, NULL, 10)); encode_int(out, ms->info.flags); encode_int(out, ms->info.date_sent); encode_int(out, ms->info.date_received); encode_int(out, ms->xev_offset); encode_string(out, ms->info.subject); encode_string(out, ms->info.from); encode_string(out, ms->info.to); body_part_save(out, (CamelMboxMessageContentInfo *)ms->info.content); return 0; } static unsigned int header_evolution_decode(const char *in, unsigned int *uid, unsigned int *flags) { char *header; if (in && (header = header_decode_token(&in))) { if (strlen(header) == strlen("00000000-0000") && sscanf(header, "%08x-%04x", uid, flags) == 2) { g_free(header); return *uid; } g_free(header); } return ~0; } static int safe_write(int fd, char *buffer, size_t towrite) { size_t donelen; size_t len; donelen = 0; while (donelen < towrite) { len = write(fd, buffer + donelen, towrite - donelen); if (len == -1) { if (errno == EINTR || errno == EAGAIN) continue; return -1; } donelen += len; } return donelen; } static int header_write(int fd, struct _header_raw *header, unsigned int uid, unsigned int flags) { struct iovec iv[3]; int outlen = 0; iv[1].iov_base = ":"; iv[1].iov_len = 1; while (header) { if (strcasecmp(header->name, "x-evolution")) { int len; iv[0].iov_base = header->name; iv[0].iov_len = strlen(header->name); iv[2].iov_base = header->value; iv[2].iov_len = strlen(header->value); do { len = writev(fd, iv, 3); } while (len == -1 && errno == EINTR); if (len == -1) return -1; outlen += len; } header = header->next; } return outlen; } /* returns -1 on error, else number of bytes written */ int camel_mbox_summary_copy_block(int fromfd, int tofd, off_t readpos, size_t bytes) { char buffer[4096]; int written = 0; off_t pos, newpos; pos = lseek(fromfd, 0, SEEK_CUR); if (pos == -1) return -1; newpos = lseek(fromfd, readpos, SEEK_SET); if (newpos == -1 || newpos != readpos) goto error; d(printf("oldpos = %d; copying %d from %d\n", (int)pos, (int)bytes, (int)readpos)); while (bytes>0) { int toread, towrite, donelen; toread = bytes; if (bytes>4096) toread = 4096; else toread = bytes; reread: towrite = read(fromfd, buffer, toread); if (towrite == -1) { if (errno == EINTR || errno == EAGAIN) goto reread; goto error; } /* check for 'end of file' */ if (towrite == 0) break; if ( (donelen = safe_write(tofd, buffer, towrite)) == -1) goto error; written += donelen; bytes -= donelen; } d(printf("written %d bytes\n", written)); newpos = lseek(fromfd, pos, SEEK_SET); if (newpos == -1 || newpos != pos); return -1; return written; error: lseek(fromfd, pos, SEEK_SET); return -1; } #define SAVEIT static int index_folder(CamelMboxSummary *s, int startoffset) { CamelMimeParser *mp; int fd; int fdout; int state; int toplevel = FALSE; const char *xev; char *data; int datalen; int enc_id=-1; int chr_id=-1; int idx_id=-1; struct _header_content_type *ct; int doindex=FALSE; CamelMimeFilterCharset *mfc = NULL; CamelMimeFilterIndex *mfi = NULL; CamelMimeFilterBasic *mf64 = NULL, *mfqp = NULL; CamelMboxMessageContentInfo *body = NULL, *parent = NULL; CamelMboxMessageInfo *message = NULL; int from_end = 0; /* start of message */ int from = 0; /* start of headers */ int last_write = 0; /* last written position */ int eof; int write_offset = 0; /* how much does the dest differ from the source pos */ int old_offset = 0; guint32 newuid; off_t xevoffset = -1; char *tmpname; printf("indexing %s (%s) from %d\n", s->folder_path, s->summary_path, startoffset); fd = open(s->folder_path, O_RDONLY); if (fd==-1) { perror("Can't open folder"); return -1; } tmpname = g_strdup_printf("%s.tmp", s->folder_path); fdout = open(tmpname, O_WRONLY|O_CREAT|O_TRUNC, 0600); if (fdout==-1) { perror("Can't open output"); g_free(tmpname); return -1; } mp = camel_mime_parser_new(); camel_mime_parser_init_with_fd(mp, fd); camel_mime_parser_scan_from(mp, TRUE); /* FIXME: cleaner fail code */ if (startoffset > 0) { if (camel_mime_parser_seek(mp, startoffset, SEEK_SET) != startoffset) { g_free(tmpname); gtk_object_unref((GtkObject *)mp); return -1; } } mfi = camel_mime_filter_index_new_ibex(s->index); while ( (state = camel_mime_parser_step(mp, &data, &datalen)) != HSCAN_EOF ) { switch(state) { case HSCAN_FROM: /* starting a new message content */ /* save the current position */ d(printf("from = %d\n", (int)camel_mime_parser_tell(mp))); toplevel = FALSE; from = camel_mime_parser_tell(mp); break; case HSCAN_FROM_END: d(printf("from-end = %d\n", (int)camel_mime_parser_tell(mp))); d(printf("message from %d to %d\n", from_end, (int)camel_mime_parser_tell(mp))); from_end = camel_mime_parser_tell(mp); break; case HSCAN_MESSAGE: case HSCAN_MULTIPART: case HSCAN_HEADER: /* starting a new header */ newuid=~0; if (!toplevel) { char name[32]; unsigned int olduid, oldflags; int headerlen; int docopy = FALSE; /* check for X-Evolution header ... if its there, nothing to do (skip content) */ xev = camel_mime_parser_header(mp, "x-evolution", &xevoffset); if (xev) { d(printf("An x-evolution header exists at: %d = %s\n", xevoffset + write_offset, xev)); xevoffset = xevoffset + write_offset; if (header_evolution_decode(xev, &olduid, &oldflags) != ~0) { d(printf(" uid = %d = %x\n", olduid, olduid)); newuid = olduid; #if 0 while (camel_mime_parser_step(mp, &data, &datalen) != HSCAN_FROM_END) ; break; #endif } else { printf("Invalid xev header? I need to write out a new one ...\n"); } } toplevel = TRUE; /* assign a new uid for this message */ if (newuid == ~0) { newuid = s->nextuid++; docopy = TRUE; } else { /* make sure we account for this uid when assigning uid's */ /* this really needs a pre-scan pass ... *sigh* */ camel_mbox_summary_set_uid(s, newuid); } /* setup index name for this uid */ sprintf(name, "%x", newuid); camel_mime_filter_index_set_name(mfi, name); /* remove all references to this name from the index */ if (s->index) ibex_unindex(s->index, name); d(printf("Message content starts at %d\n", camel_mime_parser_tell(mp))); if (docopy) { /* now, copy over bits of mbox from last write, and insert the X-Evolution header (at the top of headers) */ /* if we already have a valid x-evolution header, use that, dont need to copy */ camel_mbox_summary_copy_block(fd, fdout, last_write, from-last_write); last_write = from; headerlen = header_write(fdout, camel_mime_parser_headers_raw(mp), newuid, 0); sprintf(name, "X-Evolution: %08x-%04x\n\n", newuid, 0); safe_write(fdout, name, strlen(name)); d(printf("new X-Evolution at %d\n", headerlen + from + write_offset)); xevoffset = headerlen + from + write_offset; old_offset = write_offset; write_offset += (headerlen - (camel_mime_parser_tell(mp)-from)) + strlen(name); last_write = camel_mime_parser_tell(mp); } } else { old_offset = write_offset; } /* we only care about the rest for actual content parts */ /* TODO: Cleanup, this is a huge mess */ if (state != HSCAN_HEADER) { if (message == NULL) { message = message_struct_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset, xevoffset); parent = (CamelMboxMessageContentInfo *)message->info.content; if (newuid != ~0) { message->info.uid = g_strdup_printf("%u", newuid); } else { g_warning("This shouldn't happen?"); } } else { parent = body_part_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset); } break; } if (message == NULL) { message = message_struct_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset, xevoffset); body = (CamelMboxMessageContentInfo *)message->info.content; if (newuid != ~0) { message->info.uid = g_strdup_printf("%u", newuid); } else { g_warning("This shouldn't happen?"); } } else { body = body_part_new(mp, parent, camel_mime_parser_tell_start_headers(mp)+old_offset, camel_mime_parser_tell(mp)+write_offset); } /* check headers for types that we can index */ ct = camel_mime_parser_content_type(mp); if (header_content_type_is(ct, "text", "*")) { char *encoding; const char *charset; /* TODO: The filters should all be cached, so they aren't recreated between messages/message parts */ encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL)); if (encoding) { if (!strcasecmp(encoding, "base64")) { d(printf("Adding decoding filter for base64\n")); if (mf64 == NULL) mf64 = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_DEC); enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mf64); } else if (!strcasecmp(encoding, "quoted-printable")) { d(printf("Adding decoding filter for quoted-printable\n")); if (mfqp == NULL) mfqp = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_DEC); enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfqp); } g_free(encoding); } charset = header_content_type_param(ct, "charset"); if (charset!=NULL && !(strcasecmp(charset, "us-ascii")==0 || strcasecmp(charset, "utf-8")==0)) { d(printf("Adding conversion filter from %s to utf-8\n", charset)); if (mfc == NULL) mfc = camel_mime_filter_charset_new_convert(charset, "utf-8"); if (mfc) { chr_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfc); } else { g_warning("Cannot convert '%s' to 'utf-8', message display may be corrupt", charset); } } doindex = TRUE; /* and this filter actually does the indexing */ idx_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfi); } else { doindex = FALSE; } break; /* fixme, this needs thought *sigh* */ case HSCAN_MESSAGE_END: case HSCAN_MULTIPART_END: if (parent) { parent->endpos = camel_mime_parser_tell(mp)+write_offset; if (parent->info.parent == NULL) { camel_mbox_summary_add(s, message); message = NULL; parent = NULL; } else { parent = (CamelMboxMessageContentInfo *)parent->info.parent; } } break; case HSCAN_BODY: if (doindex) { d(printf("Got content to index:\n%.*s", datalen, data)); } break; case HSCAN_BODY_END: if (body) { body->endpos = camel_mime_parser_tell(mp)+write_offset; if (body->info.parent == NULL) { camel_mbox_summary_add(s, message); message = NULL; } } d(printf("end of content, removing decoders\n")); if (enc_id != -1) { camel_mime_parser_filter_remove(mp, enc_id); enc_id = -1; } if (chr_id != -1) { camel_mime_parser_filter_remove(mp, chr_id); chr_id = -1; } if (idx_id != -1) { camel_mime_parser_filter_remove(mp, idx_id); idx_id = -1; } break; } } /* did we actually write anything out? Then rename and be done with it. */ if (last_write>0) { eof = camel_mime_parser_tell(mp); camel_mbox_summary_copy_block(fd, fdout, last_write, eof-last_write); if (close(fdout) == -1) { perror("Could not close output file"); unlink(tmpname); } else { printf("renaming %s to %s\n", tmpname, s->folder_path); if (rename(tmpname, s->folder_path) == -1) { perror("Error renaming file"); unlink(tmpname); } } } else { /* no, then dont bother touching the inbox */ printf("No written changes to mbox, removing tmp file\n"); close(fdout); unlink(tmpname); } close(fd); if (mf64) gtk_object_unref((GtkObject *)mf64); if (mfqp) gtk_object_unref((GtkObject *)mfqp); if (mfc) gtk_object_unref((GtkObject *)mfc); if (mfi) gtk_object_unref((GtkObject *)mfi); /* force an index sync? */ if (s->index) { ibex_write(s->index); } gtk_object_unref((GtkObject *)mp); /* and finally ... update the summary sync info */ { struct stat st; if (stat(s->folder_path, &st) == 0) { s->time = st.st_mtime; s->size = st.st_size; } } g_free(tmpname); return 0; } CamelMboxSummary *camel_mbox_summary_new(const char *summary, const char *folder, ibex *index) { CamelMboxSummary *s; s = g_malloc0(sizeof(*s)); s->dirty = TRUE; s->folder_path = g_strdup(folder); s->summary_path = g_strdup(summary); /* FIXME: refcount index? */ s->index = index; s->messages = g_ptr_array_new(); s->message_uid = g_hash_table_new(g_str_hash, g_str_equal); /* always force an update */ s->time = 0; s->size = 0; s->nextuid = 1; /* TODO: force an initial load right now? */ return s; } void camel_mbox_summary_unref(CamelMboxSummary *s) { g_warning("Unimplemented function, mbox_summary_unref"); } /* check that the summary is uptodate, TRUE means it is uptodate */ int camel_mbox_summary_check(CamelMboxSummary *s) { struct stat st; /* no folder at all? */ if (stat(s->folder_path, &st) != 0) return FALSE; return (st.st_size == s->size) && (st.st_mtime == s->time); } static void camel_mbox_summary_add(CamelMboxSummary *s, CamelMboxMessageInfo *info) { if (info->info.uid == NULL) { info->info.uid = g_strdup_printf("%u", s->nextuid++); } if (g_hash_table_lookup(s->message_uid, info->info.uid)) { g_error("Trying to insert message with clashing uid's"); } d(printf("adding %s\n", info->info.uid)); g_ptr_array_add(s->messages, info); g_hash_table_insert(s->message_uid, info->info.uid, info); s->dirty = TRUE; } static int summary_header_read(FILE *fp, guint32 *version, time_t *time, size_t *size, guint32 *nextuid) { fseek(fp, 0, SEEK_SET); *version = decode_fixed_int(fp); *time = decode_fixed_int(fp); *size = decode_fixed_int(fp); *nextuid = decode_fixed_int(fp); return ferror(fp); } static void summary_clear(CamelMboxSummary *s) { int i; for (i=0;imessages->len;i++) { message_struct_free(g_ptr_array_index(s->messages, i)); } g_ptr_array_free(s->messages, TRUE); g_hash_table_destroy(s->message_uid); s->messages = g_ptr_array_new(); s->message_uid = g_hash_table_new(g_str_hash, g_str_equal); } int camel_mbox_summary_load(CamelMboxSummary *s) { struct stat st; FILE *fp; int i, total; CamelMboxMessageInfo *info; summary_clear(s); if ((fp = fopen(s->summary_path, "r")) == NULL) { g_warning("Loading non-existant summary, generating summary for folder: %s: %s", s->summary_path, strerror(errno)); index_folder(s, 0); camel_mbox_summary_save(s); } else { guint32 version, nextuid; time_t time; size_t size; if (stat(s->folder_path, &st) != 0) { g_warning("Uh, no folder anyway, aborting"); fclose(fp); return -1; } if (summary_header_read(fp, &version, &time, &size, &nextuid) != 0 || version != CAMEL_MBOX_SUMMARY_VERSION) { g_warning("Summary missing or version mismatch, reloading summary"); fclose(fp); index_folder(s, 0); camel_mbox_summary_save(s); return 0; } s->nextuid = MAX(s->nextuid, nextuid); s->time = time; s->size = size; total = decode_fixed_int(fp); if (time != st.st_mtime || size != st.st_size) { /* if its grown, then just index the new stuff, and load the rest from the summary */ if (size < st.st_size) { g_warning("Indexing/summarizing from start position: %d", size); d(printf("loading %d items from summary file\n", total)); for (i=0;idirty = FALSE; index_folder(s, size); /* if it adds any, it'll dirtify it */ camel_mbox_summary_save(s); } else { g_warning("Folder changed/smaller, reindexing everything"); index_folder(s, 0); camel_mbox_summary_save(s); fclose(fp); } return 0; } printf("loading %d items from summary file\n", total); for (i=0;idirty = FALSE; } return 0; } static int summary_header_write(FILE *fp, CamelMboxSummary *s) { fseek(fp, 0, SEEK_SET); encode_fixed_int(fp, CAMEL_MBOX_SUMMARY_VERSION); encode_fixed_int(fp, s->time); /* if we're dirty, then dont *really* save it ... */ if (s->dirty) encode_fixed_int(fp, 0); else encode_fixed_int(fp, s->size); encode_fixed_int(fp, s->nextuid); fflush(fp); return ferror(fp); } static int summary_header_save(CamelMboxSummary *s) { int fd; FILE *fp; fd = open(s->summary_path, O_WRONLY|O_CREAT, 0600); if (fd == -1) return -1; fp = fdopen(fd, "w"); if (fp == NULL) return -1; summary_header_write(fp, s); return fclose(fp); } int camel_mbox_summary_save(CamelMboxSummary *s) { int i, fd; FILE *fp; printf("saving summary? %s\n", s->summary_path); /* FIXME: error checking */ if (s->dirty) { printf("yes\n"); fd = open(s->summary_path, O_WRONLY|O_CREAT|O_TRUNC, 0600); if (fd == -1) return -1; fp = fdopen(fd, "w"); if (fp == NULL) return -1; s->dirty = FALSE; summary_header_write(fp, s); encode_fixed_int(fp, s->messages->len); printf("message count = %d\n", s->messages->len); for (i=0;imessages->len;i++) { message_struct_save(fp, g_ptr_array_index(s->messages, i)); } fclose(fp); } else { printf("no\n"); } return 0; } CamelMboxMessageInfo *camel_mbox_summary_uid(CamelMboxSummary *s, const char *uid) { return g_hash_table_lookup(s->message_uid, uid); } CamelMboxMessageInfo *camel_mbox_summary_index(CamelMboxSummary *s, int index) { return g_ptr_array_index(s->messages, index); } int camel_mbox_summary_message_count(CamelMboxSummary *s) { return s->messages->len; } guint32 camel_mbox_summary_next_uid(CamelMboxSummary *s) { guint32 uid = s->nextuid++; summary_header_save(s); return uid; } guint32 camel_mbox_summary_set_uid(CamelMboxSummary *s, guint32 uid) { if (s->nextuid < uid) { s->nextuid = uid; summary_header_save(s); } return s->nextuid; } #if 0 int main(int argc, char **argv) { if (argc<2) { printf("usage: %s mbox\n", argv[0]); return 1; } gtk_init(&argc, &argv); index_folder(argv[1]); return 0; } #endif