]> git.ipfire.org Git - thirdparty/git.git/blobdiff - remote-curl.c
environment.h: move declarations for environment.c functions from cache.h
[thirdparty/git.git] / remote-curl.c
index 9d432c299a242e4f0b337756bc6aedfcc5be45b8..eb382a1e35d4ec3583b39fde33291bad5dfc721d 100644 (file)
@@ -1,5 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "alloc.h"
 #include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
 #include "remote.h"
 #include "connect.h"
 #include "strbuf.h"
@@ -43,6 +47,7 @@ struct options {
                /* see documentation of corresponding flag in fetch-pack.h */
                from_promisor : 1,
 
+               refetch : 1,
                atomic : 1,
                object_format : 1,
                force_if_includes : 1;
@@ -185,8 +190,6 @@ static int set_option(const char *name, const char *value)
                                                 strbuf_detach(&unquoted, NULL));
                }
                return 0;
-
-#if LIBCURL_VERSION_NUM >= 0x070a08
        } else if (!strcmp(name, "family")) {
                if (!strcmp(value, "ipv4"))
                        git_curl_ipresolve = CURL_IPRESOLVE_V4;
@@ -197,10 +200,12 @@ static int set_option(const char *name, const char *value)
                else
                        return -1;
                return 0;
-#endif /* LIBCURL_VERSION_NUM >= 0x070a08 */
        } else if (!strcmp(name, "from-promisor")) {
                options.from_promisor = 1;
                return 0;
+       } else if (!strcmp(name, "refetch")) {
+               options.refetch = 1;
+               return 0;
        } else if (!strcmp(name, "filter")) {
                options.filter = xstrdup(value);
                return 0;
@@ -502,6 +507,10 @@ static struct discovery *discover_refs(const char *service, int for_push)
                show_http_message(&type, &charset, &buffer);
                die(_("Authentication failed for '%s'"),
                    transport_anonymize_url(url.buf));
+       case HTTP_NOMATCHPUBLICKEY:
+               show_http_message(&type, &charset, &buffer);
+               die(_("unable to access '%s' with http.pinnedPubkey configuration: %s"),
+                   transport_anonymize_url(url.buf), curl_errorstr);
        default:
                show_http_message(&type, &charset, &buffer);
                die(_("unable to access '%s': %s"),
@@ -575,6 +584,7 @@ struct rpc_state {
        char *service_url;
        char *hdr_content_type;
        char *hdr_accept;
+       char *hdr_accept_language;
        char *protocol_header;
        char *buf;
        size_t alloc;
@@ -602,6 +612,8 @@ struct rpc_state {
        unsigned flush_read_but_not_sent : 1;
 };
 
+#define RPC_STATE_INIT { 0 }
+
 /*
  * Appends the result of reading from rpc->out to the string represented by
  * rpc->buf and rpc->len if there is enough space. Returns 1 if there was
@@ -653,7 +665,7 @@ static int rpc_read_from_out(struct rpc_state *rpc, int options,
                        memcpy(buf - 4, "0000", 4);
                        break;
                case PACKET_READ_RESPONSE_END:
-                       die(_("remote server sent stateless separator"));
+                       die(_("remote server sent unexpected response end packet"));
                }
        }
 
@@ -709,28 +721,24 @@ static size_t rpc_out(void *ptr, size_t eltsize,
        return avail;
 }
 
-#ifndef NO_CURL_IOCTL
-static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
+static int rpc_seek(void *clientp, curl_off_t offset, int origin)
 {
        struct rpc_state *rpc = clientp;
 
-       switch (cmd) {
-       case CURLIOCMD_NOP:
-               return CURLIOE_OK;
+       if (origin != SEEK_SET)
+               BUG("rpc_seek only handles SEEK_SET, not %d", origin);
 
-       case CURLIOCMD_RESTARTREAD:
-               if (rpc->initial_buffer) {
-                       rpc->pos = 0;
-                       return CURLIOE_OK;
+       if (rpc->initial_buffer) {
+               if (offset < 0 || offset > rpc->len) {
+                       error("curl seek would be outside of rpc buffer");
+                       return CURL_SEEKFUNC_FAIL;
                }
-               error(_("unable to rewind rpc post data - try increasing http.postBuffer"));
-               return CURLIOE_FAILRESTART;
-
-       default:
-               return CURLIOE_UNKNOWNCMD;
+               rpc->pos = offset;
+               return CURL_SEEKFUNC_OK;
        }
+       error(_("unable to rewind rpc post data - try increasing http.postBuffer"));
+       return CURL_SEEKFUNC_FAIL;
 }
-#endif
 
 struct check_pktline_state {
        char len_buf[4];
@@ -858,7 +866,7 @@ static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
        curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, 4);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buf);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEDATA, &buf);
 
        err = run_slot(slot, results);
 
@@ -929,6 +937,10 @@ static int post_rpc(struct rpc_state *rpc, int stateless_connect, int flush_rece
        headers = curl_slist_append(headers, needs_100_continue ?
                "Expect: 100-continue" : "Expect:");
 
+       /* Add Accept-Language header */
+       if (rpc->hdr_accept_language)
+               headers = curl_slist_append(headers, rpc->hdr_accept_language);
+
        /* Add the extra Git-Protocol header */
        if (rpc->protocol_header)
                headers = curl_slist_append(headers, rpc->protocol_header);
@@ -949,10 +961,8 @@ retry:
                rpc->initial_buffer = 1;
                curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
                curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
-#ifndef NO_CURL_IOCTL
-               curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl);
-               curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc);
-#endif
+               curl_easy_setopt(slot->curl, CURLOPT_SEEKFUNCTION, rpc_seek);
+               curl_easy_setopt(slot->curl, CURLOPT_SEEKDATA, rpc);
                if (options.verbosity > 1) {
                        fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
                        fflush(stderr);
@@ -1023,7 +1033,7 @@ retry:
        rpc_in_data.slot = slot;
        rpc_in_data.check_pktline = stateless_connect;
        memset(&rpc_in_data.pktline_state, 0, sizeof(rpc_in_data.pktline_state));
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, &rpc_in_data);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEDATA, &rpc_in_data);
        curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
 
 
