]> git.ipfire.org Git - thirdparty/git.git/blobdiff - http.c
reftable: clarify how empty tables should be written
[thirdparty/git.git] / http.c
diff --git a/http.c b/http.c
index 2b8136275af6d1dea4d5d09b5861c6b4048989e1..62aa995245324dc30fec699ed8c570af7b152b8f 100644 (file)
--- a/http.c
+++ b/http.c
@@ -86,6 +86,13 @@ static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
 static const char *http_proxy_authmethod;
+
+static const char *http_proxy_ssl_cert;
+static const char *http_proxy_ssl_key;
+static const char *http_proxy_ssl_ca_info;
+static struct credential proxy_cert_auth = CREDENTIAL_INIT;
+static int proxy_ssl_cert_password_required;
+
 static struct {
        const char *name;
        long curlauth_param;
@@ -150,7 +157,7 @@ static unsigned long empty_auth_useless =
 
 static struct curl_slist *pragma_header;
 static struct curl_slist *no_pragma_header;
-static struct curl_slist *extra_http_headers;
+static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
 
 static struct active_request_slot *active_queue_head;
 
@@ -176,7 +183,7 @@ size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
        memcpy(ptr, buffer->buf.buf + buffer->posn, size);
        buffer->posn += size;
 
-       return size;
+       return size / eltsize;
 }
 
 #ifndef NO_CURL_IOCTL
@@ -204,12 +211,12 @@ size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
        struct strbuf *buffer = buffer_;
 
        strbuf_add(buffer, ptr, size);
-       return size;
+       return nmemb;
 }
 
 size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
 {
-       return eltsize * nmemb;
+       return nmemb;
 }
 
 static void closedown_active_slot(struct active_request_slot *slot)
@@ -365,6 +372,20 @@ static int http_options(const char *var, const char *value, void *cb)
        if (!strcmp("http.proxyauthmethod", var))
                return git_config_string(&http_proxy_authmethod, var, value);
 
+       if (!strcmp("http.proxysslcert", var))
+               return git_config_string(&http_proxy_ssl_cert, var, value);
+
+       if (!strcmp("http.proxysslkey", var))
+               return git_config_string(&http_proxy_ssl_key, var, value);
+
+       if (!strcmp("http.proxysslcainfo", var))
+               return git_config_string(&http_proxy_ssl_ca_info, var, value);
+
+       if (!strcmp("http.proxysslcertpasswordprotected", var)) {
+               proxy_ssl_cert_password_required = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp("http.cookiefile", var))
                return git_config_pathname(&curl_cookie_file, var, value);
        if (!strcmp("http.savecookies", var)) {
@@ -414,11 +435,9 @@ static int http_options(const char *var, const char *value, void *cb)
                if (!value) {
                        return config_error_nonbool(var);
                } else if (!*value) {
-                       curl_slist_free_all(extra_http_headers);
-                       extra_http_headers = NULL;
+                       string_list_clear(&extra_http_headers, 0);
                } else {
-                       extra_http_headers =
-                               curl_slist_append(extra_http_headers, value);
+                       string_list_append(&extra_http_headers, value);
                }
                return 0;
        }
@@ -513,9 +532,11 @@ static void set_proxyauth_name_password(CURL *result)
 #else
                struct strbuf s = STRBUF_INIT;
 
-               strbuf_addstr_urlencode(&s, proxy_auth.username, 1);
+               strbuf_addstr_urlencode(&s, proxy_auth.username,
+                                       is_rfc3986_unreserved);
                strbuf_addch(&s, ':');
-               strbuf_addstr_urlencode(&s, proxy_auth.password, 1);
+               strbuf_addstr_urlencode(&s, proxy_auth.password,
+                                       is_rfc3986_unreserved);
                curl_proxyuserpwd = strbuf_detach(&s, NULL);
                curl_easy_setopt(result, CURLOPT_PROXYUSERPWD, curl_proxyuserpwd);
 #endif
@@ -566,6 +587,22 @@ static int has_cert_password(void)
        return 1;
 }
 
