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



this should probably use camel-io's camel_read/camel_write instead of
creating a new camel-socket-rw.c and should probably use camel-process
to fork/exec rather than reimplementing it, but other than that I guess
it's ok

maybe rename "subcommand" to "process"? not sure that's a great name
either tho *shrug*

Jeff

On Thu, 2003-06-26 at 13:05, David Woodhouse wrote:
> 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 */
-- 
Jeffrey Stedfast
Evolution Hacker - Ximian, Inc.
fejj ximian com  - www.ximian.com




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