@@ -1064,7 +1074,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
        client.in = -1;
        client.out = -1;
        client.git_cmd = 1;
-       client.argv = client_argv;
+       strvec_pushv(&client.args, client_argv);
        if (start_command(&client))
                exit(1);
        write_or_die(client.in, preamble->buf, preamble->len);
@@ -1079,6 +1089,8 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
        strbuf_addf(&buf, "%s%s", url.buf, svc);
        rpc->service_url = strbuf_detach(&buf, NULL);
 
+       rpc->hdr_accept_language = xstrdup_or_null(http_get_accept_language_header());
+
        strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
        rpc->hdr_content_type = strbuf_detach(&buf, NULL);
 
@@ -1091,7 +1103,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
                rpc->protocol_header = NULL;
 
        while (!err) {
-               int n = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
+               int n = packet_read(rpc->out, rpc->buf, rpc->alloc, 0);
                if (!n)
                        break;
                rpc->pos = 0;
@@ -1117,6 +1129,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
        free(rpc->service_url);
        free(rpc->hdr_content_type);
        free(rpc->hdr_accept);
+       free(rpc->hdr_accept_language);
        free(rpc->protocol_header);
        free(rpc->buf);
        strbuf_release(&buf);
@@ -1152,7 +1165,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
 static int fetch_git(struct discovery *heads,
        int nr_heads, struct ref **to_fetch)
 {
-       struct rpc_state rpc;
+       struct rpc_state rpc = RPC_STATE_INIT;
        struct strbuf preamble = STRBUF_INIT;
        int i, err;
        struct strvec args = STRVEC_INIT;
@@ -1185,6 +1198,8 @@ static int fetch_git(struct discovery *heads,
                strvec_push(&args, "--deepen-relative");
        if (options.from_promisor)
                strvec_push(&args, "--from-promisor");
+       if (options.refetch)
+               strvec_push(&args, "--refetch");
        if (options.filter)
                strvec_pushf(&args, "--filter=%s", options.filter);
        strvec_push(&args, url.buf);
@@ -1273,6 +1288,29 @@ static void parse_fetch(struct strbuf *buf)
        strbuf_reset(buf);
 }
 
+static void parse_get(const char *arg)
+{
+       struct strbuf url = STRBUF_INIT;
+       struct strbuf path = STRBUF_INIT;
+       const char *space;
+
+       space = strchr(arg, ' ');
+
+       if (!space)
+               die(_("protocol error: expected '<url> <path>', missing space"));
+
+       strbuf_add(&url, arg, space - arg);
+       strbuf_addstr(&path, space + 1);
+
+       if (http_get_file(url.buf, path.buf, NULL))
+               die(_("failed to download file at URL '%s'"), url.buf);
+
+       strbuf_release(&url);
+       strbuf_release(&path);
+       printf("\n");
+       fflush(stdout);
+}
+
 static int push_dav(int nr_spec, const char **specs)
 {
        struct child_process child = CHILD_PROCESS_INIT;
@@ -1296,7 +1334,7 @@ static int push_dav(int nr_spec, const char **specs)
 
 static int push_git(struct discovery *heads, int nr_spec, const char **specs)
 {
-       struct rpc_state rpc;
+       struct rpc_state rpc = RPC_STATE_INIT;
        int i, err;
        struct strvec args;
        struct string_list_item *cas_option;
@@ -1395,8 +1433,9 @@ free_specs:
 static int stateless_connect(const char *service_name)
 {
        struct discovery *discover;
-       struct rpc_state rpc;
+       struct rpc_state rpc = RPC_STATE_INIT;
        struct strbuf buf = STRBUF_INIT;
+       const char *accept_language;
 
        /*
         * Run the info/refs request and see if the server supports protocol
@@ -1415,6 +1454,9 @@ static int stateless_connect(const char *service_name)
                printf("\n");
                fflush(stdout);
        }
+       accept_language = http_get_accept_language_header();
+       if (accept_language)
+               rpc.hdr_accept_language = xstrfmt("%s", accept_language);
 
        rpc.service_name = service_name;
        rpc.service_url = xstrfmt("%s%s", url.buf, rpc.service_name);
@@ -1464,6 +1506,7 @@ static int stateless_connect(const char *service_name)
        free(rpc.service_url);
        free(rpc.hdr_content_type);
        free(rpc.hdr_accept);
+       free(rpc.hdr_accept_language);
        free(rpc.protocol_header);
        free(rpc.buf);
        strbuf_release(&buf);
@@ -1475,18 +1518,19 @@ int cmd_main(int argc, const char **argv)
 {
        struct strbuf buf = STRBUF_INIT;
        int nongit;
+       int ret = 1;
 
        setup_git_directory_gently(&nongit);
        if (argc < 2) {
                error(_("remote-curl: usage: git remote-curl <remote> [<url>]"));
-               return 1;
+               goto cleanup;
        }
 
        options.verbosity = 1;
        options.progress = !!isatty(2);
        options.thin = 1;
-       string_list_init(&options.deepen_not, 1);
-       string_list_init(&options.push_options, 1);
+       string_list_init_dup(&options.deepen_not);
+       string_list_init_dup(&options.push_options);
 
        /*
         * Just report "remote-curl" here (folding all the various aliases
@@ -1511,7 +1555,7 @@ int cmd_main(int argc, const char **argv)
                if (strbuf_getline_lf(&buf, stdin) == EOF) {
                        if (ferror(stdin))
                                error(_("remote-curl: error reading command stream from git"));
-                       return 1;
+                       goto cleanup;
                }
                if (buf.len == 0)
                        break;
@@ -1545,9 +1589,14 @@ int cmd_main(int argc, const char **argv)
                                printf("unsupported\n");
                        fflush(stdout);
 
+               } else if (skip_prefix(buf.buf, "get ", &arg)) {
+                       parse_get(arg);
+                       fflush(stdout);
+
                } else if (!strcmp(buf.buf, "capabilities")) {
                        printf("stateless-connect\n");
                        printf("fetch\n");
+                       printf("get\n");
                        printf("option\n");
                        printf("push\n");
                        printf("check-connectivity\n");
@@ -1559,12 +1608,15 @@ int cmd_main(int argc, const char **argv)
                                break;
                } else {
                        error(_("remote-curl: unknown command '%s' from git"), buf.buf);
-                       return 1;
+                       goto cleanup;
                }
                strbuf_reset(&buf);
        } while (1);
 
        http_cleanup();
+       ret = 0;
+cleanup:
+       strbuf_release(&buf);
 
-       return 0;
+       return ret;
 }