From 1e16b255b95b45374db0b0cf69ef0ecce768ac27 Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Sun, 9 Nov 2014 15:55:53 +0100 Subject: git-imap-send: use libcurl for implementation Use libcurl's high-level API functions to implement git-imap-send instead of the previous low-level OpenSSL-based functions. Since version 7.30.0, libcurl's API has been able to communicate with IMAP servers. Using those high-level functions instead of the current ones would reduce imap-send.c by some 1200 lines of code. For now, the old ones are wrapped in #ifdefs, and the new functions are enabled by make if curl's version is >= 7.34.0, from which version on curl's CURLOPT_LOGIN_OPTIONS (enabling IMAP authentication) parameter has been available. The low-level functions will still be used for tunneling into the server for now. As I don't have access to that many IMAP servers, I haven't been able to test the new code with a wide variety of parameter combinations. I did test both secure and insecure (imaps:// and imap://) connections and values of "PLAIN" and "LOGIN" for the authMethod. In order to suppress a sparse warning about "using sizeof on a function", we use the same solution used in commit 9371322a6 ("sparse: suppress some "using sizeof on a function" warnings", 06-10-2013) which solved exactly this problem for the other commands using libcurl. Helped-by: Ramsay Jones Signed-off-by: Bernhard Reiter Signed-off-by: Junio C Hamano --- imap-send.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 154 insertions(+), 26 deletions(-) (limited to 'imap-send.c') diff --git a/imap-send.c b/imap-send.c index 7f9d30effd..4dfe4c25d7 100644 --- a/imap-send.c +++ b/imap-send.c @@ -30,13 +30,18 @@ #ifdef NO_OPENSSL typedef void *SSL; #endif +#ifdef USE_CURL_FOR_IMAP_SEND +#include "http.h" +#endif static int verbosity; +static int use_curl; /* strictly opt in */ -static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] < ", NULL }; +static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < ", 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() }; @@ -1344,14 +1349,145 @@ 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 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.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) { + 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); + } + + if (server.use_ssl) + curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, server.ssl_verify); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, server.ssl_verify); + + curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer); + + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + if (0 < verbosity) + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + 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 main(int argc, char **argv) +{ + struct strbuf all_msgs = STRBUF_INIT; + int total; int nongit_ok; git_extract_argv0_path(argv[0]); @@ -1366,6 +1502,13 @@ int main(int argc, char **argv) if (argc) usage_with_options(imap_send_usage, imap_send_options); +#ifndef USE_CURL_FOR_IMAP_SEND + if (use_curl) { + warning("--use-curl not supported in this build"); + use_curl = 0; + } +#endif + if (!server.port) server.port = server.use_ssl ? 993 : 143; @@ -1399,29 +1542,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); } -- cgit v1.2.3