/*
 * isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
 * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
 * Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU 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 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 "isync.h"

#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>

#include <db.h>

static const char *subdirs[] = { "cur", "new", "tmp" };

static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };

static int
parse_info( const char *s )
{
	unsigned i;
	int flags;

	flags = 0;
	if (s && *(s + 1) == '2' && *(s + 2) == ',')
		for (s += 3, i = 0; i < as(Flags); i++)
			if (strchr( s, Flags[i] ))
				flags |= (1 << i);
	return flags;
}

typedef struct {
	int uid, flags;
} msg_t;

static int
compare_uids( const void *l, const void *r )
{
	return ((msg_t *)l)->uid - ((msg_t *)r)->uid;
}

static DBT key, value;
static struct flock lck;

void
convert( config_t *box )
{
	DIR *d;
	struct dirent *e;
	char *s, *p, *mboxdir;
	FILE *fp;
	msg_t *msgs;
	DB *db;
	int i, ret, fd, uidval, maxuid, bl, uid, rmsgs, nmsgs, uv[2];
	unsigned u;
	struct stat sb;
	char buf[_POSIX_PATH_MAX], diumname[_POSIX_PATH_MAX],
	     uvname[_POSIX_PATH_MAX], sname[_POSIX_PATH_MAX],
	     iuvname[_POSIX_PATH_MAX], imuname[_POSIX_PATH_MAX],
	     ilname[_POSIX_PATH_MAX], iumname[_POSIX_PATH_MAX];

	mboxdir = expand_strdup( box->path );
	nfsnprintf( iuvname, sizeof(iuvname), "%s/isyncuidvalidity", mboxdir );
	nfsnprintf( diumname, sizeof(iumname), "%s/.isyncuidmap.db", mboxdir );
	nfsnprintf( uvname, sizeof(uvname), "%s/.uidvalidity", mboxdir );
	if (stat( iuvname, &sb )) {
		if (!stat( diumname, &sb ))
			altmap++;
		else if (!stat( uvname, &sb ))
			altmap--;
	  err1:
		free( mboxdir );
		return;
	}
	for (i = 0; i < 3; i++) {
		nfsnprintf( buf, sizeof(buf), "%s/%s", mboxdir, subdirs[i] );
		if (stat( buf, &sb )) {
			fprintf( stderr, "ERROR: stat %s: %s (errno %d)\n", buf,
			         strerror(errno), errno );
			fprintf( stderr,
			         "ERROR: %s does not appear to be a valid maildir style mailbox\n",
			         mboxdir );
			goto err1;
		}
	}
	nfsnprintf( iumname, sizeof(iumname), "%s/isyncuidmap.db", mboxdir );
	nfsnprintf( imuname, sizeof(imuname), "%s/isyncmaxuid", mboxdir );
	nfsnprintf( ilname, sizeof(ilname), "%s/isynclock", mboxdir );
	nfsnprintf( sname, sizeof(sname), "%s/.mbsyncstate", mboxdir );

	if ((fd = open( ilname, O_WRONLY|O_CREAT, 0600 )) < 0) {
		perror( ilname );
		goto err1;
	}
#if SEEK_SET != 0
	lck.l_whence = SEEK_SET;
#endif
#if F_WRLCK != 0
	lck.l_type = F_WRLCK;
#endif
	if (fcntl( fd, F_SETLKW, &lck )) {
		perror( ilname );
	  err2:
		close( fd );
		goto err1;
	}

	if (!(fp = fopen( iuvname, "r" ))) {
		perror( iuvname );
		goto err2;
	}
	fscanf( fp, "%d", &uidval );
	fclose( fp );
	if (!(fp = fopen( imuname, "r" ))) {
		perror( imuname );
		goto err2;
	}
	fscanf( fp, "%d", &maxuid );
	fclose( fp );

	if (!stat( iumname, &sb )) {
		if (db_create( &db, 0, 0 )) {
			fputs( "dbcreate failed\n", stderr );
			goto err2;
		}
		if (db->open( db, 0, iumname, 0, DB_HASH, 0, 0 )) {
			fputs( "cannot open db\n", stderr );
			db->close( db, 0 );
			goto err2;
		}
		altmap++;
	} else {
		db = 0;
		altmap--;
	}

	msgs = 0;
	rmsgs = 0;
	nmsgs = 0;
	for (i = 0; i < 2; i++) {
		bl = nfsnprintf( buf, sizeof(buf), "%s/%s/", mboxdir, subdirs[i] );
		if (!(d = opendir( buf ))) {
			perror( "opendir" );
		  err4:
			if (msgs)
				free( msgs );
			if (db)
				db->close( db, 0 );
			goto err2;
		}
		while ((e = readdir( d ))) {
			if (*e->d_name == '.')
				continue;
			s = strchr( e->d_name, ':' );
			if (db) {
				key.data = e->d_name;
				key.size = s ? (size_t)(s - e->d_name) : strlen( e->d_name );
				if ((ret = db->get( db, 0, &key, &value, 0 ))) {
					if (ret != DB_NOTFOUND)
						db->err( db, ret, "Maildir error: db->get()" );
					continue;
				}
				uid = *(int *)value.data;
			} else if ((p = strstr( e->d_name, ",U=" )))
				uid = atoi( p + 3 );
			else
				continue;
			if (nmsgs == rmsgs) {
				rmsgs = rmsgs * 2 + 100;
				msgs = nfrealloc( msgs, rmsgs * sizeof(msg_t) );
			}
			msgs[nmsgs].uid = uid;
			msgs[nmsgs++].flags = parse_info( s );
		}
		closedir( d );
	}

	qsort( msgs, nmsgs, sizeof(msg_t), compare_uids );

	if (!(fp = fopen( sname, "w" ))) {
		perror( sname );
		goto err4;
	}
	if (box->max_messages) {
		if (!nmsgs)
			i = maxuid;
		else {
			i = nmsgs - box->max_messages;
			if (i < 0)
				i = 0;
			i = msgs[i].uid - 1;
		}
	} else
		i = 0;
	fprintf( fp, "%d:%d %d:%d:%d\n", uidval, maxuid, uidval, i, maxuid );
	for (i = 0; i < nmsgs; i++) {
		fprintf( fp, "%d %d ", msgs[i].uid, msgs[i].uid );
		for (u = 0; u < as(Flags); u++)
			if (msgs[i].flags & (1 << u))
				fputc( Flags[u], fp );
		fputc( '\n', fp );
	}
	fclose( fp );

	if (db) {
		key.data = (void *)"UIDVALIDITY";
		key.size = 11;
		uv[0] = uidval;
		uv[1] = maxuid;
		value.data = uv;
		value.size = sizeof(uv);
		if ((ret = db->put( db, 0, &key, &value, 0 ))) {
			db->err( db, ret, "Maildir error: db->put()" );
			goto err4;
		}
		db->close( db, 0 );
		rename( iumname, diumname );
	} else {
		if (!(fp = fopen( uvname, "w" ))) {
			perror( uvname );
			goto err4;
		}
		fprintf( fp, "%d\n%d\n", uidval, maxuid );
		fclose( fp );
	}

	unlink( iuvname );
	unlink( imuname );

	close( fd );
	unlink( ilname );

	if (msgs)
		free( msgs );
	free( mboxdir );
	return;
}


syntax highlighted by Code2HTML, v. 0.9.1