Merge branch 'ja/i18n-fix'
[gitweb.git] / http.c
diff --git a/http.c b/http.c
index f3d0c0b6cdb4a9f5526d74f45d3f3c3b6a692296..eacc2a75ef2e41da9d5f5741defcf1952e6c01a3 100644 (file)
--- a/http.c
+++ b/http.c
@@ -155,6 +155,16 @@ static struct active_request_slot *active_queue_head;
 
 static char *cached_accept_language;
 
+static char *http_ssl_backend;
+
+static int http_schannel_check_revoke = 1;
+/*
+ * With the backend being set to `schannel`, setting sslCAinfo would override
+ * the Certificate Store in cURL v7.60.0 and later, which is not what we want
+ * by default.
+ */
+static int http_schannel_use_ssl_cainfo;
+
 size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
 {
        size_t size = eltsize * nmemb;
@@ -302,6 +312,22 @@ static int http_options(const char *var, const char *value, void *cb)
                curl_ssl_try = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp("http.sslbackend", var)) {
+               free(http_ssl_backend);
+               http_ssl_backend = xstrdup_or_null(value);
+               return 0;
+       }
+
+       if (!strcmp("http.schannelcheckrevoke", var)) {
+               http_schannel_check_revoke = git_config_bool(var, value);
+               return 0;
+       }
+
+       if (!strcmp("http.schannelusesslcainfo", var)) {
+               http_schannel_use_ssl_cainfo = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp("http.minsessions", var)) {
                min_curl_sessions = git_config_int(var, value);
 #ifndef USE_CURL_MULTI
@@ -803,6 +829,15 @@ static CURL *get_curl_handle(void)
        }
 #endif
 
+       if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
+           !http_schannel_check_revoke) {
+#if LIBCURL_VERSION_NUM >= 0x072c00
+               curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
+#else
+               warning(_("CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0"));
+#endif
+       }
+
        if (http_proactive_auth)
                init_curl_http_auth(result);
 
@@ -844,7 +879,13 @@ static CURL *get_curl_handle(void)
        if (ssl_pinnedkey != NULL)
                curl_easy_setopt(result, CURLOPT_PINNEDPUBLICKEY, ssl_pinnedkey);
 #endif
-       if (ssl_cainfo != NULL)
+       if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&
+           !http_schannel_use_ssl_cainfo) {
+               curl_easy_setopt(result, CURLOPT_CAINFO, NULL);
+#if LIBCURL_VERSION_NUM >= 0x073400
+               curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL);
+#endif
+       } else if (ssl_cainfo != NULL)
                curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
 
        if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
@@ -866,8 +907,7 @@ static CURL *get_curl_handle(void)
        curl_easy_setopt(result, CURLOPT_PROTOCOLS,
                         get_curl_allowed_protocols(-1));
 #else
