branch: add a --copy (-c) option to go with --move (-m)
[gitweb.git] / http.c
diff --git a/http.c b/http.c
index 90a1c0f1131c4a5fcbc50d6cc81c1be9cef3cd71..d2e11ec6f012d3b2a9570f0c322aebf53a13cc0a 100644 (file)
--- a/http.c
+++ b/http.c
@@ -19,7 +19,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
@@ -109,7 +109,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 +125,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;
@@ -323,7 +331,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,7 +343,10 @@ 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;
        }
 
@@ -382,10 +395,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;
        }
@@ -798,8 +838,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,
@@ -823,6 +869,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"));
@@ -1079,7 +1128,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;
@@ -1328,9 +1377,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) {
@@ -1347,6 +1396,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;
                }
@@ -1368,8 +1421,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;
        }
 
@@ -1727,6 +1780,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)) {