mirror of https://git.code.sf.net/p/isync/isync
Michael Elkins
24 years ago
commit
f47d0d7c11
14 changed files with 1640 additions and 0 deletions
@ -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)
|
@ -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. |
@ -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) |
@ -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"); |
||||
} |
@ -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. |
@ -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 *); |
@ -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 |
@ -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; |
||||
} |
@ -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); |
||||
} |
@ -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