[evolution-patches] [PATCH] IMAP preauth and subcommand connection.



It's possible to get at an IMAP server by 'ssh $MAILHOST imapd', and in
fact some servers that's the _only_ way to get at them... 

Index: camel/Makefile.am
===================================================================
RCS file: /cvs/gnome/evolution/camel/Makefile.am,v
retrieving revision 1.184
diff -u -p -r1.184 Makefile.am
--- camel/Makefile.am	12 Jun 2003 21:13:56 -0000	1.184
+++ camel/Makefile.am	24 Jun 2003 21:13:26 -0000
@@ -97,6 +97,7 @@ libcamel_la_SOURCES = 				\
 	camel-seekable-substream.c		\
 	camel-service.c				\
 	camel-session.c				\
+	camel-socket-rw.c			\
 	camel-store.c				\
 	camel-store-summary.c			\
 	camel-stream-buffer.c			\
@@ -104,6 +105,7 @@ libcamel_la_SOURCES = 				\
 	camel-stream-fs.c			\
 	camel-stream-mem.c			\
 	camel-stream-null.c			\
+	camel-stream-subcommand.c		\
 	camel-stream.c				\
 	camel-text-index.c			\
 	camel-tcp-stream-raw.c			\
@@ -199,6 +201,7 @@ libcamelinclude_HEADERS =			\
 	camel-seekable-substream.h		\
 	camel-service.h				\
 	camel-session.h				\
+	camel-socket-rw.h			\
 	camel-store.h				\
 	camel-store-summary.h			\
 	camel-stream-buffer.h			\
@@ -206,6 +209,7 @@ libcamelinclude_HEADERS =			\
 	camel-stream-fs.h			\
 	camel-stream-mem.h			\
 	camel-stream-null.h			\
+	camel-stream-subcommand.h		\
 	camel-stream.h				\
 	camel-text-index.h			\
 	camel-tcp-stream-raw.h			\
Index: camel/camel-tcp-stream-raw.c
===================================================================
RCS file: /cvs/gnome/evolution/camel/camel-tcp-stream-raw.c,v
retrieving revision 1.30
diff -u -p -r1.30 camel-tcp-stream-raw.c
--- camel/camel-tcp-stream-raw.c	3 Mar 2003 22:53:15 -0000	1.30
+++ camel/camel-tcp-stream-raw.c	24 Jun 2003 21:13:26 -0000
@@ -35,6 +35,7 @@
 #include <errno.h>
 
 #include "camel-tcp-stream-raw.h"
+#include "camel-socket-rw.h"
 #include "camel-operation.h"
 
 static CamelTcpStreamClass *parent_class = NULL;
@@ -113,109 +114,6 @@ camel_tcp_stream_raw_get_type (void)
 	return type;
 }
 
