Re: [evolution-patches] [PATCH] IMAP preauth and subcommand connection.
- From: David Woodhouse <dwmw2 infradead org>
- To: Jeffrey Stedfast <fejj ximian com>
- Cc: Ettore Perazzoli <ettore ximian com>, evolution-patches lists ximian com
- Subject: Re: [evolution-patches] [PATCH] IMAP preauth and subcommand connection.
- Date: Fri, 27 Jun 2003 14:38:49 +0100
On Thu, 2003-06-26 at 23:31, Jeffrey Stedfast wrote:
> On Thu, 2003-06-26 at 18:27, David Woodhouse wrote:
> > Any preference on what I do with my camel-stream-subcommand vs. your
> > camel-process? I need the environment setup, and the TIOCNOCTTY bit too.
> > Should I resubmit it with only the camel-io/camel-socket-rw bits cleaned
> > up and then we can take it from there?
>
> sure
OK, here it is then. Btw I took a brief look at camel-process.c, since I
probably could manage to use camel_process_wait() even though I need
stuff that camel_process_fork() doesn't and probably shouldn't do in
general.... it doesn't compile though so for the sake of a few lines of
duplicated code I didn't bother using it.
Index: Makefile.am
===================================================================
RCS file: /cvs/gnome/evolution/camel/Makefile.am,v
retrieving revision 1.184
diff -u -p -r1.184 Makefile.am
--- Makefile.am 12 Jun 2003 21:13:56 -0000 1.184
+++ Makefile.am 27 Jun 2003 13:33:25 -0000
@@ -104,6 +104,7 @@ libcamel_la_SOURCES = \
camel-stream-fs.c \
camel-stream-mem.c \
camel-stream-null.c \
+ camel-stream-process.c \
camel-stream.c \
camel-text-index.c \
camel-tcp-stream-raw.c \
@@ -206,6 +207,7 @@ libcamelinclude_HEADERS = \
camel-stream-fs.h \
camel-stream-mem.h \
camel-stream-null.h \
+ camel-stream-process.h \
camel-stream.h \
camel-text-index.h \
camel-tcp-stream-raw.h \
Index: 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
--- providers/imap/camel-imap-provider.c 22 May 2002 20:17:48 -0000 1.24
+++ providers/imap/camel-imap-provider.c 27 Jun 2003 13:33:26 -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: 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
--- providers/imap/camel-imap-store.c 23 Apr 2003 18:14:54 -0000 1.252
+++ providers/imap/camel-imap-store.c 27 Jun 2003 13:33:27 -0000
@@ -52,6 +52,7 @@
#include "camel-stream.h"
#include "camel-stream-buffer.h"
#include "camel-stream-fs.h"
+#include "camel-stream-process.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_process (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_process_new ();
+
+ ret = camel_stream_process_connect (CAMEL_STREAM_PROCESS(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_process (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: 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
--- providers/imap/camel-imap-store.h 24 Oct 2002 14:01:53 -0000 1.55
+++ providers/imap/camel-imap-store.h 27 Jun 2003 13:33:27 -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;
--- /dev/null Thu Jan 30 10:24:37 2003
+++ camel-stream-process.c Fri Jun 27 14:27:01 2003
@@ -0,0 +1,268 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
+/* camel-stream-process.c : stream over piped process */
+
+/*
+ * 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-process.h"
+#include "camel-io.h"
+
+extern int camel_verbose_debug;
+
+static CamelObjectClass *parent_class = NULL;
+
+/* Returns the class for a CamelStream */
+#define CS_CLASS(so) CAMEL_STREAM_PROCESS_CLASS(CAMEL_OBJECT_GET_CLASS(so))
+
+/* dummy implementations, for a PROCESS 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_process_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_process_class_init (CamelStreamProcessClass *camel_stream_process_class)
+{
+ CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_stream_process_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_process_init (gpointer object, gpointer klass)
+{
+ CamelStreamProcess *stream = CAMEL_STREAM_PROCESS (object);
+
+ stream->sockfd = -1;
+ stream->childpid = 0;
+}
+
+
+CamelType
+camel_stream_process_get_type (void)
+{
+ static CamelType camel_stream_process_type = CAMEL_INVALID_TYPE;
+
+ if (camel_stream_process_type == CAMEL_INVALID_TYPE) {
+ camel_stream_process_type =
+ camel_type_register( camel_stream_get_type(),
+ "CamelStreamProcess",
+ sizeof( CamelStreamProcess ),
+ sizeof( CamelStreamProcessClass ),
+ (CamelObjectClassInitFunc) camel_stream_process_class_init,
+ NULL,
+ (CamelObjectInitFunc) camel_stream_process_init,
+ (CamelObjectFinalizeFunc) camel_stream_process_finalise);
+ }
+
+ return camel_stream_process_type;
+}
+
+/**
+ * camel_stream_process_new:
+ *
+ * Returns a PROCESS stream.
+ *
+ * Return value: the stream
+ **/
+CamelStream *
+camel_stream_process_new(void)
+{
+ return (CamelStream *)camel_object_new(camel_stream_process_get_type ());
+}
+
+
+static ssize_t
+stream_read (CamelStream *stream, char *buffer, size_t n)
+{
+ CamelStreamProcess *stream_process = CAMEL_STREAM_PROCESS (stream);
+
+ return camel_read(stream_process->sockfd, buffer, n);
+}
+
+static ssize_t
+stream_write (CamelStream *stream, const char *buffer, size_t n)
+{
+ CamelStreamProcess *stream_process = CAMEL_STREAM_PROCESS (stream);
+
+ return camel_write(stream_process->sockfd, buffer, n);
+}
+
+static int
+stream_flush (CamelStream *stream)
+{
+ return 0;
+}
+
+static int
+stream_close (CamelStream *object)
+{
+ CamelStreamProcess *stream = CAMEL_STREAM_PROCESS (object);
+ if (camel_verbose_debug)
+ fprintf(stderr, "Process 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_process_connect(CamelStreamProcess *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 Thu Jan 30 10:24:37 2003
+++ camel-stream-process.h Fri Jun 27 10:36:47 2003
@@ -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_PROCESS(obj) CAMEL_CHECK_CAST (obj, camel_stream_process_get_type (), CamelStreamProcess)
+#define CAMEL_STREAM_PROCESS_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_stream_process_get_type (), CamelStreamProcessClass)
+#define CAMEL_IS_STREAM_PROCESS(obj) CAMEL_CHECK_TYPE (obj, camel_stream_process_get_type ())
+
+typedef struct _CamelStreamProcessClass CamelStreamProcessClass;
+typedef struct _CamelStreamProcess CamelStreamProcess;
+
+struct _CamelStreamProcess {
+ CamelStream parent;
+
+ int sockfd;
+ pid_t childpid;
+};
+
+struct _CamelStreamProcessClass {
+ CamelStreamClass parent_class;
+};
+
+CamelType camel_stream_process_get_type (void);
+CamelStream *camel_stream_process_new (void);
+int camel_stream_process_connect(CamelStreamProcess *, const char *, const char **);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! _CAMEL_STREAM_PROCESS_H */
--
dwmw2
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]