Imported Upstream version 2.12.5
[platform/upstream/git.git] / imap-send.c
index 70bcc7a..5c7e27a 100644 (file)
 #include "credential.h"
 #include "exec_cmd.h"
 #include "run-command.h"
+#include "parse-options.h"
 #ifdef NO_OPENSSL
 typedef void *SSL;
 #endif
+#ifdef USE_CURL_FOR_IMAP_SEND
+#include "http.h"
+#endif
+
+#if defined(USE_CURL_FOR_IMAP_SEND) && defined(NO_OPENSSL)
+/* only available option */
+#define USE_CURL_DEFAULT 1
+#else
+/* strictly opt in */
+#define USE_CURL_DEFAULT 0
+#endif
 
-static const char imap_send_usage[] = "git imap-send < <mbox>";
+static int verbosity;
+static int use_curl = USE_CURL_DEFAULT;
+
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+
+static struct option imap_send_options[] = {
+       OPT__VERBOSITY(&verbosity),
+       OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+       OPT_END()
+};
 
 #undef DRV_OK
 #define DRV_OK          0
@@ -38,8 +59,6 @@ static const char imap_send_usage[] = "git imap-send < <mbox>";
 #define DRV_BOX_BAD     -2
 #define DRV_STORE_BAD   -3
 
-static int Verbose, Quiet;
-
 __attribute__((format (printf, 1, 2)))
 static void imap_info(const char *, ...);
 __attribute__((format (printf, 1, 2)))
@@ -268,17 +287,20 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
        SSL_library_init();
        SSL_load_error_strings();
 
-       if (use_tls_only)
-               meth = TLSv1_method();
-       else
-               meth = SSLv23_method();
-
+       meth = SSLv23_method();
        if (!meth) {
                ssl_socket_perror("SSLv23_method");
                return -1;
        }
 
        ctx = SSL_CTX_new(meth);
+       if (!ctx) {
+               ssl_socket_perror("SSL_CTX_new");
+               return -1;
+       }
+
+       if (use_tls_only)
+               SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 
        if (verify)
                SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
@@ -418,7 +440,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
                        if (b->buf[b->offset + 1] == '\n') {
                                b->buf[b->offset] = 0;  /* terminate the string */
                                b->offset += 2; /* next line */
-                               if (Verbose)
+                               if (0 < verbosity)
                                        puts(*s);
                                return 0;
                        }
