documentation: retire unfinished documentation
[gitweb.git] / http.c
diff --git a/http.c b/http.c
index cd40b012f89397db67e851101ab38c2c57fd18c9..051fe6e5ab77a5dc6e53dce7011d0fc445f15ab0 100644 (file)
--- a/http.c
+++ b/http.c
@@ -90,6 +90,18 @@ static struct {
         * here, too
         */
 };
+#if LIBCURL_VERSION_NUM >= 0x071600
+static const char *curl_deleg;
+static struct {
+       const char *name;
+       long curl_deleg_param;
+} curl_deleg_levels[] = {
+       { "none", CURLGSSAPI_DELEGATION_NONE },
+       { "policy", CURLGSSAPI_DELEGATION_POLICY_FLAG },
+       { "always", CURLGSSAPI_DELEGATION_FLAG },
+};
+#endif
+
 static struct credential proxy_auth = CREDENTIAL_INIT;
 static const char *curl_proxyuserpwd;
 static const char *curl_cookie_file;
@@ -99,6 +111,8 @@ static int http_proactive_auth;
 static const char *user_agent;
 static int curl_empty_auth;
 
+enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;
+
 #if LIBCURL_VERSION_NUM >= 0x071700
 /* Use CURLOPT_KEYPASSWD as is */
 #elif LIBCURL_VERSION_NUM >= 0x070903
@@ -201,6 +215,13 @@ static void finish_active_slot(struct active_request_slot *slot)
                slot->callback_func(slot->callback_data);
 }
 
