Bernhard Reiter wrote:
> Use libcurl's high-level API functions to implement git-imap-send
> instead of the previous low-level OpenSSL-based functions.
Wow! This sounds lovely. Thanks for working on this.
[...]
> 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 reduces imap-send.c by some 1200 lines of code.
>
> As I don't have access to that many IMAP servers, I haven't been able to
> test a variety of parameter combinations. I did test both secure and
> insecure (imaps:// and imap://) connections -- this e-mail was generated
> that way -- but could e.g. neither test the authMethod nor the tunnel
> parameter.
The above two paragraphs belong in the commit message, too, since
they'll be just as useful for someone looking back through the history
as for someone reading the patch on-list today.
[...]
> --- a/INSTALL
> +++ b/INSTALL
[...]
> - - "libcurl" library is used by git-http-fetch and git-fetch. You
> + - "libcurl" library is used by git-http-fetch, git-fetch, and
> + git-impap-send. You might also want the "curl" executable for
Typo: s/impap-send/imap-send/
> --- a/Makefile
> +++ b/Makefile
> @@ -2067,9 +2067,9 @@ endif
> git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
> $(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 http.o GIT-LDFLAGS $(GITLIBS)
> $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
> - $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
> + $(LIBS) $(CURL_LIBCURL)
7.30.0 is only ~1 year old. Does this mean users would need to update
curl in order to build imap-send?
For example, Debian 7.6 ships curl 7.26.0 and CentOS 7 has curl 7.29.0.
Ideally this could be done as an optional feature:
1. Introduce a USE_CURL_FOR_IMAP_SEND makefile variable to take
advantage of the nice new API.
2. (optional) Use the curl_check makefile variable to turn on
USE_CURL_FOR_IMAP_SEND automatically when appropriate.
3. In a few years, when everyone has upgraded, we could simplify by
getting rid of the USE_OPENSSL_FOR_IMAP_SEND code path.
What do you think?
[...]
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -22,47 +22,13 @@
> * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> */
>
> -#include "cache.h"
Usual style is to start with a #include of "cache.h" or
"git-compat-util.h". "http.h" including cache.h for itself was an
old mistake. (I'll reply with a patch to fix that.)
[...]
> +#include <curl/curl.h>
http.h already #includes this. Do you use other helpers from
http.h/http.c or do you use libcurl directly? (Just curious.)
Some style nits:
[...]
> +static curl_socket_t opensocket(void *clientp, curlsocktype purpose,
> + struct
> curl_sockaddr *address)
Long line. Do you have ts=4? (Git uses 8-space tabs. There's some
emacs magic in Documentation/CodingGuidelines. It should be possible
to add similar hints there for other editors if they don't do the
right thing by default.)
> +{
> + curl_socket_t sockfd;
> + (void)purpose;
> + (void)address;
Elsewhere git lets unused parameters be. The unused parameter warning
is too noisy in callback-heavy code (e.g., for_each_ref) so we don't
turn it on.
> + sockfd = *(curl_socket_t *)clientp;
> + /* the actual externally set socket is passed in via the OPENSOCKETDATA
> + option */
(style nit) Comments in git look like this:
/*
* The actual, externally set socket is passed in via the
* OPENSOCKETDATA option.
*/
return sockfd;
[...]
> +static int sockopt_callback(void *clientp, curl_socket_t curlfd,
> + curlsocktype purpose)
> +{
> + /* This return code was added in libcurl 7.21.5 */
> + return CURL_SOCKOPT_ALREADY_CONNECTED;
I'd drop the comment, unless there's some subtlety involved. (E.g.,
is there some other return code that would be more appropriate but was
introudced later or something?)
[...]
> @@ -1368,12 +218,14 @@ static int git_imap_config(const char *key, const char
> *val, void *cb)
> int main(int argc, char **argv)
> {
> struct strbuf all_msgs = STRBUF_INIT;
> - struct strbuf msg = STRBUF_INIT;
> + struct buffer msg = { STRBUF_INIT, 0 };
Ah, ok --- we do use http.c stuff.
[...]
> + char path[8192];
> + int pathlen;
I realize the old code only had 8192 for the IMAP command buffer,
but could this be a strbuf now, or is there some underlying limit
somewhere else?
[...]
> @@ -1417,31 +269,89 @@ int main(int argc, char **argv)
> return 1;
> }
>
> + curl_global_init(CURL_GLOBAL_ALL);
http.c seems to make the same mistake, but should the return value
from this be checked?
> - /* write it to the imap server */
> - ctx = imap_open_store(&server);
> - if (!ctx) {
> - fprintf(stderr, "failed to open store\n");
> + curl = curl_easy_init();
> +
> + if (!curl) {
> + fprintf(stderr, "failed to init curl\n");
> return 1;
Could do
die("failed to init curl");
for more consistent message format and exit codes (128 for internal
errors, with an error message starting with 'fatal: ').
[...]
> + if (ends_with(server.host, "/"))
> + pathlen = snprintf (path, sizeof(path), "%s%s", server.host,
> imap_folder);
> + else
> + pathlen = snprintf (path, sizeof(path), "%s/%s", server.host,
> imap_folder);
> +
> + if (pathlen < 0)
> + die("Fatal: Out of memory");
> + if (pathlen >= sizeof(path))
> + die("imap URL overflow!");
With a strbuf, this could be something like
strbuf_addstr(&path, server.host);
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
strbuf_addstr(&path, imap_folder);
or
if (ends_with(...))
strbuf_addf(&path, "%s%s", ...);
else
strbuf_addf(...);
Killing the unused ctx->prefix handling is nice. :)
[...]
> + if (server.tunnel) {
> + const char *argv[] = { server.tunnel, NULL };
> + struct child_process tunnel = {NULL};
(not about this patch) Could use the child_proccess's internal
argv_array:
struct child_process tunnel = {NULL};
argv_array_push(&tunnel.args, server.tunnel);
(about this patch) Would there be a way to make this part reuse the
existing code? The only difference I see is that *srvc has been
renamed to server, which doesn't seem to be related to the change of
transport API from OpenSSL to libcurl.
[...]
> + curl_socket_t sockfd = tunnel.out; // what about tunnel.in ?
Hmm. curl expects to get a socket it can send(), recv(), setsockopt(),
etc on instead of a pair of fds to read() and write().
I wonder why someone would want to use SSL through a tunnel, though.
Currently it's impossible to get to the SSL codepath when a tunnel
is active (it's in the 'else' block an 'if (srvc->tunnel)'). If that
property is preserved, then we should be safe.
To summarize:
* I like this idea a lot! Using libcurl's imaps:// support directly
means one less dependency to worry about, and using alternate SSL
libraries like gnutls or nss becomes much easier (e.g., see
http://fedoraproject.org/wiki/FedoraCryptoConsolidation for how
that makes configuring certificate trust simpler).
* This would be easier to take if guarded by an #ifdef, so people
stuck on ancient libcurl would still be able to use git (and
ideally still use imap over SSL).
* This shouldn't have to touch the imap.tunnel support. imap-send's
imap.tunnel configuration expects the tunnel to take care of
securing the channel (e.g. by using 'openssl s_client').
* Any potential cleanups noticed along the way are very welcome,
as separate patches.
* As soon as you're ready to roll this out to a wider audience of
testers, let me know, and we can try to get it into shape for
Junio's "next" branch (and hence Debian experimental).
Thanks and hope that helps,
Jonathan
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html