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 <ramsay@ramsay1.demon.co.uk>
Signed-off-by: Bernhard Reiter <ockham@raz.or.at>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Bernhard Reiter
2014-11-09 15:55:53 +01:00
committed by Junio C Hamano
parent f1a35295c2
commit 1e16b255b9
4 changed files with 191 additions and 37 deletions

View File

@ -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] < <mbox>", NULL };
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()
};
@ -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;
if (server.tunnel)
return append_msgs_to_imap(&server, &all_msgs, 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");
#ifdef USE_CURL_FOR_IMAP_SEND
if (use_curl)
return curl_append_msgs_to_imap(&server, &all_msgs, total);
#endif
imap_close_store(ctx);
return 0;
return append_msgs_to_imap(&server, &all_msgs, total);
}