{"thread":{"id":"37338","subject":"[PATCH/RFC] git-imap-send: use libcurl for implementation","startedAt":"2014-08-12T21:50:54Z","lastAt":"2014-08-19T17:13:35Z","messageCount":7,"participants":["Bernhard Reiter","Jonathan Nieder","Jeff King","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"247629","messageId":"53EA8C3E.1080500@raz.or.at","threadId":"37338","inReplyTo":null,"subject":"[PATCH/RFC] git-imap-send: use libcurl for implementation","fromName":"Bernhard Reiter","fromEmail":"ockham@raz.or.at","sentAt":"2014-08-12T21:50:54Z","receivedAt":"2014-08-12T21:50:54Z","isPatch":true,"sender":{"key":"ockham@raz.or.at","avatar":"https://avatars.githubusercontent.com/u/96308?v=4"},"body":"\nUse libcurl's high-level API functions to implement git-imap-send\ninstead of the previous low-level OpenSSL-based functions.\n\nSigned-off-by: Bernhard Reiter <ockham@raz.or.at>\n---\nSince version 7.30.0, libcurl's API has been able to communicate with\nIMAP servers. Using those high-level functions instead of the current\nones reduces imap-send.c by some 1200 lines of code.\n\nAs I don't have access to that many IMAP servers, I haven't been able to\ntest a variety of parameter combinations. I did test both secure and\ninsecure (imaps:// and imap://) connections -- this e-mail was generated\nthat way -- but could e.g. neither test the authMethod nor the tunnel\nparameter.\n\nAs git-imap-send is one of the two instances OpenSSL is currently used\nby git -- the other one being SHA1 -- it might be worthwhile considering\ndropping it altogether as there's already a SHA1 library built into git\navailable as an alternative.\n\nKind regards\nBernhard\nPS: Please CC!\n\n INSTALL     |   14 +-\n Makefile    |    4 +-\n git.spec.in |    5 +-\n imap-send.c | 1288\n+++++------------------------------------------------------\n 4 files changed, 111 insertions(+), 1200 deletions(-)\n\n\n\n\ndiff --git a/INSTALL b/INSTALL\nindex 6ec7a24..2cd3a42 100644\n--- a/INSTALL\n+++ b/INSTALL\n@@ -108,18 +108,16 @@ Issues of note:\n \t  so you might need to install additional packages other than Perl\n \t  itself, e.g. Time::HiRes.\n \n-\t- \"openssl\" library is used by git-imap-send to use IMAP over SSL.\n-\t  If you don't need it, use NO_OPENSSL.\n-\n-\t  By default, git uses OpenSSL for SHA1 but it will use its own\n+\t- By default, git uses OpenSSL for SHA1 but it will use its own\n \t  library (inspired by Mozilla's) with either NO_OPENSSL or\n \t  BLK_SHA1.  Also included is a version optimized for PowerPC\n \t  (PPC_SHA1).\n \n-\t- \"libcurl\" library is used by git-http-fetch and git-fetch.  You\n-\t  might also want the \"curl\" executable for debugging purposes.\n-\t  If you do not use http:// or https:// repositories, you do not\n-\t  have to have them (use NO_CURL).\n+\t- \"libcurl\" library is used by git-http-fetch, git-fetch, and\n+\t  git-impap-send.  You might also want the \"curl\" executable for\n+\t  debugging purposes. If you do not use http:// or https://\n+\t  repositories, and do not want to put patches into an IMAP\n+\t  mailbox, you do not have to have them (use NO_CURL).\n \n \t- \"expat\" library; git-http-push uses it for remote lock\n \t  management over DAV.  Similar to \"curl\" above, this is optional\ndiff --git a/Makefile b/Makefile\nindex 2320de5..7805603 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2067,9 +2067,9 @@ endif\n git-%$X: %.o GIT-LDFLAGS $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)\n \n-git-imap-send$X: imap-send.o GIT-LDFLAGS $(GITLIBS)\n+git-imap-send$X: imap-send.o http.o GIT-LDFLAGS $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n-\t\t$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)\n+\t\t$(LIBS) $(CURL_LIBCURL)\n \n git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\ndiff --git a/git.spec.in b/git.spec.in\nindex d61d537..7d9230f 100644\n--- a/git.spec.in\n+++ b/git.spec.in\n@@ -8,7 +8,7 @@ License: \tGPL\n Group: \t\tDevelopment/Tools\n URL: \t\thttp://kernel.org/pub/software/scm/git/\n Source: \thttp://kernel.org/pub/software/scm/git/%{name}-%{version}.tar.gz\n-BuildRequires:\tzlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel, gettext  %{!?_without_docs:, xmlto, asciidoc > 6.0.3}\n+BuildRequires:\tzlib-devel >= 1.2, openssl-devel, curl-devel >= 7.30.0, expat-devel, gettext  %{!?_without_docs:, xmlto, asciidoc > 6.0.3}\n BuildRoot:\t%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)\n \n Requires:\tperl-Git = %{version}-%{release}\n@@ -214,6 +214,9 @@ rm -rf $RPM_BUILD_ROOT\n # No files for you!\n \n %changelog\n+* Mon Aug 11 2014 Bernhard Reiter <ockham@raz.or.at>\n+- Require version >= 7.30.0 of curl-devel for IMAP functions.\n+\n * Sun Sep 18 2011 Jakub Narebski <jnareb@gmail.com>\n - Add gitweb manpages to 'gitweb' subpackage\n \ndiff --git a/imap-send.c b/imap-send.c\nindex 524fbab..0c4583f 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -22,47 +22,13 @@\n  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n  */\n \n-#include \"cache.h\"\n-#include \"credential.h\"\n+#include \"http.h\"\n #include \"exec_cmd.h\"\n #include \"run-command.h\"\n-#ifdef NO_OPENSSL\n-typedef void *SSL;\n-#endif\n \n-static const char imap_send_usage[] = \"git imap-send < <mbox>\";\n-\n-#undef DRV_OK\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-__attribute__((format (printf, 1, 2)))\n-static void imap_info(const char *, ...);\n-__attribute__((format (printf, 1, 2)))\n-static void imap_warn(const char *, ...);\n-\n-static char *next_arg(char **);\n-\n-__attribute__((format (printf, 3, 4)))\n-static int nfsnprintf(char *buf, int blen, const char *fmt, ...);\n+#include <curl/curl.h>\n \n-static int nfvasprintf(char **strp, const char *fmt, va_list ap)\n-{\n-\tint len;\n-\tchar tmp[8192];\n-\n-\tlen = vsnprintf(tmp, sizeof(tmp), fmt, ap);\n-\tif (len < 0)\n-\t\tdie(\"Fatal: Out of memory\");\n-\tif (len >= sizeof(tmp))\n-\t\tdie(\"imap command overflow!\");\n-\t*strp = xmemdupz(tmp, len);\n-\treturn len;\n-}\n+static const char imap_send_usage[] = \"git imap-send < <mbox>\";\n \n struct imap_server_conf {\n \tchar *name;\n@@ -90,1144 +56,6 @@ static struct imap_server_conf server = {\n \tNULL,\t/* auth_method */\n };\n \n-struct imap_socket {\n-\tint fd[2];\n-\tSSL *ssl;\n-};\n-\n-struct imap_buffer {\n-\tstruct imap_socket sock;\n-\tint bytes;\n-\tint offset;\n-\tchar buf[1024];\n-};\n-\n-struct imap_cmd;\n-\n-struct imap {\n-\tint uidnext; /* from SELECT responses */\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-\tstruct imap_buffer buf; /* this is BIG, so put it last */\n-};\n-\n-struct imap_store {\n-\t/* currently open mailbox */\n-\tconst char *name; /* foreign! maybe preset? */\n-\tint uidvalidity;\n-\tstruct imap *imap;\n-\tconst char *prefix;\n-};\n-\n-struct imap_cmd_cb {\n-\tint (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt);\n-\tvoid (*done)(struct imap_store *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-\tSTARTTLS,\n-\tAUTH_CRAM_MD5\n-};\n-\n-static const char *cap_list[] = {\n-\t\"LOGINDISABLED\",\n-\t\"UIDPLUS\",\n-\t\"LITERAL+\",\n-\t\"NAMESPACE\",\n-\t\"STARTTLS\",\n-\t\"AUTH=CRAM-MD5\",\n-};\n-\n-#define RESP_OK    0\n-#define RESP_NO    1\n-#define RESP_BAD   2\n-\n-static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd);\n-\n-\n-#ifndef NO_OPENSSL\n-static void ssl_socket_perror(const char *func)\n-{\n-\tfprintf(stderr, \"%s: %s\\n\", func, ERR_error_string(ERR_get_error(), NULL));\n-}\n-#endif\n-\n-static void socket_perror(const char *func, struct imap_socket *sock, int ret)\n-{\n-#ifndef NO_OPENSSL\n-\tif (sock->ssl) {\n-\t\tint sslerr = SSL_get_error(sock->ssl, ret);\n-\t\tswitch (sslerr) {\n-\t\tcase SSL_ERROR_NONE:\n-\t\t\tbreak;\n-\t\tcase SSL_ERROR_SYSCALL:\n-\t\t\tperror(\"SSL_connect\");\n-\t\t\tbreak;\n-\t\tdefault:\n-\t\t\tssl_socket_perror(\"SSL_connect\");\n-\t\t\tbreak;\n-\t\t}\n-\t} else\n-#endif\n-\t{\n-\t\tif (ret < 0)\n-\t\t\tperror(func);\n-\t\telse\n-\t\t\tfprintf(stderr, \"%s: unexpected EOF\\n\", func);\n-\t}\n-}\n-\n-#ifdef NO_OPENSSL\n-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)\n-{\n-\tfprintf(stderr, \"SSL requested but SSL support not compiled in\\n\");\n-\treturn -1;\n-}\n-\n-#else\n-\n-static int host_matches(const char *host, const char *pattern)\n-{\n-\tif (pattern[0] == '*' && pattern[1] == '.') {\n-\t\tpattern += 2;\n-\t\tif (!(host = strchr(host, '.')))\n-\t\t\treturn 0;\n-\t\thost++;\n-\t}\n-\n-\treturn *host && *pattern && !strcasecmp(host, pattern);\n-}\n-\n-static int verify_hostname(X509 *cert, const char *hostname)\n-{\n-\tint len;\n-\tX509_NAME *subj;\n-\tchar cname[1000];\n-\tint i, found;\n-\tSTACK_OF(GENERAL_NAME) *subj_alt_names;\n-\n-\t/* try the DNS subjectAltNames */\n-\tfound = 0;\n-\tif ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) {\n-\t\tint num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names);\n-\t\tfor (i = 0; !found && i < num_subj_alt_names; i++) {\n-\t\t\tGENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);\n-\t\t\tif (subj_alt_name->type == GEN_DNS &&\n-\t\t\t    strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length &&\n-\t\t\t    host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data)))\n-\t\t\t\tfound = 1;\n-\t\t}\n-\t\tsk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);\n-\t}\n-\tif (found)\n-\t\treturn 0;\n-\n-\t/* try the common name */\n-\tif (!(subj = X509_get_subject_name(cert)))\n-\t\treturn error(\"cannot get certificate subject\");\n-\tif ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)\n-\t\treturn error(\"cannot get certificate common name\");\n-\tif (strlen(cname) == (size_t)len && host_matches(hostname, cname))\n-\t\treturn 0;\n-\treturn error(\"certificate owner '%s' does not match hostname '%s'\",\n-\t\t     cname, hostname);\n-}\n-\n-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)\n-{\n-#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)\n-\tconst SSL_METHOD *meth;\n-#else\n-\tSSL_METHOD *meth;\n-#endif\n-\tSSL_CTX *ctx;\n-\tint ret;\n-\tX509 *cert;\n-\n-\tSSL_library_init();\n-\tSSL_load_error_strings();\n-\n-\tif (use_tls_only)\n-\t\tmeth = TLSv1_method();\n-\telse\n-\t\tmeth = SSLv23_method();\n-\n-\tif (!meth) {\n-\t\tssl_socket_perror(\"SSLv23_method\");\n-\t\treturn -1;\n-\t}\n-\n-\tctx = SSL_CTX_new(meth);\n-\n-\tif (verify)\n-\t\tSSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);\n-\n-\tif (!SSL_CTX_set_default_verify_paths(ctx)) {\n-\t\tssl_socket_perror(\"SSL_CTX_set_default_verify_paths\");\n-\t\treturn -1;\n-\t}\n-\tsock->ssl = SSL_new(ctx);\n-\tif (!sock->ssl) {\n-\t\tssl_socket_perror(\"SSL_new\");\n-\t\treturn -1;\n-\t}\n-\tif (!SSL_set_rfd(sock->ssl, sock->fd[0])) {\n-\t\tssl_socket_perror(\"SSL_set_rfd\");\n-\t\treturn -1;\n-\t}\n-\tif (!SSL_set_wfd(sock->ssl, sock->fd[1])) {\n-\t\tssl_socket_perror(\"SSL_set_wfd\");\n-\t\treturn -1;\n-\t}\n-\n-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n-\t/*\n-\t * SNI (RFC4366)\n-\t * OpenSSL does not document this function, but the implementation\n-\t * returns 1 on success, 0 on failure after calling SSLerr().\n-\t */\n-\tret = SSL_set_tlsext_host_name(sock->ssl, server.host);\n-\tif (ret != 1)\n-\t\twarning(\"SSL_set_tlsext_host_name(%s) failed.\", server.host);\n-#endif\n-\n-\tret = SSL_connect(sock->ssl);\n-\tif (ret <= 0) {\n-\t\tsocket_perror(\"SSL_connect\", sock, ret);\n-\t\treturn -1;\n-\t}\n-\n-\tif (verify) {\n-\t\t/* make sure the hostname matches that of the certificate */\n-\t\tcert = SSL_get_peer_certificate(sock->ssl);\n-\t\tif (!cert)\n-\t\t\treturn error(\"unable to get peer certificate.\");\n-\t\tif (verify_hostname(cert, server.host) < 0)\n-\t\t\treturn -1;\n-\t}\n-\n-\treturn 0;\n-}\n-#endif\n-\n-static int socket_read(struct imap_socket *sock, char *buf, int len)\n-{\n-\tssize_t n;\n-#ifndef NO_OPENSSL\n-\tif (sock->ssl)\n-\t\tn = SSL_read(sock->ssl, buf, len);\n-\telse\n-#endif\n-\t\tn = xread(sock->fd[0], buf, len);\n-\tif (n <= 0) {\n-\t\tsocket_perror(\"read\", sock, n);\n-\t\tclose(sock->fd[0]);\n-\t\tclose(sock->fd[1]);\n-\t\tsock->fd[0] = sock->fd[1] = -1;\n-\t}\n-\treturn n;\n-}\n-\n-static int socket_write(struct imap_socket *sock, const char *buf, int len)\n-{\n-\tint n;\n-#ifndef NO_OPENSSL\n-\tif (sock->ssl)\n-\t\tn = SSL_write(sock->ssl, buf, len);\n-\telse\n-#endif\n-\t\tn = write_in_full(sock->fd[1], buf, len);\n-\tif (n != len) {\n-\t\tsocket_perror(\"write\", sock, n);\n-\t\tclose(sock->fd[0]);\n-\t\tclose(sock->fd[1]);\n-\t\tsock->fd[0] = sock->fd[1] = -1;\n-\t}\n-\treturn n;\n-}\n-\n-static void socket_shutdown(struct imap_socket *sock)\n-{\n-#ifndef NO_OPENSSL\n-\tif (sock->ssl) {\n-\t\tSSL_shutdown(sock->ssl);\n-\t\tSSL_free(sock->ssl);\n-\t}\n-#endif\n-\tclose(sock->fd[0]);\n-\tclose(sock->fd[1]);\n-}\n-\n-/* simple line buffering */\n-static int buffer_gets(struct imap_buffer *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\tmemmove(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\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 imap_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 imap_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 *next_arg(char **s)\n-{\n-\tchar *ret;\n-\n-\tif (!s || !*s)\n-\t\treturn NULL;\n-\twhile (isspace((unsigned char) **s))\n-\t\t(*s)++;\n-\tif (!**s) {\n-\t\t*s = NULL;\n-\t\treturn NULL;\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 = NULL;\n-\t}\n-\treturn ret;\n-}\n-\n-static int 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-\tva_end(va);\n-\treturn ret;\n-}\n-\n-static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,\n-\t\t\t\t\t struct imap_cmd_cb *cb,\n-\t\t\t\t\t const char *fmt, va_list ap)\n-{\n-\tstruct imap *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, NULL);\n-\n-\tif (!cmd->cb.data)\n-\t\tbufl = nfsnprintf(buf, sizeof(buf), \"%d %s\\r\\n\", cmd->tag, cmd->cmd);\n-\telse\n-\t\tbufl = nfsnprintf(buf, sizeof(buf), \"%d %s{%d%s}\\r\\n\",\n-\t\t\t\t  cmd->tag, cmd->cmd, cmd->cb.dlen,\n-\t\t\t\t  CAP(LITERALPLUS) ? \"+\" : \"\");\n-\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)\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    socket_write(&imap->buf.sock, \"\\r\\n\", 2) != 2) {\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 = NULL;\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 = NULL;\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-__attribute__((format (printf, 3, 4)))\n-static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,\n-\t\t\t\t       struct imap_cmd_cb *cb,\n-\t\t\t\t       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-__attribute__((format (printf, 3, 4)))\n-static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,\n-\t\t     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-__attribute__((format (printf, 3, 4)))\n-static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,\n-\t\t       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-static int skip_imap_list_l(char **sp, int level)\n-{\n-\tchar *s = *sp;\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\tif (*s == '(') {\n-\t\t\t/* sublist */\n-\t\t\ts++;\n-\t\t\tif (skip_imap_list_l(&s, level + 1))\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\tfor (; *s != '\"'; s++)\n-\t\t\t\tif (!*s)\n-\t\t\t\t\tgoto bail;\n-\t\t\ts++;\n-\t\t} else {\n-\t\t\t/* atom */\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}\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-\treturn 0;\n-\n-bail:\n-\treturn -1;\n-}\n-\n-static void skip_list(char **sp)\n-{\n-\tskip_imap_list_l(sp, 0);\n-}\n-\n-static void parse_capability(struct imap *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 < ARRAY_SIZE(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 parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,\n-\t\t\t       char *s)\n-{\n-\tstruct imap *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->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->uidvalidity = atoi(arg)) ||\n-\t\t    !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) {\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 get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)\n-{\n-\tstruct imap *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\t/* rfc2342 NAMESPACE response. */\n-\t\t\t\tskip_list(&cmd); /* Personal mailboxes */\n-\t\t\t\tskip_list(&cmd); /* Others' mailboxes */\n-\t\t\t\tskip_list(&cmd); /* Shared mailboxes */\n-\t\t\t} else if (!strcmp(\"OK\", arg) || !strcmp(\"BAD\", arg) ||\n-\t\t\t\t   !strcmp(\"NO\", arg) || !strcmp(\"BYE\", arg)) {\n-\t\t\t\tif ((resp = parse_response_code(ctx, NULL, 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\t} else if ((arg1 = next_arg(&cmd))) {\n-\t\t\t\t; /*\n-\t\t\t\t   * Unhandled response-data with at least two words.\n-\t\t\t\t   * Ignore it.\n-\t\t\t\t   *\n-\t\t\t\t   * NEEDSWORK: Previously this case handled '<num> EXISTS'\n-\t\t\t\t   * and '<num> RECENT' but as a probably-unintended side\n-\t\t\t\t   * effect it ignores other unrecognized two-word\n-\t\t\t\t   * responses.  imap-send doesn't ever try to read\n-\t\t\t\t   * messages or mailboxes these days, so consider\n-\t\t\t\t   * eliminating this case.\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 = NULL;\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\tgottag:\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, NULL, \"CREATE \\\"%.*s\\\"\", (int)(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\t memcmp(cmdp->cmd, \"LOGIN\", 5) ?\n-\t\t\t\t\t\t\tcmdp->cmd : \"LOGIN <user> <pass>\",\n-\t\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\tnormal:\n-\t\t\tif (cmdp->cb.done)\n-\t\t\t\tcmdp->cb.done(ctx, cmdp, resp);\n-\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 imap_close_server(struct imap_store *ictx)\n-{\n-\tstruct imap *imap = ictx->imap;\n-\n-\tif (imap->buf.sock.fd[0] != -1) {\n-\t\timap_exec(ictx, NULL, \"LOGOUT\");\n-\t\tsocket_shutdown(&imap->buf.sock);\n-\t}\n-\tfree(imap);\n-}\n-\n-static void imap_close_store(struct imap_store *ctx)\n-{\n-\timap_close_server(ctx);\n-\tfree(ctx);\n-}\n-\n-#ifndef NO_OPENSSL\n-\n-/*\n- * hexchar() and cram() functions are based on the code from the isync\n- * project (http://isync.sf.net/).\n- */\n-static char hexchar(unsigned int b)\n-{\n-\treturn b < 10 ? '0' + b : 'a' + (b - 10);\n-}\n-\n-#define ENCODED_SIZE(n) (4*((n+2)/3))\n-static char *cram(const char *challenge_64, const char *user, const char *pass)\n-{\n-\tint i, resp_len, encoded_len, decoded_len;\n-\tHMAC_CTX hmac;\n-\tunsigned char hash[16];\n-\tchar hex[33];\n-\tchar *response, *response_64, *challenge;\n-\n-\t/*\n-\t * length of challenge_64 (i.e. base-64 encoded string) is a good\n-\t * enough upper bound for challenge (decoded result).\n-\t */\n-\tencoded_len = strlen(challenge_64);\n-\tchallenge = xmalloc(encoded_len);\n-\tdecoded_len = EVP_DecodeBlock((unsigned char *)challenge,\n-\t\t\t\t      (unsigned char *)challenge_64, encoded_len);\n-\tif (decoded_len < 0)\n-\t\tdie(\"invalid challenge %s\", challenge_64);\n-\tHMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());\n-\tHMAC_Update(&hmac, (unsigned char *)challenge, decoded_len);\n-\tHMAC_Final(&hmac, hash, NULL);\n-\tHMAC_CTX_cleanup(&hmac);\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-\t/* response: \"<user> <digest in hex>\" */\n-\tresp_len = strlen(user) + 1 + strlen(hex) + 1;\n-\tresponse = xmalloc(resp_len);\n-\tsprintf(response, \"%s %s\", user, hex);\n-\n-\tresponse_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);\n-\tencoded_len = EVP_EncodeBlock((unsigned char *)response_64,\n-\t\t\t\t      (unsigned char *)response, resp_len);\n-\tif (encoded_len < 0)\n-\t\tdie(\"EVP_EncodeBlock error\");\n-\tresponse_64[encoded_len] = '\\0';\n-\treturn (char *)response_64;\n-}\n-\n-#else\n-\n-static char *cram(const char *challenge_64, const char *user, const char *pass)\n-{\n-\tdie(\"If you want to use CRAM-MD5 authenticate method, \"\n-\t    \"you have to build git-imap-send with OpenSSL library.\");\n-}\n-\n-#endif\n-\n-static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)\n-{\n-\tint ret;\n-\tchar *response;\n-\n-\tresponse = cram(prompt, server.user, server.pass);\n-\n-\tret = socket_write(&ctx->imap->buf.sock, response, strlen(response));\n-\tif (ret != strlen(response))\n-\t\treturn error(\"IMAP error: sending response failed\");\n-\n-\tfree(response);\n-\n-\treturn 0;\n-}\n-\n-static struct imap_store *imap_open_store(struct imap_server_conf *srvc)\n-{\n-\tstruct credential cred = CREDENTIAL_INIT;\n-\tstruct imap_store *ctx;\n-\tstruct imap *imap;\n-\tchar *arg, *rsp;\n-\tint s = -1, preauth;\n-\n-\tctx = xcalloc(1, sizeof(*ctx));\n-\n-\tctx->imap = imap = xcalloc(sizeof(*imap), 1);\n-\timap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;\n-\timap->in_progress_append = &imap->in_progress;\n-\n-\t/* open connection to IMAP server */\n-\n-\tif (srvc->tunnel) {\n-\t\tconst char *argv[] = { srvc->tunnel, NULL };\n-\t\tstruct child_process tunnel = {NULL};\n-\n-\t\timap_info(\"Starting tunnel '%s'... \", srvc->tunnel);\n-\n-\t\ttunnel.argv = argv;\n-\t\ttunnel.use_shell = 1;\n-\t\ttunnel.in = -1;\n-\t\ttunnel.out = -1;\n-\t\tif (start_command(&tunnel))\n-\t\t\tdie(\"cannot start proxy %s\", argv[0]);\n-\n-\t\timap->buf.sock.fd[0] = tunnel.out;\n-\t\timap->buf.sock.fd[1] = tunnel.in;\n-\n-\t\timap_info(\"ok\\n\");\n-\t} else {\n-#ifndef NO_IPV6\n-\t\tstruct addrinfo hints, *ai0, *ai;\n-\t\tint gai;\n-\t\tchar portstr[6];\n-\n-\t\tsnprintf(portstr, sizeof(portstr), \"%d\", srvc->port);\n-\n-\t\tmemset(&hints, 0, sizeof(hints));\n-\t\thints.ai_socktype = SOCK_STREAM;\n-\t\thints.ai_protocol = IPPROTO_TCP;\n-\n-\t\timap_info(\"Resolving %s... \", srvc->host);\n-\t\tgai = getaddrinfo(srvc->host, portstr, &hints, &ai);\n-\t\tif (gai) {\n-\t\t\tfprintf(stderr, \"getaddrinfo: %s\\n\", gai_strerror(gai));\n-\t\t\tgoto bail;\n-\t\t}\n-\t\timap_info(\"ok\\n\");\n-\n-\t\tfor (ai0 = ai; ai; ai = ai->ai_next) {\n-\t\t\tchar addr[NI_MAXHOST];\n-\n-\t\t\ts = socket(ai->ai_family, ai->ai_socktype,\n-\t\t\t\t   ai->ai_protocol);\n-\t\t\tif (s < 0)\n-\t\t\t\tcontinue;\n-\n-\t\t\tgetnameinfo(ai->ai_addr, ai->ai_addrlen, addr,\n-\t\t\t\t    sizeof(addr), NULL, 0, NI_NUMERICHOST);\n-\t\t\timap_info(\"Connecting to [%s]:%s... \", addr, portstr);\n-\n-\t\t\tif (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {\n-\t\t\t\tclose(s);\n-\t\t\t\ts = -1;\n-\t\t\t\tperror(\"connect\");\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\n-\t\t\tbreak;\n-\t\t}\n-\t\tfreeaddrinfo(ai0);\n-#else /* NO_IPV6 */\n-\t\tstruct hostent *he;\n-\t\tstruct sockaddr_in addr;\n-\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\timap_info(\"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\timap_info(\"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\timap_info(\"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\ts = -1;\n-\t\t\tperror(\"connect\");\n-\t\t}\n-#endif\n-\t\tif (s < 0) {\n-\t\t\tfputs(\"Error: unable to connect to server.\\n\", stderr);\n-\t\t\tgoto bail;\n-\t\t}\n-\n-\t\timap->buf.sock.fd[0] = s;\n-\t\timap->buf.sock.fd[1] = dup(s);\n-\n-\t\tif (srvc->use_ssl &&\n-\t\t    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {\n-\t\t\tclose(s);\n-\t\t\tgoto bail;\n-\t\t}\n-\t\timap_info(\"ok\\n\");\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, NULL, rsp);\n-\tif (!imap->caps && imap_exec(ctx, NULL, \"CAPABILITY\") != RESP_OK)\n-\t\tgoto bail;\n-\n-\tif (!preauth) {\n-#ifndef NO_OPENSSL\n-\t\tif (!srvc->use_ssl && CAP(STARTTLS)) {\n-\t\t\tif (imap_exec(ctx, NULL, \"STARTTLS\") != RESP_OK)\n-\t\t\t\tgoto bail;\n-\t\t\tif (ssl_socket_connect(&imap->buf.sock, 1,\n-\t\t\t\t\t       srvc->ssl_verify))\n-\t\t\t\tgoto bail;\n-\t\t\t/* capabilities may have changed, so get the new capabilities */\n-\t\t\tif (imap_exec(ctx, NULL, \"CAPABILITY\") != RESP_OK)\n-\t\t\t\tgoto bail;\n-\t\t}\n-#endif\n-\t\timap_info(\"Logging in...\\n\");\n-\t\tif (!srvc->user || !srvc->pass) {\n-\t\t\tcred.protocol = xstrdup(srvc->use_ssl ? \"imaps\" : \"imap\");\n-\t\t\tcred.host = xstrdup(srvc->host);\n-\n-\t\t\tif (srvc->user)\n-\t\t\t\tcred.username = xstrdup(srvc->user);\n-\t\t\tif (srvc->pass)\n-\t\t\t\tcred.password = xstrdup(srvc->pass);\n-\n-\t\t\tcredential_fill(&cred);\n-\n-\t\t\tif (!srvc->user)\n-\t\t\t\tsrvc->user = xstrdup(cred.username);\n-\t\t\tif (!srvc->pass)\n-\t\t\t\tsrvc->pass = xstrdup(cred.password);\n-\t\t}\n-\n-\t\tif (CAP(NOLOGIN)) {\n-\t\t\tfprintf(stderr, \"Skipping account %s@%s, server forbids LOGIN\\n\", srvc->user, srvc->host);\n-\t\t\tgoto bail;\n-\t\t}\n-\n-\t\tif (srvc->auth_method) {\n-\t\t\tstruct imap_cmd_cb cb;\n-\n-\t\t\tif (!strcmp(srvc->auth_method, \"CRAM-MD5\")) {\n-\t\t\t\tif (!CAP(AUTH_CRAM_MD5)) {\n-\t\t\t\t\tfprintf(stderr, \"You specified\"\n-\t\t\t\t\t\t\"CRAM-MD5 as authentication method, \"\n-\t\t\t\t\t\t\"but %s doesn't support it.\\n\", srvc->host);\n-\t\t\t\t\tgoto bail;\n-\t\t\t\t}\n-\t\t\t\t/* CRAM-MD5 */\n-\n-\t\t\t\tmemset(&cb, 0, sizeof(cb));\n-\t\t\t\tcb.cont = auth_cram_md5;\n-\t\t\t\tif (imap_exec(ctx, &cb, \"AUTHENTICATE CRAM-MD5\") != RESP_OK) {\n-\t\t\t\t\tfprintf(stderr, \"IMAP error: AUTHENTICATE CRAM-MD5 failed\\n\");\n-\t\t\t\t\tgoto bail;\n-\t\t\t\t}\n-\t\t\t} else {\n-\t\t\t\tfprintf(stderr, \"Unknown authentication method:%s\\n\", srvc->host);\n-\t\t\t\tgoto bail;\n-\t\t\t}\n-\t\t} else {\n-\t\t\tif (!imap->buf.sock.ssl)\n-\t\t\t\timap_warn(\"*** IMAP Warning *** Password is being \"\n-\t\t\t\t\t  \"sent in the clear\\n\");\n-\t\t\tif (imap_exec(ctx, NULL, \"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-\tif (cred.username)\n-\t\tcredential_approve(&cred);\n-\tcredential_clear(&cred);\n-\n-\tctx->prefix = \"\";\n-\treturn ctx;\n-\n-bail:\n-\tif (cred.username)\n-\t\tcredential_reject(&cred);\n-\tcredential_clear(&cred);\n-\n-\timap_close_store(ctx);\n-\treturn NULL;\n-}\n-\n-/*\n- * Insert CR characters as necessary in *msg to ensure that every LF\n- * character in *msg is preceded by a CR.\n- */\n-static void lf_to_crlf(struct strbuf *msg)\n-{\n-\tchar *new;\n-\tsize_t i, j;\n-\tchar lastc;\n-\n-\t/* First pass: tally, in j, the size of the new string: */\n-\tfor (i = j = 0, lastc = '\\0'; i < msg->len; i++) {\n-\t\tif (msg->buf[i] == '\\n' && lastc != '\\r')\n-\t\t\tj++; /* a CR will need to be added here */\n-\t\tlastc = msg->buf[i];\n-\t\tj++;\n-\t}\n-\n-\tnew = xmalloc(j + 1);\n-\n-\t/*\n-\t * Second pass: write the new string.  Note that this loop is\n-\t * otherwise identical to the first pass.\n-\t */\n-\tfor (i = j = 0, lastc = '\\0'; i < msg->len; i++) {\n-\t\tif (msg->buf[i] == '\\n' && lastc != '\\r')\n-\t\t\tnew[j++] = '\\r';\n-\t\tlastc = new[j++] = msg->buf[i];\n-\t}\n-\tstrbuf_attach(msg, new, j, j + 1);\n-}\n-\n-/*\n- * Store msg to IMAP.  Also detach and free the data from msg->data,\n- * leaving msg->data empty.\n- */\n-static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)\n-{\n-\tstruct imap *imap = ctx->imap;\n-\tstruct imap_cmd_cb cb;\n-\tconst char *prefix, *box;\n-\tint ret;\n-\n-\tlf_to_crlf(msg);\n-\tmemset(&cb, 0, sizeof(cb));\n-\n-\tcb.dlen = msg->len;\n-\tcb.data = strbuf_detach(msg, NULL);\n-\n-\tbox = ctx->name;\n-\tprefix = !strcmp(box, \"INBOX\") ? \"\" : ctx->prefix;\n-\tcb.create = 0;\n-\tret = imap_exec_m(ctx, &cb, \"APPEND \\\"%s%s\\\" \", prefix, box);\n-\timap->caps = imap->rcaps;\n-\tif (ret != DRV_OK)\n-\t\treturn ret;\n-\n-\treturn DRV_OK;\n-}\n-\n static void wrap_in_html(struct strbuf *msg)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -1324,6 +152,28 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)\n \treturn 1;\n }\n \n+static curl_socket_t opensocket(void *clientp, curlsocktype purpose,\n+\t\t\t\t\t\t\t\tstruct curl_sockaddr *address)\n+{\n+\tcurl_socket_t sockfd;\n+\t(void)purpose;\n+\t(void)address;\n+\tsockfd = *(curl_socket_t *)clientp;\n+\t/* the actual externally set socket is passed in via the OPENSOCKETDATA\n+\t   option */\n+\treturn sockfd;\n+}\n+\n+static int sockopt_callback(void *clientp, curl_socket_t curlfd,\n+\t\t\t\t\t\t\tcurlsocktype purpose)\n+{\n+\t(void)clientp;\n+\t(void)curlfd;\n+\t(void)purpose;\n+\t/* This return code was added in libcurl 7.21.5 */\n+\treturn CURL_SOCKOPT_ALREADY_CONNECTED;\n+}\n+\n static char *imap_folder;\n \n static int git_imap_config(const char *key, const char *val, void *cb)\n@@ -1368,12 +218,14 @@ static int git_imap_config(const char *key, const char *val, void *cb)\n int main(int argc, char **argv)\n {\n \tstruct strbuf all_msgs = STRBUF_INIT;\n-\tstruct strbuf msg = STRBUF_INIT;\n-\tstruct imap_store *ctx = NULL;\n+\tstruct buffer msg = { STRBUF_INIT, 0 };\n \tint ofs = 0;\n-\tint r;\n \tint total, n = 0;\n \tint nongit_ok;\n+\tchar path[8192];\n+\tint pathlen;\n+\tCURL *curl;\n+\tCURLcode r = CURLE_OK;\n \n \tgit_extract_argv0_path(argv[0]);\n \n@@ -1417,31 +269,89 @@ int main(int argc, char **argv)\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+\tcurl_global_init(CURL_GLOBAL_ALL);\n+\tcurl = curl_easy_init();\n+\n+\tif (!curl) {\n+\t\tfprintf(stderr, \"failed to init curl\\n\");\n \t\treturn 1;\n \t}\n \n+\tcurl_easy_setopt(curl, CURLOPT_USERNAME, server.user);\n+\tcurl_easy_setopt(curl, CURLOPT_PASSWORD, server.pass);\n+\n+\tif (ends_with(server.host, \"/\"))\n+\t\tpathlen = snprintf (path, sizeof(path), \"%s%s\", server.host, imap_folder);\n+\telse\n+\t\tpathlen = snprintf (path, sizeof(path), \"%s/%s\", server.host, imap_folder);\n+\n+\tif (pathlen < 0)\n+\t\tdie(\"Fatal: Out of memory\");\n+\tif (pathlen >= sizeof(path))\n+\t\tdie(\"imap URL overflow!\");\n+\n+\tcurl_easy_setopt(curl, CURLOPT_URL, path);\n+\tcurl_easy_setopt(curl, CURLOPT_PORT, server.port);\n+\tcurl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, server.auth_method);\n+\n+\tif (server.tunnel) {\n+\t\tconst char *argv[] = { server.tunnel, NULL };\n+\t\tstruct child_process tunnel = {NULL};\n+\n+\t\tfprintf(stderr, \"Starting tunnel '%s'... \", server.tunnel);\n+\n+\t\ttunnel.argv = argv;\n+\t\ttunnel.use_shell = 1;\n+\t\ttunnel.in = -1;\n+\t\ttunnel.out = -1;\n+\t\tif (start_command(&tunnel))\n+\t\t\tdie(\"cannot start proxy %s\", argv[0]);\n+\n+\t\tcurl_socket_t sockfd = tunnel.out; // what about tunnel.in ?\n+\n+\t\t/* call this function to get a socket */\n+\t\tcurl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket);\n+\t\tcurl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &sockfd);\n+\n+\t\t/* call this function to set options for the socket */\n+\t\tcurl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback);\n+\t}\n+\n+\tcurl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, server.ssl_verify);\n+\tcurl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, server.ssl_verify);\n+\n+\tcurl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);\n+\tcurl_easy_setopt(curl, CURLOPT_READDATA, &msg);\n+\tcurl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);\n+\n+\t/* curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); */\n+\n \tfprintf(stderr, \"sending %d message%s\\n\", total, (total != 1) ? \"s\" : \"\");\n-\tctx->name = imap_folder;\n+\n \twhile (1) {\n \t\tunsigned percent = n * 100 / total;\n \n \t\tfprintf(stderr, \"%4u%% (%d/%d) done\\r\", percent, n, total);\n-\t\tif (!split_msg(&all_msgs, &msg, &ofs))\n+\t\tint prev_len = msg.buf.len;\n+\t\tif (!split_msg(&all_msgs, &msg.buf, &ofs))\n \t\t\tbreak;\n \t\tif (server.use_html)\n-\t\t\twrap_in_html(&msg);\n-\t\tr = imap_store_msg(ctx, &msg);\n-\t\tif (r != DRV_OK)\n+\t\t\twrap_in_html(&msg.buf);\n+\n+\t\tcurl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)(msg.buf.len-prev_len));\n+\n+\t\tr = curl_easy_perform(curl);\n+\n+\t\tif(r != CURLE_OK) {\n+\t\t\tfprintf(stderr, \"curl_easy_perform() failed: %s\\n\",\n+\t\t\t\t\tcurl_easy_strerror(r));\n \t\t\tbreak;\n+\t\t}\n \t\tn++;\n \t}\n \tfprintf(stderr, \"\\n\");\n \n-\timap_close_store(ctx);\n-\n+\tcurl_easy_cleanup(curl);\n+\tcurl_global_cleanup();\n \treturn 0;\n }\n\n"},{"id":"247640","messageId":"20140813015917.GA30756@google.com","threadId":"37338","inReplyTo":"53EA8C3E.1080500@raz.or.at","subject":"Re: [PATCH/RFC] git-imap-send: use libcurl for implementation","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2014-08-13T01:59:17Z","receivedAt":"2014-08-13T01:59:17Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Bernhard Reiter wrote:\n\n> Use libcurl's high-level API functions to implement git-imap-send\n> instead of the previous low-level OpenSSL-based functions.\n\nWow!  This sounds lovely.  Thanks for working on this.\n\n[...]\n> Since version 7.30.0, libcurl's API has been able to communicate with\n> IMAP servers. Using those high-level functions instead of the current\n> ones reduces imap-send.c by some 1200 lines of code.\n>\n> As I don't have access to that many IMAP servers, I haven't been able to\n> test a variety of parameter combinations. I did test both secure and\n> insecure (imaps:// and imap://) connections -- this e-mail was generated\n> that way -- but could e.g. neither test the authMethod nor the tunnel\n> parameter.\n\nThe above two paragraphs belong in the commit message, too, since\nthey'll be just as useful for someone looking back through the history\nas for someone reading the patch on-list today.\n\n[...]\n> --- a/INSTALL\n> +++ b/INSTALL\n[...]\n> -\t- \"libcurl\" library is used by git-http-fetch and git-fetch.  You\n> +\t- \"libcurl\" library is used by git-http-fetch, git-fetch, and\n> +\t  git-impap-send.  You might also want the \"curl\" executable for\n\nTypo: s/impap-send/imap-send/\n\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -2067,9 +2067,9 @@ endif\n>  git-%$X: %.o GIT-LDFLAGS $(GITLIBS)\n>  \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)\n>  \n> -git-imap-send$X: imap-send.o GIT-LDFLAGS $(GITLIBS)\n> +git-imap-send$X: imap-send.o http.o GIT-LDFLAGS $(GITLIBS)\n>  \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n> -\t\t$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)\n> +\t\t$(LIBS) $(CURL_LIBCURL)\n\n7.30.0 is only ~1 year old.  Does this mean users would need to update\ncurl in order to build imap-send?\n\nFor example, Debian 7.6 ships curl 7.26.0 and CentOS 7 has curl 7.29.0.\n\nIdeally this could be done as an optional feature:\n\n 1. Introduce a USE_CURL_FOR_IMAP_SEND makefile variable to take\n    advantage of the nice new API.\n\n 2. (optional) Use the curl_check makefile variable to turn on\n    USE_CURL_FOR_IMAP_SEND automatically when appropriate.\n\n 3. In a few years, when everyone has upgraded, we could simplify by\n    getting rid of the USE_OPENSSL_FOR_IMAP_SEND code path.\n\nWhat do you think?\n\n[...]\n> --- a/imap-send.c\n> +++ b/imap-send.c\n> @@ -22,47 +22,13 @@\n>   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n>   */\n>  \n> -#include \"cache.h\"\n\nUsual style is to start with a #include of \"cache.h\" or\n\"git-compat-util.h\".  \"http.h\" including cache.h for itself was an\nold mistake.  (I'll reply with a patch to fix that.)\n\n[...]\n> +#include <curl/curl.h>\n\nhttp.h already #includes this.  Do you use other helpers from\nhttp.h/http.c or do you use libcurl directly?  (Just curious.)\n\nSome style nits:\n\n[...]\n> +static curl_socket_t opensocket(void *clientp, curlsocktype purpose,\n> +\t\t\t\t\t\t\t\tstruct curl_sockaddr *address)\n\nLong line.  Do you have ts=4?  (Git uses 8-space tabs.  There's some\nemacs magic in Documentation/CodingGuidelines.  It should be possible\nto add similar hints there for other editors if they don't do the\nright thing by default.)\n\n> +{\n> +\tcurl_socket_t sockfd;\n> +\t(void)purpose;\n> +\t(void)address;\n\nElsewhere git lets unused parameters be.  The unused parameter warning\nis too noisy in callback-heavy code (e.g., for_each_ref) so we don't\nturn it on.\n\n> +\tsockfd = *(curl_socket_t *)clientp;\n> +\t/* the actual externally set socket is passed in via the OPENSOCKETDATA\n> +\t   option */\n\n(style nit) Comments in git look like this:\n\n\t/*\n\t * The actual, externally set socket is passed in via the\n\t * OPENSOCKETDATA option.\n\t */\n\treturn sockfd;\n\n[...]\n> +static int sockopt_callback(void *clientp, curl_socket_t curlfd,\n> +\t\t\t\t\t\t\tcurlsocktype purpose)\n> +{\n> +\t/* This return code was added in libcurl 7.21.5 */\n> +\treturn CURL_SOCKOPT_ALREADY_CONNECTED;\n\nI'd drop the comment, unless there's some subtlety involved.  (E.g.,\nis there some other return code that would be more appropriate but was\nintroudced later or something?)\n\n[...]\n> @@ -1368,12 +218,14 @@ static int git_imap_config(const char *key, const char *val, void *cb)\n>  int main(int argc, char **argv)\n>  {\n>  \tstruct strbuf all_msgs = STRBUF_INIT;\n> -\tstruct strbuf msg = STRBUF_INIT;\n> +\tstruct buffer msg = { STRBUF_INIT, 0 };\n\nAh, ok --- we do use http.c stuff.\n\n[...]\n> +\tchar path[8192];\n> +\tint pathlen;\n\nI realize the old code only had 8192 for the IMAP command buffer,\nbut could this be a strbuf now, or is there some underlying limit\nsomewhere else?\n\n[...]\n> @@ -1417,31 +269,89 @@ int main(int argc, char **argv)\n>  \t\treturn 1;\n>  \t}\n>  \n> +\tcurl_global_init(CURL_GLOBAL_ALL);\n\nhttp.c seems to make the same mistake, but should the return value\nfrom this be checked?\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> +\tcurl = curl_easy_init();\n> +\n> +\tif (!curl) {\n> +\t\tfprintf(stderr, \"failed to init curl\\n\");\n>  \t\treturn 1;\n\nCould do\n\n\t\tdie(\"failed to init curl\");\n\nfor more consistent message format and exit codes (128 for internal\nerrors, with an error message starting with 'fatal: ').\n\n[...]\n> +\tif (ends_with(server.host, \"/\"))\n> +\t\tpathlen = snprintf (path, sizeof(path), \"%s%s\", server.host, imap_folder);\n> +\telse\n> +\t\tpathlen = snprintf (path, sizeof(path), \"%s/%s\", server.host, imap_folder);\n> +\n> +\tif (pathlen < 0)\n> +\t\tdie(\"Fatal: Out of memory\");\n> +\tif (pathlen >= sizeof(path))\n> +\t\tdie(\"imap URL overflow!\");\n\nWith a strbuf, this could be something like\n\n\tstrbuf_addstr(&path, server.host);\n\tif (!path.len || path.buf[path.len - 1] != '/')\n\t\tstrbuf_addch(&path, '/');\n\tstrbuf_addstr(&path, imap_folder);\n\nor\n\n\tif (ends_with(...))\n\t\tstrbuf_addf(&path, \"%s%s\", ...);\n\telse\n\t\tstrbuf_addf(...);\n\nKilling the unused ctx->prefix handling is nice. :)\n\n[...]\n> +\tif (server.tunnel) {\n> +\t\tconst char *argv[] = { server.tunnel, NULL };\n> +\t\tstruct child_process tunnel = {NULL};\n\n(not about this patch) Could use the child_proccess's internal\nargv_array:\n\n\t\tstruct child_process tunnel = {NULL};\n\t\targv_array_push(&tunnel.args, server.tunnel);\n\n(about this patch) Would there be a way to make this part reuse the\nexisting code?  The only difference I see is that *srvc has been\nrenamed to server, which doesn't seem to be related to the change of\ntransport API from OpenSSL to libcurl.\n\n[...]\n> +\t\tcurl_socket_t sockfd = tunnel.out; // what about tunnel.in ?\n\nHmm.  curl expects to get a socket it can send(), recv(), setsockopt(),\netc on instead of a pair of fds to read() and write().\n\nI wonder why someone would want to use SSL through a tunnel, though.\nCurrently it's impossible to get to the SSL codepath when a tunnel\nis active (it's in the 'else' block an 'if (srvc->tunnel)').  If that\nproperty is preserved, then we should be safe.\n\nTo summarize:\n\n * I like this idea a lot!  Using libcurl's imaps:// support directly\n   means one less dependency to worry about, and using alternate SSL\n   libraries like gnutls or nss becomes much easier (e.g., see\n   http://fedoraproject.org/wiki/FedoraCryptoConsolidation for how\n   that makes configuring certificate trust simpler).\n\n * This would be easier to take if guarded by an #ifdef, so people\n   stuck on ancient libcurl would still be able to use git (and\n   ideally still use imap over SSL).\n\n * This shouldn't have to touch the imap.tunnel support.  imap-send's\n   imap.tunnel configuration expects the tunnel to take care of\n   securing the channel (e.g. by using 'openssl s_client').\n\n * Any potential cleanups noticed along the way are very welcome,\n   as separate patches.\n\n * As soon as you're ready to roll this out to a wider audience of\n   testers, let me know, and we can try to get it into shape for\n   Junio's \"next\" branch (and hence Debian experimental).\n\nThanks and hope that helps,\nJonathan\n"},{"id":"247810","messageId":"20140817083022.GJ23808@peff.net","threadId":"37338","inReplyTo":"20140813015917.GA30756@google.com","subject":"Re: [PATCH/RFC] git-imap-send: use libcurl for implementation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-08-17T08:30:22Z","receivedAt":"2014-08-17T08:30:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Aug 12, 2014 at 06:59:17PM -0700, Jonathan Nieder wrote:\n\n> > +\t\tcurl_socket_t sockfd = tunnel.out; // what about tunnel.in ?\n> \n> Hmm.  curl expects to get a socket it can send(), recv(), setsockopt(),\n> etc on instead of a pair of fds to read() and write().\n\nI wonder if we could teach run_command to optionally use socketpair()\ninstead of pipe(). I'm not sure if that would cause problems on Windows,\nthough.\n\n> I wonder why someone would want to use SSL through a tunnel, though.\n> Currently it's impossible to get to the SSL codepath when a tunnel is\n> active (it's in the 'else' block an 'if (srvc->tunnel)').  If that\n> property is preserved, then we should be safe.\n\nI'm not sure I understand this comment. Even if SSL is not in use,\nwouldn't we be passing a regular pipe to curl, which would break?\n\n-Peff\n"},{"id":"247817","messageId":"53F0A66A.9000909@raz.or.at","threadId":"37338","inReplyTo":"20140817083022.GJ23808@peff.net","subject":"Re: [PATCH/RFC] git-imap-send: use libcurl for implementation","fromName":"Bernhard Reiter","fromEmail":"ockham@raz.or.at","sentAt":"2014-08-17T12:56:10Z","receivedAt":"2014-08-17T12:56:10Z","isPatch":true,"sender":{"key":"ockham@raz.or.at","avatar":"https://avatars.githubusercontent.com/u/96308?v=4"},"body":"Am 2014-08-17 um 10:30 schrieb Jeff King:\n> On Tue, Aug 12, 2014 at 06:59:17PM -0700, Jonathan Nieder wrote:\n> \n>>> +\t\tcurl_socket_t sockfd = tunnel.out; // what about tunnel.in ?\n>>\n>> Hmm.  curl expects to get a socket it can send(), recv(), setsockopt(),\n>> etc on instead of a pair of fds to read() and write().\n> \n> I wonder if we could teach run_command to optionally use socketpair()\n> instead of pipe(). \n\nThat sounds like a good idea to me.\n\n> I'm not sure if that would cause problems on Windows,\n> though.\n\nApparently socketpair is not available there. Googling \"socketpair\nwindows\" yields, among a lot of other useful resources, the following\nrelatively actively maintained ~150 LOC, BSD-3-clause-licensed,\nimplementation:\n\nhttps://github.com/ncm/selectable-socketpair\n\nThat license is GPL compatible, so should we consider including that\nimplementation with git? That's the kind of stuff that goes to\ncompat/win32, right?\n\nOne thing to consider: seems like socketpair() gives AF_LOCAL sockets,\nso I've asked [1] on the curl ML if that would work or if libcurl needs\nan AF_INET one.\n\n>> I wonder why someone would want to use SSL through a tunnel, though.\n>> Currently it's impossible to get to the SSL codepath when a tunnel is\n>> active (it's in the 'else' block an 'if (srvc->tunnel)').  If that\n>> property is preserved, then we should be safe.\n> \n> I'm not sure I understand this comment. Even if SSL is not in use,\n> wouldn't we be passing a regular pipe to curl, which would break?\n\nYeah, we can't do that, and thus would have to keep the handwritten IMAP\nimplementation just for the tunnel case (allowing to drop only the\nOpenSSL specific stuff), see my other email:\nhttp://www.mail-archive.com/git@vger.kernel.org/msg56791.html (the\nrelevant part is pretty far down at the bottom).\n\nBernhard\n\n[1] http://curl.haxx.se/mail/lib-2014-08/0131.html\n"},{"id":"247821","messageId":"20140817184252.GB2200@peff.net","threadId":"37338","inReplyTo":"53F0A66A.9000909@raz.or.at","subject":"Re: [PATCH/RFC] git-imap-send: use libcurl for implementation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-08-17T18:42:52Z","receivedAt":"2014-08-17T18:42:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Aug 17, 2014 at 02:56:10PM +0200, Bernhard Reiter wrote:\n\n> > I'm not sure if that would cause problems on Windows,\n> > though.\n> \n> Apparently socketpair is not available there. Googling \"socketpair\n> windows\" yields, among a lot of other useful resources, the following\n> relatively actively maintained ~150 LOC, BSD-3-clause-licensed,\n> implementation:\n> \n> https://github.com/ncm/selectable-socketpair\n> \n> That license is GPL compatible, so should we consider including that\n> implementation with git? That's the kind of stuff that goes to\n> compat/win32, right?\n\nThanks for researching. Sticking that in compat/ would be our usual\nstrategy, yes.\n\n> > I'm not sure I understand this comment. Even if SSL is not in use,\n> > wouldn't we be passing a regular pipe to curl, which would break?\n> \n> Yeah, we can't do that, and thus would have to keep the handwritten IMAP\n> implementation just for the tunnel case (allowing to drop only the\n> OpenSSL specific stuff), see my other email:\n> http://www.mail-archive.com/git@vger.kernel.org/msg56791.html (the\n> relevant part is pretty far down at the bottom).\n\nI'd really love it if we could make this work with tunnels and\neventually get rid of the hand-written imap code entirely. I agree with\nJonathan that we probably need to keep it around a bit for people on\nolder curl, but dropping it is a good goal in the long run. That code\nwas forked from the isync project, but mangled enough that we could not\ntake bug fixes from upstream. As not many people use imap-send, I\nsuspect it is largely unmaintained and the source of many lurking\nbugs[1]. Replacing it with curl's maintained implementation is probably\na good step.\n\n-Peff\n\n[1] That's my somewhat subjective opinion, but I feel like I have seen\n    several bugs reported in imap-send that had literally been there for\n    years. And having worked on IMAP implementations in a past life, I\n    know there are many dark corners of the protocol that vary from\n    server to server.\n"},{"id":"247903","messageId":"53F33183.80802@raz.or.at","threadId":"37338","inReplyTo":"20140817184252.GB2200@peff.net","subject":"Re: [PATCH/RFC] git-imap-send: use libcurl for implementation","fromName":"Bernhard Reiter","fromEmail":"ockham@raz.or.at","sentAt":"2014-08-19T11:14:11Z","receivedAt":"2014-08-19T11:14:11Z","isPatch":true,"sender":{"key":"ockham@raz.or.at","avatar":"https://avatars.githubusercontent.com/u/96308?v=4"},"body":"Am 2014-08-17 um 20:42 schrieb Jeff King:\n> [...]\n> \n>>> I'm not sure I understand this comment. Even if SSL is not in use,\n>>> wouldn't we be passing a regular pipe to curl, which would break?\n>>\n>> Yeah, we can't do that, and thus would have to keep the handwritten IMAP\n>> implementation just for the tunnel case (allowing to drop only the\n>> OpenSSL specific stuff), see my other email:\n>> http://www.mail-archive.com/git@vger.kernel.org/msg56791.html (the\n>> relevant part is pretty far down at the bottom).\n> \n> I'd really love it if we could make this work with tunnels and\n> eventually get rid of the hand-written imap code entirely. I agree with\n> Jonathan that we probably need to keep it around a bit for people on\n> older curl, but dropping it is a good goal in the long run. That code\n> was forked from the isync project, but mangled enough that we could not\n> take bug fixes from upstream. As not many people use imap-send, I\n> suspect it is largely unmaintained and the source of many lurking\n> bugs[1]. Replacing it with curl's maintained implementation is probably\n> a good step.\n\nI'll work on this as soon as I find some time, but as that will include\nchanges to run-command.c (and possibly other files?), I'd like to cover\nthat in a commit of its own. Do you guys think the current patch [1] is\ngood enough for \"official\" submission already? If so, do I need some\nsort of official review? Documentation/SubmittingPatches says I'm only\nsupposed to direct it to Junio after the list \"reaches consensus\", so\nI'm wondering how to get there... :-)\n\nBernhard\n"},{"id":"247952","messageId":"xmqq61howgg0.fsf@gitster.dls.corp.google.com","threadId":"37338","inReplyTo":"53F33183.80802@raz.or.at","subject":"Re: [PATCH/RFC] git-imap-send: use libcurl for implementation","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-08-19T17:13:35Z","receivedAt":"2014-08-19T17:13:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Bernhard Reiter <ockham@raz.or.at> writes:\n\n> Am 2014-08-17 um 20:42 schrieb Jeff King:\n>> [...]\n>> \n>>>> I'm not sure I understand this comment. Even if SSL is not in use,\n>>>> wouldn't we be passing a regular pipe to curl, which would break?\n>>>\n>>> Yeah, we can't do that, and thus would have to keep the handwritten IMAP\n>>> implementation just for the tunnel case (allowing to drop only the\n>>> OpenSSL specific stuff), see my other email:\n>>> http://www.mail-archive.com/git@vger.kernel.org/msg56791.html (the\n>>> relevant part is pretty far down at the bottom).\n>> \n>> I'd really love it if we could make this work with tunnels and\n>> eventually get rid of the hand-written imap code entirely. I agree with\n>> Jonathan that we probably need to keep it around a bit for people on\n>> older curl, but dropping it is a good goal in the long run. That code\n>> was forked from the isync project, but mangled enough that we could not\n>> take bug fixes from upstream. As not many people use imap-send, I\n>> suspect it is largely unmaintained and the source of many lurking\n>> bugs[1]. Replacing it with curl's maintained implementation is probably\n>> a good step.\n\nI would agree with s/a good step/a good goal/ ;-)\n\n> I'll work on this as soon as I find some time, but as that will include\n> changes to run-command.c (and possibly other files?), I'd like to cover\n> that in a commit of its own. Do you guys think the current patch [1] is\n> good enough for \"official\" submission already?\n\nMy impression from reading the discussion in this thread has been\nthat the patch that started this thread would break the tunneling\ncode, i.e. is not there yet.  Or did you mean some other patch?\n\nThe other patch $gmane/255403 from you looked good and I think I\nalready have a copy queued on 'pu' as f9dc5d65 (git-imap-send:\nsimplify tunnel construction, 2014-08-13).\n\nThanks.\n\n\n[References]\n\n*$gmane/255403*\n    http://thread.gmane.org/gmane.comp.version-control.git/255220/focus=255403\n"}]}