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

@ -9,7 +9,7 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git imap-send' [-v] [-q] 'git imap-send' [-v] [-q] [--[no-]curl]
DESCRIPTION DESCRIPTION
@ -37,6 +37,15 @@ OPTIONS
--quiet:: --quiet::
Be quiet. Be quiet.
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
option set.
--no-curl::
Talk to the IMAP server using git's own IMAP routines instead of
using libcurl.
CONFIGURATION CONFIGURATION
------------- -------------
@ -87,7 +96,9 @@ imap.preformattedHTML::
imap.authMethod:: imap.authMethod::
Specify authenticate method for authentication with IMAP server. Specify authenticate method for authentication with IMAP server.
Current supported method is 'CRAM-MD5' only. If this is not set If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
option, the only supported method is 'CRAM-MD5'. If this is not set
then 'git imap-send' uses the basic IMAP plaintext LOGIN command. then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
Examples Examples

15
INSTALL
View File

@ -108,18 +108,21 @@ Issues of note:
so you might need to install additional packages other than Perl so you might need to install additional packages other than Perl
itself, e.g. Time::HiRes. itself, e.g. Time::HiRes.
- "openssl" library is used by git-imap-send to use IMAP over SSL. - git-imap-send needs the OpenSSL library to talk IMAP over SSL if
If you don't need it, use NO_OPENSSL. you are using libcurl older than 7.34.0. Otherwise you can use
NO_OPENSSL without losing git-imap-send.
By default, git uses OpenSSL for SHA1 but it will use its own By default, git uses OpenSSL for SHA1 but it will use its own
library (inspired by Mozilla's) with either NO_OPENSSL or library (inspired by Mozilla's) with either NO_OPENSSL or
BLK_SHA1. Also included is a version optimized for PowerPC BLK_SHA1. Also included is a version optimized for PowerPC
(PPC_SHA1). (PPC_SHA1).
- "libcurl" library is used by git-http-fetch and git-fetch. You - "libcurl" library is used by git-http-fetch, git-fetch, and, if
might also want the "curl" executable for debugging purposes. the curl version >= 7.34.0, for git-imap-send. You might also
If you do not use http:// or https:// repositories, you do not want the "curl" executable for debugging purposes. If you do not
have to have them (use NO_CURL). use http:// or https:// repositories, and do not want to put
patches into an IMAP mailbox, you do not have to have them
(use NO_CURL).
- "expat" library; git-http-push uses it for remote lock - "expat" library; git-http-push uses it for remote lock
management over DAV. Similar to "curl" above, this is optional management over DAV. Similar to "curl" above, this is optional

View File

@ -995,6 +995,9 @@ ifdef HAVE_ALLOCA_H
BASIC_CFLAGS += -DHAVE_ALLOCA_H BASIC_CFLAGS += -DHAVE_ALLOCA_H
endif endif
IMAP_SEND_BUILDDEPS =
IMAP_SEND_LDFLAGS = $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
ifdef NO_CURL ifdef NO_CURL
BASIC_CFLAGS += -DNO_CURL BASIC_CFLAGS += -DNO_CURL
REMOTE_CURL_PRIMARY = REMOTE_CURL_PRIMARY =
@ -1029,6 +1032,15 @@ else
PROGRAM_OBJS += http-push.o PROGRAM_OBJS += http-push.o
endif endif
endif endif
curl_check := $(shell (echo 072200; curl-config --vernum) 2>/dev/null | sort -r | sed -ne 2p)
ifeq "$(curl_check)" "072200"
USE_CURL_FOR_IMAP_SEND = YesPlease
endif
ifdef USE_CURL_FOR_IMAP_SEND
BASIC_CFLAGS += -DUSE_CURL_FOR_IMAP_SEND
IMAP_SEND_BUILDDEPS = http.o
IMAP_SEND_LDFLAGS += $(CURL_LIBCURL)
endif
ifndef NO_EXPAT ifndef NO_EXPAT
ifdef EXPATDIR ifdef EXPATDIR
BASIC_CFLAGS += -I$(EXPATDIR)/include BASIC_CFLAGS += -I$(EXPATDIR)/include
@ -1874,7 +1886,7 @@ gettext.sp gettext.s gettext.o: GIT-PREFIX
gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \ gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \
-DGIT_LOCALE_PATH='"$(localedir_SQ)"' -DGIT_LOCALE_PATH='"$(localedir_SQ)"'
http-push.sp http.sp http-walker.sp remote-curl.sp: SPARSE_FLAGS += \ http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SPARSE_FLAGS += \
-DCURL_DISABLE_TYPECHECK -DCURL_DISABLE_TYPECHECK
ifdef NO_EXPAT ifdef NO_EXPAT
@ -1895,9 +1907,9 @@ endif
git-%$X: %.o GIT-LDFLAGS $(GITLIBS) git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
git-imap-send$X: imap-send.o GIT-LDFLAGS $(GITLIBS) git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO) $(LIBS) $(IMAP_SEND_LDFLAGS)
git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS) git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \

View File

@ -30,13 +30,18 @@
#ifdef NO_OPENSSL #ifdef NO_OPENSSL
typedef void *SSL; typedef void *SSL;
#endif #endif
#ifdef USE_CURL_FOR_IMAP_SEND
#include "http.h"
#endif
static int verbosity; 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[] = { static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity), OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_END() OPT_END()
}; };
@ -1344,14 +1349,145 @@ static void git_imap_config(void)
git_config_get_string("imap.authmethod", &server.auth_method); 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 strbuf msg = STRBUF_INIT;
struct imap_store *ctx = NULL; struct imap_store *ctx = NULL;
int ofs = 0; int ofs = 0;
int r; 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; int nongit_ok;
git_extract_argv0_path(argv[0]); git_extract_argv0_path(argv[0]);
@ -1366,6 +1502,13 @@ int main(int argc, char **argv)
if (argc) if (argc)
usage_with_options(imap_send_usage, imap_send_options); 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) if (!server.port)
server.port = server.use_ssl ? 993 : 143; server.port = server.use_ssl ? 993 : 143;
@ -1399,29 +1542,14 @@ int main(int argc, char **argv)
} }
/* write it to the imap server */ /* 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" : ""); if (server.tunnel)
while (1) { return append_msgs_to_imap(&server, &all_msgs, total);
unsigned percent = n * 100 / total;
fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total); #ifdef USE_CURL_FOR_IMAP_SEND
if (!split_msg(&all_msgs, &msg, &ofs)) if (use_curl)
break; return curl_append_msgs_to_imap(&server, &all_msgs, total);
if (server.use_html) #endif
wrap_in_html(&msg);
r = imap_store_msg(ctx, &msg);
if (r != DRV_OK)
break;
n++;
}
fprintf(stderr, "\n");
imap_close_store(ctx); return append_msgs_to_imap(&server, &all_msgs, total);
return 0;
} }