123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- /*
- * mbsync - 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * As a special exception, mbsync may be linked with the OpenSSL library,
- * despite that library's more restrictive license.
- */
- #include "isync.h"
- #include <unistd.h>
- #include <limits.h>
- #include <errno.h>
- #include <pwd.h>
- #include <sys/types.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- store_conf_t *stores;
- channel_conf_t *channels;
- group_conf_t *groups;
- int global_ops[2];
- char *global_sync_state;
- int
- parse_bool( conffile_t *cfile )
- {
- if (!strcasecmp( cfile->val, "yes" ) ||
- !strcasecmp( cfile->val, "true" ) ||
- !strcasecmp( cfile->val, "on" ) ||
- !strcmp( cfile->val, "1" ))
- return 1;
- if (strcasecmp( cfile->val, "no" ) &&
- strcasecmp( cfile->val, "false" ) &&
- strcasecmp( cfile->val, "off" ) &&
- strcmp( cfile->val, "0" ))
- fprintf( stderr, "%s:%d: invalid boolean value '%s'\n",
- cfile->file, cfile->line, cfile->val );
- return 0;
- }
- int
- parse_int( conffile_t *cfile )
- {
- char *p;
- int ret;
- ret = strtol( cfile->val, &p, 10 );
- if (*p) {
- fprintf( stderr, "%s:%d: invalid integer value '%s'\n",
- cfile->file, cfile->line, cfile->val );
- return 0;
- }
- return ret;
- }
- int
- parse_size( conffile_t *cfile )
- {
- char *p;
- int ret;
- ret = strtol (cfile->val, &p, 10);
- if (*p == 'k' || *p == 'K')
- ret *= 1024, p++;
- else if (*p == 'm' || *p == 'M')
- ret *= 1024 * 1024, p++;
- if (*p == 'b' || *p == 'B')
- p++;
- if (*p) {
- fprintf (stderr, "%s:%d: invalid size '%s'\n",
- cfile->file, cfile->line, cfile->val);
- return 0;
- }
- return ret;
- }
- static int
- getopt_helper( conffile_t *cfile, int *cops, int ops[], char **sync_state )
- {
- char *arg;
- if (!strcasecmp( "Sync", cfile->cmd )) {
- arg = cfile->val;
- do
- if (!strcasecmp( "Push", arg ))
- *cops |= XOP_PUSH;
- else if (!strcasecmp( "Pull", arg ))
- *cops |= XOP_PULL;
- else if (!strcasecmp( "ReNew", arg ))
- *cops |= OP_RENEW;
- else if (!strcasecmp( "New", arg ))
- *cops |= OP_NEW;
- else if (!strcasecmp( "Delete", arg ))
- *cops |= OP_DELETE;
- else if (!strcasecmp( "Flags", arg ))
- *cops |= OP_FLAGS;
- else if (!strcasecmp( "PullReNew", arg ))
- ops[S] |= OP_RENEW;
- else if (!strcasecmp( "PullNew", arg ))
- ops[S] |= OP_NEW;
- else if (!strcasecmp( "PullDelete", arg ))
- ops[S] |= OP_DELETE;
- else if (!strcasecmp( "PullFlags", arg ))
- ops[S] |= OP_FLAGS;
- else if (!strcasecmp( "PushReNew", arg ))
- ops[M] |= OP_RENEW;
- else if (!strcasecmp( "PushNew", arg ))
- ops[M] |= OP_NEW;
- else if (!strcasecmp( "PushDelete", arg ))
- ops[M] |= OP_DELETE;
- else if (!strcasecmp( "PushFlags", arg ))
- ops[M] |= OP_FLAGS;
- else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg ))
- *cops |= XOP_PULL|XOP_PUSH;
- else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg ))
- fprintf( stderr, "%s:%d: invalid Sync arg '%s'\n",
- cfile->file, cfile->line, arg );
- while ((arg = next_arg( &cfile->rest )));
- ops[M] |= XOP_HAVE_TYPE;
- } else if (!strcasecmp( "Expunge", cfile->cmd )) {
- arg = cfile->val;
- do
- if (!strcasecmp( "Both", arg ))
- *cops |= OP_EXPUNGE;
- else if (!strcasecmp( "Master", arg ))
- ops[M] |= OP_EXPUNGE;
- else if (!strcasecmp( "Slave", arg ))
- ops[S] |= OP_EXPUNGE;
- else if (strcasecmp( "None", arg ))
- fprintf( stderr, "%s:%d: invalid Expunge arg '%s'\n",
- cfile->file, cfile->line, arg );
- while ((arg = next_arg( &cfile->rest )));
- ops[M] |= XOP_HAVE_EXPUNGE;
- } else if (!strcasecmp( "Create", cfile->cmd )) {
- arg = cfile->val;
- do
- if (!strcasecmp( "Both", arg ))
- *cops |= OP_CREATE;
- else if (!strcasecmp( "Master", arg ))
- ops[M] |= OP_CREATE;
- else if (!strcasecmp( "Slave", arg ))
- ops[S] |= OP_CREATE;
- else if (strcasecmp( "None", arg ))
- fprintf( stderr, "%s:%d: invalid Create arg '%s'\n",
- cfile->file, cfile->line, arg );
- while ((arg = next_arg( &cfile->rest )));
- ops[M] |= XOP_HAVE_CREATE;
- } else if (!strcasecmp( "SyncState", cfile->cmd ))
- *sync_state = expand_strdup( cfile->val );
- else
- return 0;
- return 1;
- }
- int
- getcline( conffile_t *cfile )
- {
- char *p;
- while (fgets( cfile->buf, cfile->bufl, cfile->fp )) {
- cfile->line++;
- p = cfile->buf;
- if (!(cfile->cmd = next_arg( &p )))
- return 1;
- if (*cfile->cmd == '#')
- continue;
- if (!(cfile->val = next_arg( &p ))) {
- fprintf( stderr, "%s:%d: parameter missing\n",
- cfile->file, cfile->line );
- continue;
- }
- cfile->rest = p;
- return 1;
- }
- return 0;
- }
- /* XXX - this does not detect None conflicts ... */
- int
- merge_ops( int cops, int ops[] )
- {
- int aops;
- aops = ops[M] | ops[S];
- if (ops[M] & XOP_HAVE_TYPE) {
- if (aops & OP_MASK_TYPE) {
- if (aops & cops & OP_MASK_TYPE) {
- cfl:
- fprintf( stderr, "Conflicting Sync args specified.\n" );
- return 1;
- }
- ops[M] |= cops & OP_MASK_TYPE;
- ops[S] |= cops & OP_MASK_TYPE;
- if (cops & XOP_PULL) {
- if (ops[S] & OP_MASK_TYPE)
- goto cfl;
- ops[S] |= OP_MASK_TYPE;
- }
- if (cops & XOP_PUSH) {
- if (ops[M] & OP_MASK_TYPE)
- goto cfl;
- ops[M] |= OP_MASK_TYPE;
- }
- } else if (cops & (OP_MASK_TYPE|XOP_MASK_DIR)) {
- if (!(cops & OP_MASK_TYPE))
- cops |= OP_MASK_TYPE;
- else if (!(cops & XOP_MASK_DIR))
- cops |= XOP_PULL|XOP_PUSH;
- if (cops & XOP_PULL)
- ops[S] |= cops & OP_MASK_TYPE;
- if (cops & XOP_PUSH)
- ops[M] |= cops & OP_MASK_TYPE;
- }
- }
- if (ops[M] & XOP_HAVE_EXPUNGE) {
- if (aops & cops & OP_EXPUNGE) {
- fprintf( stderr, "Conflicting Expunge args specified.\n" );
- return 1;
- }
- ops[M] |= cops & OP_EXPUNGE;
- ops[S] |= cops & OP_EXPUNGE;
- }
- if (ops[M] & XOP_HAVE_CREATE) {
- if (aops & cops & OP_CREATE) {
- fprintf( stderr, "Conflicting Create args specified.\n" );
- return 1;
- }
- ops[M] |= cops & OP_CREATE;
- ops[S] |= cops & OP_CREATE;
- }
- return 0;
- }
- int
- load_config( const char *where, int pseudo )
- {
- conffile_t cfile;
- store_conf_t *store, **storeapp = &stores;
- channel_conf_t *channel, **channelapp = &channels;
- group_conf_t *group, **groupapp = &groups;
- string_list_t *chanlist, **chanlistapp;
- char *arg, *p;
- int err, len, cops, gcops, max_size, ms;
- char path[_POSIX_PATH_MAX];
- char buf[1024];
- if (!where) {
- nfsnprintf( path, sizeof(path), "%s/." EXE "rc", Home );
- cfile.file = path;
- } else
- cfile.file = where;
- if (!pseudo)
- info( "Reading configuration file %s\n", cfile.file );
- if (!(cfile.fp = fopen( cfile.file, "r" ))) {
- perror( "Cannot open config file" );
- return 1;
- }
- buf[sizeof(buf) - 1] = 0;
- cfile.buf = buf;
- cfile.bufl = sizeof(buf) - 1;
- cfile.line = 0;
- gcops = err = 0;
- reloop:
- while (getcline( &cfile )) {
- if (!cfile.cmd)
- continue;
- if (imap_driver.parse_store( &cfile, &store, &err ) ||
- maildir_driver.parse_store( &cfile, &store, &err ))
- {
- if (store) {
- if (!store->path)
- store->path = "";
- *storeapp = store;
- storeapp = &store->next;
- *storeapp = 0;
- }
- }
- else if (!strcasecmp( "Channel", cfile.cmd ))
- {
- channel = nfcalloc( sizeof(*channel) );
- channel->name = nfstrdup( cfile.val );
- cops = 0;
- max_size = -1;
- while (getcline( &cfile ) && cfile.cmd) {
- if (!strcasecmp( "MaxSize", cfile.cmd ))
- max_size = parse_size( &cfile );
- else if (!strcasecmp( "MaxMessages", cfile.cmd ))
- channel->max_messages = parse_int( &cfile );
- else if (!strcasecmp( "Pattern", cfile.cmd ) ||
- !strcasecmp( "Patterns", cfile.cmd ))
- {
- arg = cfile.val;
- do
- add_string_list( &channel->patterns, arg );
- while ((arg = next_arg( &cfile.rest )));
- }
- else if (!strcasecmp( "Master", cfile.cmd )) {
- ms = M;
- goto linkst;
- } else if (!strcasecmp( "Slave", cfile.cmd )) {
- ms = S;
- linkst:
- if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) {
- fprintf( stderr, "%s:%d: malformed mailbox spec\n",
- cfile.file, cfile.line );
- err = 1;
- continue;
- }
- *p = 0;
- for (store = stores; store; store = store->next)
- if (!strcmp( store->name, cfile.val + 1 )) {
- channel->stores[ms] = store;
- goto stpcom;
- }
- fprintf( stderr, "%s:%d: unknown store '%s'\n",
- cfile.file, cfile.line, cfile.val + 1 );
- err = 1;
- continue;
- stpcom:
- if (*++p)
- channel->boxes[ms] = nfstrdup( p );
- } else if (!getopt_helper( &cfile, &cops, channel->ops, &channel->sync_state )) {
- fprintf( stderr, "%s:%d: unknown keyword '%s'\n",
- cfile.file, cfile.line, cfile.cmd );
- err = 1;
- }
- }
- if (!channel->stores[M]) {
- fprintf( stderr, "channel '%s' refers to no master store\n", channel->name );
- err = 1;
- } else if (!channel->stores[S]) {
- fprintf( stderr, "channel '%s' refers to no slave store\n", channel->name );
- err = 1;
- } else if (merge_ops( cops, channel->ops ))
- err = 1;
- else {
- if (max_size >= 0)
- channel->stores[M]->max_size = channel->stores[S]->max_size = max_size;
- *channelapp = channel;
- channelapp = &channel->next;
- }
- }
- else if (!strcasecmp( "Group", cfile.cmd ))
- {
- group = nfmalloc( sizeof(*group) );
- group->name = nfstrdup( cfile.val );
- *groupapp = group;
- groupapp = &group->next;
- *groupapp = 0;
- chanlistapp = &group->channels;
- *chanlistapp = 0;
- p = cfile.rest;
- while ((arg = next_arg( &p ))) {
- addone:
- len = strlen( arg );
- chanlist = nfmalloc( sizeof(*chanlist) + len );
- memcpy( chanlist->string, arg, len + 1 );
- *chanlistapp = chanlist;
- chanlistapp = &chanlist->next;
- *chanlistapp = 0;
- }
- while (getcline( &cfile )) {
- if (!cfile.cmd)
- goto reloop;
- if (!strcasecmp( "Channel", cfile.cmd ) ||
- !strcasecmp( "Channels", cfile.cmd ))
- {
- p = cfile.rest;
- arg = cfile.val;
- goto addone;
- }
- else
- {
- fprintf( stderr, "%s:%d: unknown keyword '%s'\n",
- cfile.file, cfile.line, cfile.cmd );
- err = 1;
- }
- }
- break;
- }
- else if (!getopt_helper( &cfile, &gcops, global_ops, &global_sync_state ))
- {
- fprintf( stderr, "%s:%d: unknown section keyword '%s'\n",
- cfile.file, cfile.line, cfile.cmd );
- err = 1;
- while (getcline( &cfile ))
- if (!cfile.cmd)
- goto reloop;
- break;
- }
- }
- fclose (cfile.fp);
- err |= merge_ops( gcops, global_ops );
- if (!global_sync_state)
- global_sync_state = expand_strdup( "~/." EXE "/" );
- if (!err && pseudo)
- unlink( where );
- return err;
- }
- void
- parse_generic_store( store_conf_t *store, conffile_t *cfg, int *err )
- {
- if (!strcasecmp( "Trash", cfg->cmd ))
- store->trash = nfstrdup( cfg->val );
- else if (!strcasecmp( "TrashRemoteNew", cfg->cmd ))
- store->trash_remote_new = parse_bool( cfg );
- else if (!strcasecmp( "TrashNewOnly", cfg->cmd ))
- store->trash_only_new = parse_bool( cfg );
- else if (!strcasecmp( "MaxSize", cfg->cmd ))
- store->max_size = parse_size( cfg );
- else if (!strcasecmp( "MapInbox", cfg->cmd ))
- store->map_inbox = nfstrdup( cfg->val );
- else {
- fprintf( stderr, "%s:%d: unknown keyword '%s'\n",
- cfg->file, cfg->line, cfg->cmd );
- *err = 1;
- }
- }
|