/* $Id: c2c.c,v 1.59 2006/03/15 10:21:01 mad Exp $ */ /* * Copyright (c) 2003, Alexander Marx * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * "This product includes software developed by Computing Services * at * Carnegie Mellon University (http://www.cmu.edu/computing/)." * * (see http://asg.web.cmu.edu/cyrus/imapd/license.html for more info) * */ #include "c2c.h" /* ------------------------------------------------------------------------ */ char *cyrus_root=NULL, *courier_root=NULL, *mailbox=NULL, *cyrus_quota_dir=NULL, *cyrus_subscribe_dir=NULL, *cyrus_seen_dir=NULL; short verbose=0, dovecot=0, hashed_subscribe=0, hashed_quota=0, hashed_seen=0; int sflag=0, qflag=0, xflag=0, eflag=0; struct t_seenfile *seen_file=NULL; /* ------------------------------------------------------------------------ */ int main(int argc, char **argv) { int ch; #ifdef HAVE_GETOPT_LONG static struct option longopts[] = { { "verbose", no_argument, 0, 'v'}, { "help", no_argument, 0, 'h'}, { "quota-dir", required_argument, 0, 'q'}, { "subscribe-dir", required_argument, 0, 's'}, { "seen-dir", required_argument, 0, 'e'}, { "hashed", required_argument, 0, 'x'}, { "dovecot", no_argument, 0, 'd'}, { "Version", no_argument, 0, 'V'}, { 0, 0, 0, 0 } }; while((ch=getopt_long(argc, argv, COMMAND_PARAMETERS, longopts, NULL))!=-1) { #else while((ch=getopt(argc, argv, COMMAND_PARAMETERS))!=-1) { #endif switch(ch) { case 'V': fprintf(stdout, "cyrus2courier - Version %s\n", C2C_VERSION); exit(C2C_EXIT_SUCCESS); break; case 'v': verbose++; break; case 'd': dovecot=1; break; case 'q': if(verify_path(optarg)) { /* XXX: convert relative path to absolute */ cyrus_quota_dir = optarg; qflag=1; } else bail_out(C2C_EXIT_FAIL, "main", "Unable to open Quota-Directory. (%s)", optarg); break; case 's': if(verify_path(optarg)) { /* XXX: convert relative path to absolute */ cyrus_subscribe_dir = optarg; sflag = 1; } else bail_out(C2C_EXIT_FAIL, "main", "Unable to open Subscribe-Directory. (%s)", optarg); break; case 'e': if(verify_path(optarg)) { /* XXX: convert relative path to absolute */ cyrus_seen_dir = optarg; eflag = 1; } else bail_out(C2C_EXIT_FAIL, "main", "Unable to open Seen-Directory. (%s)", optarg); break; case 'x': xflag=1; if(strchr(optarg, 'q')) hashed_quota=1; if(strchr(optarg, 's')) hashed_subscribe=1; if(strchr(optarg, 'e')) hashed_seen=1; break; case '?': case 'h': default: usage(); } } argc -= optind; argv += optind; /* verify arguments */ if(argc==3) { if((qflag && sflag) && (strncmp(cyrus_quota_dir, cyrus_subscribe_dir, MAXBUF)==0)) { bail_out(C2C_EXIT_FAIL, "main", "Quota-Directory and Subscribe-Directory must differ."); } else if (qflag && cyrus_quota_dir[0]!='/') { bail_out(C2C_EXIT_FAIL, "main", "Need absolute Quota-Directoy path."); } else if (sflag && cyrus_subscribe_dir[0]!='/') { bail_out(C2C_EXIT_FAIL, "main", "Need absolute Subscribe-Directoy path."); } else if (!verify_path(argv[argc-3])) { bail_out(C2C_EXIT_FAIL, "main", "Invalid path for Cyrus-Root-Directory."); } else if (!verify_path(argv[argc-2])) { bail_out(C2C_EXIT_FAIL, "main", "Invalid path for Courier-Root-Directory."); } else if (strncmp(argv[argc-3], argv[argc-2], MAXBUF)==0) { bail_out(C2C_EXIT_FAIL, "main", "Cyrus-Root-Directory and Courier-Root-Directory must differ."); } else if (strchr(argv[argc-1], '/')!= NULL) { bail_out(C2C_EXIT_FAIL, "main", "Invalid Mailbox-Name."); } else if ((strncmp(argv[argc-1], "..", MAXBUF)==0) || (strncmp(argv[argc-1], ".", MAXBUF)==0)) { bail_out(C2C_EXIT_FAIL, "main", "Invalid Mailbox-Name."); } else if (argv[argc-2][0]!='/' || argv[argc-3][0]!='/') { bail_out(C2C_EXIT_FAIL, "main", "Need absolute Root-Directory paths."); } else if (xflag && (!hashed_subscribe && !hashed_quota && !hashed_seen)) { bail_out(C2C_EXIT_FAIL, "main", "option 'hashed' needs 'q' and/or 's' and/or 'e' as parameter."); } else { cyrus_root=argv[argc-3]; courier_root=argv[argc-2]; mailbox=argv[argc-1]; chdir(cyrus_root); traverse(cyrus_root, courier_root, mailbox); if(cyrus_seen_dir && seen_file) { flush_seenfile(seen_file); } } } else usage(); if(verbose) verbose_print(C2C_DONE, __FUNCTION__, NULL); return C2C_EXIT_SUCCESS; } /* ------------------------------------------------------------------------ */ void usage(void) { /* usage() is wrong for all platforms without getopt_long */ fprintf(stdout, "\nUsage: cyrus2courier [options] cyrusdir courierdir mailbox\n"); fprintf(stdout, "\n -V, --Version .............. print version information"); fprintf(stdout, "\n -v, --verbose .............. be verbose / repeat for more verbosity"); fprintf(stdout, "\n -q, --quota-dir= ...... cyrus quota-files directory"); fprintf(stdout, "\n -s, --subscribe-dir= .. cyrus subscribe-files directory"); fprintf(stdout, "\n -e, --seen-dir= ....... cyrus seen-files directory"); fprintf(stdout, "\n -x, --hashed=[s][q][e]...... cyrus hashed (s)ubscribe/(q)uota/s(e)en dirs"); fprintf(stdout, "\n -d, --dovecot .............. write Dovecot-compatible files\n\n"); exit(C2C_EXIT_SUCCESS); } void bail_out(int rc, char *where, char *msg, ...) { va_list args; va_start(args, msg); switch(rc) { case C2C_EXIT_SUCCESS: fprintf(stdout, "DONE\t%s\n", mailbox); break; case C2C_EXIT_WARN: fprintf(stdout, "WARN\t %s", mailbox); fprintf(stdout, "\t\t(%s) ", where); vfprintf(stdout, msg, args); break; case C2C_EXIT_FAIL: default: fprintf(stdout, "FAIL\t %s", mailbox); fprintf(stdout, "\t\t(%s) ", where); vfprintf(stdout, msg, args); break; } va_end(args); fprintf(stderr, "\n"); exit(rc); } void verbose_print(int type, const char *where, const char *format, ...) { va_list args; char *stype; static int last=-1; switch(type) { case C2C_EXIT_SUCCESS: stype="DONE"; break; case C2C_EXIT_WARN: stype="WARN"; break; case C2C_EXIT_INFO: stype="INFO"; break; case C2C_EXIT_FAIL: default: stype="FAIL"; break; } va_start(args, format); if(type!=last) { fprintf(stdout, "%s\t %s\n", stype, mailbox); last=type; } if(format) { fprintf(stdout, "\t(%s) ", where); vfprintf(stdout, format, args); fprintf(stdout, "\n"); } va_end(args); } int verify_path(const char *mypath) { DIR *dirp=NULL; int rc=0; if(dirp=opendir(mypath)) { closedir(dirp); rc=1; } return rc; } int traverse(char *cy_root, char *co_root, char *dir) { static char spc[MAXBUF] = ""; static char spd[MAXBUF] = ""; static char spe[MAXBUF] = ""; static int is_rootfolder=1; DIR *dirp; struct dirent *entry; struct stat buf; int filetype; char name[MAXNAME]; char *cc=NULL; eSTR *s; int rc=0; if(is_rootfolder) { if(!verify_path(dir)) { if(verbose) { verbose_print(C2C_FAIL, __FUNCTION__, "%s is not a mailbox.", dir); } exit(C2C_EXIT_FAIL); } if(cyrus_seen_dir) { eSTR *sf; if(!hashed_seen) { sf=va_newSTR(cyrus_seen_dir, "/", mailbox, ".seen", eSTR_END); } else { char h[4]; h[0]='/'; h[1]=mailbox[0]; h[2]='/'; h[3]='\0'; sf=va_newSTR(cyrus_seen_dir, h, mailbox, ".seen", eSTR_END); } seen_file=read_seenfile(strSTR(sf), C2C_SEENTYPE_DB); if(!seen_file) { eflag=-1; if(verbose) verbose_print(C2C_WARN, __FUNCTION__, "%s.seen file not found.", mailbox); } freeSTR(sf); } } filetype=((stat(dir, &buf) ? 0 : (buf.st_mode&S_IFMT)==S_IFDIR)) ? 'd' : 'f'; if (filetype == 'd') { /* dirp points to a directory */ /* building path infos */ if(strlen(spc)!=0) { strcat(spc, "/"); } strcat(spc, dir); /* use . to separate sub-mailboxes */ if(strlen(spd)!=0) { strcat(spd, "."); } strcat(spd, dir); /* append / to root mailbox */ if (!strcmp(spd, dir)) { strcat(spd, "/"); } spe[0]='\0'; strcat(spe, cy_root); strcat(spe, "/"); strcat(spe, spc); s=va_newSTR(co_root, "/", spd, eSTR_END); /* create courier directory structure */ courier_mkdir(co_root, spd, is_rootfolder); /* migrate quota-information */ if(cyrus_quota_dir) { courier_quota(co_root, spd, is_rootfolder); } /* migrate folder-subscription info */ if(cyrus_subscribe_dir) { courier_subscribe(co_root, spd, is_rootfolder); } /* copy/migrate the mailfiles */ cydump(spe, strSTR(s),is_rootfolder); if(is_rootfolder) { is_rootfolder=0; } freeSTR(s); /* traverse directory tree */ if(chdir(dir)==0) { if(dirp=opendir(".")) { while(entry=readdir(dirp)) { strcpy(name, entry->d_name); if(strcmp(name, ".") && strcmp(name, "..")) traverse(cy_root, co_root, name); } closedir(dirp); } if(cc=strrchr(spc, '/')) *cc='\0'; if(cc=strrchr(spd, '.')) *cc='\0'; chdir(".."); } } return rc; } int courier_quota(char *root, char *dir, int is_root) { eSTR *s, *q, *t; long quota=-1, qused=-1; FILE *fi, *fo; /* quotafiles only exist in the mailbox-rootfolder */ if(!is_root) return 1; s=va_newSTR(root, "/", dir, "/maildirsize", eSTR_END); q=va_newSTR(cyrus_quota_dir, "/", eSTR_END); if(hashed_quota) { t=newSTR(mailbox); t->str[1]='\0'; va_catSTR(q, strSTR(t), "/", eSTR_END); freeSTR(t); } va_catSTR(q, "user.", mailbox, eSTR_END); /* ** read "$(BYTES USED)\n$(kBYTES MAX)" ** write "$(BYTES MAX)S\n$(BYTES USED)" ** ** XXX: count-quotas are not supported */ if(fi=fopen(strSTR(q), "r")) { if(t=getlineSTR(fi)) { qused=atol(strSTR(t)); freeSTR(t); } if(t=getlineSTR(fi)) { quota=atol(strSTR(t))*1024; freeSTR(t); } if(fo=xfopen(strSTR(s), "w")) { char buf[14]; /* courier likes padded quota-files */ sprintf(buf, "%ldS", quota); fprintf(fo, "%-13s\n", buf); sprintf(buf, "%ld", qused); fprintf(fo, "%-13s\n", buf); fclose(fo); } fclose(fi); } else { if(verbose) verbose_print(C2C_WARN, __FUNCTION__, "cyrus quota-file missing"); } freeSTR(s); return 1; } int courier_subscribe(char *root, char *dir, int is_root) { eSTR *s, *q, *t; int len; FILE *fi, *fo; /* subscribefiles only exist in mailbox-rootfolder */ if(!is_root) return 1; s=va_newSTR(root, "/", dir, dovecot ? "/subscriptions" : "/courierimapsubscribed", eSTR_END); q=va_newSTR(cyrus_subscribe_dir, "/", eSTR_END); if(hashed_subscribe) { t=newSTR(mailbox); t->str[1]='\0'; va_catSTR(q, strSTR(t), "/", eSTR_END); freeSTR(t); } va_catSTR(q, mailbox, ".sub", eSTR_END); len=strlen("user.")+strlen(mailbox)+1; /* ** readline "user.$(MAILBOXNAME.folder.folder..." ** writeline "INBOX.folder.folder..." */ if(fi=fopen(strSTR(q), "r")) { if(fo=xfopen(strSTR(s), "w")) { while(t=getlineSTR(fi)) { if(lenSTR(t)>0) { char *p=strSTR(t); if(strlen(&p[len])!=0) { char *e=strrchr(&p[len], '\t'); if(e) *e='\0'; if(dovecot) fprintf(fo, "%s\n", &p[len]); else fprintf(fo, "INBOX.%s\n", &p[len]); } } freeSTR(t); } fclose(fo); } fclose(fi); } else { if(verbose>1) verbose_print(C2C_WARN, __FUNCTION__, "cyrus-subscribe file missing"); } freeSTR(s); freeSTR(q); return 1; } int courier_mkdir(char *root, char *dir, int is_root) { eSTR *s, *c, *n, *t, *f; s=va_newSTR(root, "/", dir, eSTR_END); c=va_newSTR(strSTR(s), "/cur", eSTR_END); n=va_newSTR(strSTR(s), "/new", eSTR_END); t=va_newSTR(strSTR(s), "/tmp", eSTR_END); f=va_newSTR(strSTR(s), "/maildirfolder", eSTR_END); /* create the necessary folders */ mkdir(strSTR(s), S_IRWXU); mkdir(strSTR(c), S_IRWXU); mkdir(strSTR(n), S_IRWXU); mkdir(strSTR(t), S_IRWXU); /* foreach subfolder we need to touch a "maildirfolder" * tells an MTA how to find the correct root-folder for * quota calculations during deliveries */ if(!is_root) { fclose(xfopen(strSTR(f), "w")); } freeSTR(s); freeSTR(c); freeSTR(n); freeSTR(t); freeSTR(f); return 1; } int courier_cpmail(char *src, unsigned long size, char *dst_a, char *dst_b, unsigned long *rsize) { eSTR *m, *d; char is[32]; struct stat s; struct timeval tv[2]; int i; /* read the mailfile */ m=infileSTR(src); /* strip away the carriage returns */ i=tounixSTR(m); *rsize=size-i; sprintf(is, "%lu", size-i); /* build filename */ d=va_newSTR(dst_a, is, dst_b, eSTR_END); /* write the stripped file */ outfileSTR(m, d->str); /* fix file modification time (=imap received time) */ stat(src, &s); tv[0].tv_sec=s.st_atime; tv[1].tv_sec=s.st_mtime; utimes(d->str, tv); freeSTR(m); freeSTR(d); return i; } int cydump(char *path, char *dest, int is_root) { FILE *fh, *fi, *fuid=NULL, *fuidpop3=NULL, *fkeywords=NULL; eSTR *s, *i, *r, *h; int cs_len=0; char src[MAXBUF]=""; char dst_a[MAXBUF]=""; char dst_b[MAXBUF]=""; char *flagname[26]; char *name; int flag = 0; char header_data[MAXBUF]=""; char *header_base; size_t header_len; char *eol; char *p; char flagletter[2]=""; struct index_header idx_header; struct index_entry idx_entry; unsigned long version, rsize; r=newSTR(path); if(verbose>2) verbose_print(C2C_INFO, __FUNCTION__, "converting %s", strSTR(r)); h=va_newSTR(strSTR(r), "/cyrus.header", eSTR_END); i=va_newSTR(strSTR(r), "/cyrus.index", eSTR_END); s=va_newSTR(strSTR(r), "/cyrus.seen", eSTR_END); if(fi = fopen(strSTR(i), "r")) { /* minor_version in header tells us which cyrus-version this is */ memset(&idx_header, 0, sizeof(idx_header)); fread(&idx_header, 3*4, 1, fi); version = ntohl(idx_header.minor_version); fseek(fi, 0, SEEK_SET); switch (version) { case 2: fread(&idx_header, 3*4 + 8*4, 1, fi); break; case 3: fread(&idx_header, 3*4 + 8*4 + 3*4, 1, fi); break; case 4: case 6: fread(&idx_header, 3*4 + 8*4 + 3*4 + 5*4, 1, fi); break; default: bail_out(C2C_EXIT_FAIL, "cydump", "Header version %lu not supported", version); } /* only good for dovecot */ sprintf(dst_a, "%s/dovecot-keywords", dest); fkeywords = xfopen(dst_a, "w"); if (fh = fopen(strSTR(h), "r")) { if(verbose>4) verbose_print(C2C_INFO, __FUNCTION__, "opened header %s", strSTR(h)); /* don't include trailing \0 */ fseek(fh, sizeof(MAILBOX_HEADER_MAGIC) - 1, SEEK_SET); header_len = fread(header_data, sizeof(char), sizeof(header_data), fh); if(verbose>4) verbose_print(C2C_INFO, __FUNCTION__, "header_len is %d", header_len); header_base = header_data; eol = memchr(header_base, '\n', header_len); /* end of the quota and uniqueid */ p = eol + 1; eol = memchr(p, '\n', header_len - (p - header_base)); /* end of the keywords */ name = p; if(verbose>4) verbose_print(C2C_INFO, __FUNCTION__, "first keyword at %lu", name - header_base); while (name <= eol && flag < 26) { p = memchr(name, ' ', eol - name); if (!p) p = eol; if (name != p) { *p = '\0'; flagname[flag] = strdup(name); fprintf(fkeywords, "%i %s\n", flag, name); if(verbose>4) verbose_print(C2C_INFO, __FUNCTION__, "found and saved keyword/flag %s", name); } else { flagname[flag] = NULL; } flag++; name = p + 1; } fclose(fh); } /* do this even if we don't load any flags above */ while (flag < 26) flagname[flag++] = NULL; sprintf(dst_a, "%s/%s", dest, dovecot ? "dovecot-uidlist" : "courierimapuiddb"); fuid = xfopen(dst_a, "w"); fprintf(fuid, "1 %lu %lu\n", (unsigned long)ntohl(idx_header.uidvalidity), (unsigned long)ntohl(idx_header.last_uid)+1); /* WARNING this will generate a version v1 pop3dsizelistefile * as used by courier-imap 2.0.0 - later versions will use v2 * which is currently NOT supported! */ if (!dovecot && (is_root)) { sprintf(dst_a, "%s/%s", dest, "courierpop3dsizelist"); fuidpop3=xfopen(dst_a, "w"); /* it looks like that we want the NEXT uid here so just add 1 */ fprintf(fuidpop3, "/1 %lu\n", (unsigned long)ntohl(idx_header.last_uid)+1); } /* analyze cyrus.seen file */ if(cyrus_seen_dir) { eSTR *ns=dupSTR(r); unsigned int cl=strlen(cyrus_root); char *nns=strSTR(ns)+cl; for(cl=0; cl keep all as unseen */ if(verbose>1) verbose_print(C2C_WARN, __FUNCTION__, "cyrus.seen file missing; keeping all mails as unseen.\n\t\t--> %s", strSTR(s)); } setfolder_seenfile(seen_file, NULL, 0); } /* foreach entry copy/migrate a mailfile */ while(fread(&idx_entry, sizeof(struct index_entry), 1, fi)) { sprintf(dst_a, "%s/cur/%lu.%lu,S=", dest, (unsigned long)ntohl(idx_entry.internaldate), (unsigned long)ntohl(idx_entry.uid)); /* maildir-flags must be in ascii sort order! */ dst_b[0]='\0'; strcat(dst_b, ":2,"); if((ntohl(idx_entry.system_flags)&FLAG_DRAFT)) strcat(dst_b, "D"); if((ntohl(idx_entry.system_flags)&FLAG_FLAGGED)) strcat(dst_b, "F"); if((ntohl(idx_entry.system_flags)&FLAG_ANSWERED)) strcat(dst_b, "R"); if(cs_len!=-1) { if(isseen_seenfile(seen_file, (unsigned long)(ntohl(idx_entry.uid)))) { strcat(dst_b, "S"); } } if((ntohl(idx_entry.system_flags)&FLAG_DELETED)) strcat(dst_b, "T"); for (flag = 0; flag < 26; flag++) { if (!flagname[flag]) continue; /* i only support 26 flags so only look in the first part */ if (ntohl(idx_entry.user_flags[0]) & (1 << flag)) { if(verbose>6) verbose_print(C2C_INFO, __FUNCTION__, "marking message %lu with keyword/flag %s (%d)", (unsigned long)ntohl(idx_entry.uid), flagname[flag], flag); /* 97 == a */ flagletter[0] = 97 + flag; strcat(dst_b, flagletter); } } sprintf(src, "%s/%lu.", path, (unsigned long)ntohl(idx_entry.uid)); /* copy/migrate the mailfile */ courier_cpmail(src, (unsigned long)ntohl(idx_entry.size), dst_a, dst_b, &rsize); /* build courierpop3dsizelistfile */ if (!dovecot && (is_root)) { fprintf(fuidpop3,"%s%lu%s %lu %lu\n", strrchr(dst_a, '/')+1, rsize, dst_b, (unsigned long)ntohl(idx_entry.size), (unsigned long)ntohl(idx_entry.uid)); } /* filenames in uiddb-files are w/o flags, dunno why. */ fprintf(fuid, "%lu %s%lu\n", (unsigned long)ntohl(idx_entry.uid), strrchr(dst_a, '/')+1, rsize); } if(!cyrus_seen_dir) { /* release seen_file resources */ flush_seenfile(seen_file); seen_file=NULL; } for (flag = 0; flag < 26; flag++) if (flagname[flag]) free(flagname[flag]); fclose(fi); fclose(fuid); if (!dovecot && (is_root)) fclose(fuidpop3); } else { /* no index file?! .. */ /* checking whether folder contains mails or not */ DIR *dirp; struct dirent *dp; int is_empty=1; if(dirp=opendir(strSTR(r))) { while ((dp=readdir(dirp))) { if(dp->d_name[strlen(dp->d_name)-1] == '.' && dp->d_name[0] != '.') { /* found some mail-files! */ is_empty=0; } } closedir(dirp); } if(is_empty) { if(verbose>1) { verbose_print(C2C_WARN, __FUNCTION__, "cyrus.index file missing; folder contains no mails; skipping\n\t\t-->%s", strSTR(r)); } } else if (verbose) { verbose_print(C2C_WARN, __FUNCTION__, "cyrus.index file missing; folder contains files(possibly mails!); FIX MANUALLY\n\t\t==>%s", strSTR(r)); } } freeSTR(s); freeSTR(i); freeSTR(r); freeSTR(h); return 0; } FILE *xfopen(const char *_p, const char *_m) { FILE *fh=NULL; if(_p && _m) { if(fh=fopen(_p, _m)) return fh; } bail_out(C2C_EXIT_FAIL, "xfopen", "Unable to open file. (%s w/ mode %s)", _p, _m); /* will never be reached - just to keep gcc happy */ return fh; } struct t_seenfile *read_seenfile(const char *_file, const int _type) { FILE *fh=NULL; struct t_seenfile *sf; eSTR *line; char *tok; int i=0; if(_file) { if(!(fh=fopen(_file, "r"))) return NULL; } if(sf=malloc(sizeof(struct t_seenfile))) { sf->type=_type; i=-1; sf->len=-1; sf->cline=-1; while(line=getlineSTR(fh)) { if(lenSTR(line)) i++; else { freeSTR(line); continue; } if(_type==C2C_SEENTYPE_DB) { /* parse seen files */ /* uniqueid fuid timestamp luid timestamp %d:%d,%d:%d,... */ tok=tokSTR(line, " \t"); /* uniqueid */ sf->seen[i].id=newSTR(tok); tok=tokSTR(NULL, " \t"); /* fuid */ tok=tokSTR(NULL, " \t"); /* timestamp */ tok=tokSTR(NULL, " \t"); /* luid */ tok=tokSTR(NULL, " \t"); /* timestamp */ tok=tokSTR(NULL, " \t"); /* timestamp */ if(tok) { sf->seen[i].seen=newSTR(tok); } else sf->seen[i].seen=newSTR(""); } else { /* parse seen files */ /* mailbox timestamp lastuid timestamp %d:%d,%d:%d,... */ tok=tokSTR(line, " \t"); /* skip mailbox */ sf->seen[i].id=newSTR(tok); tok=tokSTR(NULL, " \t"); /* skip timestamp */ tok=tokSTR(NULL, " \t"); /* skip lastuid */ tok=tokSTR(NULL, " \t"); /* skip timestamp */ tok=tokSTR(NULL, " \t"); /* seen */ if(tok) { sf->seen[i].seen=newSTR(tok); } else sf->seen[i].seen=newSTR(""); } freeSTR(line); } if(i>=0) { sf->len=i+1; } fclose(fh); } else { fclose(fh); return NULL; } return sf; } int setfolder_seenfile(struct t_seenfile *_sf, const char *_mailbox, const unsigned long _uidvalidity) { eSTR *mbox=va_newSTR("user.", _mailbox, eSTR_END); char uniqueid[8+8+1]; int i, idx; eSTR *tuplestr, *tup, *astr, *bstr; char *tok; if(!_sf) { freeSTR(mbox); return 0; } if(_sf->type==C2C_SEENTYPE_DB) { /* calc uniqueid */ mailbox_make_uniqueid(strSTR(mbox), _uidvalidity, uniqueid); if(verbose>4) verbose_print(C2C_INFO, __FUNCTION__, "mailbox uniqueid is: %s", uniqueid); /* find seenline */ _sf->cline=-1; for(i=0; i<_sf->len; i++) { if(strcmp(uniqueid, strSTR(_sf->seen[i].id))==0) { _sf->cline=i; } } } else _sf->cline=0; if(_sf->cline>=0 && _sf->len!=-1) { /* loads the seen tuplestr seencache array */ if(tuplestr=dupSTR(_sf->seen[_sf->cline].seen)) { stripSTR(tuplestr, eSTR_ALL, " \t"); tok=tokSTR(tuplestr, ","); /* initialize tok w/ tuplestr */ i=0; while(tok) { /* sanity check */ assert(icache[i].left=atol(astr->str); _sf->cache[i].right=atol(bstr->str); i++; freeSTR(astr); freeSTR(bstr); freeSTR(tup); tok=tokSTR(NULL, ","); /* next tok from tuplestr */ } freeSTR(tuplestr); _sf->clen=i; } else { _sf->clen=-1; } } freeSTR(mbox); return _sf->clen; } int isseen_seenfile(struct t_seenfile *_sf, unsigned long _uid) { int i; if(!_sf) return 0; if(_sf->cline==-1) return 0; for(i=0; i<_sf->clen; i++) { if(_uid>=_sf->cache[i].left && _uid <=_sf->cache[i].right) return 1; } return 0; } void flush_seenfile(struct t_seenfile *_sf) { int i; if(_sf) { for(i=0; i<_sf->len; i++) { freeSTR(_sf->seen[i].id); freeSTR(_sf->seen[i].seen); } free(_sf); } }