initial import
This commit is contained in:
commit
f47d0d7c11
4
Makefile.am
Normal file
4
Makefile.am
Normal file
|
@ -0,0 +1,4 @@
|
|||
bin_PROGRAMS=isync
|
||||
isync_SOURCES=main.c imap.c sync.c maildir.c
|
||||
man_MANS=isync.1
|
||||
EXTRA_DIST=sample.isyncrc $(man_MANS)
|
28
README
Normal file
28
README
Normal file
|
@ -0,0 +1,28 @@
|
|||
_
|
||||
(_)___ _ _ _ __ ___
|
||||
| / __| | | | '_ \ / __|
|
||||
| \__ \ |_| | | | | (__
|
||||
|_|___/\__, |_| |_|\___|
|
||||
|___/
|
||||
isync - IMAP4 to maildir mailbox synchronization program
|
||||
http://www.sigpipe.org/isync/
|
||||
|
||||
Author: Michael Elkins <me@mutt.org>
|
||||
|
||||
``isync'' is a command line application which synchronizes a local
|
||||
maildir-style mailbox with a remote IMAP4 mailbox, suitable for use in
|
||||
IMAP-disconnected mode. Multiple copies of the remote IMAP4 mailbox can be
|
||||
maintained, and all flags are synchronized.
|
||||
|
||||
``isync'' has been tested with the following IMAP servers:
|
||||
|
||||
Microsoft Exchange 2000 IMAP4rev1 server version 6.0.4417.0
|
||||
|
||||
* INSTALLING
|
||||
|
||||
./configure
|
||||
make install
|
||||
|
||||
* HELP
|
||||
|
||||
Please see the man page for complete documentation.
|
9
configure.in
Normal file
9
configure.in
Normal file
|
@ -0,0 +1,9 @@
|
|||
AC_INIT(isync.h)
|
||||
AM_INIT_AUTOMAKE(isync,0.1)
|
||||
AM_PROG_CC_STDC
|
||||
if test $CC = gcc; then
|
||||
CFLAGS="$CFLAGS -pipe"
|
||||
fi
|
||||
AC_CHECK_FUNCS(getopt_long)
|
||||
CFLAGS="$CFLAGS -W -Wall -pedantic -Wmissing-prototypes -Wmissing-declarations"
|
||||
AC_OUTPUT(Makefile)
|
531
imap.c
Normal file
531
imap.c
Normal file
|
@ -0,0 +1,531 @@
|
|||
/* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include "isync.h"
|
||||
|
||||
const char *Flags[] = {
|
||||
"\\Seen",
|
||||
"\\Answered",
|
||||
"\\Deleted",
|
||||
"\\Flagged",
|
||||
"\\Recent",
|
||||
"\\Draft"
|
||||
};
|
||||
|
||||
/* simple line buffering */
|
||||
static int
|
||||
buffer_gets (buffer_t * b, char **s)
|
||||
{
|
||||
int n;
|
||||
int start = b->offset;
|
||||
|
||||
*s = b->buf + start;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (b->offset + 2 > b->bytes)
|
||||
{
|
||||
/* shift down used bytes */
|
||||
*s = b->buf;
|
||||
|
||||
assert (start <= b->bytes);
|
||||
n = b->bytes - start;
|
||||
|
||||
if (n)
|
||||
memmove (b->buf, b->buf + start, n);
|
||||
b->offset = n;
|
||||
start = 0;
|
||||
|
||||
n = read (b->fd, b->buf + b->offset, sizeof (b->buf) - b->offset);
|
||||
if (n <= 0)
|
||||
{
|
||||
if (n == -1)
|
||||
perror ("read");
|
||||
else
|
||||
puts ("EOF");
|
||||
return -1;
|
||||
}
|
||||
b->bytes = b->offset + n;
|
||||
|
||||
// printf ("buffer_gets:read %d bytes\n", n);
|
||||
}
|
||||
|
||||
if (b->buf[b->offset] == '\r')
|
||||
{
|
||||
if (b->buf[b->offset + 1] == '\n')
|
||||
{
|
||||
b->buf[b->offset] = 0; /* terminate the string */
|
||||
b->offset += 2; /* next line */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
b->offset++;
|
||||
}
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
static int
|
||||
imap_exec (imap_t * imap, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char tmp[256];
|
||||
char buf[256];
|
||||
char *cmd;
|
||||
char *arg;
|
||||
char *arg1;
|
||||
message_t **cur = 0;
|
||||
message_t **rec = 0;
|
||||
|
||||
va_start (ap, fmt);
|
||||
vsnprintf (tmp, sizeof (tmp), fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
snprintf (buf, sizeof (buf), "%d %s\r\n", ++Tag, tmp);
|
||||
if (Verbose)
|
||||
fputs (buf, stdout);
|
||||
write (imap->fd, buf, strlen (buf));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (buffer_gets (imap->buf, &cmd))
|
||||
return -1;
|
||||
if (Verbose)
|
||||
puts (cmd);
|
||||
|
||||
arg = next_arg (&cmd);
|
||||
if (*arg == '*')
|
||||
{
|
||||
arg = next_arg (&cmd);
|
||||
arg1 = next_arg (&cmd);
|
||||
|
||||
if (arg1 && !strcmp ("EXISTS", arg1))
|
||||
imap->count = atoi (arg);
|
||||
else if (arg1 && !strcmp ("RECENT", arg1))
|
||||
imap->recent = atoi (arg);
|
||||
else if (!strcmp ("SEARCH", arg))
|
||||
{
|
||||
if (!rec)
|
||||
{
|
||||
rec = &imap->msgs;
|
||||
while (*rec)
|
||||
rec = &(*rec)->next;
|
||||
}
|
||||
while ((arg = next_arg (&cmd)))
|
||||
{
|
||||
*rec = calloc (1, sizeof (message_t));
|
||||
(*rec)->uid = atoi (arg);
|
||||
rec = &(*rec)->next;
|
||||
}
|
||||
}
|
||||
else if (arg1 && !strcmp ("FETCH", arg1))
|
||||
{
|
||||
if (!cur)
|
||||
{
|
||||
cur = &imap->msgs;
|
||||
while (*cur)
|
||||
cur = &(*cur)->next;
|
||||
}
|
||||
|
||||
/* new message
|
||||
* * <N> FETCH (UID <uid> FLAGS (...))
|
||||
*/
|
||||
arg = next_arg (&cmd); /* (UID */
|
||||
arg = next_arg (&cmd); /* <uid> */
|
||||
*cur = calloc (1, sizeof (message_t));
|
||||
(*cur)->uid = atoi (arg);
|
||||
|
||||
arg = next_arg (&cmd); /* FLAGS */
|
||||
if (!arg || strcmp ("FLAGS", arg))
|
||||
{
|
||||
printf ("FETCH parse error: expected FLAGS at %s\n", arg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* if we need to parse additional info, we should keep
|
||||
* a copy of this `arg' pointer
|
||||
*/
|
||||
|
||||
cmd++;
|
||||
arg = strchr (cmd, ')');
|
||||
if (!arg)
|
||||
{
|
||||
puts ("FETCH parse error");
|
||||
return -1;
|
||||
}
|
||||
*arg = 0;
|
||||
|
||||
/* parse message flags */
|
||||
while ((arg = next_arg (&cmd)))
|
||||
{
|
||||
if (!strcmp ("\\Seen", arg))
|
||||
(*cur)->flags |= D_SEEN;
|
||||
else if (!strcmp ("\\Flagged", arg))
|
||||
(*cur)->flags |= D_FLAGGED;
|
||||
else if (!strcmp ("\\Deleted", arg))
|
||||
(*cur)->flags |= D_DELETED;
|
||||
else if (!strcmp ("\\Answered", arg))
|
||||
(*cur)->flags |= D_ANSWERED;
|
||||
else if (!strcmp ("\\Draft", arg))
|
||||
(*cur)->flags |= D_DRAFT;
|
||||
else if (!strcmp ("\\Recent", arg))
|
||||
(*cur)->flags |= D_RECENT;
|
||||
else
|
||||
printf ("warning, unknown flag %s\n", arg);
|
||||
}
|
||||
|
||||
cur = &(*cur)->next;
|
||||
}
|
||||
}
|
||||
else if ((size_t) atol (arg) != Tag)
|
||||
{
|
||||
puts ("wrong tag");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
arg = next_arg (&cmd);
|
||||
if (!strcmp ("OK", arg))
|
||||
return 0;
|
||||
puts ("IMAP command failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
static int
|
||||
fetch_recent_flags (imap_t * imap)
|
||||
{
|
||||
char buf[1024];
|
||||
message_t **cur = &imap->recent_msgs;
|
||||
message_t *tmp;
|
||||
unsigned int start = -1;
|
||||
unsigned int last = -1;
|
||||
int ret = 0;
|
||||
|
||||
buf[0] = 0;
|
||||
while (*cur)
|
||||
{
|
||||
tmp = *cur;
|
||||
|
||||
if (last == (unsigned int) -1)
|
||||
{
|
||||
/* init */
|
||||
start = tmp->uid;
|
||||
last = tmp->uid;
|
||||
}
|
||||
else if (tmp->uid == last + 1)
|
||||
last++;
|
||||
else
|
||||
{
|
||||
/* out of sequence */
|
||||
if (start == last)
|
||||
ret = imap_exec (imap, "UID FETCH %d (UID FLAGS)", start);
|
||||
else
|
||||
ret =
|
||||
imap_exec (imap, "UID FETCH %d:%d (UID FLAGS)", start,
|
||||
last);
|
||||
start = tmp->uid;
|
||||
last = tmp->uid;
|
||||
}
|
||||
free (tmp);
|
||||
*cur = (*cur)->next;
|
||||
}
|
||||
|
||||
if (start != (unsigned int) -1)
|
||||
{
|
||||
if (start == last)
|
||||
ret = imap_exec (imap, "UID FETCH %d (UID FLAGS)", start);
|
||||
else
|
||||
ret =
|
||||
imap_exec (imap, "UID FETCH %d:%d (UID FLAGS)", start, last);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
imap_t *
|
||||
imap_open (config_t * box, int fast)
|
||||
{
|
||||
int ret;
|
||||
imap_t *imap;
|
||||
int s;
|
||||
struct sockaddr_in sin;
|
||||
struct hostent *he;
|
||||
|
||||
/* open connection to IMAP server */
|
||||
|
||||
memset (&sin, 0, sizeof (sin));
|
||||
sin.sin_port = htons (box->port);
|
||||
sin.sin_family = AF_INET;
|
||||
|
||||
printf ("Resolving %s... ", box->host);
|
||||
fflush (stdout);
|
||||
he = gethostbyname (box->host);
|
||||
if (!he)
|
||||
{
|
||||
perror ("gethostbyname");
|
||||
return 0;
|
||||
}
|
||||
puts ("ok");
|
||||
|
||||
sin.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
|
||||
|
||||
s = socket (PF_INET, SOCK_STREAM, 0);
|
||||
|
||||
printf ("Connecting to %s:%hu... ", inet_ntoa (sin.sin_addr),
|
||||
ntohs (sin.sin_port));
|
||||
fflush (stdout);
|
||||
if (connect (s, (struct sockaddr *) &sin, sizeof (sin)))
|
||||
{
|
||||
perror ("connect");
|
||||
exit (1);
|
||||
}
|
||||
puts ("ok");
|
||||
|
||||
imap = calloc (1, sizeof (imap_t));
|
||||
imap->fd = s;
|
||||
//imap->state = imap_state_init;
|
||||
imap->buf = calloc (1, sizeof (buffer_t));
|
||||
imap->buf->fd = s;
|
||||
imap->box = box;
|
||||
|
||||
puts ("Logging in...");
|
||||
ret = imap_exec (imap, "LOGIN %s %s", box->user, box->pass);
|
||||
if (!ret)
|
||||
{
|
||||
fputs ("Selecting mailbox... ", stdout);
|
||||
fflush (stdout);
|
||||
ret = imap_exec (imap, "SELECT %s", box->box);
|
||||
if (!ret)
|
||||
printf ("%d messages, %d recent\n", imap->count, imap->recent);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
if (fast)
|
||||
{
|
||||
if (imap->recent > 0)
|
||||
{
|
||||
puts ("Fetching info for recent messages");
|
||||
ret = imap_exec (imap, "UID SEARCH RECENT");
|
||||
if (!ret)
|
||||
ret = fetch_recent_flags (imap);
|
||||
}
|
||||
}
|
||||
else if (imap->count > 0)
|
||||
{
|
||||
puts ("Reading IMAP mailbox index");
|
||||
ret = imap_exec (imap, "FETCH 1:%d (UID FLAGS)", imap->count);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
imap_exec (imap, "LOGOUT");
|
||||
close (s);
|
||||
free (imap->buf);
|
||||
free (imap);
|
||||
imap = 0;
|
||||
}
|
||||
|
||||
return imap;
|
||||
}
|
||||
|
||||
void
|
||||
imap_close (imap_t * imap)
|
||||
{
|
||||
puts ("Closing IMAP connection");
|
||||
imap_exec (imap, "LOGOUT");
|
||||
}
|
||||
|
||||
/* write a buffer stripping all \r bytes */
|
||||
static int
|
||||
write_strip (int fd, char *buf, size_t len)
|
||||
{
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
|
||||
while (start < len)
|
||||
{
|
||||
while (end < len && buf[end] != '\r')
|
||||
end++;
|
||||
write (fd, buf + start, end - start);
|
||||
end++;
|
||||
start = end;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
send_server (int fd, const char *fmt, ...)
|
||||
{
|
||||
char buf[128];
|
||||
char cmd[128];
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
vsnprintf (buf, sizeof (buf), fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
snprintf (cmd, sizeof (cmd), "%d %s\r\n", ++Tag, buf);
|
||||
write (fd, cmd, strlen (cmd));
|
||||
|
||||
if (Verbose)
|
||||
fputs (cmd, stdout);
|
||||
}
|
||||
|
||||
int
|
||||
imap_fetch_message (imap_t * imap, unsigned int uid, int fd)
|
||||
{
|
||||
char *cmd;
|
||||
char *arg;
|
||||
size_t bytes;
|
||||
size_t n;
|
||||
char buf[1024];
|
||||
|
||||
send_server (imap->fd, "UID FETCH %d RFC822.PEEK", uid);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (buffer_gets (imap->buf, &cmd))
|
||||
return -1;
|
||||
|
||||
if (Verbose)
|
||||
puts (cmd);
|
||||
|
||||
if (*cmd == '*')
|
||||
{
|
||||
/* need to figure out how long the message is
|
||||
* * <msgno> FETCH (RFC822 {<size>}
|
||||
*/
|
||||
|
||||
next_arg (&cmd); /* * */
|
||||
next_arg (&cmd); /* <msgno> */
|
||||
next_arg (&cmd); /* FETCH */
|
||||
next_arg (&cmd); /* (RFC822 */
|
||||
arg = next_arg (&cmd);
|
||||
if (*arg != '{')
|
||||
{
|
||||
puts ("parse error getting size");
|
||||
return -1;
|
||||
}
|
||||
bytes = strtol (arg + 1, 0, 10);
|
||||
// printf ("receiving %d byte message\n", bytes);
|
||||
|
||||
/* dump whats left over in the input buffer */
|
||||
n = imap->buf->bytes - imap->buf->offset;
|
||||
|
||||
if (n > bytes)
|
||||
{
|
||||
/* the entire message fit in the buffer */
|
||||
n = bytes;
|
||||
}
|
||||
|
||||
/* ick. we have to strip out the \r\n line endings, so
|
||||
* i can't just dump the raw bytes to disk.
|
||||
*/
|
||||
write_strip (fd, imap->buf->buf + imap->buf->offset, n);
|
||||
|
||||
bytes -= n;
|
||||
|
||||
// printf ("wrote %d buffered bytes\n", n);
|
||||
|
||||
/* mark that we used part of the buffer */
|
||||
imap->buf->offset += n;
|
||||
|
||||
/* now read the rest of the message */
|
||||
while (bytes > 0)
|
||||
{
|
||||
n = bytes;
|
||||
if (n > sizeof (buf))
|
||||
n = sizeof (buf);
|
||||
n = read (imap->fd, buf, n);
|
||||
if (n > 0)
|
||||
{
|
||||
// printf("imap_fetch_message:%d:read %d bytes\n", __LINE__, n);
|
||||
write_strip (fd, buf, n);
|
||||
bytes -= n;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n == (size_t) - 1)
|
||||
perror ("read");
|
||||
else
|
||||
puts ("EOF");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// puts ("finished fetching msg");
|
||||
|
||||
buffer_gets (imap->buf, &cmd);
|
||||
if (Verbose)
|
||||
puts (cmd); /* last part of line */
|
||||
}
|
||||
else
|
||||
{
|
||||
arg = next_arg (&cmd);
|
||||
if (!arg || (size_t) atoi (arg) != Tag)
|
||||
{
|
||||
puts ("wrong tag");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* add flags to existing flags */
|
||||
int
|
||||
imap_set_flags (imap_t * imap, unsigned int uid, unsigned int flags)
|
||||
{
|
||||
char buf[256];
|
||||
int i;
|
||||
|
||||
buf[0] = 0;
|
||||
for (i = 0; i < D_MAX; i++)
|
||||
{
|
||||
if (flags & (1 << i))
|
||||
snprintf (buf + strlen (buf),
|
||||
sizeof (buf) - strlen (buf), "%s%s",
|
||||
(buf[0] != 0) ? " " : "", Flags[i]);
|
||||
}
|
||||
|
||||
return imap_exec (imap, "UID STORE %d +FLAGS.SILENT (%s)", uid, buf);
|
||||
}
|
||||
|
||||
int
|
||||
imap_expunge (imap_t * imap)
|
||||
{
|
||||
return imap_exec (imap, "EXPUNGE");
|
||||
}
|
184
isync.1
Normal file
184
isync.1
Normal file
|
@ -0,0 +1,184 @@
|
|||
.ig
|
||||
\" isync - IMAP4 to maildir mailbox synchronizer
|
||||
\" Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
\"
|
||||
\" 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
|
||||
..
|
||||
.TH isync 1 "2000 Dec 20"
|
||||
..
|
||||
.SH NAME
|
||||
isync - synchronize IMAP4 and maildir mailboxes
|
||||
..
|
||||
.SH SYNOPSIS
|
||||
.B isync
|
||||
[
|
||||
.I options...
|
||||
]
|
||||
.I file
|
||||
..
|
||||
.SH DESCRIPTION
|
||||
.B isync
|
||||
is a command line application which synchronizes a local maildir-style
|
||||
mailbox with a remote IMAP4 mailbox, suitable for use in IMAP-disconnected
|
||||
mode. Multiple copies of the remote IMAP4 mailbox can be maintained, and
|
||||
all flags are synchronized.
|
||||
..
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB-c\fR, \fB--config\fR \fIfile\fR
|
||||
Read configuration from
|
||||
.I file
|
||||
By default, configuration is read from ~/.isyncrc if it exists.
|
||||
.TP
|
||||
.B -d, --delete
|
||||
Causes
|
||||
.B isync
|
||||
to delete messages from the local maildir mailbox which do not exist on the
|
||||
IMAP server. By default,
|
||||
.I dead
|
||||
messages are
|
||||
.B not
|
||||
deleted.
|
||||
.TP
|
||||
.B -e, --expunge
|
||||
Causes
|
||||
.B isync
|
||||
to permanently remove all messages marked for deletion in both the local
|
||||
maildir mailbox and the remote IMAP mailbox. By default, messages are
|
||||
.B not
|
||||
expunged.
|
||||
.TP
|
||||
.B -f, --fast
|
||||
Causes
|
||||
.B isync
|
||||
to skip the step of synchronzing message flags between the local maildir
|
||||
mailbox and the IMAP mailbox. Only new messages existing on the server will
|
||||
be fetched into the local mailbox.
|
||||
.B NOTE:
|
||||
This command works by checking the \\Recent flag on messages in the IMAP
|
||||
mailbox. If you access the IMAP mailbox from multiple locations, the
|
||||
\\Recent flag will be lost between sessions, so you must do a full
|
||||
synchronization to fetch the messages which do not exist in the local
|
||||
mailbox.
|
||||
.TP
|
||||
.B -h, --help
|
||||
Displays a summary of command line options
|
||||
.TP
|
||||
\fB-p\fR, \fB--port\fR \fIport\fR
|
||||
Specifies the port on the IMAP server to connect to (default: 143)
|
||||
.TP
|
||||
\fB-r\fR, \fB--remote\fR \fIbox\fR
|
||||
Specifies the name of the remote IMAP mailbox to synchronize with
|
||||
(Default: INBOX)
|
||||
.TP
|
||||
\fB-s\fR, \fB--host\fR \fIhost\fR
|
||||
.P
|
||||
Specifies the hostname of the IMAP server
|
||||
.TP
|
||||
\fB-u\fR, \fB--user\fR \fIuser\fR
|
||||
Specifies the login name to access the IMAP server (default: $USER)
|
||||
.TP
|
||||
.B -v, --version
|
||||
Displays
|
||||
.B isync
|
||||
version information
|
||||
.TP
|
||||
.B -V, --verbose
|
||||
Enables
|
||||
.I verbose
|
||||
mode, which disables the IMAP network traffic.
|
||||
..
|
||||
.SH CONFIGURATION
|
||||
.B isync
|
||||
reads
|
||||
.I ~/.isyncrc
|
||||
to load default configuration data. Each line of the configuration file
|
||||
consists of a command. The following commands are understood:
|
||||
.TP
|
||||
\fBMailbox\fR \fIpath\fR
|
||||
Defines a local maildir mailbox. All configuration commands following this
|
||||
line, up until the next
|
||||
.I Mailbox
|
||||
command, apply to this mailbox only.
|
||||
..
|
||||
.TP
|
||||
\fBHost\fR \fIname\fR
|
||||
Defines the DNS name or IP address of the IMAP server
|
||||
..
|
||||
.TP
|
||||
\fBPort\fR \fIport\fR
|
||||
Defines the TCP port number on the IMAP server to use (Default: 143)
|
||||
..
|
||||
.TP
|
||||
\fBBox\fR \fImailbox\fR
|
||||
Defines the name of the remote IMAP mailbox associated with the local
|
||||
maildir mailbox (Default: INBOX)
|
||||
..
|
||||
.TP
|
||||
\fBUser\fR \fIusername\fR
|
||||
Defines the login name on the IMAP server (Default: current user)
|
||||
..
|
||||
.TP
|
||||
\fBPass\fR \fIpassword\fR
|
||||
Defines the password for
|
||||
.I username
|
||||
on the IMAP server. Note that this option is
|
||||
.B NOT
|
||||
required. If no password is specified in the configuration file,
|
||||
.B isync
|
||||
will prompt you for it.
|
||||
..
|
||||
.TP
|
||||
\fBAlias\fR \fIstring\fR
|
||||
Defines an alias for the mailbox which can be used as a shortcut on the
|
||||
command line.
|
||||
.P
|
||||
Configuration commands that appear prior to the first
|
||||
.B Mailbox
|
||||
command are considered to be
|
||||
.I global
|
||||
options which are used as defaults when those specific options are not
|
||||
specifically set for a defined Mailbox. For example, if you use the same
|
||||
login name for several IMAP servers, you can put a
|
||||
.B User
|
||||
command before the first
|
||||
.B Mailbox
|
||||
command, and then leave out the
|
||||
.B User
|
||||
command in the sections for each mailbox.
|
||||
.B isync
|
||||
will then use the global value by default.
|
||||
..
|
||||
.SH FILES
|
||||
.TP
|
||||
.B ~/.isyncrc
|
||||
Default configuration file
|
||||
..
|
||||
.SH SEE ALSO
|
||||
mutt(1), maildir(5)
|
||||
.P
|
||||
Up to date information on
|
||||
.B isync
|
||||
can be found at
|
||||
http://www.sigpipe.org/isync/.
|
||||
..
|
||||
.SH AUTHOR
|
||||
Written by Michael R. Elkins <me@mutt.org>.
|
||||
..
|
||||
.SH BUGS
|
||||
SSL is currently not used when connecting to the IMAP server. A future
|
||||
version of
|
||||
.B isync
|
||||
is expected to support this.
|
111
isync.h
Normal file
111
isync.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
/* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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 <stdarg.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fd;
|
||||
char buf[1024];
|
||||
int bytes;
|
||||
int offset;
|
||||
}
|
||||
buffer_t;
|
||||
|
||||
typedef struct config config_t;
|
||||
typedef struct mailbox mailbox_t;
|
||||
typedef struct message message_t;
|
||||
|
||||
struct config
|
||||
{
|
||||
char *path;
|
||||
char *host;
|
||||
int port;
|
||||
char *user;
|
||||
char *pass;
|
||||
char *box;
|
||||
char *alias;
|
||||
config_t *next;
|
||||
};
|
||||
|
||||
/* struct representing local mailbox file */
|
||||
struct mailbox
|
||||
{
|
||||
char *path;
|
||||
message_t *msgs;
|
||||
unsigned int changed:1;
|
||||
};
|
||||
|
||||
/* message dispositions */
|
||||
#define D_SEEN (1<<0)
|
||||
#define D_ANSWERED (1<<1)
|
||||
#define D_DELETED (1<<2)
|
||||
#define D_FLAGGED (1<<3)
|
||||
#define D_RECENT (1<<4)
|
||||
#define D_DRAFT (1<<5)
|
||||
#define D_MAX 6
|
||||
|
||||
struct message
|
||||
{
|
||||
char *file;
|
||||
unsigned int uid;
|
||||
unsigned int flags;
|
||||
message_t *next;
|
||||
unsigned int processed:1; /* message has already been evaluated */
|
||||
unsigned int new:1; /* message is in the new/ subdir */
|
||||
unsigned int changed:1; /* flags changed */
|
||||
unsigned int dead:1; /* message doesn't exist on the server */
|
||||
};
|
||||
|
||||
/* imap connection info */
|
||||
typedef struct
|
||||
{
|
||||
int fd; /* server socket */
|
||||
unsigned int count; /* # of msgs */
|
||||
unsigned int recent; /* # of recent messages */
|
||||
buffer_t *buf; /* input buffer for reading server output */
|
||||
message_t *msgs; /* list of messages on the server */
|
||||
config_t *box; /* mailbox to open */
|
||||
message_t *recent_msgs; /* list of recent messages - only contains
|
||||
* UID to be used in a FETCH FLAGS command
|
||||
*/
|
||||
}
|
||||
imap_t;
|
||||
|
||||
/* flags for sync_mailbox */
|
||||
#define SYNC_FAST (1<<0) /* don't sync flags, only fetch new msgs */
|
||||
#define SYNC_DELETE (1<<1) /* delete local that don't exist on server */
|
||||
|
||||
extern config_t global;
|
||||
extern unsigned int Tag;
|
||||
extern char Hostname[256];
|
||||
extern int Verbose;
|
||||
|
||||
char *next_arg (char **);
|
||||
|
||||
int sync_mailbox (mailbox_t *, imap_t *, int);
|
||||
|
||||
void imap_close (imap_t *);
|
||||
int imap_fetch_message (imap_t *, unsigned int, int);
|
||||
int imap_set_flags (imap_t *, unsigned int, unsigned int);
|
||||
int imap_expunge (imap_t *);
|
||||
imap_t *imap_open (config_t *, int);
|
||||
|
||||
mailbox_t *maildir_open (const char *, int fast);
|
||||
int maildir_expunge (mailbox_t *, int);
|
||||
int maildir_sync (mailbox_t *);
|
28
isyncrc.sample
Normal file
28
isyncrc.sample
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Global configuration section
|
||||
# Values here are used as defaults for any following Mailbox section that
|
||||
# doesn't specify it.
|
||||
|
||||
# my default username, if different from the local username
|
||||
User me
|
||||
#Port 143
|
||||
#Box INBOX
|
||||
|
||||
###
|
||||
### work mailbox
|
||||
###
|
||||
|
||||
Mailbox /home/me/Mail/work
|
||||
Host work.host.com
|
||||
Pass xxxxxxxx
|
||||
# define a shortcut so I can just use "isync work" from the command line
|
||||
Alias work
|
||||
|
||||
###
|
||||
### personal mailbox
|
||||
###
|
||||
|
||||
Mailbox /home/me/Mail/personal
|
||||
Host host.play.com
|
||||
# use a non-default port for this connection
|
||||
Port 6789
|
||||
Alias personal
|
208
maildir.c
Normal file
208
maildir.c
Normal file
|
@ -0,0 +1,208 @@
|
|||
/* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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 <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "isync.h"
|
||||
|
||||
/* 2,<flags> */
|
||||
static void
|
||||
parse_info (message_t * m, char *s)
|
||||
{
|
||||
if (*s == '2' && *(s + 1) == ',')
|
||||
{
|
||||
s += 2;
|
||||
while (*s)
|
||||
{
|
||||
if (*s == 'F')
|
||||
m->flags |= D_FLAGGED;
|
||||
else if (*s == 'R')
|
||||
m->flags |= D_ANSWERED;
|
||||
else if (*s == 'T')
|
||||
m->flags |= D_DELETED;
|
||||
else if (*s == 'S')
|
||||
m->flags |= D_SEEN;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* open a maildir mailbox. if `fast' is nonzero, we just check to make
|
||||
* sure its a valid mailbox and don't actually parse it. any IMAP messages
|
||||
* with the \Recent flag set are guaranteed not to be in the mailbox yet,
|
||||
* so we can save a lot of time when the user just wants to fetch new messages
|
||||
* without syncing the flags.
|
||||
*/
|
||||
mailbox_t *
|
||||
maildir_open (const char *path, int fast)
|
||||
{
|
||||
char buf[_POSIX_PATH_MAX];
|
||||
DIR *d;
|
||||
struct dirent *e;
|
||||
message_t **cur;
|
||||
message_t *p;
|
||||
mailbox_t *m;
|
||||
char *s;
|
||||
int count = 0;
|
||||
|
||||
/* check to make sure this looks like a valid maildir box */
|
||||
snprintf (buf, sizeof (buf), "%s/new", path);
|
||||
if (access (buf, F_OK))
|
||||
{
|
||||
perror ("access");
|
||||
return 0;
|
||||
}
|
||||
snprintf (buf, sizeof (buf), "%s/cur", path);
|
||||
if (access (buf, F_OK))
|
||||
{
|
||||
perror ("access");
|
||||
return 0;
|
||||
}
|
||||
m = calloc (1, sizeof (mailbox_t));
|
||||
m->path = strdup (path);
|
||||
|
||||
if (fast)
|
||||
return m;
|
||||
|
||||
cur = &m->msgs;
|
||||
for (; count < 2; count++)
|
||||
{
|
||||
/* read the msgs from the new subdir */
|
||||
snprintf (buf, sizeof (buf), "%s/%s", path,
|
||||
(count == 0) ? "new" : "cur");
|
||||
d = opendir (buf);
|
||||
if (!d)
|
||||
{
|
||||
perror ("opendir");
|
||||
return 0;
|
||||
}
|
||||
while ((e = readdir (d)))
|
||||
{
|
||||
if (*e->d_name == '.')
|
||||
continue; /* skip dot-files */
|
||||
*cur = calloc (1, sizeof (message_t));
|
||||
p = *cur;
|
||||
p->file = strdup (e->d_name);
|
||||
p->uid = -1;
|
||||
p->flags = (count == 1) ? D_SEEN : 0;
|
||||
p->new = (count == 0);
|
||||
|
||||
/* filename format is something like:
|
||||
* <unique-prefix>.UID<n>:2,<flags>
|
||||
* This is completely non-standard, but in order for mail
|
||||
* clients to understand the flags, we have to use the
|
||||
* standard :info as described by the qmail spec
|
||||
*/
|
||||
s = strstr (p->file, "UID");
|
||||
if (!s)
|
||||
puts ("warning, no uid for message");
|
||||
else
|
||||
{
|
||||
p->uid = strtol (s + 3, &s, 10);
|
||||
if (*s && *s != ':')
|
||||
{
|
||||
puts ("warning, unable to parse uid");
|
||||
p->uid = -1; /* reset */
|
||||
}
|
||||
}
|
||||
|
||||
s = strchr (p->file, ':');
|
||||
if (s)
|
||||
parse_info (p, s + 1);
|
||||
cur = &p->next;
|
||||
}
|
||||
closedir (d);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/* permanently remove messages from a maildir mailbox. if `dead' is nonzero,
|
||||
* we only remove the messags marked dead.
|
||||
*/
|
||||
int
|
||||
maildir_expunge (mailbox_t * mbox, int dead)
|
||||
{
|
||||
message_t **cur = &mbox->msgs;
|
||||
message_t *tmp;
|
||||
char path[_POSIX_PATH_MAX];
|
||||
|
||||
while (*cur)
|
||||
{
|
||||
if ((dead == 0 && (*cur)->flags & D_DELETED) ||
|
||||
(dead && (*cur)->dead))
|
||||
{
|
||||
tmp = *cur;
|
||||
*cur = (*cur)->next;
|
||||
snprintf (path, sizeof (path), "%s/%s/%s",
|
||||
mbox->path, tmp->new ? "new" : "cur", tmp->file);
|
||||
if (unlink (path))
|
||||
perror ("unlink");
|
||||
free (tmp->file);
|
||||
free (tmp);
|
||||
}
|
||||
else
|
||||
cur = &(*cur)->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
maildir_sync (mailbox_t * mbox)
|
||||
{
|
||||
message_t *cur = mbox->msgs;
|
||||
char path[_POSIX_PATH_MAX];
|
||||
char oldpath[_POSIX_PATH_MAX];
|
||||
char *p;
|
||||
|
||||
if (mbox->changed)
|
||||
{
|
||||
for (; cur; cur = cur->next)
|
||||
{
|
||||
if (cur->changed)
|
||||
{
|
||||
/* generate old path */
|
||||
snprintf (oldpath, sizeof (oldpath), "%s/%s/%s",
|
||||
mbox->path, cur->new ? "new" : "cur", cur->file);
|
||||
|
||||
/* truncate old flags (if present) */
|
||||
p = strchr (cur->file, ':');
|
||||
if (p)
|
||||
*p = 0;
|
||||
|
||||
p = strrchr (cur->file, '/');
|
||||
|
||||
/* generate new path */
|
||||
snprintf (path, sizeof (path), "%s/%s%s:2,%s%s%s%s",
|
||||
mbox->path, (cur->flags & D_SEEN) ? "cur" : "new",
|
||||
cur->file, (cur->flags & D_FLAGGED) ? "F" : "",
|
||||
(cur->flags & D_ANSWERED) ? "R" : "",
|
||||
(cur->flags & D_SEEN) ? "S" : "",
|
||||
(cur->flags & D_DELETED) ? "T" : "");
|
||||
|
||||
if (rename (oldpath, path))
|
||||
perror ("rename");
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
398
main.c
Normal file
398
main.c
Normal file
|
@ -0,0 +1,398 @@
|
|||
/* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <termios.h>
|
||||
#include "isync.h"
|
||||
|
||||
#if HAVE_GETOPT_LONG
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
struct option Opts[] = {
|
||||
{"config", 1, NULL, 'c'},
|
||||
{"delete", 0, NULL, 'd'},
|
||||
{"expunge", 0, NULL, 'e'},
|
||||
{"fast", 0, NULL, 'f'},
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"remote", 1, NULL, 'r'},
|
||||
{"host", 1, NULL, 's'},
|
||||
{"port", 1, NULL, 'p'},
|
||||
{"user", 1, NULL, 'u'},
|
||||
{"version", 0, NULL, 'v'},
|
||||
{"verbose", 0, NULL, 'V'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
#endif
|
||||
|
||||
config_t global;
|
||||
unsigned int Tag = 0;
|
||||
static config_t *box = 0;
|
||||
char Hostname[256];
|
||||
int Verbose = 0;
|
||||
|
||||
static void
|
||||
version (void)
|
||||
{
|
||||
printf ("%s %s\n", PACKAGE, VERSION);
|
||||
exit (0);
|
||||
}
|
||||
|
||||
static void
|
||||
usage (void)
|
||||
{
|
||||
printf ("%s %s IMAP4 to maildir synchronizer\n", PACKAGE, VERSION);
|
||||
puts ("Copyright (C) 2000 Michael R. Elkins <me@mutt.org>");
|
||||
printf ("usage: %s [ flags ] mailbox\n", PACKAGE);
|
||||
puts
|
||||
(" -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)");
|
||||
puts
|
||||
(" -d, --delete delete local msgs that don't exist on the server");
|
||||
puts
|
||||
(" -e, --expunge expunge deleted messages from the server");
|
||||
puts (" -f, --fast only fetch new messages");
|
||||
puts (" -h, --help display this help message");
|
||||
puts (" -p, --port PORT server IMAP port");
|
||||
puts (" -r, --remote BOX remote mailbox");
|
||||
puts (" -s, --host HOST IMAP server address");
|
||||
puts (" -u, --user USER IMAP user name");
|
||||
puts (" -v, --version display version");
|
||||
puts
|
||||
(" -V, --verbose verbose mode (display network traffic)");
|
||||
exit (0);
|
||||
}
|
||||
|
||||
static char *
|
||||
enter_password (void)
|
||||
{
|
||||
struct termios t;
|
||||
char pass[32];
|
||||
|
||||
tcgetattr (0, &t);
|
||||
t.c_lflag &= ~ECHO;
|
||||
tcsetattr (0, TCSANOW, &t);
|
||||
printf ("Password: ");
|
||||
fflush (stdout);
|
||||
pass[sizeof (pass) - 1] = 0;
|
||||
fgets (pass, sizeof (pass) - 1, stdin);
|
||||
if (pass[0])
|
||||
pass[strlen (pass) - 1] = 0; /* kill newline */
|
||||
t.c_lflag |= ECHO;
|
||||
tcsetattr (0, TCSANOW, &t);
|
||||
puts ("");
|
||||
return strdup (pass);
|
||||
}
|
||||
|
||||
static void
|
||||
load_config (char *where)
|
||||
{
|
||||
char path[_POSIX_PATH_MAX];
|
||||
char buf[1024];
|
||||
struct passwd *pw;
|
||||
config_t **cur = &box;
|
||||
char *p;
|
||||
int line = 0;
|
||||
FILE *fp;
|
||||
|
||||
if (!where)
|
||||
{
|
||||
pw = getpwuid (getuid ());
|
||||
snprintf (path, sizeof (path), "%s/.isyncrc", pw->pw_dir);
|
||||
where = path;
|
||||
}
|
||||
printf ("Reading %s\n", where);
|
||||
|
||||
fp = fopen (where, "r");
|
||||
if (!fp)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
perror ("fopen");
|
||||
return;
|
||||
}
|
||||
}
|
||||
while ((fgets (buf, sizeof (buf) - 1, fp)))
|
||||
{
|
||||
if (buf[0])
|
||||
buf[strlen (buf) - 1] = 0;
|
||||
line++;
|
||||
if (buf[0] == '#')
|
||||
continue;
|
||||
p = buf;
|
||||
while (*p && !isspace (*p))
|
||||
p++;
|
||||
while (isspace (*p))
|
||||
p++;
|
||||
if (!strncmp ("mailbox", buf, 7))
|
||||
{
|
||||
if (*cur)
|
||||
cur = &(*cur)->next;
|
||||
*cur = calloc (1, sizeof (config_t));
|
||||
(*cur)->path = strdup (p);
|
||||
}
|
||||
else if (!strncmp ("host", buf, 4))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->host = strdup (p);
|
||||
else
|
||||
global.host = strdup (p);
|
||||
}
|
||||
else if (!strncmp ("user", buf, 4))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->user = strdup (p);
|
||||
else
|
||||
global.user = strdup (p);
|
||||
}
|
||||
else if (!strncmp ("pass", buf, 4))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->pass = strdup (p);
|
||||
else
|
||||
global.pass = strdup (p);
|
||||
}
|
||||
else if (!strncmp ("port", buf, 4))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->port = atoi (p);
|
||||
else
|
||||
global.port = atoi (p);
|
||||
}
|
||||
else if (!strncmp ("box", buf, 3))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->box = strdup (p);
|
||||
else
|
||||
global.box = strdup (p);
|
||||
}
|
||||
else if (!strncmp ("alias", buf, 5))
|
||||
{
|
||||
if (*cur)
|
||||
(*cur)->alias = strdup (p);
|
||||
}
|
||||
else if (buf[0])
|
||||
printf ("%s:%d:unknown command:%s", path, line, buf);
|
||||
}
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
static config_t *
|
||||
find_box (const char *s)
|
||||
{
|
||||
config_t *p = box;
|
||||
|
||||
for (; p; p = p->next)
|
||||
if (!strcmp (s, p->path) || (p->alias && !strcmp (s, p->alias)))
|
||||
return p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
next_arg (char **s)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
if (!s)
|
||||
return 0;
|
||||
if (!*s)
|
||||
return 0;
|
||||
while (isspace (**s))
|
||||
(*s)++;
|
||||
if (!**s)
|
||||
{
|
||||
*s = 0;
|
||||
return 0;
|
||||
}
|
||||
ret = *s;
|
||||
while (**s && !isspace (**s))
|
||||
(*s)++;
|
||||
if (**s)
|
||||
*(*s)++ = 0;
|
||||
if (!**s)
|
||||
*s = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
config_t *box;
|
||||
mailbox_t *mail;
|
||||
imap_t *imap;
|
||||
int expunge = 0; /* by default, don't delete anything */
|
||||
int fast = 0;
|
||||
int delete = 0;
|
||||
char *config = 0;
|
||||
struct passwd *pw;
|
||||
|
||||
pw = getpwuid (getuid ());
|
||||
|
||||
/* defaults */
|
||||
memset (&global, 0, sizeof (global));
|
||||
global.port = 143;
|
||||
global.box = "INBOX";
|
||||
global.user = strdup (pw->pw_name);
|
||||
|
||||
#if HAVE_GETOPT_LONG
|
||||
while ((i = getopt_long (argc, argv, "defhp:u:r:s:vV", Opts, NULL)) != -1)
|
||||
#else
|
||||
while ((i = getopt (argc, argv, "defhp:u:r:s:vV")) != -1)
|
||||
#endif
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 'c':
|
||||
config = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
delete = 1;
|
||||
break;
|
||||
case 'e':
|
||||
expunge = 1;
|
||||
break;
|
||||
case 'f':
|
||||
fast = 1;
|
||||
break;
|
||||
case 'p':
|
||||
global.port = atoi (optarg);
|
||||
break;
|
||||
case 'r':
|
||||
global.box = optarg;
|
||||
break;
|
||||
case 's':
|
||||
global.host = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
free (global.user);
|
||||
global.user = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
Verbose = 1;
|
||||
break;
|
||||
case 'v':
|
||||
version ();
|
||||
default:
|
||||
usage ();
|
||||
}
|
||||
}
|
||||
|
||||
if (!argv[optind])
|
||||
{
|
||||
puts ("No box specified");
|
||||
usage ();
|
||||
}
|
||||
|
||||
gethostname (Hostname, sizeof (Hostname));
|
||||
|
||||
load_config (config);
|
||||
|
||||
box = find_box (argv[optind]);
|
||||
if (!box)
|
||||
{
|
||||
/* if enough info is given on the command line, don't worry if
|
||||
* the mailbox isn't defined.
|
||||
*/
|
||||
if (!global.host)
|
||||
{
|
||||
puts ("No such mailbox");
|
||||
exit (1);
|
||||
}
|
||||
global.path = argv[optind];
|
||||
box = &global;
|
||||
}
|
||||
|
||||
/* fill in missing info with defaults */
|
||||
if (!box->pass)
|
||||
{
|
||||
if (!global.pass)
|
||||
{
|
||||
box->pass = enter_password ();
|
||||
if (!box->pass)
|
||||
{
|
||||
puts ("Aborting, no password");
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
else
|
||||
box->pass = global.pass;
|
||||
}
|
||||
if (!box->user)
|
||||
box->user = global.user;
|
||||
if (!box->port)
|
||||
box->port = global.port;
|
||||
if (!box->host)
|
||||
box->host = global.host;
|
||||
if (!box->box)
|
||||
box->box = global.box;
|
||||
|
||||
printf ("Reading %s\n", box->path);
|
||||
mail = maildir_open (box->path, fast);
|
||||
if (!mail)
|
||||
{
|
||||
puts ("Unable to load mailbox");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
imap = imap_open (box, fast);
|
||||
if (!imap)
|
||||
exit (1);
|
||||
|
||||
puts ("Synchronizing");
|
||||
i = 0;
|
||||
i |= (fast) ? SYNC_FAST : 0;
|
||||
i |= (delete) ? SYNC_DELETE : 0;
|
||||
if (sync_mailbox (mail, imap, i))
|
||||
exit (1);
|
||||
|
||||
if (!fast)
|
||||
{
|
||||
if (expunge)
|
||||
{
|
||||
/* remove messages marked for deletion */
|
||||
puts ("Expunging messages");
|
||||
if (imap_expunge (imap))
|
||||
exit (1);
|
||||
if (maildir_expunge (mail, 0))
|
||||
exit (1);
|
||||
}
|
||||
/* remove messages deleted from server. this can safely be an
|
||||
* `else' clause since dead messages are marked as deleted by
|
||||
* sync_mailbox.
|
||||
*/
|
||||
else if (delete)
|
||||
maildir_expunge (mail, 1);
|
||||
|
||||
/* write changed flags back to the mailbox */
|
||||
printf ("Committing changes to %s\n", mail->path);
|
||||
if (maildir_sync (mail))
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* gracefully close connection to the IMAP server */
|
||||
imap_close (imap);
|
||||
|
||||
exit (0);
|
||||
}
|
134
sync.c
Normal file
134
sync.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
/* isync - IMAP4 to maildir mailbox synchronizer
|
||||
* Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include "isync.h"
|
||||
|
||||
static unsigned int MaildirCount = 0;
|
||||
|
||||
static message_t *
|
||||
find_msg (message_t * list, unsigned int uid)
|
||||
{
|
||||
for (; list; list = list->next)
|
||||
if (list->uid == uid)
|
||||
return list;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags)
|
||||
{
|
||||
message_t *cur;
|
||||
message_t *tmp;
|
||||
char path[_POSIX_PATH_MAX];
|
||||
char newpath[_POSIX_PATH_MAX];
|
||||
char *p;
|
||||
int fd;
|
||||
|
||||
for (cur = mbox->msgs; cur; cur = cur->next)
|
||||
{
|
||||
tmp = find_msg (imap->msgs, cur->uid);
|
||||
if (!tmp)
|
||||
{
|
||||
printf ("warning, uid %d doesn't exist on server\n", cur->uid);
|
||||
if (flags & SYNC_DELETE)
|
||||
{
|
||||
cur->flags |= D_DELETED;
|
||||
cur->dead = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
tmp->processed = 1;
|
||||
|
||||
if (!(flags & SYNC_FAST))
|
||||
{
|
||||
/* check if local flags are different from server flags.
|
||||
* ignore \Recent and \Draft
|
||||
*/
|
||||
if (cur->flags != (tmp->flags & ~(D_RECENT | D_DRAFT)))
|
||||
{
|
||||
/* set local flags that don't exist on the server */
|
||||
imap_set_flags (imap, cur->uid, cur->flags & ~tmp->flags);
|
||||
|
||||
/* update local flags */
|
||||
cur->flags |= (tmp->flags & ~(D_RECENT | D_DRAFT));
|
||||
cur->changed = 1;
|
||||
mbox->changed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fputs ("Fetching new messages", stdout);
|
||||
fflush (stdout);
|
||||
for (cur = imap->msgs; cur; cur = cur->next)
|
||||
{
|
||||
if (!cur->processed)
|
||||
{
|
||||
/* new message on server */
|
||||
fputs (".", stdout);
|
||||
fflush (stdout);
|
||||
|
||||
/* create new file */
|
||||
snprintf (path, sizeof (path), "%s/tmp/%s.%ld_%d.%d.UID%d",
|
||||
mbox->path, Hostname, time (0), MaildirCount++,
|
||||
getpid (), cur->uid);
|
||||
|
||||
if (cur->flags)
|
||||
{
|
||||
/* append flags */
|
||||
snprintf (path + strlen (path), sizeof (path) - strlen (path),
|
||||
":2,%s%s%s%s",
|
||||
(cur->flags & D_FLAGGED) ? "F" : "",
|
||||
(cur->flags & D_ANSWERED) ? "R" : "",
|
||||
(cur->flags & D_SEEN) ? "S" : "",
|
||||
(cur->flags & D_DELETED) ? "T" : "");
|
||||
}
|
||||
|
||||
// printf("creating %s\n", path);
|
||||
fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fd < 0)
|
||||
{
|
||||
perror ("open");
|
||||
continue;
|
||||
}
|
||||
|
||||
imap_fetch_message (imap, cur->uid, fd);
|
||||
|
||||
close (fd);
|
||||
|
||||
p = strrchr (path, '/');
|
||||
|
||||
snprintf (newpath, sizeof (newpath), "%s/%s%s", mbox->path,
|
||||
(cur->flags & D_SEEN) ? "cur" : "new", p);
|
||||
|
||||
// printf ("moving %s to %s\n", path, newpath);
|
||||
|
||||
if (rename (path, newpath))
|
||||
perror ("rename");
|
||||
}
|
||||
}
|
||||
puts ("");
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user