{"thread":{"id":"3582","subject":"[PATCH] Add git-imap-send.","startedAt":"2006-03-06T13:09:45Z","lastAt":"2006-03-10T04:58:10Z","messageCount":13,"participants":["Mike McCormack","Junio C Hamano","Johannes Schindelin","Andreas Ericsson","Mark Wooding","Linus Torvalds"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"17255","messageId":"440C3499.9080000@codeweavers.com","threadId":"3582","inReplyTo":null,"subject":"[PATCH] Add git-imap-send.","fromName":"Mike McCormack","fromEmail":"mike@codeweavers.com","sentAt":"2006-03-06T13:09:45Z","receivedAt":"2006-03-06T13:09:45Z","isPatch":true,"sender":{"key":"mike@codeweavers.com","avatar":null},"body":"\nThis probably needs a bit more work, but I'll solicit comments and \nflames anyway...\n\n\n\ngit-imap-send drops a patch series generated by git-format-patch into an \nIMAP folder. This allows patch submitters to send patches through their \nown mail program.\n\ngit-imap-send uses the following values from the GIT repository \nconfiguration:\n\nThe target IMAP folder:\n\n[imap]\n         Folder = \"INBOX.Drafts\"\n\nA command to open an ssh tunnel to the imap mail server.\n\n[imap]\n         Tunnel = \"ssh -q user@imap.server.com /usr/bin/imapd ./Maildir \n2> /dev/null\"\n\nOR\n\n[imap]\n\tHost = imap.server.com\n\tUser = bob\n\tPassword = pwd\n\tPort = 143\n\n---\n\n  .gitignore  |    1\n  Makefile    |    4\n  imap-send.c | 1935 \n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n  3 files changed, 1939 insertions(+), 1 deletions(-)\n  create mode 100644 imap-send.c\n\n\n\nb8fbeb40b92b9b6048c9f7ad66a10d8222c58b76\ndiff --git a/.gitignore b/.gitignore\nindex abbc509..c567cc0 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -42,6 +42,7 @@ git-grep\n git-hash-object\n git-http-fetch\n git-http-push\n+git-imap-send\n git-index-pack\n git-init-db\n git-local-fetch\ndiff --git a/Makefile b/Makefile\nindex b6d8804..bc49f06 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -165,7 +165,7 @@ PROGRAMS = \\\n \tgit-upload-pack$X git-verify-pack$X git-write-tree$X \\\n \tgit-update-ref$X git-symbolic-ref$X git-check-ref-format$X \\\n \tgit-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \\\n-\tgit-describe$X git-merge-tree$X git-blame$X\n+\tgit-describe$X git-merge-tree$X git-blame$X git-imap-send$X\n \n # what 'all' will build and 'install' will install, in gitexecdir\n ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)\n@@ -522,6 +522,8 @@ git-ssh-upload$X: rsh.o\n git-ssh-pull$X: rsh.o fetch.o\n git-ssh-push$X: rsh.o\n \n+git-imap-send$X: imap-send.o $(LIB_FILE)\n+\n git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE)\n \t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(LIBS) $(CURL_LIBCURL)\ndiff --git a/imap-send.c b/imap-send.c\nnew file mode 100644\nindex 0000000..b6e5151\n--- /dev/null\n+++ b/imap-send.c\n@@ -0,0 +1,1935 @@\n+/*\n+ * git-imap-send - drops patches into an imap Drafts folder\n+ *\n+ *  derived from isync/mbsync - mailbox synchronizer\n+ * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n+ * Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>\n+ * Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>\n+ * Copyright (C) 2006 Mike McCormack\n+ *\n+ *  This program is free software; you can redistribute it and/or modify\n+ *  it under the terms of the GNU General Public License as published by\n+ *  the Free Software Foundation; either version 2 of the License, or\n+ *  (at your option) any later version.\n+ *\n+ *  This program is distributed in the hope that it will be useful,\n+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ *  GNU General Public License for more details.\n+ *\n+ *  You should have received a copy of the GNU General Public License\n+ *  along with this program; if not, write to the Free Software\n+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n+ *\n+ * As a special exception, mbsync may be linked with the OpenSSL library,\n+ * despite that library's more restrictive license.\n+ */\n+\n+#include <sys/types.h>\n+#include <stdarg.h>\n+#include <stdio.h>\n+\n+#include <assert.h>\n+#include <unistd.h>\n+#include <sys/mman.h>\n+#include <sys/time.h>\n+#include <stdlib.h>\n+#include <stdio.h>\n+#include <stddef.h>\n+#include <errno.h>\n+#include <string.h>\n+#include <ctype.h>\n+#include <sys/socket.h>\n+#include <sys/ioctl.h>\n+#include <netinet/in.h>\n+#include <netinet/tcp.h>\n+#include <arpa/inet.h>\n+#include <netdb.h>\n+#if HAVE_LIBSSL\n+# include <openssl/ssl.h>\n+# include <openssl/err.h>\n+# include <openssl/hmac.h>\n+#endif\n+#include <pwd.h>\n+\n+#include \"cache.h\"\n+\n+#define as(ar) (sizeof(ar)/sizeof(ar[0]))\n+\n+typedef struct store_conf {\n+\tchar *name;\n+\tconst char *path; /* should this be here? its interpretation is driver-specific */\n+\tchar *map_inbox;\n+\tchar *trash;\n+\tunsigned max_size; /* off_t is overkill */\n+\tunsigned trash_remote_new:1, trash_only_new:1;\n+} store_conf_t;\n+\n+typedef struct string_list {\n+\tstruct string_list *next;\n+\tchar string[1];\n+} string_list_t;\n+\n+typedef struct channel_conf {\n+\tstruct channel_conf *next;\n+\tchar *name;\n+\tstore_conf_t *master, *slave;\n+\tchar *master_name, *slave_name;\n+\tchar *sync_state;\n+\tstring_list_t *patterns;\n+\tint mops, sops;\n+\tunsigned max_messages; /* for slave only */\n+} channel_conf_t;\n+\n+typedef struct group_conf {\n+\tstruct group_conf *next;\n+\tchar *name;\n+\tstring_list_t *channels;\n+} group_conf_t;\n+\n+/* For message->status */\n+#define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */\n+#define M_DEAD         (1<<1) /* expunged */\n+#define M_FLAGS        (1<<2) /* flags fetched */\n+#define M_PROCESSED    (1<<3) /* registered in pair */\n+#define M_NOT_SYNCED   (1<<4) /* not in remote mailbox, yet */\n+#define M_EXPIRED      (1<<5) /* kicked out by MaxMessages */\n+\n+typedef struct message {\n+\tstruct message *next;\n+\t/* string_list_t *keywords; */\n+\tsize_t size; /* zero implies \"not fetched\" */\n+\tint uid;\n+\tunsigned char flags, status;\n+} message_t;\n+\n+/* For opts, both in store and driver_t->select() */\n+#define OPEN_OLD        (1<<0)\n+#define OPEN_NEW        (1<<1)\n+#define OPEN_FLAGS      (1<<2)\n+#define OPEN_SIZE       (1<<3)\n+#define OPEN_CREATE     (1<<4)\n+#define OPEN_EXPUNGE    (1<<5)\n+#define OPEN_SETFLAGS   (1<<6)\n+#define OPEN_APPEND     (1<<7)\n+\n+typedef struct store {\n+\tstore_conf_t *conf; /* foreign */\n+\n+\t/* currently open mailbox */\n+\tconst char *name; /* foreign! maybe preset? */\n+\tchar *path; /* own */\n+\tmessage_t *msgs; /* own */\n+\tint uidvalidity;\n+\tunsigned char opts; /* maybe preset? */\n+\t/* note that the following do _not_ reflect stats from msgs, but mailbox totals */\n+\tint count; /* # of messages */\n+\tint recent; /* # of recent messages - don't trust this beyond the initial read */\n+} store_t;\n+\n+typedef struct {\n+\tchar *data;\n+\tint len;\n+\tunsigned char flags;\n+\tunsigned char crlf:1;\n+} msg_data_t;\n+\n+#define DRV_OK          0\n+#define DRV_MSG_BAD     -1\n+#define DRV_BOX_BAD     -2\n+#define DRV_STORE_BAD   -3\n+\n+static int Verbose, Quiet;\n+\n+static void info( const char *, ... );\n+static void warn( const char *, ... );\n+\n+static char *next_arg( char ** );\n+\n+static void free_generic_messages( message_t * );\n+\n+static int nfvasprintf( char **str, const char *fmt, va_list va );\n+static int nfsnprintf( char *buf, int blen, const char *fmt, ... );\n+\n+\n+static void arc4_init( void );\n+static unsigned char arc4_getbyte( void );\n+\n+typedef struct imap_server_conf {\n+\tchar *name;\n+\tchar *tunnel;\n+\tchar *host;\n+\tint port;\n+\tchar *user;\n+\tchar *pass;\n+#if HAVE_LIBSSL\n+\tchar *cert_file;\n+\tunsigned use_imaps:1;\n+\tunsigned require_ssl:1;\n+\tunsigned use_sslv2:1;\n+\tunsigned use_sslv3:1;\n+\tunsigned use_tlsv1:1;\n+\tunsigned require_cram:1;\n+#endif\n+} imap_server_conf_t;\n+\n+typedef struct imap_store_conf {\n+\tstore_conf_t gen;\n+\timap_server_conf_t *server;\n+\tunsigned use_namespace:1;\n+} imap_store_conf_t;\n+\n+typedef struct imap_message {\n+\tmessage_t gen;\n+/*\tint seq; will be needed when expunges are tracked */\n+} imap_message_t;\n+\n+#define NIL\t(void*)0x1\n+#define LIST\t(void*)0x2\n+\n+typedef struct _list {\n+\tstruct _list *next, *child;\n+\tchar *val;\n+\tint len;\n+} list_t;\n+\n+typedef struct {\n+\tint fd;\n+#if HAVE_LIBSSL\n+\tSSL *ssl;\n+\tunsigned int use_ssl:1;\n+#endif\n+} Socket_t;\n+\n+typedef struct {\n+\tSocket_t sock;\n+\tint bytes;\n+\tint offset;\n+\tchar buf[1024];\n+} buffer_t;\n+\n+struct imap_cmd;\n+#define max_in_progress 50 /* make this configurable? */\n+\n+typedef struct imap {\n+\tint uidnext; /* from SELECT responses */\n+\tlist_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */\n+\tstring_list_t *boxes; /* LIST results */\n+\tmessage_t **msgapp; /* FETCH results */\n+\tunsigned caps, rcaps; /* CAPABILITY results */\n+\t/* command queue */\n+\tint nexttag, num_in_progress, literal_pending;\n+\tstruct imap_cmd *in_progress, **in_progress_append;\n+#if HAVE_LIBSSL\n+\tSSL_CTX *SSLContext;\n+#endif\n+\tbuffer_t buf; /* this is BIG, so put it last */\n+} imap_t;\n+\n+typedef struct imap_store {\n+\tstore_t gen;\n+\tint uidvalidity;\n+\timap_t *imap;\n+\tconst char *prefix;\n+\tunsigned /*currentnc:1,*/ trashnc:1;\n+} imap_store_t;\n+\n+struct imap_cmd_cb {\n+\tint (*cont)( imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt );\n+\tvoid (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response);\n+\tvoid *ctx;\n+\tchar *data;\n+\tint dlen;\n+\tint uid;\n+\tunsigned create:1, trycreate:1;\n+};\n+\n+struct imap_cmd {\n+\tstruct imap_cmd *next;\n+\tstruct imap_cmd_cb cb;\n+\tchar *cmd;\n+\tint tag;\n+};\n+\n+#define CAP(cap) (imap->caps & (1 << (cap)))\n+\n+enum CAPABILITY {\n+\tNOLOGIN = 0,\n+\tUIDPLUS,\n+\tLITERALPLUS,\n+\tNAMESPACE,\n+#if HAVE_LIBSSL\n+\tCRAM,\n+\tSTARTTLS,\n+#endif\n+};\n+\n+static const char *cap_list[] = {\n+\t\"LOGINDISABLED\",\n+\t\"UIDPLUS\",\n+\t\"LITERAL+\",\n+\t\"NAMESPACE\",\n+#if HAVE_LIBSSL\n+\t\"AUTH=CRAM-MD5\",\n+\t\"STARTTLS\",\n+#endif\n+};\n+\n+#define RESP_OK    0\n+#define RESP_NO    1\n+#define RESP_BAD   2\n+\n+static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd );\n+\n+\n+static const char *Flags[] = {\n+\t\"Draft\",\n+\t\"Flagged\",\n+\t\"Answered\",\n+\t\"Seen\",\n+\t\"Deleted\",\n+};\n+\n+#if HAVE_LIBSSL\n+\n+/* this gets called when a certificate is to be verified */\n+static int\n+verify_cert( SSL *ssl )\n+{\n+\tX509 *cert;\n+\tint err;\n+\tchar buf[256];\n+\tint ret = -1;\n+\tBIO *bio;\n+\n+\tcert = SSL_get_peer_certificate( ssl );\n+\tif (!cert) {\n+\t\tfprintf( stderr, \"Error, no server certificate\\n\" );\n+\t\treturn -1;\n+\t}\n+\n+\terr = SSL_get_verify_result( ssl );\n+\tif (err == X509_V_OK)\n+\t\treturn 0;\n+\n+\tfprintf( stderr, \"Error, can't verify certificate: %s (%d)\\n\",\n+\t         X509_verify_cert_error_string(err), err );\n+\n+\tX509_NAME_oneline( X509_get_subject_name( cert ), buf, sizeof(buf) );\n+\tinfo( \"\\nSubject: %s\\n\", buf );\n+\tX509_NAME_oneline( X509_get_issuer_name( cert ), buf, sizeof(buf) );\n+\tinfo( \"Issuer:  %s\\n\", buf );\n+\tbio = BIO_new( BIO_s_mem() );\n+\tASN1_TIME_print( bio, X509_get_notBefore( cert ) );\n+\tmemset( buf, 0, sizeof(buf) );\n+\tBIO_read( bio, buf, sizeof(buf) - 1 );\n+\tinfo( \"Valid from: %s\\n\", buf );\n+\tASN1_TIME_print( bio, X509_get_notAfter( cert ) );\n+\tmemset( buf, 0, sizeof(buf) );\n+\tBIO_read( bio, buf, sizeof(buf) - 1 );\n+\tBIO_free( bio );\n+\tinfo( \"      to:   %s\\n\", buf );\n+\n+\tfputs( \"\\n*** WARNING ***  There is no way to verify this certificate.  It is\\n\"\n+\t       \"                 possible that a hostile attacker has replaced the\\n\"\n+\t       \"                 server certificate.  Continue at your own risk!\\n\"\n+\t       \"\\nAccept this certificate anyway? [no]: \",  stderr );\n+\tif (fgets( buf, sizeof(buf), stdin ) && (buf[0] == 'y' || buf[0] == 'Y')) {\n+\t\tret = 0;\n+\t\tfprintf( stderr, \"\\n*** Fine, but don't say I didn't warn you!\\n\\n\" );\n+\t}\n+\treturn ret;\n+}\n+\n+static int\n+init_ssl_ctx( imap_store_t *ctx )\n+{\n+\timap_t *imap = ctx->imap;\n+\timap_store_conf_t *conf = (imap_store_conf_t *)ctx->gen.conf;\n+\timap_server_conf_t *srvc = conf->server;\n+\tSSL_METHOD *method;\n+\tint options = 0;\n+\n+\tif (srvc->use_tlsv1 && !srvc->use_sslv2 && !srvc->use_sslv3)\n+\t\tmethod = TLSv1_client_method();\n+\telse\n+\t\tmethod = SSLv23_client_method();\n+\timap->SSLContext = SSL_CTX_new( method );\n+\n+\tif (!srvc->cert_file) {\n+\t\tfprintf( stderr, \"Error, CertificateFile not defined\\n\" );\n+\t\treturn -1;\n+\t} else if (access( srvc->cert_file, R_OK ))\n+\t\twarn( \"*** Warning: can't read CertificateFile, so can't verify server certificates\\n\" );\n+\telse if (!SSL_CTX_load_verify_locations( imap->SSLContext, srvc->cert_file, NULL )) {\n+\t\tfprintf( stderr, \"Error, SSL_CTX_load_verify_locations: %s\\n\",\n+\t\t         ERR_error_string( ERR_get_error(), 0 ) );\n+\t\treturn -1;\n+\t}\n+\n+\tif (!srvc->use_sslv2)\n+\t\toptions |= SSL_OP_NO_SSLv2;\n+\tif (!srvc->use_sslv3)\n+\t\toptions |= SSL_OP_NO_SSLv3;\n+\tif (!srvc->use_tlsv1)\n+\t\toptions |= SSL_OP_NO_TLSv1;\n+\n+\tSSL_CTX_set_options( imap->SSLContext, options );\n+\n+\t/* we check the result of the verification after SSL_connect() */\n+\tSSL_CTX_set_verify( imap->SSLContext, SSL_VERIFY_NONE, 0 );\n+\treturn 0;\n+}\n+#endif /* HAVE_LIBSSL */\n+\n+static void\n+socket_perror( const char *func, Socket_t *sock, int ret )\n+{\n+#if HAVE_LIBSSL\n+\tint err;\n+\n+\tif (sock->use_ssl) {\n+\t\tswitch ((err = SSL_get_error( sock->ssl, ret ))) {\n+\t\tcase SSL_ERROR_SYSCALL:\n+\t\tcase SSL_ERROR_SSL:\n+\t\t\tif ((err = ERR_get_error()) == 0) {\n+\t\t\t\tif (ret == 0)\n+\t\t\t\t\tfprintf( stderr, \"SSL_%s:got EOF\\n\", func );\n+\t\t\t\telse\n+\t\t\t\t\tfprintf( stderr, \"SSL_%s:%d:%s\\n\", func, errno, strerror(errno) );\n+\t\t\t} else\n+\t\t\t\tfprintf( stderr, \"SSL_%s:%d:%s\\n\", func, err, ERR_error_string( err, 0 ) );\n+\t\t\treturn;\n+\t\tdefault:\n+\t\t\tfprintf( stderr, \"SSL_%s:%d:unhandled SSL error\\n\", func, err );\n+\t\t\tbreak;\n+\t\t}\n+\t\treturn;\n+\t}\n+#else\n+\t(void)sock;\n+#endif\n+\tif (ret < 0)\n+\t\tperror( func );\n+\telse\n+\t\tfprintf( stderr, \"%s: unexpected EOF\\n\", func );\n+}\n+\n+static int\n+socket_read( Socket_t *sock, char *buf, int len )\n+{\n+\tint n =\n+#if HAVE_LIBSSL\n+\t\tsock->use_ssl ? SSL_read( sock->ssl, buf, len ) :\n+#endif\n+\t\tread( sock->fd, buf, len );\n+\tif (n <= 0) {\n+\t\tsocket_perror( \"read\", sock, n );\n+\t\tclose( sock->fd );\n+\t\tsock->fd = -1;\n+\t}\n+\treturn n;\n+}\n+\n+static int\n+socket_write( Socket_t *sock, char *buf, int len )\n+{\n+\tint n =\n+#if HAVE_LIBSSL\n+\t\tsock->use_ssl ? SSL_write( sock->ssl, buf, len ) :\n+#endif\n+\t\twrite( sock->fd, buf, len );\n+\tif (n != len) {\n+\t\tsocket_perror( \"write\", sock, n );\n+\t\tclose( sock->fd );\n+\t\tsock->fd = -1;\n+\t}\n+\treturn n;\n+}\n+\n+#if 0\n+\n+static int\n+socket_pending( Socket_t *sock )\n+{\n+\tint num = -1;\n+\n+\tif (ioctl( sock->fd, FIONREAD, &num ) < 0)\n+\t\treturn -1;\n+\tif (num > 0)\n+\t\treturn num;\n+#if HAVE_LIBSSL\n+\tif (sock->use_ssl)\n+\t\treturn SSL_pending( sock->ssl );\n+#endif\n+\treturn 0;\n+}\n+\n+#endif\n+\n+/* simple line buffering */\n+static int\n+buffer_gets( buffer_t * b, char **s )\n+{\n+\tint n;\n+\tint start = b->offset;\n+\n+\t*s = b->buf + start;\n+\n+\tfor (;;) {\n+\t\t/* make sure we have enough data to read the \\r\\n sequence */\n+\t\tif (b->offset + 1 >= b->bytes) {\n+\t\t\tif (start) {\n+\t\t\t\t/* shift down used bytes */\n+\t\t\t\t*s = b->buf;\n+\n+\t\t\t\tassert( start <= b->bytes );\n+\t\t\t\tn = b->bytes - start;\n+\n+\t\t\t\tif (n)\n+\t\t\t\t\tmemcpy( b->buf, b->buf + start, n );\n+\t\t\t\tb->offset -= start;\n+\t\t\t\tb->bytes = n;\n+\t\t\t\tstart = 0;\n+\t\t\t}\n+\n+\t\t\tn = socket_read( &b->sock, b->buf + b->bytes,\n+\t\t\t                 sizeof(b->buf) - b->bytes );\n+\n+\t\t\tif (n <= 0)\n+\t\t\t\treturn -1;\n+\n+\t\t\tb->bytes += n;\n+\t\t}\n+\n+\t\tif (b->buf[b->offset] == '\\r') {\n+\t\t\tassert( b->offset + 1 < b->bytes );\n+\t\t\tif (b->buf[b->offset + 1] == '\\n') {\n+\t\t\t\tb->buf[b->offset] = 0;  /* terminate the string */\n+\t\t\t\tb->offset += 2; /* next line */\n+\t\t\t\tif (Verbose)\n+\t\t\t\t\tputs( *s );\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t}\n+\n+\t\tb->offset++;\n+\t}\n+\t/* not reached */\n+}\n+\n+static void\n+info( const char *msg, ... )\n+{\n+\tva_list va;\n+\n+\tif (!Quiet) {\n+\t\tva_start( va, msg );\n+\t\tvprintf( msg, va );\n+\t\tva_end( va );\n+\t\tfflush( stdout );\n+\t}\n+}\n+\n+static void\n+warn( const char *msg, ... )\n+{\n+\tva_list va;\n+\n+\tif (Quiet < 2) {\n+\t\tva_start( va, msg );\n+\t\tvfprintf( stderr, msg, va );\n+\t\tva_end( va );\n+\t}\n+}\n+\n+static char *\n+next_arg( char **s )\n+{\n+\tchar *ret;\n+\n+\tif (!s || !*s)\n+\t\treturn 0;\n+\twhile (isspace( (unsigned char) **s ))\n+\t\t(*s)++;\n+\tif (!**s) {\n+\t\t*s = 0;\n+\t\treturn 0;\n+\t}\n+\tif (**s == '\"') {\n+\t\t++*s;\n+\t\tret = *s;\n+\t\t*s = strchr( *s, '\"' );\n+\t} else {\n+\t\tret = *s;\n+\t\twhile (**s && !isspace( (unsigned char) **s ))\n+\t\t\t(*s)++;\n+\t}\n+\tif (*s) {\n+\t\tif (**s)\n+\t\t\t*(*s)++ = 0;\n+\t\tif (!**s)\n+\t\t\t*s = 0;\n+\t}\n+\treturn ret;\n+}\n+\n+static void\n+free_generic_messages( message_t *msgs )\n+{\n+\tmessage_t *tmsg;\n+\n+\tfor (; msgs; msgs = tmsg) {\n+\t\ttmsg = msgs->next;\n+\t\tfree( msgs );\n+\t}\n+}\n+\n+static int\n+vasprintf( char **strp, const char *fmt, va_list ap )\n+{\n+\tint len;\n+\tchar tmp[1024];\n+\n+\tif ((len = vsnprintf( tmp, sizeof(tmp), fmt, ap )) < 0 || !(*strp = xmalloc( len + 1 )))\n+\t\treturn -1;\n+\tif (len >= (int)sizeof(tmp))\n+\t\tvsprintf( *strp, fmt, ap );\n+\telse\n+\t\tmemcpy( *strp, tmp, len + 1 );\n+\treturn len;\n+}\n+\n+static int\n+nfsnprintf( char *buf, int blen, const char *fmt, ... )\n+{\n+\tint ret;\n+\tva_list va;\n+\n+\tva_start( va, fmt );\n+\tif (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)\n+\t\tdie( \"Fatal: buffer too small. Please report a bug.\\n\");\n+\tva_end( va );\n+\treturn ret;\n+}\n+\n+static int\n+nfvasprintf( char **str, const char *fmt, va_list va )\n+{\n+\tint ret = vasprintf( str, fmt, va );\n+\tif (ret < 0)\n+\t\tdie( \"Fatal: Out of memory\\n\");\n+\treturn ret;\n+}\n+\n+static struct {\n+\tunsigned char i, j, s[256];\n+} rs;\n+\n+static void\n+arc4_init( void )\n+{\n+\tint i, fd;\n+\tunsigned char j, si, dat[128];\n+\n+\tif ((fd = open( \"/dev/urandom\", O_RDONLY )) < 0 && (fd = open( \"/dev/random\", O_RDONLY )) < 0) {\n+\t\tfprintf( stderr, \"Fatal: no random number source available.\\n\" );\n+\t\texit( 3 );\n+\t}\n+\tif (read( fd, dat, 128 ) != 128) {\n+\t\tfprintf( stderr, \"Fatal: cannot read random number source.\\n\" );\n+\t\texit( 3 );\n+\t}\n+\tclose( fd );\n+\n+\tfor (i = 0; i < 256; i++)\n+\t\trs.s[i] = i;\n+\tfor (i = j = 0; i < 256; i++) {\n+\t\tsi = rs.s[i];\n+\t\tj += si + dat[i & 127];\n+\t\trs.s[i] = rs.s[j];\n+\t\trs.s[j] = si;\n+\t}\n+\trs.i = rs.j = 0;\n+\n+\tfor (i = 0; i < 256; i++)\n+\t\tarc4_getbyte();\n+}\n+\n+static unsigned char\n+arc4_getbyte( void )\n+{\n+\tunsigned char si, sj;\n+\n+\trs.i++;\n+\tsi = rs.s[rs.i];\n+\trs.j += si;\n+\tsj = rs.s[rs.j];\n+\trs.s[rs.i] = sj;\n+\trs.s[rs.j] = si;\n+\treturn rs.s[(si + sj) & 0xff];\n+}\n+\n+static struct imap_cmd *\n+v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb,\n+                  const char *fmt, va_list ap )\n+{\n+\timap_t *imap = ctx->imap;\n+\tstruct imap_cmd *cmd;\n+\tint n, bufl;\n+\tchar buf[1024];\n+\n+\tcmd = xmalloc( sizeof(struct imap_cmd) );\n+\tnfvasprintf( &cmd->cmd, fmt, ap );\n+\tcmd->tag = ++imap->nexttag;\n+\n+\tif (cb)\n+\t\tcmd->cb = *cb;\n+\telse\n+\t\tmemset( &cmd->cb, 0, sizeof(cmd->cb) );\n+\n+\twhile (imap->literal_pending)\n+\t\tget_cmd_result( ctx, 0 );\n+\n+\tbufl = nfsnprintf( buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?\n+\t                   \"%d %s{%d+}\\r\\n\" : \"%d %s{%d}\\r\\n\" : \"%d %s\\r\\n\",\n+\t                   cmd->tag, cmd->cmd, cmd->cb.dlen );\n+\tif (Verbose) {\n+\t\tif (imap->num_in_progress)\n+\t\t\tprintf( \"(%d in progress) \", imap->num_in_progress );\n+\t\tif (memcmp( cmd->cmd, \"LOGIN\", 5 ))\n+\t\t\tprintf( \">>> %s\", buf );\n+\t\telse\n+\t\t\tprintf( \">>> %d LOGIN <user> <pass>\\n\", cmd->tag );\n+\t}\n+\tif (socket_write( &imap->buf.sock, buf, bufl ) != bufl) {\n+\t\tfree( cmd->cmd );\n+\t\tfree( cmd );\n+\t\tif (cb && cb->data)\n+\t\t\tfree( cb->data );\n+\t\treturn NULL;\n+\t}\n+\tif (cmd->cb.data) {\n+\t\tif (CAP(LITERALPLUS)) {\n+\t\t\tn = socket_write( &imap->buf.sock, cmd->cb.data, cmd->cb.dlen );\n+\t\t\tfree( cmd->cb.data );\n+\t\t\tif (n != cmd->cb.dlen ||\n+\t\t\t    (n = socket_write( &imap->buf.sock, \"\\r\\n\", 2 )) != 2)\n+\t\t\t{\n+\t\t\t\tfree( cmd->cmd );\n+\t\t\t\tfree( cmd );\n+\t\t\t\treturn NULL;\n+\t\t\t}\n+\t\t\tcmd->cb.data = 0;\n+\t\t} else\n+\t\t\timap->literal_pending = 1;\n+\t} else if (cmd->cb.cont)\n+\t\timap->literal_pending = 1;\n+\tcmd->next = 0;\n+\t*imap->in_progress_append = cmd;\n+\timap->in_progress_append = &cmd->next;\n+\timap->num_in_progress++;\n+\treturn cmd;\n+}\n+\n+static struct imap_cmd *\n+issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )\n+{\n+\tstruct imap_cmd *ret;\n+\tva_list ap;\n+\n+\tva_start( ap, fmt );\n+\tret = v_issue_imap_cmd( ctx, cb, fmt, ap );\n+\tva_end( ap );\n+\treturn ret;\n+}\n+\n+#if 0\n+\n+static struct imap_cmd *\n+issue_imap_cmd_w( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )\n+{\n+\timap_t *imap = ctx->imap;\n+\tstruct imap_cmd *ret;\n+\tva_list ap;\n+\n+\tva_start( ap, fmt );\n+\tret = v_issue_imap_cmd( ctx, cb, fmt, ap );\n+\tva_end( ap );\n+\twhile (imap->num_in_progress > max_in_progress ||\n+\t       socket_pending( &imap->buf.sock ))\n+\t\tget_cmd_result( ctx, 0 );\n+\treturn ret;\n+}\n+\n+#endif\n+\n+static int\n+imap_exec( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )\n+{\n+\tva_list ap;\n+\tstruct imap_cmd *cmdp;\n+\n+\tva_start( ap, fmt );\n+\tcmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );\n+\tva_end( ap );\n+\tif (!cmdp)\n+\t\treturn RESP_BAD;\n+\n+\treturn get_cmd_result( ctx, cmdp );\n+}\n+\n+#if 0\n+\n+static int\n+imap_exec_b( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )\n+{\n+\tva_list ap;\n+\tstruct imap_cmd *cmdp;\n+\n+\tva_start( ap, fmt );\n+\tcmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );\n+\tva_end( ap );\n+\tif (!cmdp)\n+\t\treturn DRV_STORE_BAD;\n+\n+\tswitch (get_cmd_result( ctx, cmdp )) {\n+\tcase RESP_BAD: return DRV_STORE_BAD;\n+\tcase RESP_NO: return DRV_BOX_BAD;\n+\tdefault: return DRV_OK;\n+\t}\n+}\n+\n+#endif\n+\n+static int\n+imap_exec_m( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )\n+{\n+\tva_list ap;\n+\tstruct imap_cmd *cmdp;\n+\n+\tva_start( ap, fmt );\n+\tcmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );\n+\tva_end( ap );\n+\tif (!cmdp)\n+\t\treturn DRV_STORE_BAD;\n+\n+\tswitch (get_cmd_result( ctx, cmdp )) {\n+\tcase RESP_BAD: return DRV_STORE_BAD;\n+\tcase RESP_NO: return DRV_MSG_BAD;\n+\tdefault: return DRV_OK;\n+\t}\n+}\n+\n+/*\n+static void\n+drain_imap_replies( imap_t *imap )\n+{\n+\twhile (imap->num_in_progress)\n+\t\tget_cmd_result( imap, 0 );\n+}\n+*/\n+\n+static int\n+is_atom( list_t *list )\n+{\n+\treturn list && list->val && list->val != NIL && list->val != LIST;\n+}\n+\n+static int\n+is_list( list_t *list )\n+{\n+\treturn list && list->val == LIST;\n+}\n+\n+static void\n+free_list( list_t *list )\n+{\n+\tlist_t *tmp;\n+\n+\tfor (; list; list = tmp) {\n+\t\ttmp = list->next;\n+\t\tif (is_list( list ))\n+\t\t\tfree_list( list->child );\n+\t\telse if (is_atom( list ))\n+\t\t\tfree( list->val );\n+\t\tfree( list );\n+\t}\n+}\n+\n+static int\n+parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )\n+{\n+\tlist_t *cur;\n+\tchar *s = *sp, *p;\n+\tint n, bytes;\n+\n+\tfor (;;) {\n+\t\twhile (isspace( (unsigned char)*s ))\n+\t\t\ts++;\n+\t\tif (level && *s == ')') {\n+\t\t\ts++;\n+\t\t\tbreak;\n+\t\t}\n+\t\t*curp = cur = xmalloc( sizeof(*cur) );\n+\t\tcurp = &cur->next;\n+\t\tcur->val = 0; /* for clean bail */\n+\t\tif (*s == '(') {\n+\t\t\t/* sublist */\n+\t\t\ts++;\n+\t\t\tcur->val = LIST;\n+\t\t\tif (parse_imap_list_l( imap, &s, &cur->child, level + 1 ))\n+\t\t\t\tgoto bail;\n+\t\t} else if (imap && *s == '{') {\n+\t\t\t/* literal */\n+\t\t\tbytes = cur->len = strtol( s + 1, &s, 10 );\n+\t\t\tif (*s != '}')\n+\t\t\t\tgoto bail;\n+\n+\t\t\ts = cur->val = xmalloc( cur->len );\n+\n+\t\t\t/* dump whats left over in the input buffer */\n+\t\t\tn = imap->buf.bytes - imap->buf.offset;\n+\n+\t\t\tif (n > bytes)\n+\t\t\t\t/* the entire message fit in the buffer */\n+\t\t\t\tn = bytes;\n+\n+\t\t\tmemcpy( s, imap->buf.buf + imap->buf.offset, n );\n+\t\t\ts += n;\n+\t\t\tbytes -= n;\n+\n+\t\t\t/* mark that we used part of the buffer */\n+\t\t\timap->buf.offset += n;\n+\n+\t\t\t/* now read the rest of the message */\n+\t\t\twhile (bytes > 0) {\n+\t\t\t\tif ((n = socket_read (&imap->buf.sock, s, bytes)) <= 0)\n+\t\t\t\t\tgoto bail;\n+\t\t\t\ts += n;\n+\t\t\t\tbytes -= n;\n+\t\t\t}\n+\n+\t\t\tif (buffer_gets( &imap->buf, &s ))\n+\t\t\t\tgoto bail;\n+\t\t} else if (*s == '\"') {\n+\t\t\t/* quoted string */\n+\t\t\ts++;\n+\t\t\tp = s;\n+\t\t\tfor (; *s != '\"'; s++)\n+\t\t\t\tif (!*s)\n+\t\t\t\t\tgoto bail;\n+\t\t\tcur->len = s - p;\n+\t\t\ts++;\n+\t\t\tcur->val = xmalloc( cur->len + 1 );\n+\t\t\tmemcpy( cur->val, p, cur->len );\n+\t\t\tcur->val[cur->len] = 0;\n+\t\t} else {\n+\t\t\t/* atom */\n+\t\t\tp = s;\n+\t\t\tfor (; *s && !isspace( (unsigned char)*s ); s++)\n+\t\t\t\tif (level && *s == ')')\n+\t\t\t\t\tbreak;\n+\t\t\tcur->len = s - p;\n+\t\t\tif (cur->len == 3 && !memcmp (\"NIL\", p, 3))\n+\t\t\t\tcur->val = NIL;\n+\t\t\telse {\n+\t\t\t\tcur->val = xmalloc( cur->len + 1 );\n+\t\t\t\tmemcpy( cur->val, p, cur->len );\n+\t\t\t\tcur->val[cur->len] = 0;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!level)\n+\t\t\tbreak;\n+\t\tif (!*s)\n+\t\t\tgoto bail;\n+\t}\n+\t*sp = s;\n+\t*curp = 0;\n+\treturn 0;\n+\n+  bail:\n+\t*curp = 0;\n+\treturn -1;\n+}\n+\n+static list_t *\n+parse_imap_list( imap_t *imap, char **sp )\n+{\n+\tlist_t *head;\n+\n+\tif (!parse_imap_list_l( imap, sp, &head, 0 ))\n+\t\treturn head;\n+\tfree_list( head );\n+\treturn NULL;\n+}\n+\n+static list_t *\n+parse_list( char **sp )\n+{\n+\treturn parse_imap_list( 0, sp );\n+}\n+\n+static int\n+parse_fetch( imap_t *imap, char *cmd ) /* move this down */\n+{\n+\tlist_t *tmp, *list, *flags;\n+\tchar *body = 0;\n+\timap_message_t *cur;\n+\tmsg_data_t *msgdata;\n+\tstruct imap_cmd *cmdp;\n+\tint uid = 0, mask = 0, status = 0, size = 0;\n+\tunsigned i;\n+\n+\tlist = parse_imap_list( imap, &cmd );\n+\n+\tif (!is_list( list )) {\n+\t\tfprintf( stderr, \"IMAP error: bogus FETCH response\\n\" );\n+\t\tfree_list( list );\n+\t\treturn -1;\n+\t}\n+\n+\tfor (tmp = list->child; tmp; tmp = tmp->next) {\n+\t\tif (is_atom( tmp )) {\n+\t\t\tif (!strcmp( \"UID\", tmp->val )) {\n+\t\t\t\ttmp = tmp->next;\n+\t\t\t\tif (is_atom( tmp ))\n+\t\t\t\t\tuid = atoi( tmp->val );\n+\t\t\t\telse\n+\t\t\t\t\tfprintf( stderr, \"IMAP error: unable to parse UID\\n\" );\n+\t\t\t} else if (!strcmp( \"FLAGS\", tmp->val )) {\n+\t\t\t\ttmp = tmp->next;\n+\t\t\t\tif (is_list( tmp )) {\n+\t\t\t\t\tfor (flags = tmp->child; flags; flags = flags->next) {\n+\t\t\t\t\t\tif (is_atom( flags )) {\n+\t\t\t\t\t\t\tif (flags->val[0] == '\\\\') { /* ignore user-defined flags for now */\n+\t\t\t\t\t\t\t\tif (!strcmp( \"Recent\", flags->val + 1)) {\n+\t\t\t\t\t\t\t\t\tstatus |= M_RECENT;\n+\t\t\t\t\t\t\t\t\tgoto flagok;\n+\t\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\t\tfor (i = 0; i < as(Flags); i++)\n+\t\t\t\t\t\t\t\t\tif (!strcmp( Flags[i], flags->val + 1 )) {\n+\t\t\t\t\t\t\t\t\t\tmask |= 1 << i;\n+\t\t\t\t\t\t\t\t\t\tgoto flagok;\n+\t\t\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\t\tfprintf( stderr, \"IMAP warning: unknown system flag %s\\n\", flags->val );\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t  flagok: ;\n+\t\t\t\t\t\t} else\n+\t\t\t\t\t\t\tfprintf( stderr, \"IMAP error: unable to parse FLAGS list\\n\" );\n+\t\t\t\t\t}\n+\t\t\t\t\tstatus |= M_FLAGS;\n+\t\t\t\t} else\n+\t\t\t\t\tfprintf( stderr, \"IMAP error: unable to parse FLAGS\\n\" );\n+\t\t\t} else if (!strcmp( \"RFC822.SIZE\", tmp->val )) {\n+\t\t\t\ttmp = tmp->next;\n+\t\t\t\tif (is_atom( tmp ))\n+\t\t\t\t\tsize = atoi( tmp->val );\n+\t\t\t\telse\n+\t\t\t\t\tfprintf( stderr, \"IMAP error: unable to parse SIZE\\n\" );\n+\t\t\t} else if (!strcmp( \"BODY[]\", tmp->val )) {\n+\t\t\t\ttmp = tmp->next;\n+\t\t\t\tif (is_atom( tmp )) {\n+\t\t\t\t\tbody = tmp->val;\n+\t\t\t\t\ttmp->val = 0;       /* don't free together with list */\n+\t\t\t\t\tsize = tmp->len;\n+\t\t\t\t} else\n+\t\t\t\t\tfprintf( stderr, \"IMAP error: unable to parse BODY[]\\n\" );\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tif (body) {\n+\t\tfor (cmdp = imap->in_progress; cmdp; cmdp = cmdp->next)\n+\t\t\tif (cmdp->cb.uid == uid)\n+\t\t\t\tgoto gotuid;\n+\t\tfprintf( stderr, \"IMAP error: unexpected FETCH response (UID %d)\\n\", uid );\n+\t\tfree_list( list );\n+\t\treturn -1;\n+\t  gotuid:\n+\t\tmsgdata = (msg_data_t *)cmdp->cb.ctx;\n+\t\tmsgdata->data = body;\n+\t\tmsgdata->len = size;\n+\t\tmsgdata->crlf = 1;\n+\t\tif (status & M_FLAGS)\n+\t\t\tmsgdata->flags = mask;\n+\t} else if (uid) { /* ignore async flag updates for now */\n+\t\t/* XXX this will need sorting for out-of-order (multiple queries) */\n+\t\tcur = xcalloc( sizeof(*cur), 1 );\n+\t\t*imap->msgapp = &cur->gen;\n+\t\timap->msgapp = &cur->gen.next;\n+\t\tcur->gen.next = 0;\n+\t\tcur->gen.uid = uid;\n+\t\tcur->gen.flags = mask;\n+\t\tcur->gen.status = status;\n+\t\tcur->gen.size = size;\n+\t}\n+\n+\tfree_list( list );\n+\treturn 0;\n+}\n+\n+static void\n+parse_capability( imap_t *imap, char *cmd )\n+{\n+\tchar *arg;\n+\tunsigned i;\n+\n+\timap->caps = 0x80000000;\n+\twhile ((arg = next_arg( &cmd )))\n+\t\tfor (i = 0; i < as(cap_list); i++)\n+\t\t\tif (!strcmp( cap_list[i], arg ))\n+\t\t\t\timap->caps |= 1 << i;\n+\timap->rcaps = imap->caps;\n+}\n+\n+static int\n+parse_response_code( imap_store_t *ctx, struct imap_cmd_cb *cb, char *s )\n+{\n+\timap_t *imap = ctx->imap;\n+\tchar *arg, *p;\n+\n+\tif (*s != '[')\n+\t\treturn RESP_OK;\t\t/* no response code */\n+\ts++;\n+\tif (!(p = strchr( s, ']' ))) {\n+\t\tfprintf( stderr, \"IMAP error: malformed response code\\n\" );\n+\t\treturn RESP_BAD;\n+\t}\n+\t*p++ = 0;\n+\targ = next_arg( &s );\n+\tif (!strcmp( \"UIDVALIDITY\", arg )) {\n+\t\tif (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg ))) {\n+\t\t\tfprintf( stderr, \"IMAP error: malformed UIDVALIDITY status\\n\" );\n+\t\t\treturn RESP_BAD;\n+\t\t}\n+\t} else if (!strcmp( \"UIDNEXT\", arg )) {\n+\t\tif (!(arg = next_arg( &s )) || !(imap->uidnext = atoi( arg ))) {\n+\t\t\tfprintf( stderr, \"IMAP error: malformed NEXTUID status\\n\" );\n+\t\t\treturn RESP_BAD;\n+\t\t}\n+\t} else if (!strcmp( \"CAPABILITY\", arg )) {\n+\t\tparse_capability( imap, s );\n+\t} else if (!strcmp( \"ALERT\", arg )) {\n+\t\t/* RFC2060 says that these messages MUST be displayed\n+\t\t * to the user\n+\t\t */\n+\t\tfor (; isspace( (unsigned char)*p ); p++);\n+\t\tfprintf( stderr, \"*** IMAP ALERT *** %s\\n\", p );\n+\t} else if (cb && cb->ctx && !strcmp( \"APPENDUID\", arg )) {\n+\t\tif (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg )) ||\n+\t\t    !(arg = next_arg( &s )) || !(*(int *)cb->ctx = atoi( arg )))\n+\t\t{\n+\t\t\tfprintf( stderr, \"IMAP error: malformed APPENDUID status\\n\" );\n+\t\t\treturn RESP_BAD;\n+\t\t}\n+\t}\n+\treturn RESP_OK;\n+}\n+\n+static int\n+get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )\n+{\n+\timap_t *imap = ctx->imap;\n+\tstruct imap_cmd *cmdp, **pcmdp, *ncmdp;\n+\tchar *cmd, *arg, *arg1, *p;\n+\tint n, resp, resp2, tag;\n+\n+\tfor (;;) {\n+\t\tif (buffer_gets( &imap->buf, &cmd ))\n+\t\t\treturn RESP_BAD;\n+\n+\t\targ = next_arg( &cmd );\n+\t\tif (*arg == '*') {\n+\t\t\targ = next_arg( &cmd );\n+\t\t\tif (!arg) {\n+\t\t\t\tfprintf( stderr, \"IMAP error: unable to parse untagged response\\n\" );\n+\t\t\t\treturn RESP_BAD;\n+\t\t\t}\n+\n+\t\t\tif (!strcmp( \"NAMESPACE\", arg )) {\n+\t\t\t\timap->ns_personal = parse_list( &cmd );\n+\t\t\t\timap->ns_other = parse_list( &cmd );\n+\t\t\t\timap->ns_shared = parse_list( &cmd );\n+\t\t\t} else if (!strcmp( \"OK\", arg ) || !strcmp( \"BAD\", arg ) ||\n+\t\t\t           !strcmp( \"NO\", arg ) || !strcmp( \"BYE\", arg )) {\n+\t\t\t\tif ((resp = parse_response_code( ctx, 0, cmd )) != RESP_OK)\n+\t\t\t\t\treturn resp;\n+\t\t\t} else if (!strcmp( \"CAPABILITY\", arg ))\n+\t\t\t\tparse_capability( imap, cmd );\n+\t\t\telse if ((arg1 = next_arg( &cmd ))) {\n+\t\t\t\tif (!strcmp( \"EXISTS\", arg1 ))\n+\t\t\t\t\tctx->gen.count = atoi( arg );\n+\t\t\t\telse if (!strcmp( \"RECENT\", arg1 ))\n+\t\t\t\t\tctx->gen.recent = atoi( arg );\n+\t\t\t\telse if(!strcmp ( \"FETCH\", arg1 )) {\n+\t\t\t\t\tif (parse_fetch( imap, cmd ))\n+\t\t\t\t\t\treturn RESP_BAD;\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\tfprintf( stderr, \"IMAP error: unable to parse untagged response\\n\" );\n+\t\t\t\treturn RESP_BAD;\n+\t\t\t}\n+\t\t} else if (!imap->in_progress) {\n+\t\t\tfprintf( stderr, \"IMAP error: unexpected reply: %s %s\\n\", arg, cmd ? cmd : \"\" );\n+\t\t\treturn RESP_BAD;\n+\t\t} else if (*arg == '+') {\n+\t\t\t/* This can happen only with the last command underway, as\n+\t\t\t   it enforces a round-trip. */\n+\t\t\tcmdp = (struct imap_cmd *)((char *)imap->in_progress_append -\n+\t\t\t       offsetof(struct imap_cmd, next));\n+\t\t\tif (cmdp->cb.data) {\n+\t\t\t\tn = socket_write( &imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen );\n+\t\t\t\tfree( cmdp->cb.data );\n+\t\t\t\tcmdp->cb.data = 0;\n+\t\t\t\tif (n != (int)cmdp->cb.dlen)\n+\t\t\t\t\treturn RESP_BAD;\n+\t\t\t} else if (cmdp->cb.cont) {\n+\t\t\t\tif (cmdp->cb.cont( ctx, cmdp, cmd ))\n+\t\t\t\t\treturn RESP_BAD;\n+\t\t\t} else {\n+\t\t\t\tfprintf( stderr, \"IMAP error: unexpected command continuation request\\n\" );\n+\t\t\t\treturn RESP_BAD;\n+\t\t\t}\n+\t\t\tif (socket_write( &imap->buf.sock, \"\\r\\n\", 2 ) != 2)\n+\t\t\t\treturn RESP_BAD;\n+\t\t\tif (!cmdp->cb.cont)\n+\t\t\t\timap->literal_pending = 0;\n+\t\t\tif (!tcmd)\n+\t\t\t\treturn DRV_OK;\n+\t\t} else {\n+\t\t\ttag = atoi( arg );\n+\t\t\tfor (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)\n+\t\t\t\tif (cmdp->tag == tag)\n+\t\t\t\t\tgoto gottag;\n+\t\t\tfprintf( stderr, \"IMAP error: unexpected tag %s\\n\", arg );\n+\t\t\treturn RESP_BAD;\n+\t\t  gottag:\n+\t\t\tif (!(*pcmdp = cmdp->next))\n+\t\t\t\timap->in_progress_append = pcmdp;\n+\t\t\timap->num_in_progress--;\n+\t\t\tif (cmdp->cb.cont || cmdp->cb.data)\n+\t\t\t\timap->literal_pending = 0;\n+\t\t\targ = next_arg( &cmd );\n+\t\t\tif (!strcmp( \"OK\", arg ))\n+\t\t\t\tresp = DRV_OK;\n+\t\t\telse {\n+\t\t\t\tif (!strcmp( \"NO\", arg )) {\n+\t\t\t\t\tif (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp( cmd, \"[TRYCREATE]\", 11 ))) { /* SELECT, APPEND or UID COPY */\n+\t\t\t\t\t\tp = strchr( cmdp->cmd, '\"' );\n+\t\t\t\t\t\tif (!issue_imap_cmd( ctx, 0, \"CREATE \\\"%.*s\\\"\", strchr( p + 1, '\"' ) - p + 1, p )) {\n+\t\t\t\t\t\t\tresp = RESP_BAD;\n+\t\t\t\t\t\t\tgoto normal;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\t/* not waiting here violates the spec, but a server that does not\n+\t\t\t\t\t\t   grok this nonetheless violates it too. */\n+\t\t\t\t\t\tcmdp->cb.create = 0;\n+\t\t\t\t\t\tif (!(ncmdp = issue_imap_cmd( ctx, &cmdp->cb, \"%s\", cmdp->cmd ))) {\n+\t\t\t\t\t\t\tresp = RESP_BAD;\n+\t\t\t\t\t\t\tgoto normal;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tfree( cmdp->cmd );\n+\t\t\t\t\t\tfree( cmdp );\n+\t\t\t\t\t\tif (!tcmd)\n+\t\t\t\t\t\t\treturn 0;\t/* ignored */\n+\t\t\t\t\t\tif (cmdp == tcmd)\n+\t\t\t\t\t\t\ttcmd = ncmdp;\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\t}\n+\t\t\t\t\tresp = RESP_NO;\n+\t\t\t\t} else /*if (!strcmp( \"BAD\", arg ))*/\n+\t\t\t\t\tresp = RESP_BAD;\n+\t\t\t\tfprintf( stderr, \"IMAP command '%s' returned response (%s) - %s\\n\",\n+\t\t\t\t         memcmp (cmdp->cmd, \"LOGIN\", 5) ?\n+\t\t\t\t         \t\tcmdp->cmd : \"LOGIN <user> <pass>\",\n+\t\t\t\t         \t\targ, cmd ? cmd : \"\");\n+\t\t\t}\n+\t\t\tif ((resp2 = parse_response_code( ctx, &cmdp->cb, cmd )) > resp)\n+\t\t\t\tresp = resp2;\n+\t\t  normal:\n+\t\t\tif (cmdp->cb.done)\n+\t\t\t\tcmdp->cb.done( ctx, cmdp, resp );\n+\t\t\tif (cmdp->cb.data)\n+\t\t\t\tfree( cmdp->cb.data );\n+\t\t\tfree( cmdp->cmd );\n+\t\t\tfree( cmdp );\n+\t\t\tif (!tcmd || tcmd == cmdp)\n+\t\t\t\treturn resp;\n+\t\t}\n+\t}\n+\t/* not reached */\n+}\n+\n+static void\n+imap_close_server( imap_store_t *ictx )\n+{\n+\timap_t *imap = ictx->imap;\n+\n+\tif (imap->buf.sock.fd != -1) {\n+\t\timap_exec( ictx, 0, \"LOGOUT\" );\n+\t\tclose( imap->buf.sock.fd );\n+\t}\n+#ifdef HAVE_LIBSSL\n+\tif (imap->SSLContext)\n+\t\tSSL_CTX_free( imap->SSLContext );\n+#endif\n+\tfree_list( imap->ns_personal );\n+\tfree_list( imap->ns_other );\n+\tfree_list( imap->ns_shared );\n+\tfree( imap );\n+}\n+\n+static void\n+imap_close_store( store_t *ctx )\n+{\n+\timap_close_server( (imap_store_t *)ctx );\n+\tfree_generic_messages( ctx->msgs );\n+\tfree( ctx );\n+}\n+\n+#ifdef HAVE_LIBSSL\n+static int\n+start_tls( imap_store_t *ctx )\n+{\n+\timap_t *imap = ctx->imap;\n+\tint ret;\n+\tstatic int ssl_inited;\n+\n+\tif (!ssl_inited) {\n+\t\tSSL_library_init();\n+\t\tSSL_load_error_strings();\n+\t\tssl_inited = 1;\n+\t}\n+\n+        if (init_ssl_ctx( ctx ))\n+\t\treturn 1;\n+\n+\timap->buf.sock.ssl = SSL_new( imap->SSLContext );\n+\tSSL_set_fd( imap->buf.sock.ssl, imap->buf.sock.fd );\n+\tif ((ret = SSL_connect( imap->buf.sock.ssl )) <= 0) {\n+\t\tsocket_perror( \"connect\", &imap->buf.sock, ret );\n+\t\treturn 1;\n+\t}\n+\n+\t/* verify the server certificate */\n+\tif (verify_cert( imap->buf.sock.ssl ))\n+\t\treturn 1;\n+\n+\timap->buf.sock.use_ssl = 1;\n+\tinfo( \"Connection is now encrypted\\n\" );\n+\treturn 0;\n+}\n+\n+#define ENCODED_SIZE(n) (4*((n+2)/3))\n+\n+static char\n+hexchar( unsigned int b )\n+{\n+\tif (b < 10)\n+\t\treturn '0' + b;\n+\treturn 'a' + (b - 10);\n+}\n+\n+/* XXX merge into do_cram_auth? */\n+static char *\n+cram( const char *challenge, const char *user, const char *pass )\n+{\n+\tHMAC_CTX hmac;\n+\tchar hash[16];\n+\tchar hex[33];\n+\tint i;\n+\tunsigned int hashlen = sizeof(hash);\n+\tchar buf[256];\n+\tint len = strlen( challenge );\n+\tchar *response = xcalloc( 1 + len, 1 );\n+\tchar *final;\n+\n+\t/* response will always be smaller than challenge because we are\n+\t * decoding.\n+\t */\n+\tlen = EVP_DecodeBlock( (unsigned char *)response, (unsigned char *)challenge, strlen( challenge ) );\n+\n+\tHMAC_Init( &hmac, (unsigned char *) pass, strlen( pass ), EVP_md5() );\n+\tHMAC_Update( &hmac, (unsigned char *)response, strlen( response ) );\n+\tHMAC_Final( &hmac, (unsigned char *)hash, &hashlen );\n+\n+\tassert( hashlen == sizeof(hash) );\n+\n+\tfree( response );\n+\n+\thex[32] = 0;\n+\tfor (i = 0; i < 16; i++) {\n+\t\thex[2 * i] = hexchar( (hash[i] >> 4) & 0xf );\n+\t\thex[2 * i + 1] = hexchar( hash[i] & 0xf );\n+\t}\n+\n+\tnfsnprintf( buf, sizeof(buf), \"%s %s\", user, hex );\n+\n+\tlen = strlen( buf );\n+\tlen = ENCODED_SIZE( len ) + 1;\n+\tfinal = xmalloc( len );\n+\tfinal[len - 1] = 0;\n+\n+\tassert( EVP_EncodeBlock( (unsigned char *)final, (unsigned char *)buf, strlen( buf ) ) == len - 1 );\n+\n+\treturn final;\n+}\n+\n+static int\n+do_cram_auth (imap_store_t *ctx, struct imap_cmd *cmdp, const char *prompt)\n+{\n+\timap_t *imap = ctx->imap;\n+\timap_server_conf_t *srvc = ((imap_store_conf_t *)ctx->gen.conf)->server;\n+\tchar *resp;\n+\tint n, l;\n+\n+\tresp = cram( prompt, srvc->user, srvc->pass );\n+\n+\tif (Verbose)\n+\t\tprintf( \">+> %s\\n\", resp );\n+\tl = strlen( resp );\n+\tn = socket_write( &imap->buf.sock, resp, l );\n+\tfree( resp );\n+\tif (n != l)\n+\t\treturn -1;\n+\tcmdp->cb.cont = 0;\n+\treturn 0;\n+}\n+#endif\n+\n+static store_t *\n+imap_open_store( imap_server_conf_t *srvc )\n+{\n+\timap_store_t *ctx;\n+\timap_t *imap;\n+\tchar *arg, *rsp;\n+\tstruct hostent *he;\n+\tstruct sockaddr_in addr;\n+\tint s, a[2], preauth;\n+#if HAVE_LIBSSL\n+\tint use_ssl;\n+#endif\n+\n+\tctx = xcalloc( sizeof(*ctx), 1 );\n+\n+\tctx->imap = imap = xcalloc( sizeof(*imap), 1 );\n+\timap->buf.sock.fd = -1;\n+\timap->in_progress_append = &imap->in_progress;\n+\n+\t/* open connection to IMAP server */\n+#if HAVE_LIBSSL\n+\tuse_ssl = 0;\n+#endif\n+\n+\tif (srvc->tunnel) {\n+\t\tinfo( \"Starting tunnel '%s'... \", srvc->tunnel );\n+\n+\t\tif (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) {\n+\t\t\tperror( \"socketpair\" );\n+\t\t\texit( 1 );\n+\t\t}\n+\n+\t\tif (fork() == 0) {\n+\t\t\tif (dup2( a[0], 0 ) == -1 || dup2( a[0], 1 ) == -1)\n+\t\t\t\t_exit( 127 );\n+\t\t\tclose( a[0] );\n+\t\t\tclose( a[1] );\n+\t\t\texecl( \"/bin/sh\", \"sh\", \"-c\", srvc->tunnel, 0 );\n+\t\t\t_exit( 127 );\n+\t\t}\n+\n+\t\tclose (a[0]);\n+\n+\t\timap->buf.sock.fd = a[1];\n+\n+\t\tinfo( \"ok\\n\" );\n+\t} else {\n+\t\tmemset( &addr, 0, sizeof(addr) );\n+\t\taddr.sin_port = htons( srvc->port );\n+\t\taddr.sin_family = AF_INET;\n+\n+\t\tinfo( \"Resolving %s... \", srvc->host );\n+\t\the = gethostbyname( srvc->host );\n+\t\tif (!he) {\n+\t\t\tperror( \"gethostbyname\" );\n+\t\t\tgoto bail;\n+\t\t}\n+\t\tinfo( \"ok\\n\" );\n+\n+\t\taddr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);\n+\n+\t\ts = socket( PF_INET, SOCK_STREAM, 0 );\n+\n+\t\tinfo( \"Connecting to %s:%hu... \", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );\n+\t\tif (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) {\n+\t\t\tclose( s );\n+\t\t\tperror( \"connect\" );\n+\t\t\tgoto bail;\n+\t\t}\n+\t\tinfo( \"ok\\n\" );\n+\n+\t\timap->buf.sock.fd = s;\n+\n+#if HAVE_LIBSSL\n+\t\tif (srvc->use_imaps) {\n+\t\t\tif (start_tls( ctx ))\n+\t\t\t\tgoto bail;\n+\t\t\tuse_ssl = 1;\n+\t\t}\n+#endif\n+\t}\n+\n+\t/* read the greeting string */\n+\tif (buffer_gets( &imap->buf, &rsp )) {\n+\t\tfprintf( stderr, \"IMAP error: no greeting response\\n\" );\n+\t\tgoto bail;\n+\t}\n+\targ = next_arg( &rsp );\n+\tif (!arg || *arg != '*' || (arg = next_arg( &rsp )) == NULL) {\n+\t\tfprintf( stderr, \"IMAP error: invalid greeting response\\n\" );\n+\t\tgoto bail;\n+\t}\n+\tpreauth = 0;\n+\tif (!strcmp( \"PREAUTH\", arg ))\n+\t\tpreauth = 1;\n+\telse if (strcmp( \"OK\", arg ) != 0) {\n+\t\tfprintf( stderr, \"IMAP error: unknown greeting response\\n\" );\n+\t\tgoto bail;\n+\t}\n+\tparse_response_code( ctx, 0, rsp );\n+\tif (!imap->caps && imap_exec( ctx, 0, \"CAPABILITY\" ) != RESP_OK)\n+\t\tgoto bail;\n+\n+\tif (!preauth) {\n+#if HAVE_LIBSSL\n+\t\tif (!srvc->use_imaps && (srvc->use_sslv2 || srvc->use_sslv3 || srvc->use_tlsv1)) {\n+\t\t\t/* always try to select SSL support if available */\n+\t\t\tif (CAP(STARTTLS)) {\n+\t\t\t\tif (imap_exec( ctx, 0, \"STARTTLS\" ) != RESP_OK)\n+\t\t\t\t\tgoto bail;\n+\t\t\t\tif (start_tls( ctx ))\n+\t\t\t\t\tgoto bail;\n+\t\t\t\tuse_ssl = 1;\n+\n+\t\t\t\tif (imap_exec( ctx, 0, \"CAPABILITY\" ) != RESP_OK)\n+\t\t\t\t\tgoto bail;\n+\t\t\t} else {\n+\t\t\t\tif (srvc->require_ssl) {\n+\t\t\t\t\tfprintf( stderr, \"IMAP error: SSL support not available\\n\" );\n+\t\t\t\t\tgoto bail;\n+\t\t\t\t} else\n+\t\t\t\t\twarn( \"IMAP warning: SSL support not available\\n\" );\n+\t\t\t}\n+\t\t}\n+#endif\n+\n+\t\tinfo (\"Logging in...\\n\");\n+\t\tif (!srvc->user) {\n+\t\t\tfprintf( stderr, \"Skipping server %s, no user\\n\", srvc->host );\n+\t\t\tgoto bail;\n+\t\t}\n+\t\tif (!srvc->pass) {\n+\t\t\tchar prompt[80];\n+\t\t\tsprintf( prompt, \"Password (%s@%s): \", srvc->user, srvc->host );\n+\t\t\targ = getpass( prompt );\n+\t\t\tif (!arg) {\n+\t\t\t\tperror( \"getpass\" );\n+\t\t\t\texit( 1 );\n+\t\t\t}\n+\t\t\tif (!*arg) {\n+\t\t\t\tfprintf( stderr, \"Skipping account %s@%s, no password\\n\", srvc->user, srvc->host );\n+\t\t\t\tgoto bail;\n+\t\t\t}\n+\t\t\t/*\n+\t\t\t * getpass() returns a pointer to a static buffer.  make a copy\n+\t\t\t * for long term storage.\n+\t\t\t */\n+\t\t\tsrvc->pass = strdup( arg );\n+\t\t}\n+#if HAVE_LIBSSL\n+\t\tif (CAP(CRAM)) {\n+\t\t\tstruct imap_cmd_cb cb;\n+\n+\t\t\tinfo( \"Authenticating with CRAM-MD5\\n\" );\n+\t\t\tmemset( &cb, 0, sizeof(cb) );\n+\t\t\tcb.cont = do_cram_auth;\n+\t\t\tif (imap_exec( ctx, &cb, \"AUTHENTICATE CRAM-MD5\" ) != RESP_OK)\n+\t\t\t\tgoto bail;\n+\t\t} else if (srvc->require_cram) {\n+\t\t\tfprintf( stderr, \"IMAP error: CRAM-MD5 authentication is not supported by server\\n\" );\n+\t\t\tgoto bail;\n+\t\t} else\n+#endif\n+\t\t{\n+\t\t\tif (CAP(NOLOGIN)) {\n+\t\t\t\tfprintf( stderr, \"Skipping account %s@%s, server forbids LOGIN\\n\", srvc->user, srvc->host );\n+\t\t\t\tgoto bail;\n+\t\t\t}\n+#if HAVE_LIBSSL\n+\t\t\tif (!use_ssl)\n+#endif\n+\t\t\t\twarn( \"*** IMAP Warning *** Password is being sent in the clear\\n\" );\n+\t\t\tif (imap_exec( ctx, 0, \"LOGIN \\\"%s\\\" \\\"%s\\\"\", srvc->user, srvc->pass ) != RESP_OK) {\n+\t\t\t\tfprintf( stderr, \"IMAP error: LOGIN failed\\n\" );\n+\t\t\t\tgoto bail;\n+\t\t\t}\n+\t\t}\n+\t} /* !preauth */\n+\n+\tctx->prefix = \"\";\n+\tctx->trashnc = 1;\n+\treturn (store_t *)ctx;\n+\n+  bail:\n+\timap_close_store( &ctx->gen );\n+\treturn 0;\n+}\n+\n+static int\n+imap_make_flags( int flags, char *buf )\n+{\n+\tconst char *s;\n+\tunsigned i, d;\n+\n+\tfor (i = d = 0; i < as(Flags); i++)\n+\t\tif (flags & (1 << i)) {\n+\t\t\tbuf[d++] = ' ';\n+\t\t\tbuf[d++] = '\\\\';\n+\t\t\tfor (s = Flags[i]; *s; s++)\n+\t\t\t\tbuf[d++] = *s;\n+\t\t}\n+\tbuf[0] = '(';\n+\tbuf[d++] = ')';\n+\treturn d;\n+}\n+\n+#define TUIDL 8\n+\n+static int\n+imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )\n+{\n+\timap_store_t *ctx = (imap_store_t *)gctx;\n+\timap_t *imap = ctx->imap;\n+\tstruct imap_cmd_cb cb;\n+\tchar *fmap, *buf;\n+\tconst char *prefix, *box;\n+\tint ret, i, j, d, len, extra, nocr;\n+\tint start, sbreak = 0, ebreak = 0;\n+\tchar flagstr[128], tuid[TUIDL * 2 + 1];\n+\n+\tmemset( &cb, 0, sizeof(cb) );\n+\n+\tfmap = data->data;\n+\tlen = data->len;\n+\tnocr = !data->crlf;\n+\textra = 0, i = 0;\n+\tif (!CAP(UIDPLUS) && uid) {\n+\t  nloop:\n+\t\tstart = i;\n+\t\twhile (i < len)\n+\t\t\tif (fmap[i++] == '\\n') {\n+\t\t\t\textra += nocr;\n+\t\t\t\tif (i - 2 + nocr == start) {\n+\t\t\t\t\tsbreak = ebreak = i - 2 + nocr;\n+\t\t\t\t\tgoto mktid;\n+\t\t\t\t}\n+\t\t\t\tif (!memcmp( fmap + start, \"X-TUID: \", 8 )) {\n+\t\t\t\t\textra -= (ebreak = i) - (sbreak = start) + nocr;\n+\t\t\t\t\tgoto mktid;\n+\t\t\t\t}\n+\t\t\t\tgoto nloop;\n+\t\t\t}\n+\t\t/* invalid message */\n+\t\tfree( fmap );\n+\t\treturn DRV_MSG_BAD;\n+\t mktid:\n+\t\tfor (j = 0; j < TUIDL; j++)\n+\t\t\tsprintf( tuid + j * 2, \"%02x\", arc4_getbyte() );\n+\t\textra += 8 + TUIDL * 2 + 2;\n+\t}\n+\tif (nocr)\n+\t\tfor (; i < len; i++)\n+\t\t\tif (fmap[i] == '\\n')\n+\t\t\t\textra++;\n+\n+\tcb.dlen = len + extra;\n+\tbuf = cb.data = xmalloc( cb.dlen );\n+\ti = 0;\n+\tif (!CAP(UIDPLUS) && uid) {\n+\t\tif (nocr) {\n+\t\t\tfor (; i < sbreak; i++)\n+\t\t\t\tif (fmap[i] == '\\n') {\n+\t\t\t\t\t*buf++ = '\\r';\n+\t\t\t\t\t*buf++ = '\\n';\n+\t\t\t\t} else\n+\t\t\t\t\t*buf++ = fmap[i];\n+\t\t} else {\n+\t\t\tmemcpy( buf, fmap, sbreak );\n+\t\t\tbuf += sbreak;\n+\t\t}\n+\t\tmemcpy( buf, \"X-TUID: \", 8 );\n+\t\tbuf += 8;\n+\t\tmemcpy( buf, tuid, TUIDL * 2 );\n+\t\tbuf += TUIDL * 2;\n+\t\t*buf++ = '\\r';\n+\t\t*buf++ = '\\n';\n+\t\ti = ebreak;\n+\t}\n+\tif (nocr) {\n+\t\tfor (; i < len; i++)\n+\t\t\tif (fmap[i] == '\\n') {\n+\t\t\t\t*buf++ = '\\r';\n+\t\t\t\t*buf++ = '\\n';\n+\t\t\t} else\n+\t\t\t\t*buf++ = fmap[i];\n+\t} else\n+\t\tmemcpy( buf, fmap + i, len - i );\n+\n+\tfree( fmap );\n+\n+\td = 0;\n+\tif (data->flags) {\n+\t\td = imap_make_flags( data->flags, flagstr );\n+\t\tflagstr[d++] = ' ';\n+\t}\n+\tflagstr[d] = 0;\n+\n+\tif (!uid) {\n+\t\tbox = gctx->conf->trash;\n+\t\tprefix = ctx->prefix;\n+\t\tcb.create = 1;\n+\t\tif (ctx->trashnc)\n+\t\t\timap->caps = imap->rcaps & ~(1 << LITERALPLUS);\n+\t} else {\n+\t\tbox = gctx->name;\n+\t\tprefix = !strcmp( box, \"INBOX\" ) ? \"\" : ctx->prefix;\n+\t\tcb.create = (gctx->opts & OPEN_CREATE) != 0;\n+\t\t/*if (ctx->currentnc)\n+\t\t\timap->caps = imap->rcaps & ~(1 << LITERALPLUS);*/\n+\t}\n+\tcb.ctx = uid;\n+\tret = imap_exec_m( ctx, &cb, \"APPEND \\\"%s%s\\\" %s\", prefix, box, flagstr );\n+\timap->caps = imap->rcaps;\n+\tif (ret != DRV_OK)\n+\t\treturn ret;\n+\tif (!uid)\n+\t\tctx->trashnc = 0;\n+\telse {\n+\t\t/*ctx->currentnc = 0;*/\n+\t\tgctx->count++;\n+\t}\n+\n+\treturn DRV_OK;\n+}\n+\n+\n+/*\n+ * mm-push-imap - push a patch into an imap folder\n+ */\n+\n+#define CHUNKSIZE 0x1000\n+\n+static int\n+read_message( FILE *f, msg_data_t *msg )\n+{\n+\tint len, r;\n+\n+\tmemset( msg, 0, sizeof *msg );\n+\tlen = CHUNKSIZE;\n+\tmsg->data = xmalloc( len+1 );\n+\tmsg->data[0] = 0;\n+\n+\twhile(!feof( f )) {\n+\t\tif (msg->len >= len) {\n+\t\t\tvoid *p;\n+\t\t\tlen += CHUNKSIZE;\n+\t\t\tp = xrealloc(msg->data, len+1);\n+\t\t\tif (!p)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tr = fread( &msg->data[msg->len], 1, len - msg->len, f );\n+\t\tif (r <= 0)\n+\t\t\tbreak;\n+\t\tmsg->len += r;\n+\t}\n+\tmsg->data[msg->len] = 0;\n+\treturn msg->len;\n+}\n+\n+static int count_messages( msg_data_t *msg )\n+{\n+\tint count = 0;\n+\tchar *p = msg->data;\n+\n+\twhile (1) {\n+\t\tif (!strncmp( \"From \", p, 5 )) {\n+\t\t\tcount++;\n+\t\t\tp += 5;\n+\t\t}\n+\t\tp = strstr( p+5, \"\\nFrom \");\n+\t\tif (!p)\n+\t\t\tbreak;\n+\t\tp++;\n+\t}\n+\treturn count;\n+}\n+\n+static int\n+split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )\n+{\n+\tchar *p, *data;\n+\n+\tmemset( msg, 0, sizeof *msg );\n+\tif (*ofs >= all_msgs->len)\n+\t\treturn 0;\n+\n+\tdata = &all_msgs->data[ *ofs ];\n+\tmsg->len = all_msgs->len - *ofs;\n+\n+\tif (msg->len < 5 || strncmp( data, \"From \", 5 ))\n+\t\treturn 0;\n+\n+\tp = strstr( data, \"\\nFrom \" );\n+\tif (p)\n+\t\tmsg->len = &p[1] - data;\n+\n+\tmsg->data = xmalloc( msg->len + 1 );\n+\tif (!msg->data)\n+\t\treturn 0;\n+\n+\tmemcpy( msg->data, data, msg->len );\n+\tmsg->data[ msg->len ] = 0;\n+\n+\t*ofs += msg->len;\n+ \treturn 1;\n+}\n+\n+static imap_server_conf_t server =\n+{\n+\tNULL,\t/* name */\n+\tNULL,\t/* tunnel */\n+\tNULL,\t/* host */\n+\t0,\t/* port */\n+\tNULL,\t/* user */\n+\tNULL,\t/* pass */\n+#if HAVE_LIBSSL\n+\tNULL,\t/* cert_file */\n+\t0,\t/* use_imaps */\n+\t0,\t/* require_ssl */\n+\t0,\t/* use_sslv2 */\n+\t0,\t/* use_sslv3 */\n+\t0,\t/* use_tlsv1 */\n+\t0,\t/* require_cram */\n+#endif\n+};\n+\n+static char *imap_folder;\n+\n+static int\n+git_imap_config(const char *key, const char *val)\n+{\n+\tchar imap_key[] = \"imap.\";\n+\n+\tif (strncmp( key, imap_key, sizeof imap_key - 1 ))\n+\t\treturn 0;\n+\tkey += sizeof imap_key - 1;\n+\n+\tif (!strcasecmp( \"Folder\", key )) {\n+\t\timap_folder = strdup( val );\n+\t} else if (!strcasecmp( \"Host\", key )) {\n+#if HAVE_LIBSSL\n+\t\tif (!memcmp( \"imaps:\", val, 6 )) {\n+\t\t\tval += 6;\n+\t\t\tserver.use_imaps = 1;\n+\t\t\tserver.use_sslv2 = 1;\n+\t\t\tserver.use_sslv3 = 1;\n+\t\t\tif (!server.port)\n+\t\t\t\tserver.port = 993;\n+\t\t} else\n+#endif\n+\t\t{\n+\t\t\tif (!memcmp( \"imap:\", val, 5 ))\n+\t\t\t\tval += 5;\n+\t\t\tif (!server.port)\n+\t\t\t\tserver.port = 143;\n+\t\t}\n+\t\tif (!memcmp( \"//\", val, 2 ))\n+\t\t\tval += 2;\n+\t\tserver.host = strdup( val );\n+\t}\n+\telse if (!strcasecmp( \"User\", key ))\n+\t\tserver.user = strdup( val );\n+\telse if (!strcasecmp( \"Pass\", key ))\n+\t\tserver.pass = strdup( val );\n+\telse if (!strcasecmp( \"Port\", key ))\n+\t\tserver.port = git_config_int( key, val );\n+\telse if (!strcasecmp( \"Tunnel\", key ))\n+\t\tserver.tunnel = strdup( val );\n+#if HAVE_LIBSSL\n+\telse if (!strcasecmp( \"CertificateFile\", key ))\n+\t\tserver.cert_file = expand_strdup( val );\n+\telse if (!strcasecmp( \"RequireSSL\", key ))\n+\t\tserver.require_ssl = git_config_bool( key, val );\n+\telse if (!strcasecmp( \"UseSSLv2\", key ))\n+\t\tserver.use_sslv2 = git_config_bool( key, val );\n+\telse if (!strcasecmp( \"UseSSLv3\", key ))\n+\t\tserver.use_sslv3 = git_config_bool( key, val );\n+\telse if (!strcasecmp( \"UseTLSv1\", key ))\n+\t\tserver.use_tlsv1 = git_config_bool( key, val );\n+\telse if (!strcasecmp( \"RequireCRAM\", key ))\n+\t\tserver.require_cram = git_config_bool( key, val );\n+#endif\n+\treturn 0;\n+}\n+\n+int\n+main(int argc, char **argv)\n+{\n+\tmsg_data_t all_msgs, msg;\n+\tstore_t *ctx = 0;\n+\tint uid = 0;\n+\tint ofs = 0;\n+\tint r;\n+\tint total, n = 0;\n+\n+\t/* init the random number generator */\n+\tarc4_init();\n+\n+\tgit_config( git_imap_config );\n+\n+\tif (!imap_folder) {\n+\t\tfprintf( stderr, \"no imap store specified\\n\" );\n+\t\treturn 1;\n+\t}\n+\n+\t/* read the messages */\n+\tif (!read_message( stdin, &all_msgs )) {\n+\t\tfprintf(stderr,\"nothing to send\\n\");\n+\t\treturn 1;\n+\t}\n+\n+\t/* write it to the imap server */\n+\tctx = imap_open_store( &server );\n+\tif (!ctx) {\n+\t\tfprintf(stderr,\"failed to open store\\n\");\n+\t\treturn 1;\n+\t}\n+\n+\ttotal = count_messages( &all_msgs );\n+\tfprintf( stderr, \"sending %d messages\\n\", total );\n+\tctx->name = imap_folder;\n+\twhile (1) {\n+\t\tfprintf( stderr, \"sent (%d/%d)\\r\", n, total );\n+\t\tif (!split_msg( &all_msgs, &msg, &ofs ))\n+\t\t\tbreak;\n+\t\tr = imap_store_msg( ctx, &msg, &uid );\n+\t\tif (r != DRV_OK) break;\n+\t\tn++;\n+\t}\n+\tfprintf( stderr,\"\\n\" );\n+\n+\timap_close_store( ctx );\n+\n+\treturn 0;\n+}\n\n"},{"id":"17364","messageId":"7vacbz7vod.fsf@assigned-by-dhcp.cox.net","threadId":"3582","inReplyTo":"440C3499.9080000@codeweavers.com","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-03-09T10:35:14Z","receivedAt":"2006-03-09T10:35:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mike McCormack <mike@codeweavers.com> writes:\n\n> This probably needs a bit more work, but I'll solicit comments and\n> flames anyway...\n\nOK, then please work a bit more ;-)\n\nI kind of like this approach and even wish my e-mail workflow\ninvolved an imap server with draft folders.  Currently I do\neverything in Gnus and my drafts are on local disk.\n\n> The target IMAP folder:\n>\n> [imap]\n>         Folder = \"INBOX.Drafts\"\n>\n> A command to open an ssh tunnel to the imap mail server.\n\nThe .git/config file is a good choice for storing this\ninformation, because you might even use different draft folders\nfor different projects, i.e. the configuration is\nper-repository.\n\n> [imap]\n>         Tunnel = \"ssh -q user@imap.server.com /usr/bin/imapd ./Maildir\n> 2> /dev/null\"\n> [imap]\n> \tHost = imap.server.com\n> \tUser = bob\n> \tPassword = pwd\n> \tPort = 143\n\nThese I am not so sure.  It _might_ make sense to have something\nlike this under $HOME/.  Isn't there an established convention\nfor storing something like this for existing MUAs?\n\n> + *  This program is free software; you can redistribute it and/or modify\n> + *  it under the terms of the GNU General Public License as published by\n> + *  the Free Software Foundation; either version 2 of the License, or\n> + *  (at your option) any later version.\n> + *...\n> + * As a special exception, mbsync may be linked with the OpenSSL library,\n> + * despite that library's more restrictive license.\n\nHmmm.  \n\n> +#include <sys/types.h>\n>...\n> +#include <pwd.h>\n> +\n> +#include \"cache.h\"\n\nIf you are including \"cache.h\", you probably do not need to\ninclude many of the standard include files.\n\n> +#define as(ar) (sizeof(ar)/sizeof(ar[0]))\n\nWe have something like this in apply.c, exec_cmd.c and git.c;\nprobably we would want a macro in \"cache.h\".\n\n\tgit.c:326:#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))\n\n> +static int\n> +git_imap_config(const char *key, const char *val)\n> +{\n>...\n> +\tif (!strcasecmp( \"Folder\", key )) {\n\ngit_config calls you after downcasing the keys, so you do not\nneed to do strcasecmp.  Just spell things out in lowercase.\n\n> +\t\tif (!memcmp( \"imaps:\", val, 6 )) {\n> +\t\t\tif (!memcmp( \"imap:\", val, 5 ))\n\nIs val always longer than 5 or 6 bytes here?\n"},{"id":"17366","messageId":"Pine.LNX.4.63.0603091227560.20277@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"3582","inReplyTo":"7vacbz7vod.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-03-09T11:30:38Z","receivedAt":"2006-03-09T11:30:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 9 Mar 2006, Junio C Hamano wrote:\n\n> Mike McCormack <mike@codeweavers.com> writes:\n> \n> > +\t\tif (!memcmp( \"imaps:\", val, 6 )) {\n> > +\t\t\tif (!memcmp( \"imap:\", val, 5 ))\n> \n> Is val always longer than 5 or 6 bytes here?\n\nThat does not matter, since they are strings, and the memcmp should not \nlook further if they are shorter (because the comparison to '\\0' failed \nalready).\n\nHowever, if !memcmp(\"imaps:\", val, 6), it means that val starts with \nthe string \"imaps:\", right? Then !memcmp(\"imap:\", val, 5) must always \nfail, no?\n\nCiao,\nDscho\n"},{"id":"17367","messageId":"4410140E.2000609@op5.se","threadId":"3582","inReplyTo":"Pine.LNX.4.63.0603091227560.20277@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2006-03-09T11:39:58Z","receivedAt":"2006-03-09T11:39:58Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Johannes Schindelin wrote:\n> Hi,\n> \n> On Thu, 9 Mar 2006, Junio C Hamano wrote:\n> \n> \n>>Mike McCormack <mike@codeweavers.com> writes:\n>>\n>>\n>>>+\t\tif (!memcmp( \"imaps:\", val, 6 )) {\n>>>+\t\t\tif (!memcmp( \"imap:\", val, 5 ))\n>>\n>>Is val always longer than 5 or 6 bytes here?\n> \n> \n> That does not matter, since they are strings, and the memcmp should not \n> look further if they are shorter (because the comparison to '\\0' failed \n> already).\n> \n\nThat's what strcmp() does. memcmp() walks the lenghth even if it \nencounters nul bytes. Perhaps you confuse it with strncmp()?\n\n\n> However, if !memcmp(\"imaps:\", val, 6), it means that val starts with \n> the string \"imaps:\", right? Then !memcmp(\"imap:\", val, 5) must always \n> fail, no?\n> \n\nYes. Recent gcc's will recognize it as dead code and remove it. It's \nstill ugly though.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"17368","messageId":"Pine.LNX.4.63.0603091243001.20908@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"3582","inReplyTo":"4410140E.2000609@op5.se","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-03-09T11:44:01Z","receivedAt":"2006-03-09T11:44:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 9 Mar 2006, Andreas Ericsson wrote:\n\n> Johannes Schindelin wrote:\n> > Hi,\n> > \n> > On Thu, 9 Mar 2006, Junio C Hamano wrote:\n> > \n> > \n> > > Mike McCormack <mike@codeweavers.com> writes:\n> > > \n> > > \n> > > > +\t\tif (!memcmp( \"imaps:\", val, 6 )) {\n> > > > +\t\t\tif (!memcmp( \"imap:\", val, 5 ))\n> > > \n> > > Is val always longer than 5 or 6 bytes here?\n> > \n> > \n> > That does not matter, since they are strings, and the memcmp should not look\n> > further if they are shorter (because the comparison to '\\0' failed already).\n> > \n> \n> That's what strcmp() does. memcmp() walks the lenghth even if it encounters\n> nul bytes. Perhaps you confuse it with strncmp()?\n\nSorry, I was unclear. Of course, memcmp() does not stop on NUL. But it \nstops when that NUL is different from what the other pointer has. Which is \nthe case here.\n\nCiao,\nDscho\n"},{"id":"17374","messageId":"slrne10b8c.fr9.mdw@metalzone.distorted.org.uk","threadId":"3582","inReplyTo":"Pine.LNX.4.63.0603091243001.20908@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Mark Wooding","fromEmail":"mdw@distorted.org.uk","sentAt":"2006-03-09T13:26:36Z","receivedAt":"2006-03-09T13:26:36Z","isPatch":true,"sender":{"key":"mdw@distorted.org.uk","avatar":null},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n\n> Sorry, I was unclear. Of course, memcmp() does not stop on NUL. But it \n> stops when that NUL is different from what the other pointer has. Which is \n> the case here.\n\nDoes it really?  My copy of the spec doesn't say that.  It says only\nthis:\n\n: 7.21.4.1  The memcmp function\n:\n: Synopsis\n:\n: #include <string.h>\n: int memcmp(const void *s1, const void *s2, size_t n);\n:\n: Description\n:\n: The memcmp() function compares the first n characters of the object\n: pointed to by s1 to the first n characters of the object pointed to by\n: s2.262)\n:\n: Returns\n:\n: The memcmp function returns an integer greater than, equal to, or less\n: than zero, accordingly as the object pointed to by s1 is greater than,\n: equal to, or less than the object pointed to by s2.\n:\n: 262) The contents of ``holes'' used as padding for purposes of\n: alignment within structure objects are indeterminate.  Strings shorter\n: than their allocated space and unions may also cause problems in\n: comparison.\n\nThere are reasons for which it'd be desirable that memcmp really compare\nall the bytes, even if it can in theory stop early: in particular, there\nare cases where early exit can leak timing information which makes it\npossible to attack cryptographic protocols.\n\nI'd have to recommend strncmp for this job.\n\n-- [mdw]\n"},{"id":"17375","messageId":"Pine.LNX.4.63.0603091445280.21440@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"3582","inReplyTo":"slrne10b8c.fr9.mdw@metalzone.distorted.org.uk","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-03-09T13:47:00Z","receivedAt":"2006-03-09T13:47:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 9 Mar 2006, Mark Wooding wrote:\n\n> There are reasons for which it'd be desirable that memcmp really compare\n> all the bytes, even if it can in theory stop early: in particular, there\n> are cases where early exit can leak timing information which makes it\n> possible to attack cryptographic protocols.\n\nI would be astonished if memcmp has to be timing proof for *all* \napplications, just to keep crypto people happy. I don't *want* a slow-down \nin my super-duper 3d shooter.\n\n> I'd have to recommend strncmp for this job.\n\nFully agree.\n\nCiao,\nDscho\n"},{"id":"17384","messageId":"Pine.LNX.4.64.0603090836030.18022@g5.osdl.org","threadId":"3582","inReplyTo":"Pine.LNX.4.63.0603091227560.20277@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-03-09T16:41:16Z","receivedAt":"2006-03-09T16:41:16Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Thu, 9 Mar 2006, Johannes Schindelin wrote:\n> > \n> > > +\t\tif (!memcmp( \"imaps:\", val, 6 )) {\n> > > +\t\t\tif (!memcmp( \"imap:\", val, 5 ))\n> > \n> > Is val always longer than 5 or 6 bytes here?\n> \n> That does not matter, since they are strings, and the memcmp should not \n> look further if they are shorter (because the comparison to '\\0' failed \n> already).\n\nNo.\n\nIt's true that any sane memcmp() will stop when it notices a difference, \nand it's also true that the return value semantics of memcmp() means that \nit has to walk beginning-to-end.\n\nHOWEVER. The key phrase is \"_when_ it notices a difference\".\n\nIt's quite common for optimized memcmp()'s to do things like loading \nseveral words from both the source and the destinations, and testing them \ntogether, and only start doing the byte-by-byte comparison when the \"big\" \ncomparison has failed.\n\nSo when you do a\n\n\tif (!memcmp(string, mystring, mystringlength))\n\t\t...\n\nit's entirely possible that it will load bytes from \"string\" _past_ the \nend of the string because of an unrolled inner loop that does things \nmultiple bytes at a time. They won't be used in the eventual result, but \njust the fact that they are loaded from memory can mean that your program \ntakes a SIGSEGV, for example, becaue it turns out \"string\" was just a \nsingle NUL byte at the end of a page, and there's nothing after it.\n\nIOW, it's a bad optimization.\n\nUse \"strncmp()\" instead. Yes, it can be slower, exactly because it has to \ncheck more, but it checks more exactly because memcmp() can cause \nundefined behaviour by running off the end of a string.\n\n\t\tLinus\n"},{"id":"17389","messageId":"7vmzfz5w2k.fsf@assigned-by-dhcp.cox.net","threadId":"3582","inReplyTo":"Pine.LNX.4.64.0603090836030.18022@g5.osdl.org","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-03-09T18:09:39Z","receivedAt":"2006-03-09T18:09:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> it's entirely possible that it will load bytes from \"string\" _past_ the \n> end of the string because of an unrolled inner loop that does things \n> multiple bytes at a time. They won't be used in the eventual result, but \n> just the fact that they are loaded from memory can mean that your program \n> takes a SIGSEGV, for example, becaue it turns out \"string\" was just a \n> single NUL byte at the end of a page, and there's nothing after it.\n\nFunny.  I've seen this exact bug in memcmp and strcmp in earlier\nSunOS (pre Solaris) libc when I was working on something like\nValgrind in my previous life.\n"},{"id":"17391","messageId":"Pine.LNX.4.64.0603091017530.18022@g5.osdl.org","threadId":"3582","inReplyTo":"7vmzfz5w2k.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-03-09T18:21:50Z","receivedAt":"2006-03-09T18:21:50Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Thu, 9 Mar 2006, Junio C Hamano wrote:\n>\n> Linus Torvalds <torvalds@osdl.org> writes:\n> \n> > it's entirely possible that it will load bytes from \"string\" _past_ the \n> > end of the string because of an unrolled inner loop that does things \n> > multiple bytes at a time. They won't be used in the eventual result, but \n> > just the fact that they are loaded from memory can mean that your program \n> > takes a SIGSEGV, for example, becaue it turns out \"string\" was just a \n> > single NUL byte at the end of a page, and there's nothing after it.\n> \n> Funny.  I've seen this exact bug in memcmp and strcmp in earlier\n> SunOS (pre Solaris) libc when I was working on something like\n> Valgrind in my previous life.\n\nWell, Valgrind actually can complain for no good reason.\n\nSystem libraries often take advantage of knowing how the CPU and the \nsystem memory layout works. For example, just from knowing that pages are \nalways aligned to a certain (largish) boundary, you can know that it's \nperfectly safe to do certain optimizations and prefetch past the end of an \nobject, as long as it's in the same page (and the easiest way to verify \nthat is to just do it when something is aligned).\n\nSo Valgrind will sometimes complain about perfectly bug-free code, just \nbecause the bug-free code accesses outside the \"strictly allowable\" \nregion because it knows it can. \n\nValgrind can be taught about system libraries like that, but especially if \nit's an early port to a new architecture or OS, valgrind will often \ncomplain unnecessarily.\n\n\t\t\tLinus\n"},{"id":"17392","messageId":"7v4q275u7m.fsf@assigned-by-dhcp.cox.net","threadId":"3582","inReplyTo":"Pine.LNX.4.64.0603091017530.18022@g5.osdl.org","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-03-09T18:49:49Z","receivedAt":"2006-03-09T18:49:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n>> > ... They won't be used in the eventual result, but \n>> > just the fact that they are loaded from memory can mean that your program \n>> > takes a SIGSEGV, for example, becaue it turns out \"string\" was just a \n>> > single NUL byte at the end of a page, and there's nothing after it.\n>> \n>> Funny.  I've seen this exact bug in memcmp and strcmp in earlier\n>> SunOS (pre Solaris) libc when I was working on something like\n>> Valgrind in my previous life.\n>\n> Well, Valgrind actually can complain for no good reason.\n\nTo clarify, it was _not_ Valgrind I was mucking with.  And the\noptimization used in the system library was wrong -- it stepped\nover the page boundary without checking.  I found that it was\nfixed in later releases (that was all before Sun started\ncalling their system Solaris).\n"},{"id":"17400","messageId":"slrne11il0.fr9.mdw@metalzone.distorted.org.uk","threadId":"3582","inReplyTo":"Pine.LNX.4.63.0603091445280.21440@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Mark Wooding","fromEmail":"mdw@distorted.org.uk","sentAt":"2006-03-10T00:38:56Z","receivedAt":"2006-03-10T00:38:56Z","isPatch":true,"sender":{"key":"mdw@distorted.org.uk","avatar":null},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n\n> I would be astonished if memcmp has to be timing proof for *all*\n> applications, just to keep crypto people happy. I don't *want* a\n> slow-down in my super-duper 3d shooter.\n\nOh, obviously that would be lunatic! ;-) Just making the point that it's\nnot /completely/ obvious that early stopping is what everyone wants...\n\n-- [mdw]\n"},{"id":"17410","messageId":"44110762.106@codeweavers.com","threadId":"3582","inReplyTo":"7vacbz7vod.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Add git-imap-send.","fromName":"Mike McCormack","fromEmail":"mike@codeweavers.com","sentAt":"2006-03-10T04:58:10Z","receivedAt":"2006-03-10T04:58:10Z","isPatch":true,"sender":{"key":"mike@codeweavers.com","avatar":null},"body":"\nJunio C Hamano wrote:\n\n>>[imap]\n>>\tHost = imap.server.com\n>>\tUser = bob\n>>\tPassword = pwd\n>>\tPort = 143\n> \n> These I am not so sure.  It _might_ make sense to have something\n> like this under $HOME/.  Isn't there an established convention\n> for storing something like this for existing MUAs?\n\nIt seems better to keep all the configuration in the same place.  It's \npossible (although unlikely) that somebody uses two different IMAP \nservers to send mail... maybe one for work and one for play?\n\nI think it would also be good to be able to specify these as command \nline options, as some people might be uncomfortable with having their \npassword in a file (though it would be better to use an ssh tunnel).\n\n>>+ * As a special exception, mbsync may be linked with the OpenSSL library,\n>>+ * despite that library's more restrictive license.\n\n> Hmmm.\n\nI'll remove this, and all the SSL specific code, as it also complicates \nthe Makefile and adds an extra dependency.\n\nI've fixed all the other issues you pointed out, and will resend the patch.\n\nMike\n"}]}