-       warning("protocol restrictions not applied to curl redirects because\n"
-               "your curl version is too old (>= 7.19.4)");
+       warning(_("Protocol restrictions not supported with cURL < 7.19.4"));
 #endif
        if (getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
@@ -995,6 +1035,33 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
        git_config(urlmatch_config_entry, &config);
        free(normalized_url);
 
+#if LIBCURL_VERSION_NUM >= 0x073800
+       if (http_ssl_backend) {
+               const curl_ssl_backend **backends;
+               struct strbuf buf = STRBUF_INIT;
+               int i;
+
+               switch (curl_global_sslset(-1, http_ssl_backend, &backends)) {
+               case CURLSSLSET_UNKNOWN_BACKEND:
+                       strbuf_addf(&buf, _("Unsupported SSL backend '%s'. "
+                                           "Supported SSL backends:"),
+                                           http_ssl_backend);
+                       for (i = 0; backends[i]; i++)
+                               strbuf_addf(&buf, "\n\t%s", backends[i]->name);
+                       die("%s", buf.buf);
+               case CURLSSLSET_NO_BACKENDS:
+                       die(_("Could not set SSL backend to '%s': "
+                             "cURL was built without SSL backends"),
+                           http_ssl_backend);
+               case CURLSSLSET_TOO_LATE:
+                       die(_("Could not set SSL backend to '%s': already set"),
+                           http_ssl_backend);
+               case CURLSSLSET_OK:
+                       break; /* Okay! */
+               }
+       }
+#endif
+
        if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
                die("curl_global_init failed");
 
@@ -1788,7 +1855,7 @@ static int http_request(const char *url,
 
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
-       curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip");
+       curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
 
        ret = run_one_slot(slot, &results);
 
@@ -2038,7 +2105,8 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
        int ret = 0, i = 0;
        char *url, *data;
        struct strbuf buf = STRBUF_INIT;
-       unsigned char sha1[20];
+       unsigned char hash[GIT_MAX_RAWSZ];
+       const unsigned hexsz = the_hash_algo->hexsz;
 
        end_url_with_slash(&buf, base_url);
        strbuf_addstr(&buf, "objects/info/packs");
@@ -2054,13 +2122,13 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
                switch (data[i]) {
                case 'P':
                        i++;
-                       if (i + 52 <= buf.len &&
+                       if (i + hexsz + 12 <= buf.len &&
                            starts_with(data + i, " pack-") &&
-                           starts_with(data + i + 46, ".pack\n")) {
-                               get_sha1_hex(data + i + 6, sha1);
-                               fetch_and_setup_pack_index(packs_head, sha1,
+                           starts_with(data + i + hexsz + 6, ".pack\n")) {
+                               get_sha1_hex(data + i + 6, hash);
+                               fetch_and_setup_pack_index(packs_head, hash,
                                                      base_url);
-                               i += 51;
+                               i += hexsz + 11;
                                break;
                        }
                default:
@@ -2082,6 +2150,7 @@ void release_http_pack_request(struct http_pack_request *preq)
                preq->packfile = NULL;
        }
        preq->slot = NULL;
+       strbuf_release(&preq->tmpfile);
        free(preq->url);
        free(preq);
 }
@@ -2104,19 +2173,19 @@ int finish_http_pack_request(struct http_pack_request *preq)
                lst = &((*lst)->next);
        *lst = (*lst)->next;
 
-       if (!strip_suffix(preq->tmpfile, ".pack.temp", &len))
+       if (!strip_suffix(preq->tmpfile.buf, ".pack.temp", &len))
                BUG("pack tmpfile does not end in .pack.temp?");
-       tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile);
+       tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile.buf);
 
        argv_array_push(&ip.args, "index-pack");
        argv_array_pushl(&ip.args, "-o", tmp_idx, NULL);
-       argv_array_push(&ip.args, preq->tmpfile);
+       argv_array_push(&ip.args, preq->tmpfile.buf);
        ip.git_cmd = 1;
        ip.no_stdin = 1;
        ip.no_stdout = 1;
 
        if (run_command(&ip)) {
-               unlink(preq->tmpfile);
+               unlink(preq->tmpfile.buf);
                unlink(tmp_idx);
                free(tmp_idx);
                return -1;
@@ -2124,7 +2193,7 @@ int finish_http_pack_request(struct http_pack_request *preq)
 
        unlink(sha1_pack_index_name(p->sha1));
 
-       if (finalize_object_file(preq->tmpfile, sha1_pack_name(p->sha1))
+       if (finalize_object_file(preq->tmpfile.buf, sha1_pack_name(p->sha1))
         || finalize_object_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
                free(tmp_idx);
                return -1;
@@ -2143,6 +2212,7 @@ struct http_pack_request *new_http_pack_request(
        struct http_pack_request *preq;
 
        preq = xcalloc(1, sizeof(*preq));
+       strbuf_init(&preq->tmpfile, 0);
        preq->target = target;
 
        end_url_with_slash(&buf, base_url);
@@ -2150,12 +2220,11 @@ struct http_pack_request *new_http_pack_request(
                sha1_to_hex(target->sha1));
        preq->url = strbuf_detach(&buf, NULL);
 
-       snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp",
-               sha1_pack_name(target->sha1));
-       preq->packfile = fopen(preq->tmpfile, "a");
+       strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(target->sha1));
+       preq->packfile = fopen(preq->tmpfile.buf, "a");
        if (!preq->packfile) {
                error("Unable to open local file %s for pack",
-                     preq->tmpfile);
+                     preq->tmpfile.buf);
                goto abort;
        }
 
@@ -2182,6 +2251,7 @@ struct http_pack_request *new_http_pack_request(
        return preq;
 
 abort:
+       strbuf_release(&preq->tmpfile);
        free(preq->url);
        free(preq);
        return NULL;
@@ -2232,7 +2302,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
 {
        char *hex = sha1_to_hex(sha1);
        struct strbuf filename = STRBUF_INIT;
-       char prevfile[PATH_MAX];
+       struct strbuf prevfile = STRBUF_INIT;
        int prevlocal;
        char prev_buf[PREV_BUF_SIZE];
        ssize_t prev_read = 0;
@@ -2240,40 +2310,41 @@ struct http_object_request *new_http_object_request(const char *base_url,
        struct http_object_request *freq;
 
        freq = xcalloc(1, sizeof(*freq));
+       strbuf_init(&freq->tmpfile, 0);
        hashcpy(freq->sha1, sha1);
        freq->localfile = -1;
 
        sha1_file_name(the_repository, &filename, sha1);
-       snprintf(freq->tmpfile, sizeof(freq->tmpfile),
-                "%s.temp", filename.buf);
+       strbuf_addf(&freq->tmpfile, "%s.temp", filename.buf);
 
-       snprintf(prevfile, sizeof(prevfile), "%s.prev", filename.buf);
-       unlink_or_warn(prevfile);
-       rename(freq->tmpfile, prevfile);
-       unlink_or_warn(freq->tmpfile);
+       strbuf_addf(&prevfile, "%s.prev", filename.buf);
+       unlink_or_warn(prevfile.buf);
+       rename(freq->tmpfile.buf, prevfile.buf);
+       unlink_or_warn(freq->tmpfile.buf);
        strbuf_release(&filename);
 
        if (freq->localfile != -1)
                error("fd leakage in start: %d", freq->localfile);
-       freq->localfile = open(freq->tmpfile,
+       freq->localfile = open(freq->tmpfile.buf,
                               O_WRONLY | O_CREAT | O_EXCL, 0666);
        /*
         * This could have failed due to the "lazy directory creation";
         * try to mkdir the last path component.
         */
        if (freq->localfile < 0 && errno == ENOENT) {
-               char *dir = strrchr(freq->tmpfile, '/');
+               char *dir = strrchr(freq->tmpfile.buf, '/');
                if (dir) {
                        *dir = 0;
-                       mkdir(freq->tmpfile, 0777);
+                       mkdir(freq->tmpfile.buf, 0777);
                        *dir = '/';
                }
-               freq->localfile = open(freq->tmpfile,
+               freq->localfile = open(freq->tmpfile.buf,
                                       O_WRONLY | O_CREAT | O_EXCL, 0666);
        }
 
        if (freq->localfile < 0) {
-               error_errno("Couldn't create temporary file %s", freq->tmpfile);
+               error_errno("Couldn't create temporary file %s",
+                           freq->tmpfile.buf);
                goto abort;
        }
 
@@ -2287,7 +2358,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
         * If a previous temp file is present, process what was already
         * fetched.
         */
-       prevlocal = open(prevfile, O_RDONLY);
+       prevlocal = open(prevfile.buf, O_RDONLY);
        if (prevlocal != -1) {
                do {
                        prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
@@ -2304,7 +2375,8 @@ struct http_object_request *new_http_object_request(const char *base_url,
                } while (prev_read > 0);
                close(prevlocal);
        }
-       unlink_or_warn(prevfile);
+       unlink_or_warn(prevfile.buf);
+       strbuf_release(&prevfile);
 
        /*
         * Reset inflate/SHA1 if there was an error reading the previous temp
@@ -2319,7 +2391,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
                        lseek(freq->localfile, 0, SEEK_SET);
                        if (ftruncate(freq->localfile, 0) < 0) {
                                error_errno("Couldn't truncate temporary file %s",
-                                           freq->tmpfile);
+                                           freq->tmpfile.buf);
                                goto abort;
                        }
                }
@@ -2349,6 +2421,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
        return freq;
 
 abort:
+       strbuf_release(&prevfile);
        free(freq->url);
        free(freq);
        return NULL;
@@ -2376,24 +2449,24 @@ int finish_http_object_request(struct http_object_request *freq)
        if (freq->http_code == 416) {
                warning("requested range invalid; we may already have all the data.");
        } else if (freq->curl_result != CURLE_OK) {
-               if (stat(freq->tmpfile, &st) == 0)
+               if (stat(freq->tmpfile.buf, &st) == 0)
                        if (st.st_size == 0)
-                               unlink_or_warn(freq->tmpfile);
+                               unlink_or_warn(freq->tmpfile.buf);
                return -1;
        }
 
        git_inflate_end(&freq->stream);
        git_SHA1_Final(freq->real_sha1, &freq->c);
        if (freq->zret != Z_STREAM_END) {
-               unlink_or_warn(freq->tmpfile);
+               unlink_or_warn(freq->tmpfile.buf);
                return -1;
        }
-       if (hashcmp(freq->sha1, freq->real_sha1)) {
-               unlink_or_warn(freq->tmpfile);
+       if (!hasheq(freq->sha1, freq->real_sha1)) {
+               unlink_or_warn(freq->tmpfile.buf);
                return -1;
        }
        sha1_file_name(the_repository, &filename, freq->sha1);
-       freq->rename = finalize_object_file(freq->tmpfile, filename.buf);
+       freq->rename = finalize_object_file(freq->tmpfile.buf, filename.buf);
        strbuf_release(&filename);
 
        return freq->rename;
@@ -2401,7 +2474,7 @@ int finish_http_object_request(struct http_object_request *freq)
 
 void abort_http_object_request(struct http_object_request *freq)
 {
-       unlink_or_warn(freq->tmpfile);
+       unlink_or_warn(freq->tmpfile.buf);
 
        release_http_object_request(freq);
 }
@@ -2412,13 +2485,12 @@ void release_http_object_request(struct http_object_request *freq)
                close(freq->localfile);
                freq->localfile = -1;
        }
-       if (freq->url != NULL) {
-               FREE_AND_NULL(freq->url);
-       }
+       FREE_AND_NULL(freq->url);
        if (freq->slot != NULL) {
                freq->slot->callback_func = NULL;
                freq->slot->callback_data = NULL;
                release_active_slot(freq->slot);
                freq->slot = NULL;
        }
+       strbuf_release(&freq->tmpfile);
 }