Merge branch 'jn/vcs-svn-cleanup' into next
[gitweb.git] / http.c
diff --git a/http.c b/http.c
index 051fe6e5ab77a5dc6e53dce7011d0fc445f15ab0..9e40a465fdec6467726e980da413478b9cf78492 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,5 +1,6 @@
 #include "git-compat-util.h"
 #include "http.h"
+#include "config.h"
 #include "pack.h"
 #include "sideband.h"
 #include "run-command.h"
@@ -10,6 +11,7 @@
 #include "pkt-line.h"
 #include "gettext.h"
 #include "transport.h"
+#include "packfile.h"
 
 static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
 #if LIBCURL_VERSION_NUM >= 0x070a08
@@ -19,7 +21,7 @@ long int git_curl_ipresolve;
 #endif
 int active_requests;
 int http_is_verbose;
-size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
+ssize_t http_post_buffer = 16 * LARGE_PACKET_MAX;
 
 #if LIBCURL_VERSION_NUM >= 0x070a06
 #define LIBCURL_CAN_HANDLE_AUTH_ANY
@@ -90,7 +92,7 @@ static struct {
         * here, too
         */
 };
-#if LIBCURL_VERSION_NUM >= 0x071600
+#ifdef CURLGSSAPI_DELEGATION_FLAG
 static const char *curl_deleg;
 static struct {
        const char *name;
@@ -109,7 +111,7 @@ static int curl_save_cookies;
 struct credential http_auth = CREDENTIAL_INIT;
 static int http_proactive_auth;
 static const char *user_agent;
-static int curl_empty_auth;
+static int curl_empty_auth = -1;
 
 enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;
 
@@ -125,6 +127,14 @@ static struct credential cert_auth = CREDENTIAL_INIT;
 static int ssl_cert_password_required;
 #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
 static unsigned long http_auth_methods = CURLAUTH_ANY;
+static int http_auth_methods_restricted;
+/* Modes for which empty_auth cannot actually help us. */
+static unsigned long empty_auth_useless =
+       CURLAUTH_BASIC
+#ifdef CURLAUTH_DIGEST_IE
+       | CURLAUTH_DIGEST_IE
+#endif
+       | CURLAUTH_DIGEST;
 #endif
 
 static struct curl_slist *pragma_header;
@@ -263,10 +273,10 @@ static int http_options(const char *var, const char *value, void *cb)
        if (!strcmp("http.sslversion", var))
                return git_config_string(&ssl_version, var, value);
        if (!strcmp("http.sslcert", var))
-               return git_config_string(&ssl_cert, var, value);
+               return git_config_pathname(&ssl_cert, var, value);
 #if LIBCURL_VERSION_NUM >= 0x070903
        if (!strcmp("http.sslkey", var))
-               return git_config_string(&ssl_key, var, value);
+               return git_config_pathname(&ssl_key, var, value);
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
        if (!strcmp("http.sslcapath", var))
@@ -323,7 +333,9 @@ static int http_options(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp("http.postbuffer", var)) {
-               http_post_buffer = git_config_int(var, value);
+               http_post_buffer = git_config_ssize_t(var, value);
+               if (http_post_buffer < 0)
+                       warning(_("negative value for http.postbuffer; defaulting to %d"), LARGE_PACKET_MAX);
                if (http_post_buffer < LARGE_PACKET_MAX)
                        http_post_buffer = LARGE_PACKET_MAX;
                return 0;
@@ -333,12 +345,15 @@ static int http_options(const char *var, const char *value, void *cb)
                return git_config_string(&user_agent, var, value);
 
        if (!strcmp("http.emptyauth", var)) {
-               curl_empty_auth = git_config_bool(var, value);
+               if (value && !strcmp("auto", value))
+                       curl_empty_auth = -1;
+               else
+                       curl_empty_auth = git_config_bool(var, value);
                return 0;
        }
 
        if (!strcmp("http.delegation", var)) {
-#if LIBCURL_VERSION_NUM >= 0x071600
+#ifdef CURLGSSAPI_DELEGATION_FLAG
                return git_config_string(&curl_deleg, var, value);
 #else
                warning(_("Delegation control is not supported with cURL < 7.22.0"));
@@ -382,10 +397,37 @@ static int http_options(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
+static int curl_empty_auth_enabled(void)
+{
+       if (curl_empty_auth >= 0)
+               return curl_empty_auth;
+
+#ifndef LIBCURL_CAN_HANDLE_AUTH_ANY
+       /*
+        * Our libcurl is too old to do AUTH_ANY in the first place;
+        * just default to turning the feature off.
+        */
+#else
+       /*
+        * In the automatic case, kick in the empty-auth
+        * hack as long as we would potentially try some
+        * method more exotic than "Basic" or "Digest".
+        *
+        * But only do this when this is our second or
+        * subsequent request, as by then we know what
+        * methods are available.
+        */
+       if (http_auth_methods_restricted &&
+           (http_auth_methods & ~empty_auth_useless))
+               return 1;
+#endif
+       return 0;
+}
+
 static void init_curl_http_auth(CURL *result)
 {
        if (!http_auth.username || !*http_auth.username) {
-               if (curl_empty_auth)
+               if (curl_empty_auth_enabled())
                        curl_easy_setopt(result, CURLOPT_USERPWD, ":");
                return;
        }
@@ -636,11 +678,27 @@ void setup_curl_trace(CURL *handle)
        curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL);
 }
 
+#ifdef CURLPROTO_HTTP
+static long get_curl_allowed_protocols(int from_user)
+{
+       long allowed_protocols = 0;
+
+       if (is_transport_allowed("http", from_user))
+               allowed_protocols |= CURLPROTO_HTTP;
+       if (is_transport_allowed("https", from_user))
+               allowed_protocols |= CURLPROTO_HTTPS;
+       if (is_transport_allowed("ftp", from_user))
+               allowed_protocols |= CURLPROTO_FTP;
+       if (is_transport_allowed("ftps", from_user))
+               allowed_protocols |= CURLPROTO_FTPS;
+
+       return allowed_protocols;
+}
+#endif
 
 static CURL *get_curl_handle(void)
 {
        CURL *result = curl_easy_init();
-       long allowed_protocols = 0;
 
        if (!result)
                die("curl_easy_init failed");
@@ -662,7 +720,7 @@ static CURL *get_curl_handle(void)
        curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
 #endif
 
-#if LIBCURL_VERSION_NUM >= 0x071600
+#ifdef CURLGSSAPI_DELEGATION_FLAG
        if (curl_deleg) {
                int i;
                for (i = 0; i < ARRAY_SIZE(curl_deleg_levels); i++) {
@@ -735,21 +793,14 @@ static CURL *get_curl_handle(void)
 #elif LIBCURL_VERSION_NUM >= 0x071101
        curl_easy_setopt(result, CURLOPT_POST301, 1);
 #endif
-#if LIBCURL_VERSION_NUM >= 0x071304
-       if (is_transport_allowed("http"))
-               allowed_protocols |= CURLPROTO_HTTP;
-       if (is_transport_allowed("https"))
-               allowed_protocols |= CURLPROTO_HTTPS;
-       if (is_transport_allowed("ftp"))
-               allowed_protocols |= CURLPROTO_FTP;
-       if (is_transport_allowed("ftps"))
-               allowed_protocols |= CURLPROTO_FTPS;
-       curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS, allowed_protocols);
-       curl_easy_setopt(result, CURLOPT_PROTOCOLS, allowed_protocols);
+#ifdef CURLPROTO_HTTP
+       curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS,
+                        get_curl_allowed_protocols(0));
+       curl_easy_setopt(result, CURLOPT_PROTOCOLS,
+                        get_curl_allowed_protocols(-1));
 #else
-       if (transport_restrict_protocols())
-               warning("protocol restrictions not applied to curl redirects because\n"
-                       "your curl version is too old (>= 7.19.4)");
+       warning("protocol restrictions not applied to curl redirects because\n"
+               "your curl version is too old (>= 7.19.4)");
 #endif
        if (getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
@@ -791,8 +842,14 @@ static CURL *get_curl_handle(void)
                }
        }
 
-       if (curl_http_proxy) {
-               curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
+       if (curl_http_proxy && curl_http_proxy[0] == '\0') {
+               /*
+                * Handle case with the empty http.proxy value here to keep
+                * common code clean.
+                * NB: empty option disables proxying at all.
+                */
+               curl_easy_setopt(result, CURLOPT_PROXY, "");
+       } else if (curl_http_proxy) {
 #if LIBCURL_VERSION_NUM >= 0x071800
                if (starts_with(curl_http_proxy, "socks5h"))
                        curl_easy_setopt(result,
@@ -816,6 +873,9 @@ static CURL *get_curl_handle(void)
                        strbuf_release(&url);
                }
 
+               if (!proxy_auth.host)
+                       die("Invalid proxy URL '%s'", curl_http_proxy);
+
                curl_easy_setopt(result, CURLOPT_PROXY, proxy_auth.host);
 #if LIBCURL_VERSION_NUM >= 0x071304
                var_override(&curl_no_proxy, getenv("NO_PROXY"));
@@ -970,8 +1030,7 @@ void http_cleanup(void)
 
        if (proxy_auth.password) {
                memset(proxy_auth.password, 0, strlen(proxy_auth.password));
-               free(proxy_auth.password);
-               proxy_auth.password = NULL;
+               FREE_AND_NULL(proxy_auth.password);
        }
 
        free((void *)curl_proxyuserpwd);
@@ -982,13 +1041,11 @@ void http_cleanup(void)
 
        if (cert_auth.password != NULL) {
                memset(cert_auth.password, 0, strlen(cert_auth.password));
-               free(cert_auth.password);
-               cert_auth.password = NULL;
+               FREE_AND_NULL(cert_auth.password);
        }
        ssl_cert_password_required = 0;
 
-       free(cached_accept_language);
-       cached_accept_language = NULL;
+       FREE_AND_NULL(cached_accept_language);
 }
 
 struct active_request_slot *get_active_slot(void)
@@ -1072,7 +1129,7 @@ struct active_request_slot *get_active_slot(void)
 #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
        curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
 #endif
-       if (http_auth.password || curl_empty_auth)
+       if (http_auth.password || curl_empty_auth_enabled())
                init_curl_http_auth(slot->curl);
 
        return slot;
@@ -1321,9 +1378,9 @@ static int handle_curl_result(struct slot_results *results)
                 * FAILONERROR it is lost, so we can give only the numeric
                 * status code.
                 */
-               snprintf(curl_errorstr, sizeof(curl_errorstr),
-                        "The requested URL returned error: %ld",
-                        results->http_code);
+               xsnprintf(curl_errorstr, sizeof(curl_errorstr),
+                         "The requested URL returned error: %ld",
+                         results->http_code);
        }
 
        if (results->curl_result == CURLE_OK) {
@@ -1340,6 +1397,10 @@ static int handle_curl_result(struct slot_results *results)
                } else {
 #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
                        http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
+                       if (results->auth_avail) {
+                               http_auth_methods &= results->auth_avail;
+                               http_auth_methods_restricted = 1;
+                       }
 #endif
                        return HTTP_REAUTH;
                }
@@ -1361,8 +1422,8 @@ int run_one_slot(struct active_request_slot *slot,
 {
        slot->results = results;
        if (!start_active_slot(slot)) {
-               snprintf(curl_errorstr, sizeof(curl_errorstr),
-                        "failed to start HTTP request");
+               xsnprintf(curl_errorstr, sizeof(curl_errorstr),
+                         "failed to start HTTP request");
                return HTTP_START_FAILED;
        }
 
@@ -1720,6 +1781,9 @@ static int http_request_reauth(const char *url,
 {
        int ret = http_request(url, result, target, options);
 
+       if (ret != HTTP_OK && ret != HTTP_REAUTH)
+               return ret;
+
        if (options && options->effective_url && options->base_url) {
                if (update_url_from_redirect(options->base_url,
                                             url, options->effective_url)) {
@@ -1833,8 +1897,7 @@ static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
 
        if (http_get_file(url, tmp, NULL) != HTTP_OK) {
                error("Unable to get pack index %s", url);
-               free(tmp);
-               tmp = NULL;
+               FREE_AND_NULL(tmp);
        }
 
        free(url);
@@ -2265,8 +2328,7 @@ void release_http_object_request(struct http_object_request *freq)
                freq->localfile = -1;
        }
        if (freq->url != NULL) {
-               free(freq->url);
-               freq->url = NULL;
+               FREE_AND_NULL(freq->url);
        }
        if (freq->slot != NULL) {
                freq->slot->callback_func = NULL;