Browse Source

PREAUTH support from Oswald Buddenhagen <ossi@kde.org>

Added Tunnel directive to allow the user to specify a shell command to run
to set up an IMAP connection in place of a TCP socket (eg., to run over
an SSH session).
Michael Elkins 23 năm trước cách đây
mục cha
commit
fe438026b0
7 tập tin đã thay đổi với 624 bổ sung526 xóa
  1. 7 0
      config.c
  2. 1 1
      configure.in
  3. 593 520
      imap.c
  4. 6 1
      isync.1
  5. 6 3
      isync.h
  6. 10 0
      isyncrc.sample
  7. 1 1
      main.c

+ 7 - 0
config.c

@@ -230,6 +230,13 @@ load_config (const char *where)
 	    else
 		global.copy_deleted_to = strdup (val);
 	}
+	else if (!strcasecmp ("Tunnel", cmd))
+	{
+	    if (*cur)
+		(*cur)->tunnel = strdup (val);
+	    else
+		global.tunnel = strdup (val);
+	}
 	else if (!strcasecmp ("Expunge", cmd))
 	{
 	    if (*cur)

+ 1 - 1
configure.in

@@ -1,5 +1,5 @@
 AC_INIT(isync.h)
-AM_INIT_AUTOMAKE(isync,0.8)
+AM_INIT_AUTOMAKE(isync,0.9)
 AM_PROG_CC_STDC
 AC_ARG_WITH(ssl-dir, [  --with-ssl-dir=DIR	location where openssl is insalled],
 	[if test -d $withval/lib; then

+ 593 - 520
imap.c

@@ -179,7 +179,7 @@ socket_read (Socket_t * sock, char *buf, size_t len)
     if (sock->use_ssl)
 	return SSL_read (sock->ssl, buf, len);
 #endif
-    return read (sock->fd, buf, len);
+    return read (sock->rdfd, buf, len);
 }
 
 static int
@@ -189,7 +189,7 @@ socket_write (Socket_t * sock, char *buf, size_t len)
     if (sock->use_ssl)
 	return SSL_write (sock->ssl, buf, len);
 #endif
-    return write (sock->fd, buf, len);
+    return write (sock->wrfd, buf, len);
 }
 
 static void
@@ -455,8 +455,8 @@ imap_exec (imap_t * imap, const char *fmt, ...)
 		imap->ns_shared = parse_list (cmd, 0);
 	    }
 	    else if (!strcmp ("OK", arg) || !strcmp ("BAD", arg) ||
-		     !strcmp ("NO", arg) || !strcmp ("PREAUTH", arg) ||
-		     !strcmp ("BYE", arg))
+		     !strcmp ("NO", arg) || !strcmp ("BYE", arg) ||
+		     !strcmp ("PREAUTH", arg))
 	    {
 		parse_response_code (imap, cmd);
 	    }
@@ -554,635 +554,708 @@ imap_exec (imap_t * imap, const char *fmt, ...)
  * mailbox.
  */
 imap_t *