+#if LIBCURL_VERSION_NUM >= 0x073400
+static int has_proxy_cert_password(void)
+{
+       if (http_proxy_ssl_cert == NULL || proxy_ssl_cert_password_required != 1)
+               return 0;
+       if (!proxy_cert_auth.password) {
+               proxy_cert_auth.protocol = xstrdup("cert");
+               proxy_cert_auth.host = xstrdup("");
+               proxy_cert_auth.username = xstrdup("");
+               proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
+               credential_fill(&proxy_cert_auth);
+       }
+       return 1;
+}
+#endif
+
 #if LIBCURL_VERSION_NUM >= 0x071900
 static void set_curl_keepalive(CURL *c)
 {
@@ -681,8 +718,8 @@ static void curl_dump_header(const char *text, unsigned char *ptr, size_t size,
        for (header = headers; *header; header++) {
                if (hide_sensitive_header)
                        redact_sensitive_header(*header);
-               strbuf_insert((*header), 0, text, strlen(text));
-               strbuf_insert((*header), strlen(text), ": ", 2);
+               strbuf_insertstr((*header), 0, text);
+               strbuf_insertstr((*header), strlen(text), ": ");
                strbuf_rtrim((*header));
                strbuf_addch((*header), '\n');
                trace_strbuf(&trace_curl, (*header));
@@ -925,8 +962,14 @@ static CURL *get_curl_handle(void)
 #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);
+       } else if (ssl_cainfo != NULL || http_proxy_ssl_ca_info != NULL) {
+               if (ssl_cainfo != NULL)
+                       curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
+#if LIBCURL_VERSION_NUM >= 0x073400
+               if (http_proxy_ssl_ca_info != NULL)
+                       curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, http_proxy_ssl_ca_info);
+#endif
+       }
 
        if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
                curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
@@ -1019,9 +1062,18 @@ static CURL *get_curl_handle(void)
                                CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
 #endif
 #if LIBCURL_VERSION_NUM >= 0x073400
-               else if (starts_with(curl_http_proxy, "https"))
-                       curl_easy_setopt(result,
-                               CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
+               else if (starts_with(curl_http_proxy, "https")) {
+                       curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
+
+                       if (http_proxy_ssl_cert)
+                               curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert);
+
+                       if (http_proxy_ssl_key)
+                               curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key);
+
+                       if (has_proxy_cert_password())
+                               curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password);
+               }
 #endif
                if (strstr(curl_http_proxy, "://"))
                        credential_from_url(&proxy_auth, curl_http_proxy);
@@ -1074,6 +1126,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
 
        git_config(urlmatch_config_entry, &config);
        free(normalized_url);
+       string_list_clear(&config.vars, 1);
 
 #if LIBCURL_VERSION_NUM >= 0x073800
        if (http_ssl_backend) {
@@ -1160,6 +1213,13 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
                max_requests = DEFAULT_MAX_REQUESTS;
 #endif
 
+       set_from_env(&http_proxy_ssl_cert, "GIT_PROXY_SSL_CERT");
+       set_from_env(&http_proxy_ssl_key, "GIT_PROXY_SSL_KEY");
+       set_from_env(&http_proxy_ssl_ca_info, "GIT_PROXY_SSL_CAINFO");
+
+       if (getenv("GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED"))
+               proxy_ssl_cert_password_required = 1;
+
        if (getenv("GIT_CURL_FTP_NO_EPSV"))
                curl_ftp_no_epsv = 1;
 
@@ -1200,8 +1260,7 @@ void http_cleanup(void)
 #endif
        curl_global_cleanup();
 
-       curl_slist_free_all(extra_http_headers);
-       extra_http_headers = NULL;
+       string_list_clear(&extra_http_headers, 0);
 
        curl_slist_free_all(pragma_header);
        pragma_header = NULL;
@@ -1231,6 +1290,12 @@ void http_cleanup(void)
        }
        ssl_cert_password_required = 0;
 
+       if (proxy_cert_auth.password != NULL) {
+               memset(proxy_cert_auth.password, 0, strlen(proxy_cert_auth.password));
+               FREE_AND_NULL(proxy_cert_auth.password);
+       }
+       proxy_ssl_cert_password_required = 0;
+
        FREE_AND_NULL(cached_accept_language);
 }
 
@@ -1545,7 +1610,8 @@ char *get_remote_object_url(const char *url, const char *hex,
        return strbuf_detach(&buf, NULL);
 }
 
