123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- /*
- * mdconvert - Maildir UID scheme converter
- * Copyright (C) 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, see <http://www.gnu.org/licenses/>.
- */
- #include <autodefs.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/stat.h>
- #include <stdarg.h>
- #include <dirent.h>
- #include <limits.h>
- #include <errno.h>
- #include <string.h>
- #include <ctype.h>
- #include <db.h>
- #define EXE "mdconvert"
- #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
- # define ATTR_NORETURN __attribute__((noreturn))
- # define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var)))
- #else
- # define ATTR_NORETURN
- # define ATTR_PRINTFLIKE(fmt,var)
- #endif
- static void ATTR_NORETURN
- oob( void )
- {
- fputs( "Fatal: buffer too small. Please report a bug.\n", stderr );
- abort();
- }
- static void ATTR_PRINTFLIKE(1, 2)
- sys_error( const char *msg, ... )
- {
- va_list va;
- char buf[1024];
- va_start( va, msg );
- if ((unsigned)vsnprintf( buf, sizeof(buf), msg, va ) >= sizeof(buf))
- oob();
- va_end( va );
- perror( buf );
- }
- static int ATTR_PRINTFLIKE(3, 4)
- nfsnprintf( char *buf, int blen, const char *fmt, ... )
- {
- int ret;
- va_list va;
- va_start( va, fmt );
- if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
- oob();
- va_end( va );
- return ret;
- }
- static const char *subdirs[] = { "cur", "new" };
- static struct flock lck;
- static DBT key, value;
- static int
- convert( const char *box, int altmap )
- {
- DB *db;
- DIR *d;
- struct dirent *e;
- const char *u, *ru;
- char *p, *s, *dpath, *spath, *dbpath;
- int i, n, ret, sfd, dfd, bl, ml, uv[2], uid;
- struct stat st;
- char buf[_POSIX_PATH_MAX], buf2[_POSIX_PATH_MAX];
- char umpath[_POSIX_PATH_MAX], uvpath[_POSIX_PATH_MAX], tdpath[_POSIX_PATH_MAX];
- if (stat( box, &st ) || !S_ISDIR(st.st_mode)) {
- fprintf( stderr, "'%s' is no Maildir mailbox.\n", box );
- return 1;
- }
- nfsnprintf( umpath, sizeof(umpath), "%s/.isyncuidmap.db", box );
- nfsnprintf( uvpath, sizeof(uvpath), "%s/.uidvalidity", box );
- if (altmap)
- dpath = umpath, spath = uvpath, dbpath = tdpath;
- else
- spath = umpath, dpath = uvpath, dbpath = umpath;
- nfsnprintf( tdpath, sizeof(tdpath), "%s.tmp", dpath );
- if ((sfd = open( spath, O_RDWR )) < 0) {
- if (errno != ENOENT)
- sys_error( "Cannot open %s", spath );
- return 1;
- }
- if (fcntl( sfd, F_SETLKW, &lck )) {
- sys_error( "Cannot lock %s", spath );
- goto sbork;
- }
- if ((dfd = open( tdpath, O_RDWR|O_CREAT, 0600 )) < 0) {
- sys_error( "Cannot create %s", tdpath );
- goto sbork;
- }
- if (db_create( &db, 0, 0 )) {
- fputs( "Error: db_create() failed\n", stderr );
- goto tbork;
- }
- if ((ret = (db->open)( db, 0, dbpath, 0, DB_HASH, altmap ? DB_CREATE|DB_TRUNCATE : 0, 0 ))) {
- db->err( db, ret, "Error: db->open(%s)", dbpath );
- dbork:
- db->close( db, 0 );
- tbork:
- unlink( tdpath );
- close( dfd );
- sbork:
- close( sfd );
- return 1;
- }
- key.data = (void *)"UIDVALIDITY";
- key.size = 11;
- if (altmap) {
- if ((n = read( sfd, buf, sizeof(buf) )) <= 0 ||
- (buf[n] = 0, sscanf( buf, "%d\n%d", &uv[0], &uv[1] ) != 2))
- {
- fprintf( stderr, "Error: cannot read UIDVALIDITY of '%s'.\n", box );
- goto dbork;
- }
- value.data = uv;
- value.size = sizeof(uv);
- if ((ret = db->put( db, 0, &key, &value, 0 ))) {
- db->err( db, ret, "Error: cannot write UIDVALIDITY for '%s'", box );
- goto dbork;
- }
- } else {
- if ((ret = db->get( db, 0, &key, &value, 0 ))) {
- db->err( db, ret, "Error: cannot read UIDVALIDITY of '%s'", box );
- goto dbork;
- }
- n = sprintf( buf, "%d\n%d\n", ((int *)value.data)[0], ((int *)value.data)[1] );
- if (write( dfd, buf, n ) != n) {
- fprintf( stderr, "Error: cannot write UIDVALIDITY for '%s'.\n", box );
- goto dbork;
- }
- }
- again:
- for (i = 0; i < 2; i++) {
- bl = nfsnprintf( buf, sizeof(buf), "%s/%s/", box, subdirs[i] );
- if (!(d = opendir( buf ))) {
- sys_error( "Cannot list %s", buf );
- goto dbork;
- }
- while ((e = readdir( d ))) {
- if (*e->d_name == '.')
- continue;
- nfsnprintf( buf + bl, sizeof(buf) - bl, "%s", e->d_name );
- memcpy( buf2, buf, bl );
- p = strstr( e->d_name, ",U=" );
- if (p)
- for (u = p, ru = p + 3; isdigit( (unsigned char)*ru ); ru++);
- else
- u = ru = strchr( e->d_name, ':' );
- if (u)
- ml = u - e->d_name;
- else
- ru = "", ml = sizeof(buf);
- if (altmap) {
- if (!p)
- continue;
- key.data = e->d_name;
- key.size = (size_t)(strchr( e->d_name, ',' ) - e->d_name);
- uid = atoi( p + 3 );
- value.data = &uid;
- value.size = sizeof(uid);
- if ((ret = db->put( db, 0, &key, &value, 0 ))) {
- db->err( db, ret, "Error: cannot write UID for '%s'", box );
- goto ebork;
- }
- nfsnprintf( buf2 + bl, sizeof(buf2) - bl, "%.*s%s", ml, e->d_name, ru );
- } else {
- s = strpbrk( e->d_name, ",:" );
- 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, "Error: cannot read UID for '%s'", box );
- goto ebork;
- }
- continue;
- }
- uid = *(int *)value.data;
- nfsnprintf( buf2 + bl, sizeof(buf2) - bl, "%.*s,U=%d%s", ml, e->d_name, uid, ru );
- }
- if (rename( buf, buf2 )) {
- if (errno == ENOENT)
- goto again;
- sys_error( "Cannot rename %s to %s", buf, buf2 );
- ebork:
- closedir( d );
- goto dbork;
- }
- }
- closedir( d );
- }
- db->close( db, 0 );
- close( dfd );
- if (rename( tdpath, dpath )) {
- sys_error( "Cannot rename %s to %s", tdpath, dpath );
- return 1;
- }
- if (unlink( spath ))
- sys_error( "Cannot remove %s", spath );
- close( sfd );
- return 0;
- }
- int
- main( int argc, char **argv )
- {
- int oint, ret, altmap = 0;
- for (oint = 1; oint < argc; oint++) {
- if (!strcmp( argv[oint], "-h" ) || !strcmp( argv[oint], "--help" )) {
- puts(
- "Usage: " EXE " [-a] mailbox...\n"
- " -a, --alt convert to alternative (DB based) UID scheme\n"
- " -n, --native convert to native (file name based) UID scheme (default)\n"
- " -h, --help show this help message\n"
- " -v, --version display version"
- );
- return 0;
- } else if (!strcmp( argv[oint], "-v" ) || !strcmp( argv[oint], "--version" )) {
- puts( EXE " " VERSION " - Maildir UID scheme converter" );
- return 0;
- } else if (!strcmp( argv[oint], "-a" ) || !strcmp( argv[oint], "--alt" )) {
- altmap = 1;
- } else if (!strcmp( argv[oint], "-n" ) || !strcmp( argv[oint], "--native" )) {
- altmap = 0;
- } else if (!strcmp( argv[oint], "--" )) {
- oint++;
- break;
- } else if (argv[oint][0] == '-') {
- fprintf( stderr, "Unrecognized option '%s'. Try " EXE " -h\n", argv[oint] );
- return 1;
- } else
- break;
- }
- if (oint == argc) {
- fprintf( stderr, "Mailbox specification missing. Try " EXE " -h\n" );
- return 1;
- }
- #if SEEK_SET != 0
- lck.l_whence = SEEK_SET;
- #endif
- #if F_WRLCK != 0
- lck.l_type = F_WRLCK;
- #endif
- ret = 0;
- for (; oint < argc; oint++)
- ret |= convert( argv[oint], altmap );
- return ret;
- }
|