-imap_open (config_t * box, unsigned int minuid, imap_t * imap)
+imap_open (config_t * box, unsigned int minuid, imap_t * imap, int flags)
 {
-    int ret;
-    int s;
-    struct sockaddr_in addr;
-    struct hostent *he;
-    int reuse = 0;
+  int ret;
+  int s;
+  struct sockaddr_in addr;
+  struct hostent *he;
+  char *arg, *rsp;
+  int reuse = 0;
+  int preauth = 0;
 #if HAVE_LIBSSL
-    int use_ssl = 0;
+  int use_ssl = 0;
 #endif
 
-    if (imap)
-    {
-	/* determine whether or not we can reuse the existing session */
-	if (strcmp (box->host, imap->box->host) ||
-	    strcmp (box->user, imap->box->user) ||
-	    box->port != imap->box->port
+  if (imap)
+  {
+    /* determine whether or not we can reuse the existing session */
+    if (strcmp (box->host, imap->box->host) ||
+	strcmp (box->user, imap->box->user) ||
+	box->port != imap->box->port
 #if HAVE_LIBSSL
-	    /* ensure that security requirements are met */
-	    || (box->require_ssl ^ imap->box->require_ssl)
-	    || (box->require_cram ^ imap->box->require_cram)
+	/* ensure that security requirements are met */
+	|| (box->require_ssl ^ imap->box->require_ssl)
+	|| (box->require_cram ^ imap->box->require_cram)
 #endif
-	    )
-	{
-	    /* can't reuse */
-	    imap_close (imap);
-	    imap = 0;
-	}
-	else
-	{
-	    reuse = 1;
-	    /* reset mailbox-specific state info */
-	    imap->recent = 0;
-	    imap->deleted = 0;
-	    imap->count = 0;
-	    imap->maxuid = 0;
-	    free_message (imap->msgs);
-	    imap->msgs = 0;
-	}
+       )
+    {
+      /* can't reuse */
+      imap_close (imap);
+      imap = 0;
     }
-
-    if (!imap)
+    else
     {
-	imap = calloc (1, sizeof (imap_t));
-	imap->sock = calloc (1, sizeof (Socket_t));
-	imap->buf = calloc (1, sizeof (buffer_t));
-	imap->buf->sock = imap->sock;
+      reuse = 1;
+      /* reset mailbox-specific state info */
+      imap->recent = 0;
+      imap->deleted = 0;
+      imap->count = 0;
+      imap->maxuid = 0;
+      free_message (imap->msgs);
+      imap->msgs = 0;
     }
+  }
 
-    imap->box = box;
-    imap->minuid = minuid;
-    imap->prefix = "";
+  if (!imap)
+  {
+    imap = calloc (1, sizeof (imap_t));
+    imap->sock = calloc (1, sizeof (Socket_t));
+    imap->buf = calloc (1, sizeof (buffer_t));
+    imap->buf->sock = imap->sock;
+  }
 
-    if (!reuse)
-    {
-	/* open connection to IMAP server */
+  imap->box = box;
+  imap->minuid = minuid;
+  imap->prefix = "";
 
-	memset (&addr, 0, sizeof (addr));
-	addr.sin_port = htons (box->port);
-	addr.sin_family = AF_INET;
+  if (!reuse)
+  {
+    /* open connection to IMAP server */
 
-	printf ("Resolving %s... ", box->host);
-	fflush (stdout);
-	he = gethostbyname (box->host);
-	if (!he)
+    if (box->tunnel)
+    {
+      int a[2];
+      int b[2];
+
+      printf ("Executing: %s...", box->tunnel);
+      fflush (stdout);
+
+      if (pipe (a))
+      {
+      }
+      if (pipe (b))
+      {
+      }
+
+      if (fork () == 0)
+      {
+	if (dup2 (a[0],0))
 	{
-	    perror ("gethostbyname");
-	    return 0;
+	  _exit(127);
 	}
-	puts ("ok");
+	close (a[1]);
+	if (dup2 (b[1],1))
+	{
+	  _exit (127);
+	}
+	close (b[0]);
+	execl ("/bin/sh","sh","-c", box->tunnel);
+	_exit (127);
+      }
 
-	addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
+      close (a[0]);
+      close (b[1]);
 
-	s = socket (PF_INET, SOCK_STREAM, 0);
+      imap->sock->rdfd = b[0];
+      imap->sock->wrfd = a[1];
 
-	printf ("Connecting to %s:%hu... ", inet_ntoa (addr.sin_addr),
-		ntohs (addr.sin_port));
-	fflush (stdout);
-	if (connect (s, (struct sockaddr *) &addr, sizeof (addr)))
-	{
-	    perror ("connect");
-	    exit (1);
-	}
-	puts ("ok");
+      puts ("ok");
+    }
+    else
+    {
+      memset (&addr, 0, sizeof (addr));
+      addr.sin_port = htons (box->port);
+      addr.sin_family = AF_INET;
+
+      printf ("Resolving %s... ", box->host);
+      fflush (stdout);
+      he = gethostbyname (box->host);
+      if (!he)
+      {
+	perror ("gethostbyname");
+	return 0;
+      }
+      puts ("ok");
 
-	imap->sock->fd = s;
+      addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
+
+      s = socket (PF_INET, SOCK_STREAM, 0);
+
+      printf ("Connecting to %s:%hu... ", inet_ntoa (addr.sin_addr),
+	      ntohs (addr.sin_port));
+      fflush (stdout);
+      if (connect (s, (struct sockaddr *) &addr, sizeof (addr)))
+      {
+	perror ("connect");
+	exit (1);
+      }
+      puts ("ok");
+
+      imap->sock->rdfd = s;
+      imap->sock->wrfd = s;
     }
+  }
 
-    do
+  do
+  {
+    /* read the greeting string */
+    if (buffer_gets (imap->buf, &rsp))
     {
-	/* if we are reusing the existing connection, we can skip the
-	 * authentication steps.
-	 */
-	if (!reuse)
-	{
-#if HAVE_LIBSSL
-	    if (box->use_imaps)
-		use_ssl = 1;
-	    else
-	    {
-		/* let's see what this puppy can do... */
-		if ((ret = imap_exec (imap, "CAPABILITY")))
-		    break;
+      puts ("Error, no greeting response");
+      ret = -1;
+      break;
+    }
+    if (Verbose)
+      puts (rsp);
+    arg = next_arg (&rsp);
+    if (!arg || *arg != '*' || (arg = next_arg (&rsp)) == NULL)
+    {
+      puts ("Error, invalid greeting response");
+      ret = -1;
+      break;
+    }
+    if (!strcmp ("PREAUTH", arg))
+      preauth = 1;
+    else if (strcmp ("OK", arg) != 0)
+    {
+      puts ("Error, unknown greeting response");
+      ret = -1;
+      break;
+    }
 
-		if (box->use_sslv2 || box->use_sslv3 || box->use_tlsv1)
-		{
-		    /* always try to select SSL support if available */
-		    if (imap->have_starttls)
-		    {
-			if ((ret = imap_exec (imap, "STARTTLS")))
-			    break;
-			use_ssl = 1;
-		    }
-		}
-	    }
+    /* if we are reusing the existing connection, we can skip the
+     * authentication steps.
+     */
+    if (!reuse)
+    {
+#if HAVE_LIBSSL
+      if (box->use_imaps)
+	use_ssl = 1;
+      else
+      {
+	/* let's see what this puppy can do... */
+	if ((ret = imap_exec (imap, "CAPABILITY")))
+	  break;
+
+	if (box->use_sslv2 || box->use_sslv3 || box->use_tlsv1)
+	{
+	  /* always try to select SSL support if available */
+	  if (imap->have_starttls)
+	  {
+	    if ((ret = imap_exec (imap, "STARTTLS")))
+	      break;
+	    use_ssl = 1;
+	  }
+	}
+      }
 
-	    if (!use_ssl)
-	    {
-		if (box->require_ssl)
-		{
-		    puts ("Error, SSL support not available");
-		    ret = -1;
-		    break;
-		}
-		else
-		    puts ("Warning, SSL support not available");
-	    }
-	    else
-	    {
-		/* initialize SSL */
-		if (init_ssl (box))
-		{
-		    ret = -1;
-		    break;
-		}
+      if (!use_ssl)
+      {
+	if (box->require_ssl)
+	{
+	  puts ("Error, SSL support not available");
+	  ret = -1;
+	  break;
+	}
+	else
+	  puts ("Warning, SSL support not available");
+      }
+      else
+      {
+	/* initialize SSL */
+	if (init_ssl (box))
+	{
+	  ret = -1;
+	  break;
+	}
 
-		imap->sock->ssl = SSL_new (SSLContext);
-		SSL_set_fd (imap->sock->ssl, imap->sock->fd);
-		ret = SSL_connect (imap->sock->ssl);
-		if (ret <= 0)
-		{
-		    socket_perror ("connect", imap->sock, ret);
-		    break;
-		}
+	imap->sock->ssl = SSL_new (SSLContext);
+	SSL_set_fd (imap->sock->ssl, imap->sock->rdfd);
+	ret = SSL_connect (imap->sock->ssl);
+	if (ret <= 0)
+	{
+	  socket_perror ("connect", imap->sock, ret);
+	  break;
+	}
 
-		/* verify the server certificate */
-		if ((ret = verify_cert (imap->sock->ssl)))
-		    break;
+	/* verify the server certificate */
+	if ((ret = verify_cert (imap->sock->ssl)))
+	  break;
 
-		/* to conform to RFC2595 we need to forget all information
-		 * retrieved from CAPABILITY invocations before STARTTLS.
-		 */
-		imap->have_namespace = 0;
-		imap->have_cram = 0;
-		imap->have_starttls = 0;
+	/* to conform to RFC2595 we need to forget all information
+	 * retrieved from CAPABILITY invocations before STARTTLS.
+	 */
+	imap->have_namespace = 0;
+	imap->have_cram = 0;
+	imap->have_starttls = 0;
 
-		imap->sock->use_ssl = 1;
-		puts ("SSL support enabled");
+	imap->sock->use_ssl = 1;
+	puts ("SSL support enabled");
 
-		if ((ret = imap_exec (imap, "CAPABILITY")))
-		    break;
-	    }
+	if ((ret = imap_exec (imap, "CAPABILITY")))
+	  break;
+      }
 #else
-	    if ((ret = imap_exec (imap, "CAPABILITY")))
-		break;
+      if ((ret = imap_exec (imap, "CAPABILITY")))
+	break;
 #endif
 
-	    puts ("Logging in...");
+      if (!preauth)
+      {
+	puts ("Logging in...");
 #if HAVE_LIBSSL
-	    if (imap->have_cram)
-	    {
-		puts ("Authenticating with CRAM-MD5");
-		imap->cram = 1;
-		if ((ret = imap_exec (imap, "AUTHENTICATE CRAM-MD5")))
-		    break;
-	    }
-	    else if (imap->box->require_cram)
-	    {
-		puts
-		    ("Error, CRAM-MD5 authentication is not supported by server");
-		ret = -1;
-		break;
-	    }
-	    else
+	if (imap->have_cram)
+	{
+	  puts ("Authenticating with CRAM-MD5");
+	  imap->cram = 1;
+	  if ((ret = imap_exec (imap, "AUTHENTICATE CRAM-MD5")))
+	    break;
+	}
+	else if (imap->box->require_cram)
+	{
+	  puts
+	    ("Error, CRAM-MD5 authentication is not supported by server");
+	  ret = -1;
+	  break;
+	}
+	else
 #endif
-	    {
+	{
 #if HAVE_LIBSSL
-		if (!use_ssl)
+	  if (!use_ssl)
 #endif
-		    puts
-			("*** Warning *** Password is being sent in the clear");
-		if (
-		    (ret =
-		     imap_exec (imap, "LOGIN \"%s\" \"%s\"", box->user,
-				box->pass)))
-		{
-		    puts ("Error, LOGIN failed");
-		    break;
-		}
-	    }
-
-	    /* get NAMESPACE info */
-	    if (box->use_namespace && imap->have_namespace)
-	    {
-		if ((ret = imap_exec (imap, "NAMESPACE")))
-		    break;
-	    }
-	}			/* !reuse */
-
-	/* XXX for now assume personal namespace */
-	if (imap->box->use_namespace && is_list (imap->ns_personal) &&
-	    is_list (imap->ns_personal->child) &&
-	    is_atom (imap->ns_personal->child->child))
-	{
-	    imap->prefix = imap->ns_personal->child->child->val;
-	}
-
-	fputs ("Selecting mailbox... ", stdout);
-	fflush (stdout);
-	if (
-	    (ret =
-	     imap_exec (imap, "SELECT \"%s%s\"", imap->prefix, box->box)))
+	    puts
+	      ("*** Warning *** Password is being sent in the clear");
+	  if (
+	      (ret =
+	       imap_exec (imap, "LOGIN \"%s\" \"%s\"", box->user,
+			  box->pass)))
+	  {
+	    puts ("Error, LOGIN failed");
 	    break;
-	printf ("%d messages, %d recent\n", imap->count, imap->recent);
-
-	puts ("Reading IMAP mailbox index");
-	if (imap->count > 0)
-	{
-	    if ((ret = imap_exec (imap, "UID FETCH %d:* (FLAGS RFC822.SIZE)",
-				  imap->minuid)))
-		break;
+	  }
 	}
+      }
+
+      /* get NAMESPACE info */
+      if (box->use_namespace && imap->have_namespace)
+      {
+	if ((ret = imap_exec (imap, "NAMESPACE")))
+	  break;
+      }
+    }			/* !reuse */
+
+    /* XXX for now assume personal namespace */
+    if (imap->box->use_namespace && is_list (imap->ns_personal) &&
+	is_list (imap->ns_personal->child) &&
+	is_atom (imap->ns_personal->child->child))
+    {
+      imap->prefix = imap->ns_personal->child->child->val;
     }
-    while (0);
 
-    if (ret)
+    fputs ("Selecting mailbox... ", stdout);
+    fflush (stdout);
+    if ((ret = imap_exec (imap, "SELECT \"%s%s\"", imap->prefix, box->box)))
+      break;
+    printf ("%d messages, %d recent\n", imap->count, imap->recent);
+
+    puts ("Reading IMAP mailbox index");
+    if (imap->count > 0)
     {
-	imap_close (imap);
-	imap = 0;
+      if ((ret = imap_exec (imap, "UID FETCH %d:* (FLAGS RFC822.SIZE)",
+			    imap->minuid)))
+	break;
     }
+  }
+  while (0);
 
-    return imap;
+  if (ret)
+  {
+    imap_close (imap);
+    imap = 0;
+  }
+
+  return imap;
 }
 
 void
 imap_close (imap_t * imap)
 {
-    if (imap)
-    {
-	imap_exec (imap, "LOGOUT");
-	close (imap->sock->fd);
-	free (imap->sock);
-	free (imap->buf);
-	free_message (imap->msgs);
-	memset (imap, 0xff, sizeof (imap_t));
-	free (imap);
-    }
+  if (imap)
+  {
+    imap_exec (imap, "LOGOUT");
+    close (imap->sock->rdfd);
+    if (imap->sock->rdfd != imap->sock->wrfd)
+      close (imap->sock->wrfd);
+    free (imap->sock);
+    free (imap->buf);
+    free_message (imap->msgs);
+    memset (imap, 0xff, sizeof (imap_t));
+    free (imap);
+  }
 }
 
 /* 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;
-    ssize_t n;
-
-    while (start < len)
+  size_t start = 0;
+  size_t end = 0;
+  ssize_t n;
+
+  while (start < len)
+  {
+    while (end < len && buf[end] != '\r')
+      end++;
+    n = write (fd, buf + start, end - start);
+    if (n == -1)
     {
-	while (end < len && buf[end] != '\r')
-	    end++;
-	n = write (fd, buf + start, end - start);
-	if (n == -1)
-	{
-	    perror ("write");
-	    return -1;
-	}
-	else if ((size_t) n != end - start)
-	{
-	    /* short write, try again */
-	    start += n;
-	}
-	else
-	{
-	    /* write complete */
-	    end++;
-	    start = end;
-	}
+      perror ("write");
+      return -1;
     }
-    return 0;
+    else if ((size_t) n != end - start)
+    {
+      /* short write, try again */
+      start += n;
+    }
+    else
+    {
+      /* write complete */
+      end++;
+      start = end;
+    }
+  }
+  return 0;
 }
 
 static int
 send_server (Socket_t * sock, const char *fmt, ...)
 {
-    char buf[128];
-    char cmd[128];
-    va_list ap;
-    int n;
-
-    va_start (ap, fmt);
-    vsnprintf (buf, sizeof (buf), fmt, ap);
-    va_end (ap);
-
-    snprintf (cmd, sizeof (cmd), "%d %s\r\n", ++Tag, buf);
-    n = socket_write (sock, cmd, strlen (cmd));
-    if (n <= 0)
-    {
-	socket_perror ("write", sock, n);
-	return -1;
-    }
-
-    if (Verbose)
-	fputs (cmd, stdout);
-
-    return 0;
+  char buf[128];
+  char cmd[128];
+  va_list ap;
+  int n;
+
+  va_start (ap, fmt);
+  vsnprintf (buf, sizeof (buf), fmt, ap);
+  va_end (ap);
+
+  snprintf (cmd, sizeof (cmd), "%d %s\r\n", ++Tag, buf);
+  n = socket_write (sock, cmd, strlen (cmd));
+  if (n <= 0)
+  {
+    socket_perror ("write", sock, n);
+    return -1;
+  }
+
+  if (Verbose)
+    fputs (cmd, stdout);
+
+  return 0;
 }
 
 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->sock, "UID FETCH %d BODY.PEEK[]", uid);