-#ifdef SIMULATE_FLAKY_NETWORK
-static ssize_t
-flaky_tcp_write (int fd, const char *buffer, size_t buflen)
-{
-	size_t len = buflen;
-	ssize_t nwritten;
-	int val;
-	
-	if (buflen == 0)
-		return 0;
-	
-	val = 1 + (int) (10.0 * rand () / (RAND_MAX + 1.0));
-	
-	switch (val) {
-	case 1:
-		printf ("flaky_tcp_write (%d, ..., %d): (-1) EINTR\n", fd, buflen);
-		errno = EINTR;
-		return -1;
-	case 2:
-		printf ("flaky_tcp_write (%d, ..., %d): (-1) EAGAIN\n", fd, buflen);
-		errno = EAGAIN;
-		return -1;
-	case 3:
-		printf ("flaky_tcp_write (%d, ..., %d): (-1) EWOULDBLOCK\n", fd, buflen);
-		errno = EWOULDBLOCK;
-		return -1;
-	case 4:
-	case 5:
-	case 6:
-		len = 1 + (size_t) (buflen * rand () / (RAND_MAX + 1.0));
-		len = MIN (len, buflen);
-		/* fall through... */
-	default:
-		printf ("flaky_tcp_write (%d, ..., %d): (%d) '%.*s'", fd, buflen, len, (int) len, buffer);
-		nwritten = write (fd, buffer, len);
-		if (nwritten < 0)
-			printf (" errno => %s\n", strerror (errno));
-		else if (nwritten < len)
-			printf (" only wrote %d bytes\n", nwritten);
-		else
-			printf ("\n");
-		
-		return nwritten;
-	}
-}
-
-#define write(fd, buffer, buflen) flaky_tcp_write (fd, buffer, buflen)
-
-static ssize_t
-flaky_tcp_read (int fd, char *buffer, size_t buflen)
-{
-	size_t len = buflen;
-	ssize_t nread;
-	int val;
-	
-	if (buflen == 0)
-		return 0;
-	
-	val = 1 + (int) (10.0 * rand () / (RAND_MAX + 1.0));
-	
-	switch (val) {
-	case 1:
-		printf ("flaky_tcp_read (%d, ..., %d): (-1) EINTR\n", fd, buflen);
-		errno = EINTR;
-		return -1;
-	case 2:
-		printf ("flaky_tcp_read (%d, ..., %d): (-1) EAGAIN\n", fd, buflen);
-		errno = EAGAIN;
-		return -1;
-	case 3:
-		printf ("flaky_tcp_read (%d, ..., %d): (-1) EWOULDBLOCK\n", fd, buflen);
-		errno = EWOULDBLOCK;
-		return -1;
-	case 4:
-	case 5:
-	case 6:
-	case 7:
-	case 8:
-	case 9:
-	case 10:
-		len = 1 + (size_t) (10.0 * rand () / (RAND_MAX + 1.0));
-		len = MIN (len, buflen);
-		/* fall through... */
-	default:
-		printf ("flaky_tcp_read (%d, ..., %d): (%d)", fd, buflen, len);
-		nread = read (fd, buffer, len);
-		if (nread < 0)
-			printf (" errno => %s\n", strerror (errno));
-		else if (nread < len)
-			printf (" only read %d bytes\n", nread);
-		else
-			printf ("\n");
-		
-		return nread;
-	}
-}
-
-#define read(fd, buffer, buflen) flaky_tcp_read (fd, buffer, buflen)
-
-#endif /* SIMULATE_FLAKY_NETWORK */
-
-
-
 /**
  * camel_tcp_stream_raw_new:
  *
@@ -235,129 +133,16 @@ static ssize_t
 stream_read (CamelStream *stream, char *buffer, size_t n)
 {
 	CamelTcpStreamRaw *tcp_stream_raw = CAMEL_TCP_STREAM_RAW (stream);
-	ssize_t nread;
-	int cancel_fd;
-	
-	if (camel_operation_cancel_check (NULL)) {
-		errno = EINTR;
-		return -1;
-	}
-	
-	cancel_fd = camel_operation_cancel_fd (NULL);
-	if (cancel_fd == -1) {
-		do {
-			nread = read (tcp_stream_raw->sockfd, buffer, n);
-		} while (nread == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
-	} else {
-		int error, flags, fdmax;
-		fd_set rdset;
-		
-		flags = fcntl (tcp_stream_raw->sockfd, F_GETFL);
-		fcntl (tcp_stream_raw->sockfd, F_SETFL, flags | O_NONBLOCK);
-		
-		do {
-			FD_ZERO (&rdset);
-			FD_SET (tcp_stream_raw->sockfd, &rdset);
-			FD_SET (cancel_fd, &rdset);
-			fdmax = MAX (tcp_stream_raw->sockfd, cancel_fd) + 1;
-			
-			nread = -1;
-			if (select (fdmax, &rdset, 0, 0, NULL) != -1) {
-				if (FD_ISSET (cancel_fd, &rdset)) {
-					fcntl (tcp_stream_raw->sockfd, F_SETFL, flags);
-					errno = EINTR;
-					return -1;
-				}
-				
-				do {
-					nread = read (tcp_stream_raw->sockfd, buffer, n);
-				} while (nread == -1 && errno == EINTR);
-			} else if (errno == EINTR) {
-				errno = EAGAIN;
-			}
-		} while (nread == -1 && (errno == EAGAIN || errno == EWOULDBLOCK));
-		
-		error = errno;
-		fcntl (tcp_stream_raw->sockfd, F_SETFL, flags);
-		errno = error;
-	}
-	
-	return nread;
+
+	return camel_socket_read(tcp_stream_raw->sockfd, buffer, n);
 }
 
 static ssize_t
 stream_write (CamelStream *stream, const char *buffer, size_t n)
 {
 	CamelTcpStreamRaw *tcp_stream_raw = CAMEL_TCP_STREAM_RAW (stream);
-	ssize_t w, written = 0;
-	int cancel_fd;
-	
-	if (camel_operation_cancel_check (NULL)) {
-		errno = EINTR;
-		return -1;
-	}
-	
-	cancel_fd = camel_operation_cancel_fd (NULL);
-	if (cancel_fd == -1) {
-		do {
-			do {
-				w = write (tcp_stream_raw->sockfd, buffer + written, n - written);
-			} while (w == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
-			
-			if (w > 0)
-				written += w;
-		} while (w != -1 && written < n);
-	} else {
-		int error, flags, fdmax;
-		fd_set rdset, wrset;
-		
-		flags = fcntl (tcp_stream_raw->sockfd, F_GETFL);
-		fcntl (tcp_stream_raw->sockfd, F_SETFL, flags | O_NONBLOCK);
-		
-		fdmax = MAX (tcp_stream_raw->sockfd, cancel_fd) + 1;
-		do {
-			FD_ZERO (&rdset);
-			FD_ZERO (&wrset);
-			FD_SET (tcp_stream_raw->sockfd, &wrset);
-			FD_SET (cancel_fd, &rdset);
-			
-			w = -1;
-			if (select (fdmax, &rdset, &wrset, 0, NULL) != -1) {
-				if (FD_ISSET (cancel_fd, &rdset)) {
-					fcntl (tcp_stream_raw->sockfd, F_SETFL, flags);
-					errno = EINTR;
-					return -1;
-				}
-				
-				do {
-					w = write (tcp_stream_raw->sockfd, buffer + written, n - written);
-				} while (w == -1 && errno == EINTR);
-				
-				if (w == -1) {
-					if (errno == EAGAIN || errno == EWOULDBLOCK) {
-						w = 0;
-					} else {
-						error = errno;
-						fcntl (tcp_stream_raw->sockfd, F_SETFL, flags);
-						errno = error;
-						return -1;
-					}
-				} else
-					written += w;
-			} else if (errno == EINTR) {
-				w = 0;
-			}
-		} while (w != -1 && written < n);
-		
-		error = errno;
-		fcntl (tcp_stream_raw->sockfd, F_SETFL, flags);
-		errno = error;
-	}
-	
-	if (w == -1)
-		return -1;
-	
-	return written;
+
+	return camel_socket_write(tcp_stream_raw->sockfd, buffer, n);
 }
 
 static int
Index: camel/providers/imap/camel-imap-provider.c
===================================================================
RCS file: /cvs/gnome/evolution/camel/providers/imap/camel-imap-provider.c,v
retrieving revision 1.24
diff -u -p -r1.24 camel-imap-provider.c
--- camel/providers/imap/camel-imap-provider.c	22 May 2002 20:17:48 -0000	1.24
+++ camel/providers/imap/camel-imap-provider.c	24 Jun 2003 21:13:27 -0000
@@ -39,6 +39,13 @@ static gint check_equal (char *s1, char 
 static gint imap_url_equal (gconstpointer a, gconstpointer b);
 
 CamelProviderConfEntry imap_conf_entries[] = {
+	{ CAMEL_PROVIDER_CONF_SECTION_START, "cmdsection", NULL,
+	  N_("Connection to server") },
+	{ CAMEL_PROVIDER_CONF_CHECKBOX, "use_command", NULL,
+	  N_("Use custom command to connect to server"), "0" },
+	{ CAMEL_PROVIDER_CONF_ENTRY, "command", "use_command",
+	  N_("Command:"), "ssh -C -l $URLUSER $URLHOST exec /usr/sbin/imapd" },
+	{ CAMEL_PROVIDER_CONF_SECTION_END },
 	{ CAMEL_PROVIDER_CONF_SECTION_START, "mailcheck", NULL,
 	  N_("Checking for new mail") },
 	{ CAMEL_PROVIDER_CONF_CHECKBOX, "check_all", NULL,
Index: camel/providers/imap/camel-imap-store.c
===================================================================
RCS file: /cvs/gnome/evolution/camel/providers/imap/camel-imap-store.c,v
retrieving revision 1.252
diff -u -p -r1.252 camel-imap-store.c
--- camel/providers/imap/camel-imap-store.c	23 Apr 2003 18:14:54 -0000	1.252
+++ camel/providers/imap/camel-imap-store.c	24 Jun 2003 21:13:30 -0000
@@ -52,6 +52,7 @@
 #include "camel-stream.h"
 #include "camel-stream-buffer.h"
 #include "camel-stream-fs.h"
+#include "camel-stream-subcommand.h"
 #include "camel-tcp-stream-raw.h"
 #include "camel-tcp-stream-ssl.h"
 #include "camel-url.h"
@@ -251,6 +252,7 @@ camel_imap_store_init (gpointer object, 
 	imap_store->dir_sep = '\0';
 	imap_store->current_folder = NULL;
 	imap_store->connected = FALSE;
+	imap_store->preauthed = FALSE;
 	
 	imap_store->tag_prefix = imap_tag_prefix++;
 	if (imap_tag_prefix > 'Z')
@@ -599,9 +601,10 @@ connect_to_server (CamelService *service
 	store->istream = camel_stream_buffer_new (tcp_stream, CAMEL_STREAM_BUFFER_READ);
 	
 	store->connected = TRUE;
+	store->preauthed = FALSE;
 	store->command = 0;
 	
-	/* Read the greeting, if any. FIXME: deal with PREAUTH */
+	/* Read the greeting, if any, and deal with PREAUTH */
 	if (camel_imap_store_readline (store, &buf, ex) < 0) {
 		if (store->istream) {
 			camel_object_unref (CAMEL_OBJECT (store->istream));
@@ -616,6 +619,8 @@ connect_to_server (CamelService *service
 		store->connected = FALSE;
 		return FALSE;
 	}
+	if (!strncmp(buf, "* PREAUTH", 9))
+		store->preauthed = TRUE;
 	g_free (buf);
 	
 	/* get the imap server capabilities */
@@ -727,6 +732,98 @@ connect_to_server (CamelService *service
 #endif /* HAVE_SSL */
 }
 
+static gboolean
+connect_to_server_subcommand (CamelService *service, const char *cmd, CamelException *ex)
+{
+	CamelImapStore *store = (CamelImapStore *) service;
+	CamelStream *cmd_stream;
+	int ret, i = 0;
+	char *buf;
+	char *child_env[7];
+	
+	buf = camel_url_to_string(service->url, 0);
+	child_env[i++] = g_strdup_printf("URL=%s", buf);
+	g_free(buf);
+
+	child_env[i++] = g_strdup_printf("URLHOST=%s", service->url->host);
+	if (service->url->port)
+		child_env[i++] = g_strdup_printf("URLPORT=%d", service->url->port);
+	if (service->url->user)
+		child_env[i++] = g_strdup_printf("URLUSER=%s", service->url->user);
+	if (service->url->passwd)
+		child_env[i++] = g_strdup_printf("URLPASSWD=%s", service->url->passwd);
+	if (service->url->path)
+		child_env[i++] = g_strdup_printf("URLPATH=%s", service->url->path);
+	child_env[i] = NULL;
+
+	cmd_stream = camel_stream_subcommand_new ();
+	
+	ret = camel_stream_subcommand_connect (CAMEL_STREAM_SUBCOMMAND(cmd_stream),
+					       cmd, (const char **)child_env);
+
+	while (i)
+		g_free(child_env[--i]);
+		
+	if (ret == -1) {
+		if (errno == EINTR)
+			camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
+					     _("Connection cancelled"));
+		else
+			camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+					      _("Could not connect with command \"%s\": %s"),
+					      cmd, g_strerror (errno));
+		
+		camel_object_unref (CAMEL_OBJECT (cmd_stream));
+		
+		return FALSE;
+	}
+	
+	store->ostream = cmd_stream;
+	store->istream = camel_stream_buffer_new (cmd_stream, CAMEL_STREAM_BUFFER_READ);
+	
+	store->connected = TRUE;
+	store->preauthed = FALSE;
+	store->command = 0;
+	
+	/* Read the greeting, if any, and deal with PREAUTH */
+	if (camel_imap_store_readline (store, &buf, ex) < 0) {
+		if (store->istream) {
+			camel_object_unref (CAMEL_OBJECT (store->istream));
+			store->istream = NULL;
+		}
+		
+		if (store->ostream) {
+			camel_object_unref (CAMEL_OBJECT (store->ostream));
+			store->ostream = NULL;
+		}
+		
+		store->connected = FALSE;
+		return FALSE;
+	}
+	if (!strncmp(buf, "* PREAUTH", 9))
+		store->preauthed = TRUE;
+	g_free (buf);
+	
+	/* get the imap server capabilities */
+	if (!imap_get_capability (service, ex)) {
+		if (store->istream) {
+			camel_object_unref (CAMEL_OBJECT (store->istream));
+			store->istream = NULL;
+		}
+		
+		if (store->ostream) {
+			camel_object_unref (CAMEL_OBJECT (store->ostream));
+			store->ostream = NULL;
+		}
+		
+		store->connected = FALSE;
+		return FALSE;
+	}
+	
+	return TRUE;
+	
+}
+
 static struct {
 	char *value;
 	int mode;
@@ -741,10 +838,16 @@ static struct {
 static gboolean
 connect_to_server_wrapper (CamelService *service, CamelException *ex)
 {
+	const char *command;
 #ifdef HAVE_SSL
 	const char *use_ssl;
 	int i, ssl_mode;
-	
+#endif
+	command = camel_url_get_param (service->url, "command");
+	if (command)
+		return connect_to_server_subcommand (service, command, ex);
+
+#ifdef HAVE_SSL
 	use_ssl = camel_url_get_param (service->url, "use_ssl");
 	if (use_ssl) {
 		for (i = 0; ssl_options[i].value; i++)
@@ -1069,6 +1172,13 @@ imap_auth_loop (CamelService *service, C
 	
 	CAMEL_SERVICE_ASSERT_LOCKED (store, connect_lock);
 	
+	if (store->preauthed) {
+		if (camel_verbose_debug)
+			fprintf(stderr, "Server %s has preauthenticated us.\n",
+				service->url->host);
+		return TRUE;
+	}
+
 	if (service->url->authmech) {
 		if (!g_hash_table_lookup (store->authtypes, service->url->authmech)) {
 			camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
Index: camel/providers/imap/camel-imap-store.h
===================================================================
RCS file: /cvs/gnome/evolution/camel/providers/imap/camel-imap-store.h,v
retrieving revision 1.55
diff -u -p -r1.55 camel-imap-store.h
--- camel/providers/imap/camel-imap-store.h	24 Oct 2002 14:01:53 -0000	1.55
+++ camel/providers/imap/camel-imap-store.h	24 Jun 2003 21:13:30 -0000
@@ -107,6 +107,7 @@ struct _CamelImapStore {
 	
 	/* Information about the command channel / connection status */
 	gboolean connected;
+	gboolean preauthed;
 	char tag_prefix;
 	guint32 command;
 	CamelFolder *current_folder;
--- camel/ChangeLog.1	2003-06-24 22:10:45.000000000 +0100
+++ camel/ChangeLog	2003-06-24 22:10:50.000000000 +0100
@@ -1,5 +1,25 @@
 2003-06-24  David Woodhouse  <dwmw2 infradead org>
 
+	* providers/imap/camel-imap-provider.c: Add 'command' configuration
+	option for connecting to IMAP servers.
+	
+	* providers/imap/camel-imap-store.c: Use camel-stream-subcommand
+	for connection to server if configured accordingly. Handle
+	preauthentication.
+
+	* providers/imap/camel-imap-store.h: Add 'preauthed' field for noting
+	preauthentication state.
+
+	* camel-stream-subcommand.[ch]: New files for stream connected
+	via piped subcommand. 
+
+	* camel-socket-rw.[ch]: Move socket read/write functions from
+	camel-stream-tcp-raw.c, for avoidance of duplication.
+	
+	* Makefile.am: Add new files above.
+
+2003-06-24  David Woodhouse  <dwmw2 infradead org>
+
 	* camel-mime-utils.c (header_format_date): Put day of week
 	into outgoing email.
 
--- /dev/null	2003-01-30 10:24:37.000000000 +0000
+++ camel/camel-socket-rw.c	2003-06-24 21:35:47.000000000 +0100
@@ -0,0 +1,264 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj ximian com>
+ *
+ *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "camel-operation.h"
+#include "camel-socket-rw.h"
+
+#ifdef SIMULATE_FLAKY_NETWORK
+static ssize_t
+flaky_tcp_write (int fd, const char *buffer, size_t buflen)
+{
+	size_t len = buflen;
+	ssize_t nwritten;
+	int val;
+	
+	if (buflen == 0)
+		return 0;
+	
+	val = 1 + (int) (10.0 * rand () / (RAND_MAX + 1.0));
+	
+	switch (val) {
+	case 1:
+		printf ("flaky_tcp_write (%d, ..., %d): (-1) EINTR\n", fd, buflen);
+		errno = EINTR;
+		return -1;
+	case 2:
+		printf ("flaky_tcp_write (%d, ..., %d): (-1) EAGAIN\n", fd, buflen);
+		errno = EAGAIN;
+		return -1;
+	case 3:
+		printf ("flaky_tcp_write (%d, ..., %d): (-1) EWOULDBLOCK\n", fd, buflen);
+		errno = EWOULDBLOCK;
+		return -1;
+	case 4:
+	case 5:
+	case 6:
+		len = 1 + (size_t) (buflen * rand () / (RAND_MAX + 1.0));
+		len = MIN (len, buflen);
+		/* fall through... */
+	default:
+		printf ("flaky_tcp_write (%d, ..., %d): (%d) '%.*s'", fd, buflen, len, (int) len, buffer);
+		nwritten = write (fd, buffer, len);
+		if (nwritten < 0)
+			printf (" errno => %s\n", strerror (errno));
+		else if (nwritten < len)
+			printf (" only wrote %d bytes\n", nwritten);
+		else
+			printf ("\n");
+		
+		return nwritten;
+	}
+}
+
+#define write(fd, buffer, buflen) flaky_tcp_write (fd, buffer, buflen)
+
+static ssize_t
+flaky_tcp_read (int fd, char *buffer, size_t buflen)
+{
+	size_t len = buflen;
+	ssize_t nread;
+	int val;
+	
+	if (buflen == 0)
+		return 0;
+	
+	val = 1 + (int) (10.0 * rand () / (RAND_MAX + 1.0));
+	
+	switch (val) {
+	case 1:
+		printf ("flaky_tcp_read (%d, ..., %d): (-1) EINTR\n", fd, buflen);
+		errno = EINTR;
+		return -1;
+	case 2:
+		printf ("flaky_tcp_read (%d, ..., %d): (-1) EAGAIN\n", fd, buflen);
+		errno = EAGAIN;
+		return -1;
+	case 3:
+		printf ("flaky_tcp_read (%d, ..., %d): (-1) EWOULDBLOCK\n", fd, buflen);
+		errno = EWOULDBLOCK;
+		return -1;
+	case 4:
+	case 5:
+	case 6:
+	case 7:
+	case 8:
+	case 9:
+	case 10:
+		len = 1 + (size_t) (10.0 * rand () / (RAND_MAX + 1.0));
+		len = MIN (len, buflen);
+		/* fall through... */
+	default:
+		printf ("flaky_tcp_read (%d, ..., %d): (%d)", fd, buflen, len);
+		nread = read (fd, buffer, len);
+		if (nread < 0)
+			printf (" errno => %s\n", strerror (errno));
+		else if (nread < len)
+			printf (" only read %d bytes\n", nread);
+		else
+			printf ("\n");
+		
+		return nread;
+	}
+}
+
+#define read(fd, buffer, buflen) flaky_tcp_read (fd, buffer, buflen)
+
+#endif /* SIMULATE_FLAKY_NETWORK */
+
+ssize_t
+camel_socket_read (int sockfd, char *buffer, size_t n)
+{
+	ssize_t nread;
+	int cancel_fd;
+	
+	if (camel_operation_cancel_check (NULL)) {
+		errno = EINTR;
+		return -1;
+	}
+	
+	cancel_fd = camel_operation_cancel_fd (NULL);
+	if (cancel_fd == -1) {
+		do {
+			nread = read (sockfd, buffer, n);
+		} while (nread == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
+	} else {
+		int error, flags, fdmax;
+		fd_set rdset;
+		
+		flags = fcntl (sockfd, F_GETFL);
+		fcntl (sockfd, F_SETFL, flags | O_NONBLOCK);
+		
+		do {
+			FD_ZERO (&rdset);
+			FD_SET (sockfd, &rdset);
+			FD_SET (cancel_fd, &rdset);
+			fdmax = MAX (sockfd, cancel_fd) + 1;
+			
+			nread = -1;
+			if (select (fdmax, &rdset, 0, 0, NULL) != -1) {
+				if (FD_ISSET (cancel_fd, &rdset)) {
+					fcntl (sockfd, F_SETFL, flags);
+					errno = EINTR;
+					return -1;
+				}
+				
+				do {
+					nread = read (sockfd, buffer, n);
+				} while (nread == -1 && errno == EINTR);
+			} else if (errno == EINTR) {
+				errno = EAGAIN;
+			}
+		} while (nread == -1 && (errno == EAGAIN || errno == EWOULDBLOCK));
+		
+		error = errno;
+		fcntl (sockfd, F_SETFL, flags);
+		errno = error;
+	}
+	
+	return nread;
+}
+
+ssize_t
+camel_socket_write (int sockfd, const char *buffer, size_t n)
+{
+	ssize_t w, written = 0;
+	int cancel_fd;
+	
+	if (camel_operation_cancel_check (NULL)) {
+		errno = EINTR;
+		return -1;
+	}
+	
+	cancel_fd = camel_operation_cancel_fd (NULL);
+	if (cancel_fd == -1) {
+		do {
+			do {
+				w = write (sockfd, buffer + written, n - written);
+			} while (w == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
+			
+			if (w > 0)
+				written += w;
+		} while (w != -1 && written < n);
+	} else {
+		int error, flags, fdmax;
+		fd_set rdset, wrset;
+		
+		flags = fcntl (sockfd, F_GETFL);
+		fcntl (sockfd, F_SETFL, flags | O_NONBLOCK);
+		
+		fdmax = MAX (sockfd, cancel_fd) + 1;
+		do {
+			FD_ZERO (&rdset);
+			FD_ZERO (&wrset);
+			FD_SET (sockfd, &wrset);
+			FD_SET (cancel_fd, &rdset);
+			
+			w = -1;
+			if (select (fdmax, &rdset, &wrset, 0, NULL) != -1) {
+				if (FD_ISSET (cancel_fd, &rdset)) {
+					fcntl (sockfd, F_SETFL, flags);
+					errno = EINTR;
+					return -1;
+				}
+				
+				do {
+					w = write (sockfd, buffer + written, n - written);
+				} while (w == -1 && errno == EINTR);
+				
+				if (w == -1) {
+					if (errno == EAGAIN || errno == EWOULDBLOCK) {
+						w = 0;
+					} else {
+						error = errno;
+						fcntl (sockfd, F_SETFL, flags);
+						errno = error;
+						return -1;
+					}
+				} else
+					written += w;
+			} else if (errno == EINTR) {
+				w = 0;
+			}
+		} while (w != -1 && written < n);
+		
+		error = errno;
+		fcntl (sockfd, F_SETFL, flags);
+		errno = error;
+	}
+	
+	if (w == -1)
+		return -1;
+	
+	return written;
+}
--- /dev/null	2003-01-30 10:24:37.000000000 +0000
+++ camel/camel-socket-rw.h	2003-06-24 21:35:47.000000000 +0100
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Jeffrey Stedfast <fejj ximian com>
+ *
+ *  Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef CAMEL_SOCKET_RW_H
+#define CAMEL_SOCKET_RW_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include <sys/types.h>
+
+ssize_t camel_socket_read (int sockfd, char *buffer, size_t n);
+ssize_t camel_socket_write (int sockfd, const char *buffer, size_t n);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CAMEL_SOCKET_RW_H */
--- /dev/null	2003-01-30 10:24:37.000000000 +0000
+++ camel/camel-stream-subcommand.c	2003-06-24 21:35:47.000000000 +0100
@@ -0,0 +1,267 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
+/* camel-stream-subcommand.c : stream over piped subcommand */
+
+/*
+ *  Copyright (C) 2003 Ximian Inc.
+ *
+ *  Authors: David Woodhouse <dwmw2 infradead org>,
+ *	     Jeffrey Stedfast <fejj ximian com>
+ *  
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "camel-stream-subcommand.h"
+#include "camel-socket-rw.h"
+
+extern int camel_verbose_debug;
+
+static CamelObjectClass *parent_class = NULL;
+
+/* Returns the class for a CamelStream */
+#define CS_CLASS(so) CAMEL_STREAM_SUBCOMMAND_CLASS(CAMEL_OBJECT_GET_CLASS(so))
+
+/* dummy implementations, for a SUBCOMMAND stream */
+static ssize_t   stream_read       (CamelStream *stream, char *buffer, size_t n);
+static ssize_t   stream_write      (CamelStream *stream, const char *buffer, size_t n);
+static int       stream_close      (CamelStream *stream);
+static int       stream_flush      (CamelStream *stream);
+
+static void
+camel_stream_subcommand_finalise (CamelObject *object)
+{
+	/* Ensure we clean up after ourselves -- kill
+	   the child process and reap it. */
+	stream_close(CAMEL_STREAM (object));
+}
+
+static void
+camel_stream_subcommand_class_init (CamelStreamSubcommandClass *camel_stream_subcommand_class)
+{
+	CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_stream_subcommand_class;
+
+	parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
+
+	/* virtual method definition */
+	camel_stream_class->read = stream_read;
+	camel_stream_class->write = stream_write;
+	camel_stream_class->close = stream_close;
+	camel_stream_class->flush = stream_flush;
+}
+
+static void
+camel_stream_subcommand_init (gpointer object, gpointer klass)
+{
+        CamelStreamSubcommand *stream = CAMEL_STREAM_SUBCOMMAND (object);
+         
+        stream->sockfd = -1;
+	stream->childpid = 0;
+}
+
+
+CamelType
+camel_stream_subcommand_get_type (void)
+{
+	static CamelType camel_stream_subcommand_type = CAMEL_INVALID_TYPE;
+
+	if (camel_stream_subcommand_type == CAMEL_INVALID_TYPE) {
+		camel_stream_subcommand_type = camel_type_register( camel_stream_get_type(),
+							      "CamelStreamSubcommand",
+							      sizeof( CamelStreamSubcommand ),
+							      sizeof( CamelStreamSubcommandClass ),
+							      (CamelObjectClassInitFunc) camel_stream_subcommand_class_init,
+							      NULL,
+							      (CamelObjectInitFunc) camel_stream_subcommand_init,
+							      (CamelObjectFinalizeFunc) camel_stream_subcommand_finalise);
+	}
+
+	return camel_stream_subcommand_type;
+}
+
+/**
+ * camel_stream_subcommand_new:
+ *
+ * Returns a SUBCOMMAND stream.
+ *
+ * Return value: the stream
+ **/
+CamelStream *
+camel_stream_subcommand_new(void)
+{
+	return (CamelStream *)camel_object_new(camel_stream_subcommand_get_type ());
+}
+
+
+static ssize_t
+stream_read (CamelStream *stream, char *buffer, size_t n)
+{
+	CamelStreamSubcommand *stream_subcommand = CAMEL_STREAM_SUBCOMMAND (stream);
+
+	return camel_socket_read(stream_subcommand->sockfd, buffer, n);
+}
+
+static ssize_t
+stream_write (CamelStream *stream, const char *buffer, size_t n)
+{
+	CamelStreamSubcommand *stream_subcommand = CAMEL_STREAM_SUBCOMMAND (stream);
+
+	return camel_socket_write(stream_subcommand->sockfd, buffer, n);
+}
+
+static int
+stream_flush (CamelStream *stream)
+{
+	return 0;
+}
+
+static int
+stream_close (CamelStream *object)
+{
+	CamelStreamSubcommand *stream = CAMEL_STREAM_SUBCOMMAND (object);
+	if (camel_verbose_debug)
+		fprintf(stderr, "SubCommand stream close. sockfd %d, childpid %d\n",
+			stream->sockfd, stream->childpid);
+
+	if (stream->sockfd != -1) {
+		close(stream->sockfd);
+		stream->sockfd = -1;
+	}
+	if (stream->childpid) {
+		int ret, i;
+		for (i=0; i<4; i++) {
+			ret = waitpid(stream->childpid, NULL, WNOHANG);
+			if (camel_verbose_debug)
+				fprintf(stderr, "waitpid() for pid %d returned %d (errno %d)\n",
+					stream->childpid, ret, ret==-1?errno:0);
+			if (ret == stream->childpid || errno == ECHILD)
+				break;
+			switch(i) {
+			case 0:
+				if (camel_verbose_debug)
+					fprintf(stderr, "Sending SIGTERM to pid %d\n",
+						stream->childpid);
+				kill(stream->childpid, SIGTERM);
+				break;
+			case 2:
+				if (camel_verbose_debug)
+					fprintf(stderr, "Sending SIGKILL to pid %d\n",
+						stream->childpid);
+				kill(stream->childpid, SIGKILL);
+				break;
+			case 1:
+			case 3:
+				sleep(1);
+				break;
+			}
+		}
+		stream->childpid = 0;
+	}
+	return 0;
+}
+
+static void do_exec_command(int fd, const char *command, char **env)
+{
+	int i, maxopen;
+
+	/* Not a lot we can do if there's an error other than bail. */
+	if (dup2(fd, 0) == -1)
+		exit(1);
+	if (dup2(fd, 1) == -1)
+		exit(1);
+	
+	/* What to do with stderr? Possibly put it through a separate pipe
+	   and bring up a dialog box with its output if anything does get
+	   spewed to it? It'd help the user understand what was going wrong
+	   with their command, but it's hard to do cleanly. For now we just
+	   leave it as it is. Perhaps we should close it and reopen /dev/null? */
+
+	maxopen = sysconf(_SC_OPEN_MAX);
+	for (i=3; i < maxopen; i++)
+		close(i);
+
+	setsid();
+#ifdef TIOCNOTTY
+	/* Detach from the controlling tty if we have one. Otherwise, 
+	   SSH might do something stupid like trying to use it instead 
+	   of running $SSH_ASKPASS. Doh. */
+	fd = open("/dev/tty", O_RDONLY);
+	if (fd != -1) {
+		ioctl(fd, TIOCNOTTY, NULL);
+		close(fd);
+	}
+#endif /* TIOCNOTTY */
+
+	/* Set up child's environment. We _add_ to it, don't use execle, 
+	   because otherwise we'd destroy stuff like SSH_AUTH_SOCK etc. */
+	for (; env && *env; env++) {
+		char *eq = strchr(*env, '=');
+		if (!eq) {
+			unsetenv(*env);
+			continue;
+		}
+		*eq = 0;
+		eq++;
+		setenv(*env, eq, 1);
+	}
+
+	execl("/bin/sh", "/bin/sh", "-c", command, NULL);
+
+	if (camel_verbose_debug)
+		fprintf(stderr, "exec failed %d\n", errno);
+	exit(1);
+}
+
+int
+camel_stream_subcommand_connect(CamelStreamSubcommand *stream, const char *command, const char **env)
+{
+	int sockfds[2];
+
+	if (stream->sockfd != -1 || stream->childpid) {
+		stream_close(CAMEL_STREAM (stream));
+	}
+	
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds))
+		return -1;
+
+	stream->childpid = fork();
+	if (!stream->childpid) {
+		do_exec_command(sockfds[1], command, (char **)env);
+	} else if (stream->childpid == -1) {
+		close(sockfds[0]);
+		close(sockfds[1]);
+		stream->sockfd = -1;
+		return -1;
+	}
+
+	close(sockfds[1]);
+	stream->sockfd = sockfds[0];
+
+	return 0;
+}
--- /dev/null	2003-01-30 10:24:37.000000000 +0000
+++ camel/camel-stream-subcommand.h	2003-06-24 21:35:47.000000000 +0100
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Copyright (C) 2003 Ximian Inc.
+ *
+ *  Authors: David Woodhouse <dwmw2 infradead org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+
+#ifndef _CAMEL_STREAM_NULL_H
+#define _CAMEL_STREAM_NULL_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#include <camel/camel-stream.h>
+
+#define CAMEL_STREAM_SUBCOMMAND(obj)         CAMEL_CHECK_CAST (obj, camel_stream_subcommand_get_type (), CamelStreamSubcommand)
+#define CAMEL_STREAM_SUBCOMMAND_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_stream_subcommand_get_type (), CamelStreamSubcommandClass)
+#define CAMEL_IS_STREAM_SUBCOMMAND(obj)      CAMEL_CHECK_TYPE (obj, camel_stream_subcommand_get_type ())
+
+typedef struct _CamelStreamSubcommandClass CamelStreamSubcommandClass;
+typedef struct _CamelStreamSubcommand CamelStreamSubcommand;
+
+struct _CamelStreamSubcommand {
+	CamelStream parent;
+	
+	int sockfd;
+	pid_t childpid;
+};
+
+struct _CamelStreamSubcommandClass {
+	CamelStreamClass parent_class;
+};
+
+CamelType		camel_stream_subcommand_get_type	(void);
+CamelStream            *camel_stream_subcommand_new		(void);
+int camel_stream_subcommand_connect(CamelStreamSubcommand *, const char *, const char **);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! _CAMEL_STREAM_SUBCOMMAND_H */



-- 
dwmw2




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]