+static void xmulti_remove_handle(struct active_request_slot *slot)
+{
+#ifdef USE_CURL_MULTI
+       curl_multi_remove_handle(curlm, slot->curl);
+#endif
+}
+
 #ifdef USE_CURL_MULTI
 static void process_curl_messages(void)
 {
@@ -216,7 +237,7 @@ static void process_curl_messages(void)
                               slot->curl != curl_message->easy_handle)
                                slot = slot->next;
                        if (slot != NULL) {
-                               curl_multi_remove_handle(curlm, slot->curl);
+                               xmulti_remove_handle(slot);
                                slot->curl_result = curl_result;
                                finish_active_slot(slot);
                        } else {
@@ -316,6 +337,15 @@ static int http_options(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp("http.delegation", var)) {
+#if LIBCURL_VERSION_NUM >= 0x071600
+               return git_config_string(&curl_deleg, var, value);
+#else
+               warning(_("Delegation control is not supported with cURL < 7.22.0"));
+               return 0;
+#endif
+       }
+
        if (!strcmp("http.pinnedpubkey", var)) {
 #if LIBCURL_VERSION_NUM >= 0x072c00
                return git_config_pathname(&ssl_pinnedkey, var, value);
@@ -338,13 +368,23 @@ static int http_options(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp("http.followredirects", var)) {
+               if (value && !strcmp(value, "initial"))
+                       http_follow_config = HTTP_FOLLOW_INITIAL;
+               else if (git_config_bool(var, value))
+                       http_follow_config = HTTP_FOLLOW_ALWAYS;
+               else
+                       http_follow_config = HTTP_FOLLOW_NONE;
+               return 0;
+       }
+
        /* Fall back on the default ones */
        return git_default_config(var, value, cb);
 }
 
 static void init_curl_http_auth(CURL *result)
 {
-       if (!http_auth.username) {
+       if (!http_auth.username || !*http_auth.username) {
                if (curl_empty_auth)
                        curl_easy_setopt(result, CURLOPT_USERPWD, ":");
                return;
@@ -622,6 +662,22 @@ static CURL *get_curl_handle(void)
        curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
 #endif
 
+#if LIBCURL_VERSION_NUM >= 0x071600
+       if (curl_deleg) {
+               int i;
+               for (i = 0; i < ARRAY_SIZE(curl_deleg_levels); i++) {
+                       if (!strcmp(curl_deleg, curl_deleg_levels[i].name)) {
+                               curl_easy_setopt(result, CURLOPT_GSSAPI_DELEGATION,
+                                               curl_deleg_levels[i].curl_deleg_param);
+                               break;
+                       }
+               }
+               if (i == ARRAY_SIZE(curl_deleg_levels))
+                       warning("Unknown delegation method '%s': using default",
+                               curl_deleg);
+       }
+#endif
+
        if (http_proactive_auth)
                init_curl_http_auth(result);
 
@@ -673,7 +729,6 @@ static CURL *get_curl_handle(void)
                                 curl_low_speed_time);
        }
 
-       curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
        curl_easy_setopt(result, CURLOPT_MAXREDIRS, 20);
 #if LIBCURL_VERSION_NUM >= 0x071301
        curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
@@ -690,6 +745,7 @@ static CURL *get_curl_handle(void)
        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);
 #else
        if (transport_restrict_protocols())
                warning("protocol restrictions not applied to curl redirects because\n"
@@ -723,7 +779,7 @@ static CURL *get_curl_handle(void)
         * precedence here, as in CURL.
         */
        if (!curl_http_proxy) {
-               if (!strcmp(http_auth.protocol, "https")) {
+               if (http_auth.protocol && !strcmp(http_auth.protocol, "https")) {
                        var_override(&curl_http_proxy, getenv("HTTPS_PROXY"));
                        var_override(&curl_http_proxy, getenv("https_proxy"));
                } else {
@@ -881,9 +937,7 @@ void http_cleanup(void)
        while (slot != NULL) {
                struct active_request_slot *next = slot->next;
                if (slot->curl != NULL) {
-#ifdef USE_CURL_MULTI
-                       curl_multi_remove_handle(curlm, slot->curl);
-#endif
+                       xmulti_remove_handle(slot);
                        curl_easy_cleanup(slot->curl);
                }
                free(slot);
@@ -1002,6 +1056,16 @@ struct active_request_slot *get_active_slot(void)
        curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
        curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL);
 
+       /*
+        * Default following to off unless "ALWAYS" is configured; this gives
+        * callers a sane starting point, and they can tweak for individual
+        * HTTP_FOLLOW_* cases themselves.
+        */
+       if (http_follow_config == HTTP_FOLLOW_ALWAYS)
+               curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);
+       else
+               curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 0);
+
 #if LIBCURL_VERSION_NUM >= 0x070a08
        curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
 #endif
@@ -1022,6 +1086,8 @@ int start_active_slot(struct active_request_slot *slot)
 
        if (curlm_result != CURLM_OK &&
            curlm_result != CURLM_CALL_MULTI_PERFORM) {
+               warning("curl_multi_add_handle failed: %s",
+                       curl_multi_strerror(curlm_result));
                active_requests--;
                slot->in_use = 0;
                return 0;
@@ -1161,13 +1227,13 @@ void run_active_slot(struct active_request_slot *slot)
 static void release_active_slot(struct active_request_slot *slot)
 {
        closedown_active_slot(slot);
-       if (slot->curl && curl_session_count > min_curl_sessions) {
-#ifdef USE_CURL_MULTI
-               curl_multi_remove_handle(curlm, slot->curl);
-#endif
-               curl_easy_cleanup(slot->curl);
-               slot->curl = NULL;
-               curl_session_count--;
+       if (slot->curl) {
+               xmulti_remove_handle(slot);
+               if (curl_session_count > min_curl_sessions) {
+                       curl_easy_cleanup(slot->curl);
+                       slot->curl = NULL;
+                       curl_session_count--;
+               }
        }
 #ifdef USE_CURL_MULTI
        fill_active_slots();
@@ -1242,9 +1308,12 @@ static int handle_curl_result(struct slot_results *results)
         * If we see a failing http code with CURLE_OK, we have turned off
         * FAILONERROR (to keep the server's custom error response), and should
         * translate the code into failure here.
+        *
+        * Likewise, if we see a redirect (30x code), that means we turned off
+        * redirect-following, and we should treat the result as an error.
         */
        if (results->curl_result == CURLE_OK &&
-           results->http_code >= 400) {
+           results->http_code >= 300) {
                results->curl_result = CURLE_HTTP_RETURNED_ERROR;
                /*
                 * Normally curl will already have put the "reason phrase"
@@ -1563,6 +1632,9 @@ static int http_request(const char *url,
                strbuf_addstr(&buf, " no-cache");
        if (options && options->keep_error)
                curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
+       if (options && options->initial_request &&
+           http_follow_config == HTTP_FOLLOW_INITIAL)
+               curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);
 
        headers = curl_slist_append(headers, buf.buf);
 
@@ -1611,16 +1683,16 @@ static int http_request(const char *url,
  *
  * Note that this assumes a sane redirect scheme. It's entirely possible
  * in the example above to end up at a URL that does not even end in
- * "info/refs".  In such a case we simply punt, as there is not much we can
- * do (and such a scheme is unlikely to represent a real git repository,
- * which means we are likely about to abort anyway).
+ * "info/refs".  In such a case we die. There's not much we can do, such a
+ * scheme is unlikely to represent a real git repository, and failing to
+ * rewrite the base opens options for malicious redirects to do funny things.
  */
 static int update_url_from_redirect(struct strbuf *base,
                                    const char *asked,
                                    const struct strbuf *got)
 {
        const char *tail;
-       size_t tail_len;
+       size_t new_len;
 
        if (!strcmp(asked, got->buf))
                return 0;
@@ -1629,14 +1701,16 @@ static int update_url_from_redirect(struct strbuf *base,
                die("BUG: update_url_from_redirect: %s is not a superset of %s",
                    asked, base->buf);
 
-       tail_len = strlen(tail);
-
-       if (got->len < tail_len ||
-           strcmp(tail, got->buf + got->len - tail_len))
-               return 0; /* insane redirect scheme */
+       new_len = got->len;
+       if (!strip_suffix_mem(got->buf, &new_len, tail))
+               die(_("unable to update url base from redirection:\n"
+                     "  asked for: %s\n"
+                     "   redirect: %s"),
+                   asked, got->buf);
 
        strbuf_reset(base);
-       strbuf_add(base, got->buf, got->len - tail_len);
+       strbuf_add(base, got->buf, new_len);
+
        return 1;
 }
 
@@ -1984,7 +2058,7 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
                if (c != CURLE_OK)
                        die("BUG: curl_easy_getinfo for HTTP code failed: %s",
                                curl_easy_strerror(c));
-               if (slot->http_code >= 400)
+               if (slot->http_code >= 300)
                        return size;
        }