-
-    for (;;)
-    {
-	if (buffer_gets (imap->buf, &cmd))
-	    return -1;
+  char *cmd;
+  char *arg;
+  size_t bytes;
+  size_t n;
+  char buf[1024];
 
-	if (Verbose)
-	    puts (cmd);
-
-	if (*cmd == '*')
-	{
-	    /* need to figure out how long the message is
-	     * * <msgno> FETCH (RFC822 {<size>}
-	     */
+  send_server (imap->sock, "UID FETCH %d BODY.PEEK[]", uid);
 
-	    next_arg (&cmd);	/* * */
-	    next_arg (&cmd);	/* <msgno> */
-	    arg = next_arg (&cmd);	/* FETCH */
+  for (;;)
+  {
+    if (buffer_gets (imap->buf, &cmd))
+      return -1;
 
-	    if (strcasecmp ("FETCH", arg) != 0)
-	    {
-		/* this is likely an untagged response, such as when new
-		 * mail arrives in the middle of the session.  just skip
-		 * it for now.
-		 * 
-		 * eg.,
-		 * "* 4000 EXISTS"
-		 * "* 2 RECENT"
-		 *
-		 */
-		printf ("skipping untagged response: %s\n", arg);
-		continue;
-	    }
-
-	    while ((arg = next_arg (&cmd)) && *arg != '{')
-		;
-	    if (!arg)
-	    {
-		puts ("parse error getting size");
-		return -1;
-	    }
-	    bytes = strtol (arg + 1, 0, 10);
-
-	    /* 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.
-	     */
-	    if (write_strip (fd, imap->buf->buf + imap->buf->offset, n))
-	    {
-		/* write failed, message is not delivered */
-		return -1;
-	    }
+    if (Verbose)
+      puts (cmd);
 
-	    bytes -= n;
+    if (*cmd == '*')
+    {
+      /* need to figure out how long the message is
+       * * <msgno> FETCH (RFC822 {<size>}
+       */
+
+      next_arg (&cmd);	/* * */
+      next_arg (&cmd);	/* <msgno> */
+      arg = next_arg (&cmd);	/* FETCH */
+
+      if (strcasecmp ("FETCH", arg) != 0)
+      {
+	/* this is likely an untagged response, such as when new
+	 * mail arrives in the middle of the session.  just skip
+	 * it for now.
+	 * 
+	 * eg.,
+	 * "* 4000 EXISTS"
+	 * "* 2 RECENT"
+	 *
+	 */
+	printf ("skipping untagged response: %s\n", arg);
+	continue;
+      }
+
+      while ((arg = next_arg (&cmd)) && *arg != '{')
+	;
+      if (!arg)
+      {
+	puts ("parse error getting size");
+	return -1;
+      }
+      bytes = strtol (arg + 1, 0, 10);
+
+      /* 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.
+       */
+      if (write_strip (fd, imap->buf->buf + imap->buf->offset, n))
+      {
+	/* write failed, message is not delivered */
+	return -1;
+      }
 
-	    /* mark that we used part of the buffer */
-	    imap->buf->offset += n;
+      bytes -= n;
 
-	    /* now read the rest of the message */
-	    while (bytes > 0)
-	    {
-		n = bytes;
-		if (n > sizeof (buf))
-		    n = sizeof (buf);
-		n = socket_read (imap->sock, buf, n);
-		if (n > 0)
-		{
-		    if (write_strip (fd, buf, n))
-		    {
-			/* write failed */
-			return -1;
-		    }
-		    bytes -= n;
-		}
-		else
-		{
-		    socket_perror ("read", imap->sock, n);
-		    return -1;
-		}
-	    }
+      /* mark that we used part of the buffer */
+      imap->buf->offset += n;
 
-	    buffer_gets (imap->buf, &cmd);
-	    if (Verbose)
-		puts (cmd);	/* last part of line */
+      /* now read the rest of the message */
+      while (bytes > 0)
+      {
+	n = bytes;
+	if (n > sizeof (buf))
+	  n = sizeof (buf);
+	n = socket_read (imap->sock, buf, n);
+	if (n > 0)
+	{
+	  if (write_strip (fd, buf, n))
+	  {
+	    /* write failed */
+	    return -1;
+	  }
+	  bytes -= n;
 	}
 	else
 	{
-	    arg = next_arg (&cmd);
-	    if (!arg || (size_t) atoi (arg) != Tag)
-	    {
-		puts ("wrong tag");
-		return -1;
-	    }
-	    arg = next_arg (&cmd);
-	    if (!strcmp ("OK", arg))
-		return 0;
-	    return -1;
+	  socket_perror ("read", imap->sock, n);
+	  return -1;
 	}
+      }
+
+      buffer_gets (imap->buf, &cmd);
+      if (Verbose)
+	puts (cmd);	/* last part of line */
     }
-    /* not reached */
+    else
+    {
+      arg = next_arg (&cmd);
+      if (!arg || (size_t) atoi (arg) != Tag)
+      {
+	puts ("wrong tag");
+	return -1;
+      }
+      arg = next_arg (&cmd);
+      if (!strcmp ("OK", arg))
+	return 0;
+      return -1;
+    }
+  }
+  /* not reached */
 }
 
 /* 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);
+  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");
+  return imap_exec (imap, "EXPUNGE");
 }
 
 int
 imap_copy_message (imap_t * imap, unsigned int uid, const char *mailbox)
 {
-    return imap_exec (imap, "UID COPY %u \"%s%s\"", uid, imap->prefix,
-		      mailbox);
+  return imap_exec (imap, "UID COPY %u \"%s%s\"", uid, imap->prefix,
+		    mailbox);
 }
 
 int
 imap_append_message (imap_t * imap, int fd, message_t * msg)
 {
-    char buf[1024];
-    size_t len;
-    size_t sofar = 0;
-    int lines = 0;
-    char flagstr[128];
-    char *s;
-    size_t i;
-    size_t start, end;
-    char *arg;
-
-    /* ugh, we need to count the number of newlines */
-    while (sofar < msg->size)
+  char buf[1024];
+  size_t len;
+  size_t sofar = 0;
+  int lines = 0;
+  char flagstr[128];
+  char *s;
+  size_t i;
+  size_t start, end;
+  char *arg;
+
+  /* ugh, we need to count the number of newlines */
+  while (sofar < msg->size)
+  {
+    len = msg->size - sofar;
+    if (len > sizeof (buf))
+      len = sizeof (buf);
+    len = read (fd, buf, len);
+    if (len == (size_t) - 1)
     {
-	len = msg->size - sofar;
-	if (len > sizeof (buf))
-	    len = sizeof (buf);
-	len = read (fd, buf, len);
-	if (len == (size_t) - 1)
-	{
-	    perror ("read");
-	    return -1;
-	}
-	for (i = 0; i < len; i++)
-	    if (buf[i] == '\n')
-		lines++;
-	sofar += len;
+      perror ("read");
+      return -1;
     }
-
-    flagstr[0] = 0;
-    if (msg->flags)
+    for (i = 0; i < len; i++)
+      if (buf[i] == '\n')
+	lines++;
+    sofar += len;
+  }
+
+  flagstr[0] = 0;
+  if (msg->flags)
+  {
+    strcpy (flagstr, "(");
+    if (msg->flags & D_DELETED)
+      snprintf (flagstr + strlen (flagstr),
+		sizeof (flagstr) - strlen (flagstr), "%s\\Deleted",
+		flagstr[1] ? " " : "");
+    if (msg->flags & D_ANSWERED)
+      snprintf (flagstr + strlen (flagstr),
+		sizeof (flagstr) - strlen (flagstr), "%s\\Answered",
+		flagstr[1] ? " " : "");
+    if (msg->flags & D_SEEN)
+      snprintf (flagstr + strlen (flagstr),
+		sizeof (flagstr) - strlen (flagstr), "%s\\Seen",
+		flagstr[1] ? " " : "");
+    if (msg->flags & D_FLAGGED)
+      snprintf (flagstr + strlen (flagstr),
+		sizeof (flagstr) - strlen (flagstr), "%s\\Flagged",
+		flagstr[1] ? " " : "");
+    if (msg->flags & D_DRAFT)
+      snprintf (flagstr + strlen (flagstr),
+		sizeof (flagstr) - strlen (flagstr), "%s\\Draft",
+		flagstr[1] ? " " : "");
+    snprintf (flagstr + strlen (flagstr),
+	      sizeof (flagstr) - strlen (flagstr), ") ");
+  }
+
+  send_server (imap->sock, "APPEND %s%s %s{%d}",
+	       imap->prefix, imap->box->box, flagstr, msg->size + lines);
+
+  if (buffer_gets (imap->buf, &s))
+    return -1;
+  if (Verbose)
+    puts (s);
+
+  if (*s != '+')
+  {
+    puts ("Error, expected `+' from server (aborting)");
+    return -1;
+  }
+
+  /* rewind */
+  lseek (fd, 0, 0);
+
+  sofar = 0;
+  while (sofar < msg->size)
+  {
+    len = msg->size - sofar;
+    if (len > sizeof (buf))
+      len = sizeof (buf);
+    len = read (fd, buf, len);
+    if (len == (size_t) - 1)
+      return -1;
+    start = 0;
+    while (start < len)
     {
-	strcpy (flagstr, "(");
-	if (msg->flags & D_DELETED)
-	    snprintf (flagstr + strlen (flagstr),
-		      sizeof (flagstr) - strlen (flagstr), "%s\\Deleted",
-		      flagstr[1] ? " " : "");
-	if (msg->flags & D_ANSWERED)
-	    snprintf (flagstr + strlen (flagstr),
-		      sizeof (flagstr) - strlen (flagstr), "%s\\Answered",
-		      flagstr[1] ? " " : "");
-	if (msg->flags & D_SEEN)
-	    snprintf (flagstr + strlen (flagstr),
-		      sizeof (flagstr) - strlen (flagstr), "%s\\Seen",
-		      flagstr[1] ? " " : "");
-	if (msg->flags & D_FLAGGED)
-	    snprintf (flagstr + strlen (flagstr),
-		      sizeof (flagstr) - strlen (flagstr), "%s\\Flagged",
-		      flagstr[1] ? " " : "");
-	if (msg->flags & D_DRAFT)
-	    snprintf (flagstr + strlen (flagstr),
-		      sizeof (flagstr) - strlen (flagstr), "%s\\Draft",
-		      flagstr[1] ? " " : "");
-	snprintf (flagstr + strlen (flagstr),
-		  sizeof (flagstr) - strlen (flagstr), ") ");
+      end = start;
+      while (end < len && buf[end] != '\n')
+	end++;
+      if (start != end)
+	socket_write (imap->sock, buf + start, end - start);
+      /* only send a crlf if we actually hit the end of a line.  we
+       * might be in the middle of a line in which case we don't
+       * send one.
+       */
+      if (end != len)
+	socket_write (imap->sock, "\r\n", 2);
+      start = end + 1;
     }
+    sofar += len;
+  }
+  socket_write (imap->sock, "\r\n", 2);
 
-    send_server (imap->sock, "APPEND %s%s %s{%d}",
-		 imap->prefix, imap->box->box, flagstr, msg->size + lines);
-
+  for (;;)
+  {
     if (buffer_gets (imap->buf, &s))
-	return -1;
+      return -1;
+
     if (Verbose)
-	puts (s);
+      puts (s);
 
-    if (*s != '+')
+    arg = next_arg (&s);
+    if (*arg == '*')
     {
-	puts ("Error, expected `+' from server (aborting)");
-	return -1;
+      /* XXX just ignore it for now */
     }
-
-    /* rewind */
-    lseek (fd, 0, 0);
-
-    sofar = 0;
-    while (sofar < msg->size)
+    else if (atoi (arg) != (int) Tag)
     {
-	len = msg->size - sofar;
-	if (len > sizeof (buf))
-	    len = sizeof (buf);
-	len = read (fd, buf, len);
-	if (len == (size_t) - 1)
-	    return -1;
-	start = 0;
-	while (start < len)
-	{
-	    end = start;
-	    while (end < len && buf[end] != '\n')
-		end++;
-	    if (start != end)
-		socket_write (imap->sock, buf + start, end - start);
-	    /* only send a crlf if we actually hit the end of a line.  we
-	     * might be in the middle of a line in which case we don't
-	     * send one.
-	     */
-	    if (end != len)
-		socket_write (imap->sock, "\r\n", 2);
-	    start = end + 1;
-	}
-	sofar += len;
+      puts ("wrong tag");
+      return -1;
     }
-    socket_write (imap->sock, "\r\n", 2);
-
-    for (;;)
+    else
     {
-	if (buffer_gets (imap->buf, &s))
-	    return -1;
-
-	if (Verbose)
-	    puts (s);
+      int uid;
 
-	arg = next_arg (&s);
-	if (*arg == '*')
-	{
-	    /* XXX just ignore it for now */
-	}
-	else if (atoi (arg) != (int) Tag)
-	{
-	    puts ("wrong tag");
-	    return -1;
-	}
-	else
-	{
-	    int uid;
-
-	    arg = next_arg (&s);
-	    if (strcmp (arg, "OK"))
-		return -1;
-	    arg = next_arg (&s);
-	    if (*arg != '[')
-		break;
-	    arg++;
-	    if (strcasecmp ("APPENDUID", arg))
-	    {
-		puts ("Error, expected APPENDUID");
-		break;
-	    }
-	    arg = next_arg (&s);
-	    if (!arg)
-		break;
-	    if (atoi (arg) != (int) imap->uidvalidity)
-	    {
-		puts ("Error, UIDVALIDITY doesn't match APPENDUID");
-		return -1;
-	    }
-	    arg = next_arg (&s);
-	    if (!arg)
-		break;
-	    uid = strtol (arg, &s, 10);
-	    if (*s != ']')
-	    {
-		/* parse error */
-		break;
-	    }
-	    return uid;
-	}
+      arg = next_arg (&s);
+      if (strcmp (arg, "OK"))
+	return -1;
+      arg = next_arg (&s);
+      if (*arg != '[')
+	break;
+      arg++;
+      if (strcasecmp ("APPENDUID", arg))
+      {
+	puts ("Error, expected APPENDUID");
+	break;
+      }
+      arg = next_arg (&s);
+      if (!arg)
+	break;
+      if (atoi (arg) != (int) imap->uidvalidity)
+      {
+	puts ("Error, UIDVALIDITY doesn't match APPENDUID");
+	return -1;
+      }
+      arg = next_arg (&s);
+      if (!arg)
+	break;
+      uid = strtol (arg, &s, 10);
+      if (*s != ']')
+      {
+	/* parse error */
+	break;
+      }
+      return uid;
     }
+  }
 
-    return 0;
+  return 0;
 }