-static int handle_curl_result(struct slot_results *results)
+void normalize_curl_result(CURLcode *result, long http_code,
+                          char *errorstr, size_t errorlen)
 {
        /*
         * If we see a failing http code with CURLE_OK, we have turned off
@@ -1555,19 +1621,24 @@ static int handle_curl_result(struct slot_results *results)
         * 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 >= 300) {
-               results->curl_result = CURLE_HTTP_RETURNED_ERROR;
+       if (*result == CURLE_OK && http_code >= 300) {
+               *result = CURLE_HTTP_RETURNED_ERROR;
                /*
                 * Normally curl will already have put the "reason phrase"
                 * from the server into curl_errorstr; unfortunately without
                 * FAILONERROR it is lost, so we can give only the numeric
                 * status code.
                 */
-               xsnprintf(curl_errorstr, sizeof(curl_errorstr),
+               xsnprintf(errorstr, errorlen,
                          "The requested URL returned error: %ld",
-                         results->http_code);
+                         http_code);
        }
+}
+
+static int handle_curl_result(struct slot_results *results)
+{
+       normalize_curl_result(&results->curl_result, results->http_code,
+                             curl_errorstr, sizeof(curl_errorstr));
 
        if (results->curl_result == CURLE_OK) {
                credential_approve(&http_auth);
@@ -1619,10 +1690,11 @@ int run_one_slot(struct active_request_slot *slot,
 
 struct curl_slist *http_copy_default_headers(void)
 {
-       struct curl_slist *headers = NULL, *h;
+       struct curl_slist *headers = NULL;
+       const struct string_list_item *item;
 
-       for (h = extra_http_headers; h; h = h->next)
-               headers = curl_slist_append(headers, h->data);
+       for_each_string_list_item(item, &extra_http_headers)
+               headers = curl_slist_append(headers, item->string);
 
        return headers;
 }
@@ -2066,7 +2138,7 @@ int http_fetch_ref(const char *base, struct ref *ref)
        url = quote_ref_url(base, ref->name);
        if (http_get_strbuf(url, &buffer, &options) == HTTP_OK) {
                strbuf_rtrim(&buffer);
-               if (buffer.len == 40)
+               if (buffer.len == the_hash_algo->hexsz)
                        ret = get_oid_hex(buffer.buf, &ref->old_oid);
                else if (starts_with(buffer.buf, "ref: ")) {
                        ref->symref = xstrdup(buffer.buf + 5);
@@ -2080,19 +2152,19 @@ int http_fetch_ref(const char *base, struct ref *ref)
 }
 
 /* Helpers for fetching packs */
-static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
+static char *fetch_pack_index(unsigned char *hash, const char *base_url)
 {
        char *url, *tmp;
        struct strbuf buf = STRBUF_INIT;
 
        if (http_is_verbose)
-               fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1));
+               fprintf(stderr, "Getting index for pack %s\n", hash_to_hex(hash));
 
        end_url_with_slash(&buf, base_url);
-       strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1));
+       strbuf_addf(&buf, "objects/pack/pack-%s.idx", hash_to_hex(hash));
        url = strbuf_detach(&buf, NULL);
 
-       strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
+       strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(hash));
        tmp = strbuf_detach(&buf, NULL);
 
        if (http_get_file(url, tmp, NULL) != HTTP_OK) {
@@ -2148,11 +2220,11 @@ add_pack:
 int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
 {
        struct http_get_options options = {0};
-       int ret = 0, i = 0;
-       char *url, *data;
+       int ret = 0;
+       char *url;
+       const char *data;
        struct strbuf buf = STRBUF_INIT;
-       unsigned char hash[GIT_MAX_RAWSZ];
-       const unsigned hexsz = the_hash_algo->hexsz;
+       struct object_id oid;
 
        end_url_with_slash(&buf, base_url);
        strbuf_addstr(&buf, "objects/info/packs");
@@ -2164,24 +2236,17 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
                goto cleanup;
 
        data = buf.buf;
-       while (i < buf.len) {
-               switch (data[i]) {
-               case 'P':
-                       i++;
-                       if (i + hexsz + 12 <= buf.len &&
-                           starts_with(data + i, " pack-") &&
-                           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 += hexsz + 11;
-                               break;
-                       }
-               default:
-                       while (i < buf.len && data[i] != '\n')
-                               i++;
+       while (*data) {
+               if (skip_prefix(data, "P pack-", &data) &&
+                   !parse_oid_hex(data, &oid, &data) &&
+                   skip_prefix(data, ".pack", &data) &&
+                   (*data == '\n' || *data == '\0')) {
+                       fetch_and_setup_pack_index(packs_head, oid.hash, base_url);
+               } else {
+                       data = strchrnul(data, '\n');
                }
-               i++;
+               if (*data)
+                       data++; /* skip past newline */
        }
 
 cleanup:
@@ -2237,10 +2302,10 @@ int finish_http_pack_request(struct http_pack_request *preq)
                return -1;
        }
 
-       unlink(sha1_pack_index_name(p->sha1));
+       unlink(sha1_pack_index_name(p->hash));
 
-       if (finalize_object_file(preq->tmpfile.buf, sha1_pack_name(p->sha1))
-        || finalize_object_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
+       if (finalize_object_file(preq->tmpfile.buf, sha1_pack_name(p->hash))
+        || finalize_object_file(tmp_idx, sha1_pack_index_name(p->hash))) {
                free(tmp_idx);
                return -1;
        }
@@ -2263,10 +2328,10 @@ struct http_pack_request *new_http_pack_request(
 
        end_url_with_slash(&buf, base_url);
        strbuf_addf(&buf, "objects/pack/pack-%s.pack",
-               sha1_to_hex(target->sha1));
+               hash_to_hex(target->hash));
        preq->url = strbuf_detach(&buf, NULL);
 
-       strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(target->sha1));
+       strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(target->hash));
        preq->packfile = fopen(preq->tmpfile.buf, "a");
        if (!preq->packfile) {
                error("Unable to open local file %s for pack",
@@ -2290,7 +2355,8 @@ struct http_pack_request *new_http_pack_request(
                if (http_is_verbose)
                        fprintf(stderr,
                                "Resuming fetch of pack %s at byte %"PRIuMAX"\n",
-                               sha1_to_hex(target->sha1), (uintmax_t)prev_posn);
+                               hash_to_hex(target->hash),
+                               (uintmax_t)prev_posn);
                http_opt_request_remainder(preq->slot->curl, prev_posn);
        }
 
@@ -2320,14 +2386,14 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
                        BUG("curl_easy_getinfo for HTTP code failed: %s",
                                curl_easy_strerror(c));
                if (slot->http_code >= 300)
-                       return size;
+                       return nmemb;
        }
 
        do {
                ssize_t retval = xwrite(freq->localfile,
                                        (char *) ptr + posn, size - posn);
                if (retval < 0)
-                       return posn;
+                       return posn / eltsize;
                posn += retval;
        } while (posn < size);
 
@@ -2337,10 +2403,10 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
                freq->stream.next_out = expn;
                freq->stream.avail_out = sizeof(expn);
                freq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);
-               git_SHA1_Update(&freq->c, expn,
-                               sizeof(expn) - freq->stream.avail_out);
+               the_hash_algo->update_fn(&freq->c, expn,
+                                        sizeof(expn) - freq->stream.avail_out);
        } while (freq->stream.avail_in && freq->zret == Z_OK);
-       return size;
+       return nmemb;
 }
 
 struct http_object_request *new_http_object_request(const char *base_url,
@@ -2396,7 +2462,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
 
        git_inflate_init(&freq->stream);
 
-       git_SHA1_Init(&freq->c);
+       the_hash_algo->init_fn(&freq->c);
 
        freq->url = get_remote_object_url(base_url, hex, 0);
 
@@ -2431,7 +2497,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
        if (prev_read == -1) {
                memset(&freq->stream, 0, sizeof(freq->stream));
                git_inflate_init(&freq->stream);
-               git_SHA1_Init(&freq->c);
+               the_hash_algo->init_fn(&freq->c);
                if (prev_posn>0) {
                        prev_posn = 0;
                        lseek(freq->localfile, 0, SEEK_SET);
@@ -2502,7 +2568,7 @@ int finish_http_object_request(struct http_object_request *freq)
        }
 
        git_inflate_end(&freq->stream);
-       git_SHA1_Final(freq->real_oid.hash, &freq->c);
+       the_hash_algo->final_fn(freq->real_oid.hash, &freq->c);
        if (freq->zret != Z_STREAM_END) {
                unlink_or_warn(freq->tmpfile.buf);
                return -1;