2004-03-27 16:07:20 +00:00
|
|
|
/*
|
|
|
|
* mbsync - mailbox synchronizer
|
2002-12-28 15:31:20 +00:00
|
|
|
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
2011-04-10 17:25:46 +00:00
|
|
|
* Copyright (C) 2002-2006,2011 Oswald Buddenhagen <ossi@users.sf.net>
|
2001-01-11 10:13:47 +00:00
|
|
|
*
|
|
|
|
* 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
|
2011-04-10 17:34:36 +00:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2005-12-18 13:41:50 +00:00
|
|
|
*
|
2004-03-27 16:07:20 +00:00
|
|
|
* As a special exception, mbsync may be linked with the OpenSSL library,
|
2002-10-30 02:31:20 +00:00
|
|
|
* despite that library's more restrictive license.
|
2001-01-11 10:13:47 +00:00
|
|
|
*/
|
|
|
|
|
2013-12-08 19:46:40 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "sync.h"
|
2003-05-07 00:06:37 +00:00
|
|
|
|
2012-09-15 09:24:57 +00:00
|
|
|
#include <assert.h>
|
2001-01-11 10:13:47 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <sys/types.h>
|
2012-09-15 09:24:57 +00:00
|
|
|
#include <ctype.h>
|
2001-01-11 10:13:47 +00:00
|
|
|
#include <string.h>
|
2001-02-28 01:02:50 +00:00
|
|
|
#include <stdlib.h>
|
2001-11-09 00:23:50 +00:00
|
|
|
#include <stdio.h>
|
2001-01-11 10:13:47 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
store_conf_t *stores;
|
2001-01-11 10:13:47 +00:00
|
|
|
|
2014-07-12 18:35:55 +00:00
|
|
|
char *
|
2012-09-15 09:24:57 +00:00
|
|
|
get_arg( conffile_t *cfile, int required, int *comment )
|
|
|
|
{
|
|
|
|
char *ret, *p, *t;
|
2013-09-25 18:56:52 +00:00
|
|
|
int escaped, quoted;
|
2012-09-15 09:24:57 +00:00
|
|
|
char c;
|
|
|
|
|
|
|
|
p = cfile->rest;
|
|
|
|
assert( p );
|
2014-12-07 12:19:30 +00:00
|
|
|
while ((c = *p) && isspace( (uchar)c ))
|
2012-09-15 09:24:57 +00:00
|
|
|
p++;
|
|
|
|
if (!c || c == '#') {
|
|
|
|
if (comment)
|
|
|
|
*comment = (c == '#');
|
2012-09-15 09:49:24 +00:00
|
|
|
if (required) {
|
2012-09-15 09:24:57 +00:00
|
|
|
error( "%s:%d: parameter missing\n", cfile->file, cfile->line );
|
2012-09-15 09:49:24 +00:00
|
|
|
cfile->err = 1;
|
|
|
|
}
|
2012-09-15 09:24:57 +00:00
|
|
|
ret = 0;
|
|
|
|
} else {
|
2013-09-25 18:56:52 +00:00
|
|
|
for (escaped = 0, quoted = 0, ret = t = p; c; c = *p) {
|
2012-09-15 09:24:57 +00:00
|
|
|
p++;
|
2013-09-25 18:56:52 +00:00
|
|
|
if (escaped && c >= 32) {
|
|
|
|
escaped = 0;
|
|
|
|
*t++ = c;
|
|
|
|
} else if (c == '\\')
|
|
|
|
escaped = 1;
|
|
|
|
else if (c == '"')
|
2012-09-15 09:24:57 +00:00
|
|
|
quoted ^= 1;
|
2014-12-07 12:19:30 +00:00
|
|
|
else if (!quoted && isspace( (uchar)c ))
|
2012-09-15 09:24:57 +00:00
|
|
|
break;
|
|
|
|
else
|
|
|
|
*t++ = c;
|
|
|
|
}
|
|
|
|
*t = 0;
|
2013-09-25 18:56:52 +00:00
|
|
|
if (escaped) {
|
|
|
|
error( "%s:%d: unterminated escape sequence\n", cfile->file, cfile->line );
|
|
|
|
cfile->err = 1;
|
|
|
|
ret = 0;
|
|
|
|
}
|
2012-09-15 09:24:57 +00:00
|
|
|
if (quoted) {
|
|
|
|
error( "%s:%d: missing closing quote\n", cfile->file, cfile->line );
|
2012-09-15 09:49:24 +00:00
|
|
|
cfile->err = 1;
|
2012-09-15 09:24:57 +00:00
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cfile->rest = p;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
int
|
|
|
|
parse_bool( conffile_t *cfile )
|
2001-01-11 10:13:47 +00:00
|
|
|
{
|
2004-03-27 16:07:20 +00:00
|
|
|
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" ) &&
|
2012-09-15 09:49:24 +00:00
|
|
|
strcmp( cfile->val, "0" )) {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "%s:%d: invalid boolean value '%s'\n",
|
|
|
|
cfile->file, cfile->line, cfile->val );
|
2012-09-15 09:49:24 +00:00
|
|
|
cfile->err = 1;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
return 0;
|
2001-01-11 10:13:47 +00:00
|
|
|
}
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
int
|
|
|
|
parse_int( conffile_t *cfile )
|
2001-11-14 17:40:10 +00:00
|
|
|
{
|
2004-03-27 16:07:20 +00:00
|
|
|
char *p;
|
|
|
|
int ret;
|
2005-12-18 13:41:50 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
ret = strtol( cfile->val, &p, 10 );
|
|
|
|
if (*p) {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "%s:%d: invalid integer value '%s'\n",
|
|
|
|
cfile->file, cfile->line, cfile->val );
|
2012-09-15 09:49:24 +00:00
|
|
|
cfile->err = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return ret;
|
2001-11-14 17:40:10 +00:00
|
|
|
}
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
int
|
|
|
|
parse_size( conffile_t *cfile )
|
2001-01-16 19:45:08 +00:00
|
|
|
{
|
2004-03-27 16:07:20 +00:00
|
|
|
char *p;
|
|
|
|
int ret;
|
2001-01-16 19:45:08 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
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')
|
2001-11-13 00:36:44 +00:00
|
|
|
p++;
|
2004-03-27 16:07:20 +00:00
|
|
|
if (*p) {
|
|
|
|
fprintf (stderr, "%s:%d: invalid size '%s'\n",
|
|
|
|
cfile->file, cfile->line, cfile->val);
|
2012-09-15 09:49:24 +00:00
|
|
|
cfile->err = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
return 0;
|
2001-01-16 19:45:08 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
return ret;
|
2001-01-16 19:45:08 +00:00
|
|
|
}
|
|
|
|
|
2014-12-14 16:03:16 +00:00
|
|
|
static const struct {
|
|
|
|
int op;
|
|
|
|
const char *name;
|
|
|
|
} boxOps[] = {
|
|
|
|
{ OP_EXPUNGE, "Expunge" },
|
|
|
|
{ OP_CREATE, "Create" },
|
2014-12-29 01:08:48 +00:00
|
|
|
{ OP_REMOVE, "Remove" },
|
2014-12-14 16:03:16 +00:00
|
|
|
};
|
|
|
|
|
2005-12-18 13:41:50 +00:00
|
|
|
static int
|
2013-11-24 18:32:42 +00:00
|
|
|
getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
|
Bunch 'o patches from Oswald Buddenhagen:
i implemented some cool stuff (tm).
first, the long missing "create server-side missing mailboxes". -C now
creates both local and remote boxes; -L and -R create only local/remote.
second, i implemented a 1:1 remote:local folder mapping (-1) with an
optional INBOX exception (inbox/-I). the remote folder is specified with
the folder keyword (or -F switch) and takes precedence over the
namespace setting. the local directory with the mailboxes can now be
specified on the command line, too (-M).
another patch:
- made the -1 switch settable permanently (OneToOne). after all, you
usually define your mailbox layout once forever. removed -A, as it is
semantically -a modified by -1.
- cleaned up message output a bit. still, the quiet variable should be
used throughout the program. at best, create some generic output
function, which obeys a global verbosity level variable.
- optimized + cleaned up configuration parser slightly
- minor cleanups
add an (almost) unique id to every uploaded message and search for it
right after. i thought about using the message-id, but a) it is not
guaranteed to be unique in a mailbox (imagine you edit a mail and store
the dupe in the same box) and b) some mails (e.g., postponed) don't even
have one. a downside of the current implementation is, that this
id-header remains in the mailbox, but given that it wastes only 27 bytes
per mail and removing it would mean several roundtrips more, this seems
acceptable.
i changed the line-counting loop to use a mmapped file instead of
reading it in chunks, as it makes things simpler and is probably even
faster for big mails.
the amount of goto statements in my code may be scary, but c is simply
lacking a multi-level break statement. :)
this is the "shut up" patch. :) it makes the -q option consequent, so to
say.
additionally it adds an -l option which gathers all defined/found
mailboxes and just outputs the list. don't ask what i need it for. ;)
2002-10-30 02:23:05 +00:00
|
|
|
{
|
2004-03-27 16:07:20 +00:00
|
|
|
char *arg;
|
2014-12-14 16:03:16 +00:00
|
|
|
uint i;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
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 ))
|
2013-11-24 18:32:42 +00:00
|
|
|
conf->ops[S] |= OP_RENEW;
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "PullNew", arg ))
|
2013-11-24 18:32:42 +00:00
|
|
|
conf->ops[S] |= OP_NEW;
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "PullDelete", arg ))
|
2013-11-24 18:32:42 +00:00
|
|
|
conf->ops[S] |= OP_DELETE;
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "PullFlags", arg ))
|
2013-11-24 18:32:42 +00:00
|
|
|
conf->ops[S] |= OP_FLAGS;
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "PushReNew", arg ))
|
2013-11-24 18:32:42 +00:00
|
|
|
conf->ops[M] |= OP_RENEW;
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "PushNew", arg ))
|
2013-11-24 18:32:42 +00:00
|
|
|
conf->ops[M] |= OP_NEW;
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "PushDelete", arg ))
|
2013-11-24 18:32:42 +00:00
|
|
|
conf->ops[M] |= OP_DELETE;
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "PushFlags", arg ))
|
2013-11-24 18:32:42 +00:00
|
|
|
conf->ops[M] |= OP_FLAGS;
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "All", arg ) || !strcasecmp( "Full", arg ))
|
|
|
|
*cops |= XOP_PULL|XOP_PUSH;
|
2012-09-15 09:49:24 +00:00
|
|
|
else if (strcasecmp( "None", arg ) && strcasecmp( "Noop", arg )) {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "%s:%d: invalid Sync arg '%s'\n",
|
|
|
|
cfile->file, cfile->line, arg );
|
2012-09-15 09:49:24 +00:00
|
|
|
cfile->err = 1;
|
|
|
|
}
|
2012-09-15 09:24:57 +00:00
|
|
|
while ((arg = get_arg( cfile, ARG_OPTIONAL, 0 )));
|
2013-11-24 18:32:42 +00:00
|
|
|
conf->ops[M] |= XOP_HAVE_TYPE;
|
2004-03-27 16:07:20 +00:00
|
|
|
} else if (!strcasecmp( "SyncState", cfile->cmd ))
|
2013-11-24 18:32:42 +00:00
|
|
|
conf->sync_state = expand_strdup( cfile->val );
|
2013-11-24 18:39:33 +00:00
|
|
|
else if (!strcasecmp( "CopyArrivalDate", cfile->cmd ))
|
|
|
|
conf->use_internal_date = parse_bool( cfile );
|
|
|
|
else if (!strcasecmp( "MaxMessages", cfile->cmd ))
|
|
|
|
conf->max_messages = parse_int( cfile );
|
2013-11-24 19:26:33 +00:00
|
|
|
else if (!strcasecmp( "ExpireUnread", cfile->cmd ))
|
|
|
|
conf->expire_unread = parse_bool( cfile );
|
2014-12-14 16:03:16 +00:00
|
|
|
else {
|
|
|
|
for (i = 0; i < as(boxOps); i++) {
|
|
|
|
if (!strcasecmp( boxOps[i].name, cfile->cmd )) {
|
|
|
|
int op = boxOps[i].op;
|
|
|
|
arg = cfile->val;
|
|
|
|
do {
|
|
|
|
if (!strcasecmp( "Both", arg )) {
|
|
|
|
*cops |= op;
|
|
|
|
} else if (!strcasecmp( "Master", arg )) {
|
|
|
|
conf->ops[M] |= op;
|
|
|
|
} else if (!strcasecmp( "Slave", arg )) {
|
|
|
|
conf->ops[S] |= op;
|
|
|
|
} else if (strcasecmp( "None", arg )) {
|
|
|
|
error( "%s:%d: invalid %s arg '%s'\n",
|
|
|
|
cfile->file, cfile->line, boxOps[i].name, arg );
|
|
|
|
cfile->err = 1;
|
|
|
|
}
|
|
|
|
} while ((arg = get_arg( cfile, ARG_OPTIONAL, 0 )));
|
|
|
|
conf->ops[M] |= op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
return 0;
|
2014-12-14 16:03:16 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
return 1;
|
Bunch 'o patches from Oswald Buddenhagen:
i implemented some cool stuff (tm).
first, the long missing "create server-side missing mailboxes". -C now
creates both local and remote boxes; -L and -R create only local/remote.
second, i implemented a 1:1 remote:local folder mapping (-1) with an
optional INBOX exception (inbox/-I). the remote folder is specified with
the folder keyword (or -F switch) and takes precedence over the
namespace setting. the local directory with the mailboxes can now be
specified on the command line, too (-M).
another patch:
- made the -1 switch settable permanently (OneToOne). after all, you
usually define your mailbox layout once forever. removed -A, as it is
semantically -a modified by -1.
- cleaned up message output a bit. still, the quiet variable should be
used throughout the program. at best, create some generic output
function, which obeys a global verbosity level variable.
- optimized + cleaned up configuration parser slightly
- minor cleanups
add an (almost) unique id to every uploaded message and search for it
right after. i thought about using the message-id, but a) it is not
guaranteed to be unique in a mailbox (imagine you edit a mail and store
the dupe in the same box) and b) some mails (e.g., postponed) don't even
have one. a downside of the current implementation is, that this
id-header remains in the mailbox, but given that it wastes only 27 bytes
per mail and removing it would mean several roundtrips more, this seems
acceptable.
i changed the line-counting loop to use a mmapped file instead of
reading it in chunks, as it makes things simpler and is probably even
faster for big mails.
the amount of goto statements in my code may be scary, but c is simply
lacking a multi-level break statement. :)
this is the "shut up" patch. :) it makes the -q option consequent, so to
say.
additionally it adds an -l option which gathers all defined/found
mailboxes and just outputs the list. don't ask what i need it for. ;)
2002-10-30 02:23:05 +00:00
|
|
|
}
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
int
|
|
|
|
getcline( conffile_t *cfile )
|
2001-01-11 10:13:47 +00:00
|
|
|
{
|
2015-02-15 11:48:46 +00:00
|
|
|
char *arg;
|
2012-09-15 09:24:57 +00:00
|
|
|
int comment;
|
2001-01-11 10:13:47 +00:00
|
|
|
|
2015-02-15 11:48:46 +00:00
|
|
|
if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, 0 ))) {
|
|
|
|
error( "%s:%d: excess token '%s'\n", cfile->file, cfile->line, arg );
|
|
|
|
cfile->err = 1;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
while (fgets( cfile->buf, cfile->bufl, cfile->fp )) {
|
|
|
|
cfile->line++;
|
2012-09-15 09:24:57 +00:00
|
|
|
cfile->rest = cfile->buf;
|
|
|
|
if (!(cfile->cmd = get_arg( cfile, ARG_OPTIONAL, &comment ))) {
|
|
|
|
if (comment)
|
|
|
|
continue;
|
2004-03-27 16:07:20 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2012-09-15 09:24:57 +00:00
|
|
|
if (!(cfile->val = get_arg( cfile, ARG_REQUIRED, 0 )))
|
|
|
|
continue;
|
2004-03-27 16:07:20 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2001-02-14 20:46:41 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
/* XXX - this does not detect None conflicts ... */
|
|
|
|
int
|
2005-12-28 10:02:22 +00:00
|
|
|
merge_ops( int cops, int ops[] )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2014-12-14 16:03:16 +00:00
|
|
|
int aops, op;
|
|
|
|
uint i;
|
2001-01-11 10:13:47 +00:00
|
|
|
|
2005-12-28 10:02:22 +00:00
|
|
|
aops = ops[M] | ops[S];
|
|
|
|
if (ops[M] & XOP_HAVE_TYPE) {
|
2004-03-27 16:07:20 +00:00
|
|
|
if (aops & OP_MASK_TYPE) {
|
|
|
|
if (aops & cops & OP_MASK_TYPE) {
|
|
|
|
cfl:
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "Conflicting Sync args specified.\n" );
|
2004-03-27 16:07:20 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2005-12-28 10:02:22 +00:00
|
|
|
ops[M] |= cops & OP_MASK_TYPE;
|
|
|
|
ops[S] |= cops & OP_MASK_TYPE;
|
2004-03-27 16:07:20 +00:00
|
|
|
if (cops & XOP_PULL) {
|
2005-12-28 10:02:22 +00:00
|
|
|
if (ops[S] & OP_MASK_TYPE)
|
2004-03-27 16:07:20 +00:00
|
|
|
goto cfl;
|
2005-12-28 10:02:22 +00:00
|
|
|
ops[S] |= OP_MASK_TYPE;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
if (cops & XOP_PUSH) {
|
2005-12-28 10:02:22 +00:00
|
|
|
if (ops[M] & OP_MASK_TYPE)
|
2004-03-27 16:07:20 +00:00
|
|
|
goto cfl;
|
2005-12-28 10:02:22 +00:00
|
|
|
ops[M] |= OP_MASK_TYPE;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
} 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)
|
2005-12-28 10:02:22 +00:00
|
|
|
ops[S] |= cops & OP_MASK_TYPE;
|
2004-03-27 16:07:20 +00:00
|
|
|
if (cops & XOP_PUSH)
|
2005-12-28 10:02:22 +00:00
|
|
|
ops[M] |= cops & OP_MASK_TYPE;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2001-01-16 19:45:08 +00:00
|
|
|
}
|
2014-12-14 16:03:16 +00:00
|
|
|
for (i = 0; i < as(boxOps); i++) {
|
|
|
|
op = boxOps[i].op;
|
|
|
|
if (ops[M] & (op * (XOP_HAVE_EXPUNGE / OP_EXPUNGE))) {
|
|
|
|
if (aops & cops & op) {
|
|
|
|
error( "Conflicting %s args specified.\n", boxOps[i].name );
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
ops[M] |= cops & op;
|
|
|
|
ops[S] |= cops & op;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
Bunch 'o patches from Oswald Buddenhagen:
i implemented some cool stuff (tm).
first, the long missing "create server-side missing mailboxes". -C now
creates both local and remote boxes; -L and -R create only local/remote.
second, i implemented a 1:1 remote:local folder mapping (-1) with an
optional INBOX exception (inbox/-I). the remote folder is specified with
the folder keyword (or -F switch) and takes precedence over the
namespace setting. the local directory with the mailboxes can now be
specified on the command line, too (-M).
another patch:
- made the -1 switch settable permanently (OneToOne). after all, you
usually define your mailbox layout once forever. removed -A, as it is
semantically -a modified by -1.
- cleaned up message output a bit. still, the quiet variable should be
used throughout the program. at best, create some generic output
function, which obeys a global verbosity level variable.
- optimized + cleaned up configuration parser slightly
- minor cleanups
add an (almost) unique id to every uploaded message and search for it
right after. i thought about using the message-id, but a) it is not
guaranteed to be unique in a mailbox (imagine you edit a mail and store
the dupe in the same box) and b) some mails (e.g., postponed) don't even
have one. a downside of the current implementation is, that this
id-header remains in the mailbox, but given that it wastes only 27 bytes
per mail and removing it would mean several roundtrips more, this seems
acceptable.
i changed the line-counting loop to use a mmapped file instead of
reading it in chunks, as it makes things simpler and is probably even
faster for big mails.
the amount of goto statements in my code may be scary, but c is simply
lacking a multi-level break statement. :)
this is the "shut up" patch. :) it makes the -q option consequent, so to
say.
additionally it adds an -l option which gathers all defined/found
mailboxes and just outputs the list. don't ask what i need it for. ;)
2002-10-30 02:23:05 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
return 0;
|
2001-01-11 10:13:47 +00:00
|
|
|
}
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
int
|
|
|
|
load_config( const char *where, int pseudo )
|
2001-01-11 10:13:47 +00:00
|
|
|
{
|
2004-03-27 16:07:20 +00:00
|
|
|
conffile_t cfile;
|
2005-12-28 10:02:22 +00:00
|
|
|
store_conf_t *store, **storeapp = &stores;
|
2004-03-27 16:07:20 +00:00
|
|
|
channel_conf_t *channel, **channelapp = &channels;
|
|
|
|
group_conf_t *group, **groupapp = &groups;
|
|
|
|
string_list_t *chanlist, **chanlistapp;
|
2005-12-28 10:02:22 +00:00
|
|
|
char *arg, *p;
|
2012-09-15 09:46:42 +00:00
|
|
|
int len, cops, gcops, max_size, ms, i;
|
2004-03-27 16:07:20 +00:00
|
|
|
char path[_POSIX_PATH_MAX];
|
|
|
|
char buf[1024];
|
2001-01-11 10:13:47 +00:00
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
if (!where) {
|
2014-04-12 14:06:33 +00:00
|
|
|
assert( !pseudo );
|
2004-03-27 16:07:20 +00:00
|
|
|
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" ))) {
|
2011-04-10 13:32:25 +00:00
|
|
|
sys_error( "Cannot open config file '%s'", cfile.file );
|
2004-03-27 16:07:20 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
buf[sizeof(buf) - 1] = 0;
|
|
|
|
cfile.buf = buf;
|
|
|
|
cfile.bufl = sizeof(buf) - 1;
|
|
|
|
cfile.line = 0;
|
2012-09-15 09:46:42 +00:00
|
|
|
cfile.err = 0;
|
2015-02-15 11:48:46 +00:00
|
|
|
cfile.rest = 0;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2012-09-15 09:46:42 +00:00
|
|
|
gcops = 0;
|
2013-11-24 19:26:33 +00:00
|
|
|
global_conf.expire_unread = -1;
|
2004-03-27 16:07:20 +00:00
|
|
|
reloop:
|
|
|
|
while (getcline( &cfile )) {
|
|
|
|
if (!cfile.cmd)
|
|
|
|
continue;
|
2006-03-20 18:36:49 +00:00
|
|
|
for (i = 0; i < N_DRIVERS; i++)
|
2012-09-15 09:46:42 +00:00
|
|
|
if (drivers[i]->parse_store( &cfile, &store )) {
|
2006-03-20 18:36:49 +00:00
|
|
|
if (store) {
|
2013-05-11 08:12:33 +00:00
|
|
|
if (!store->max_size)
|
|
|
|
store->max_size = INT_MAX;
|
2006-03-20 18:36:49 +00:00
|
|
|
*storeapp = store;
|
|
|
|
storeapp = &store->next;
|
|
|
|
*storeapp = 0;
|
|
|
|
}
|
|
|
|
goto reloop;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2006-03-20 18:36:49 +00:00
|
|
|
if (!strcasecmp( "Channel", cfile.cmd ))
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
channel = nfcalloc( sizeof(*channel) );
|
|
|
|
channel->name = nfstrdup( cfile.val );
|
2013-11-24 18:39:33 +00:00
|
|
|
channel->max_messages = global_conf.max_messages;
|
2013-11-24 19:26:33 +00:00
|
|
|
channel->expire_unread = global_conf.expire_unread;
|
2013-11-24 18:39:33 +00:00
|
|
|
channel->use_internal_date = global_conf.use_internal_date;
|
2004-03-27 16:07:20 +00:00
|
|
|
cops = 0;
|
|
|
|
max_size = -1;
|
|
|
|
while (getcline( &cfile ) && cfile.cmd) {
|
|
|
|
if (!strcasecmp( "MaxSize", cfile.cmd ))
|
|
|
|
max_size = parse_size( &cfile );
|
|
|
|
else if (!strcasecmp( "Pattern", cfile.cmd ) ||
|
|
|
|
!strcasecmp( "Patterns", cfile.cmd ))
|
|
|
|
{
|
|
|
|
arg = cfile.val;
|
|
|
|
do
|
|
|
|
add_string_list( &channel->patterns, arg );
|
2012-09-15 09:24:57 +00:00
|
|
|
while ((arg = get_arg( &cfile, ARG_OPTIONAL, 0 )));
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
else if (!strcasecmp( "Master", cfile.cmd )) {
|
2005-12-28 10:02:22 +00:00
|
|
|
ms = M;
|
2004-03-27 16:07:20 +00:00
|
|
|
goto linkst;
|
|
|
|
} else if (!strcasecmp( "Slave", cfile.cmd )) {
|
2005-12-28 10:02:22 +00:00
|
|
|
ms = S;
|
2004-03-27 16:07:20 +00:00
|
|
|
linkst:
|
|
|
|
if (*cfile.val != ':' || !(p = strchr( cfile.val + 1, ':' ))) {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "%s:%d: malformed mailbox spec\n",
|
|
|
|
cfile.file, cfile.line );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfile.err = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*p = 0;
|
|
|
|
for (store = stores; store; store = store->next)
|
|
|
|
if (!strcmp( store->name, cfile.val + 1 )) {
|
2005-12-28 10:02:22 +00:00
|
|
|
channel->stores[ms] = store;
|
2004-03-27 16:07:20 +00:00
|
|
|
goto stpcom;
|
|
|
|
}
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "%s:%d: unknown store '%s'\n",
|
|
|
|
cfile.file, cfile.line, cfile.val + 1 );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfile.err = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
continue;
|
|
|
|
stpcom:
|
|
|
|
if (*++p)
|
2005-12-28 10:02:22 +00:00
|
|
|
channel->boxes[ms] = nfstrdup( p );
|
2013-11-24 18:32:42 +00:00
|
|
|
} else if (!getopt_helper( &cfile, &cops, channel )) {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "%s:%d: unknown keyword '%s'\n", cfile.file, cfile.line, cfile.cmd );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfile.err = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
2005-12-28 10:02:22 +00:00
|
|
|
if (!channel->stores[M]) {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "channel '%s' refers to no master store\n", channel->name );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfile.err = 1;
|
2005-12-28 10:02:22 +00:00
|
|
|
} else if (!channel->stores[S]) {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "channel '%s' refers to no slave store\n", channel->name );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfile.err = 1;
|
2005-12-28 10:02:22 +00:00
|
|
|
} else if (merge_ops( cops, channel->ops ))
|
2012-09-15 09:46:42 +00:00
|
|
|
cfile.err = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
else {
|
2014-03-19 09:09:20 +00:00
|
|
|
if (max_size >= 0) {
|
|
|
|
if (!max_size)
|
|
|
|
max_size = INT_MAX;
|
2005-12-28 10:02:22 +00:00
|
|
|
channel->stores[M]->max_size = channel->stores[S]->max_size = max_size;
|
2014-03-19 09:09:20 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
*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;
|
2012-09-15 09:24:57 +00:00
|
|
|
while ((arg = get_arg( &cfile, ARG_OPTIONAL, 0 ))) {
|
2004-03-27 16:07:20 +00:00
|
|
|
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 ))
|
|
|
|
{
|
|
|
|
arg = cfile.val;
|
|
|
|
goto addone;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "%s:%d: unknown keyword '%s'\n",
|
|
|
|
cfile.file, cfile.line, cfile.cmd );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfile.err = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2012-09-15 13:15:22 +00:00
|
|
|
else if (!strcasecmp( "FSync", cfile.cmd ))
|
|
|
|
{
|
2013-11-02 20:42:34 +00:00
|
|
|
UseFSync = parse_bool( &cfile );
|
2012-09-15 13:15:22 +00:00
|
|
|
}
|
2014-10-25 15:30:57 +00:00
|
|
|
else if (!strcasecmp( "FieldDelimiter", cfile.cmd ))
|
|
|
|
{
|
|
|
|
if (strlen( cfile.val ) != 1) {
|
|
|
|
error( "%s:%d: Field delimiter must be exactly one character long\n", cfile.file, cfile.line );
|
|
|
|
cfile.err = 1;
|
|
|
|
} else {
|
|
|
|
FieldDelimiter = cfile.val[0];
|
|
|
|
if (!ispunct( FieldDelimiter )) {
|
|
|
|
error( "%s:%d: Field delimiter must be a punctuation character\n", cfile.file, cfile.line );
|
|
|
|
cfile.err = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-11-24 18:32:42 +00:00
|
|
|
else if (!getopt_helper( &cfile, &gcops, &global_conf ))
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "%s:%d: unknown section keyword '%s'\n",
|
|
|
|
cfile.file, cfile.line, cfile.cmd );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfile.err = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
while (getcline( &cfile ))
|
|
|
|
if (!cfile.cmd)
|
|
|
|
goto reloop;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose (cfile.fp);
|
2013-11-24 18:32:42 +00:00
|
|
|
cfile.err |= merge_ops( gcops, global_conf.ops );
|
|
|
|
if (!global_conf.sync_state)
|
|
|
|
global_conf.sync_state = expand_strdup( "~/." EXE "/" );
|
2012-09-15 09:46:42 +00:00
|
|
|
if (!cfile.err && pseudo)
|
2004-03-27 16:07:20 +00:00
|
|
|
unlink( where );
|
2012-09-15 09:46:42 +00:00
|
|
|
return cfile.err;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|