+ 6 - 1
isync.1

@@ -16,7 +16,7 @@
 \"  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 "2002 Jan 16"
+.TH isync 1 "2002 Apr 19"
 ..
 .SH NAME
 isync - synchronize IMAP4 and maildir mailboxes
@@ -224,6 +224,11 @@ is 0, the maximum file size is
 .B unlimited.
 ..
 .TP
+\fBTunnel\fR \fIcommand\fR
+Specify a command to run to establish a connection rather than opening a TCP
+socket.  This allows you to run an IMAP session over an SSH tunnel, for
+example.
+.TP
 \fBUseNamespace\fR \fIyes|no\fR
 Selects whether
 .B isync

+ 6 - 3
isync.h

@@ -30,7 +30,8 @@
 
 typedef struct
 {
-    int fd;
+    int rdfd;	/* read filedes */
+    int wrfd;	/* write filedes */
 #if HAVE_LIBSSL
     SSL *ssl;
     unsigned int use_ssl:1;
@@ -61,6 +62,7 @@ struct config
     char *box;
     char *alias;
     char *copy_deleted_to;
+    char *tunnel;
     unsigned int max_messages;
     off_t max_size;
     config_t *next;
@@ -119,7 +121,8 @@ typedef struct _list list_t;
 #define NIL	(void*)0x1
 #define LIST	(void*)0x2
 
-struct _list {
+struct _list
+{
     char *val;
     list_t *next;
     list_t *child;
@@ -187,7 +190,7 @@ int imap_copy_message (imap_t * imap, unsigned int uid, const char *mailbox);
 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 *, unsigned int, imap_t *);
+imap_t *imap_open (config_t *, unsigned int, imap_t *, int);
 int imap_append_message (imap_t *, int, message_t *);
 
 mailbox_t *maildir_open (const char *, int flags);

+ 10 - 0
isyncrc.sample

@@ -40,3 +40,13 @@ Host host.play.com
 # use a non-default port for this connection
 Port 6789
 Alias personal
+
+
+###
+### Remote mailbox over a SSH tunnel
+###
+
+Mailbox /home/me/Mail/remote
+Host host.remote.com
+Tunnel "ssh -q host.remote.com /usr/sbin/imapd"
+Alias remote

+ 1 - 1
main.c

@@ -292,7 +292,7 @@ main (int argc, char **argv)
 		break;
 	    }
 
-	    imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap);
+	    imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap, 0);
 	    if (!imap)
 	    {
 		fprintf (stderr, "%s: skipping mailbox due to IMAP error\n",