/* $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);
}
}