@@ -433,7 +455,7 @@ static void imap_info(const char *msg, ...)
 {
        va_list va;
 
-       if (!Quiet) {
+       if (0 <= verbosity) {
                va_start(va, msg);
                vprintf(msg, va);
                va_end(va);
@@ -445,7 +467,7 @@ static void imap_warn(const char *msg, ...)
 {
        va_list va;
 
-       if (Quiet < 2) {
+       if (-2 < verbosity) {
                va_start(va, msg);
                vfprintf(stderr, msg, va);
                va_end(va);
@@ -489,7 +511,7 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
 
        va_start(va, fmt);
        if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
-               die("Fatal: buffer too small. Please report a bug.");
+               die("BUG: buffer too small. Please report a bug.");
        va_end(va);
        return ret;
 }
@@ -522,7 +544,7 @@ static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
                                  cmd->tag, cmd->cmd, cmd->cb.dlen,
                                  CAP(LITERALPLUS) ? "+" : "");
 
-       if (Verbose) {
+       if (0 < verbosity) {
                if (imap->num_in_progress)
                        printf("(%d in progress) ", imap->num_in_progress);
                if (!starts_with(cmd->cmd, "LOGIN"))
@@ -843,7 +865,6 @@ static char hexchar(unsigned int b)
 static char *cram(const char *challenge_64, const char *user, const char *pass)
 {
        int i, resp_len, encoded_len, decoded_len;
-       HMAC_CTX hmac;
        unsigned char hash[16];
        char hex[33];
        char *response, *response_64, *challenge;
@@ -858,10 +879,8 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
                                      (unsigned char *)challenge_64, encoded_len);
        if (decoded_len < 0)
                die("invalid challenge %s", challenge_64);
-       HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
-       HMAC_Update(&hmac, (unsigned char *)challenge, decoded_len);
-       HMAC_Final(&hmac, hash, NULL);
-       HMAC_CTX_cleanup(&hmac);
+       if (!HMAC(EVP_md5(), pass, strlen(pass), (unsigned char *)challenge, decoded_len, hash, NULL))
+               die("HMAC error");
 
        hex[32] = 0;
        for (i = 0; i < 16; i++) {
@@ -870,16 +889,14 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
        }
 
        /* response: "<user> <digest in hex>" */
-       resp_len = strlen(user) + 1 + strlen(hex) + 1;
-       response = xmalloc(resp_len);
-       sprintf(response, "%s %s", user, hex);
+       response = xstrfmt("%s %s", user, hex);
+       resp_len = strlen(response);
 
-       response_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);
+       response_64 = xmallocz(ENCODED_SIZE(resp_len));
        encoded_len = EVP_EncodeBlock((unsigned char *)response_64,
                                      (unsigned char *)response, resp_len);
        if (encoded_len < 0)
                die("EVP_EncodeBlock error");
-       response_64[encoded_len] = '\0';
        return (char *)response_64;
 }
 
@@ -1065,10 +1082,8 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *f
                        cred.protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
                        cred.host = xstrdup(srvc->host);
 
-                       if (srvc->user)
-                               cred.username = xstrdup(srvc->user);
-                       if (srvc->pass)
-                               cred.password = xstrdup(srvc->pass);
+                       cred.username = xstrdup_or_null(srvc->user);
+                       cred.password = xstrdup_or_null(srvc->pass);
 
                        credential_fill(&cred);
 
@@ -1078,11 +1093,6 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *f
                                srvc->pass = xstrdup(cred.password);
                }
 
-               if (CAP(NOLOGIN)) {
-                       fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
-                       goto bail;
-               }
-
                if (srvc->auth_method) {
                        struct imap_cmd_cb cb;
 
@@ -1106,6 +1116,11 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *f
                                goto bail;
                        }
                } else {
+                       if (CAP(NOLOGIN)) {
+                               fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+                                       srvc->user, srvc->host);
+                               goto bail;
+                       }
                        if (!imap->buf.sock.ssl)
                                imap_warn("*** IMAP Warning *** Password is being "
                                          "sent in the clear\n");
@@ -1170,7 +1185,7 @@ static void lf_to_crlf(struct strbuf *msg)
                j++;
        }
 
-       new = xmalloc(j + 1);
+       new = xmallocz(j);
 
        /*
         * Second pass: write the new string.  Note that this loop is
@@ -1338,26 +1353,173 @@ static void git_imap_config(void)
        git_config_get_string("imap.authmethod", &server.auth_method);
 }
 
-int main(int argc, char **argv)
+static int append_msgs_to_imap(struct imap_server_conf *server,
+                              struct strbuf* all_msgs, int total)
 {
-       struct strbuf all_msgs = STRBUF_INIT;
        struct strbuf msg = STRBUF_INIT;
        struct imap_store *ctx = NULL;
        int ofs = 0;
        int r;
-       int total, n = 0;
-       int nongit_ok;
+       int n = 0;
+
+       ctx = imap_open_store(server, server->folder);
+       if (!ctx) {
+               fprintf(stderr, "failed to open store\n");
+               return 1;
+       }
+       ctx->name = server->folder;
+
+       fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+       while (1) {
+               unsigned percent = n * 100 / total;
+
+               fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
+
+               if (!split_msg(all_msgs, &msg, &ofs))
+                       break;
+               if (server->use_html)
+                       wrap_in_html(&msg);
+               r = imap_store_msg(ctx, &msg);
+               if (r != DRV_OK)
+                       break;
+               n++;
+       }
+       fprintf(stderr, "\n");
+
+       imap_close_store(ctx);
+
+       return 0;
+}
+
+#ifdef USE_CURL_FOR_IMAP_SEND
+static CURL *setup_curl(struct imap_server_conf *srvc)
+{
+       CURL *curl;
+       struct strbuf path = STRBUF_INIT;
+
+       if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
+               die("curl_global_init failed");
+
+       curl = curl_easy_init();
+
+       if (!curl)
+               die("curl_easy_init failed");
+
+       curl_easy_setopt(curl, CURLOPT_USERNAME, server.user);
+       curl_easy_setopt(curl, CURLOPT_PASSWORD, server.pass);
+
+       strbuf_addstr(&path, server.use_ssl ? "imaps://" : "imap://");
+       strbuf_addstr(&path, server.host);
+       if (!path.len || path.buf[path.len - 1] != '/')
+               strbuf_addch(&path, '/');
+       strbuf_addstr(&path, server.folder);
+
+       curl_easy_setopt(curl, CURLOPT_URL, path.buf);
+       strbuf_release(&path);
+       curl_easy_setopt(curl, CURLOPT_PORT, server.port);
+
+       if (server.auth_method) {
+#if LIBCURL_VERSION_NUM < 0x072200
+               warning("No LOGIN_OPTIONS support in this cURL version");
+#else
+               struct strbuf auth = STRBUF_INIT;
+               strbuf_addstr(&auth, "AUTH=");
+               strbuf_addstr(&auth, server.auth_method);
+               curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+               strbuf_release(&auth);
+#endif
+       }
 
-       git_extract_argv0_path(argv[0]);
+       if (!server.use_ssl)
+               curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_TRY);
 
-       git_setup_gettext();
+       curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, server.ssl_verify);
+       curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, server.ssl_verify);
 
-       if (argc != 1)
-               usage(imap_send_usage);
+       curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+
+       curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
+       if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
+               curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+       setup_curl_trace(curl);
+
+       return curl;
+}
+
+static int curl_append_msgs_to_imap(struct imap_server_conf *server,
+                                   struct strbuf* all_msgs, int total) {
+       int ofs = 0;
+       int n = 0;
+       struct buffer msgbuf = { STRBUF_INIT, 0 };
+       CURL *curl;
+       CURLcode res = CURLE_OK;
+
+       curl = setup_curl(server);
+       curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
+
+       fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+       while (1) {
+               unsigned percent = n * 100 / total;
+               int prev_len;
+
+               fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
+
+               prev_len = msgbuf.buf.len;
+               if (!split_msg(all_msgs, &msgbuf.buf, &ofs))
+                       break;
+               if (server->use_html)
+                       wrap_in_html(&msgbuf.buf);
+               lf_to_crlf(&msgbuf.buf);
+
+               curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,
+                                (curl_off_t)(msgbuf.buf.len-prev_len));
+
+               res = curl_easy_perform(curl);
+
+               if(res != CURLE_OK) {
+                       fprintf(stderr, "curl_easy_perform() failed: %s\n",
+                                       curl_easy_strerror(res));
+                       break;
+               }
+
+               n++;
+       }
+       fprintf(stderr, "\n");
+
+       curl_easy_cleanup(curl);
+       curl_global_cleanup();
+
+       return 0;
+}
+#endif
+
+int cmd_main(int argc, const char **argv)
+{
+       struct strbuf all_msgs = STRBUF_INIT;
+       int total;
+       int nongit_ok;
 
        setup_git_directory_gently(&nongit_ok);
        git_imap_config();
 
+       argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+
+       if (argc)
+               usage_with_options(imap_send_usage, imap_send_options);
+
+#ifndef USE_CURL_FOR_IMAP_SEND
+       if (use_curl) {
+               warning("--curl not supported in this build");
+               use_curl = 0;
+       }
+#elif defined(NO_OPENSSL)
+       if (!use_curl) {
+               warning("--no-curl not supported in this build");
+               use_curl = 1;
+       }
+#endif
+
        if (!server.port)
                server.port = server.use_ssl ? 993 : 143;
 
@@ -1391,29 +1553,14 @@ int main(int argc, char **argv)
        }
 
        /* write it to the imap server */
-       ctx = imap_open_store(&server, server.folder);
-       if (!ctx) {
-               fprintf(stderr, "failed to open store\n");
-               return 1;
-       }
 
-       fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
-       while (1) {
-               unsigned percent = n * 100 / total;
-
-               fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
-               if (!split_msg(&all_msgs, &msg, &ofs))
-                       break;
-               if (server.use_html)
-                       wrap_in_html(&msg);
-               r = imap_store_msg(ctx, &msg);
-               if (r != DRV_OK)
-                       break;
-               n++;
-       }
-       fprintf(stderr, "\n");
+       if (server.tunnel)
+               return append_msgs_to_imap(&server, &all_msgs, total);
 
-       imap_close_store(ctx);
+#ifdef USE_CURL_FOR_IMAP_SEND
+       if (use_curl)
+               return curl_append_msgs_to_imap(&server, &all_msgs, total);
+#endif
 
-       return 0;
+       return append_msgs_to_imap(&server, &all_msgs, total);
 }