diff options
Diffstat (limited to 'connect.c')
-rw-r--r-- | connect.c | 203 |
1 files changed, 142 insertions, 61 deletions
@@ -17,8 +17,8 @@ #include "alias.h" static char *server_capabilities_v1; -static struct argv_array server_capabilities_v2 = ARGV_ARRAY_INIT; -static const char *parse_feature_value(const char *, const char *, int *); +static struct strvec server_capabilities_v2 = STRVEC_INIT; +static const char *next_server_feature_value(const char *feature, int *len, int *offset); static int check_ref(const char *name, unsigned int flags) { @@ -70,9 +70,9 @@ int server_supports_v2(const char *c, int die_on_error) { int i; - for (i = 0; i < server_capabilities_v2.argc; i++) { + for (i = 0; i < server_capabilities_v2.nr; i++) { const char *out; - if (skip_prefix(server_capabilities_v2.argv[i], c, &out) && + if (skip_prefix(server_capabilities_v2.v[i], c, &out) && (!*out || *out == '=')) return 1; } @@ -83,14 +83,29 @@ int server_supports_v2(const char *c, int die_on_error) return 0; } +int server_feature_v2(const char *c, const char **v) +{ + int i; + + for (i = 0; i < server_capabilities_v2.nr; i++) { + const char *out; + if (skip_prefix(server_capabilities_v2.v[i], c, &out) && + (*out == '=')) { + *v = out + 1; + return 1; + } + } + return 0; +} + int server_supports_feature(const char *c, const char *feature, int die_on_error) { int i; - for (i = 0; i < server_capabilities_v2.argc; i++) { + for (i = 0; i < server_capabilities_v2.nr; i++) { const char *out; - if (skip_prefix(server_capabilities_v2.argv[i], c, &out) && + if (skip_prefix(server_capabilities_v2.v[i], c, &out) && (!*out || *(out++) == '=')) { if (parse_feature_request(out, feature)) return 1; @@ -108,7 +123,7 @@ int server_supports_feature(const char *c, const char *feature, static void process_capabilities_v2(struct packet_reader *reader) { while (packet_reader_read(reader) == PACKET_READ_NORMAL) - argv_array_push(&server_capabilities_v2, reader->line); + strvec_push(&server_capabilities_v2, reader->line); if (reader->status != PACKET_READ_FLUSH) die(_("expected flush after capabilities")); @@ -181,17 +196,16 @@ reject: static void annotate_refs_with_symref_info(struct ref *ref) { struct string_list symref = STRING_LIST_INIT_DUP; - const char *feature_list = server_capabilities_v1; + int offset = 0; - while (feature_list) { + while (1) { int len; const char *val; - val = parse_feature_value(feature_list, "symref", &len); + val = next_server_feature_value("symref", &len, &offset); if (!val) break; parse_one_symref_info(&symref, val, len); - feature_list = val + 1; } string_list_sort(&symref); @@ -205,21 +219,36 @@ static void annotate_refs_with_symref_info(struct ref *ref) string_list_clear(&symref, 0); } -static void process_capabilities(const char *line, int *len) +static void process_capabilities(struct packet_reader *reader, int *linelen) { + const char *feat_val; + int feat_len; + const char *line = reader->line; int nul_location = strlen(line); - if (nul_location == *len) + if (nul_location == *linelen) return; server_capabilities_v1 = xstrdup(line + nul_location + 1); - *len = nul_location; + *linelen = nul_location; + + feat_val = server_feature_value("object-format", &feat_len); + if (feat_val) { + char *hash_name = xstrndup(feat_val, feat_len); + int hash_algo = hash_algo_by_name(hash_name); + if (hash_algo != GIT_HASH_UNKNOWN) + reader->hash_algo = &hash_algos[hash_algo]; + free(hash_name); + } else { + reader->hash_algo = &hash_algos[GIT_HASH_SHA1]; + } } -static int process_dummy_ref(const char *line) +static int process_dummy_ref(const struct packet_reader *reader) { + const char *line = reader->line; struct object_id oid; const char *name; - if (parse_oid_hex(line, &oid, &name)) + if (parse_oid_hex_algop(line, &oid, &name, reader->hash_algo)) return 0; if (*name != ' ') return 0; @@ -235,13 +264,15 @@ static void check_no_capabilities(const char *line, int len) line + strlen(line)); } -static int process_ref(const char *line, int len, struct ref ***list, - unsigned int flags, struct oid_array *extra_have) +static int process_ref(const struct packet_reader *reader, int len, + struct ref ***list, unsigned int flags, + struct oid_array *extra_have) { + const char *line = reader->line; struct object_id old_oid; const char *name; - if (parse_oid_hex(line, &old_oid, &name)) + if (parse_oid_hex_algop(line, &old_oid, &name, reader->hash_algo)) return 0; if (*name != ' ') return 0; @@ -261,16 +292,17 @@ static int process_ref(const char *line, int len, struct ref ***list, return 1; } -static int process_shallow(const char *line, int len, +static int process_shallow(const struct packet_reader *reader, int len, struct oid_array *shallow_points) { + const char *line = reader->line; const char *arg; struct object_id old_oid; if (!skip_prefix(line, "shallow ", &arg)) return 0; - if (get_oid_hex(arg, &old_oid)) + if (get_oid_hex_algop(arg, &old_oid, reader->hash_algo)) die(_("protocol error: expected shallow sha-1, got '%s'"), arg); if (!shallow_points) die(_("repository on the other end cannot be shallow")); @@ -317,20 +349,20 @@ struct ref **get_remote_heads(struct packet_reader *reader, switch (state) { case EXPECTING_FIRST_REF: - process_capabilities(reader->line, &len); - if (process_dummy_ref(reader->line)) { + process_capabilities(reader, &len); + if (process_dummy_ref(reader)) { state = EXPECTING_SHALLOW; break; } state = EXPECTING_REF; /* fallthrough */ case EXPECTING_REF: - if (process_ref(reader->line, len, &list, flags, extra_have)) + if (process_ref(reader, len, &list, flags, extra_have)) break; state = EXPECTING_SHALLOW; /* fallthrough */ case EXPECTING_SHALLOW: - if (process_shallow(reader->line, len, shallow_points)) + if (process_shallow(reader, len, shallow_points)) break; die(_("protocol error: unexpected '%s'"), reader->line); case EXPECTING_DONE: @@ -344,7 +376,7 @@ struct ref **get_remote_heads(struct packet_reader *reader, } /* Returns 1 when a valid ref has been added to `list`, 0 otherwise */ -static int process_ref_v2(const char *line, struct ref ***list) +static int process_ref_v2(struct packet_reader *reader, struct ref ***list) { int ret = 1; int i = 0; @@ -352,6 +384,7 @@ static int process_ref_v2(const char *line, struct ref ***list) struct ref *ref; struct string_list line_sections = STRING_LIST_INIT_DUP; const char *end; + const char *line = reader->line; /* * Ref lines have a number of fields which are space deliminated. The @@ -364,7 +397,7 @@ static int process_ref_v2(const char *line, struct ref ***list) goto out; } - if (parse_oid_hex(line_sections.items[i++].string, &old_oid, &end) || + if (parse_oid_hex_algop(line_sections.items[i++].string, &old_oid, &end, reader->hash_algo) || *end) { ret = 0; goto out; @@ -372,7 +405,7 @@ static int process_ref_v2(const char *line, struct ref ***list) ref = alloc_ref(line_sections.items[i++].string); - oidcpy(&ref->old_oid, &old_oid); + memcpy(ref->old_oid.hash, old_oid.hash, reader->hash_algo->rawsz); **list = ref; *list = &ref->next; @@ -385,7 +418,8 @@ static int process_ref_v2(const char *line, struct ref ***list) struct object_id peeled_oid; char *peeled_name; struct ref *peeled; - if (parse_oid_hex(arg, &peeled_oid, &end) || *end) { + if (parse_oid_hex_algop(arg, &peeled_oid, &end, + reader->hash_algo) || *end) { ret = 0; goto out; } @@ -393,7 +427,8 @@ static int process_ref_v2(const char *line, struct ref ***list) peeled_name = xstrfmt("%s^{}", ref->name); peeled = alloc_ref(peeled_name); - oidcpy(&peeled->old_oid, &peeled_oid); + memcpy(peeled->old_oid.hash, peeled_oid.hash, + reader->hash_algo->rawsz); **list = peeled; *list = &peeled->next; @@ -418,11 +453,12 @@ void check_stateless_delimiter(int stateless_rpc, struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, struct ref **list, int for_push, - const struct argv_array *ref_prefixes, + const struct strvec *ref_prefixes, const struct string_list *server_options, int stateless_rpc) { int i; + const char *hash_name; *list = NULL; if (server_supports_v2("ls-refs", 1)) @@ -431,6 +467,16 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, if (server_supports_v2("agent", 0)) packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized()); + if (server_feature_v2("object-format", &hash_name)) { + int hash_algo = hash_algo_by_name(hash_name); + if (hash_algo == GIT_HASH_UNKNOWN) + die(_("unknown object format '%s' specified by server"), hash_name); + reader->hash_algo = &hash_algos[hash_algo]; + packet_write_fmt(fd_out, "object-format=%s", reader->hash_algo->name); + } else { + reader->hash_algo = &hash_algos[GIT_HASH_SHA1]; + } + if (server_options && server_options->nr && server_supports_v2("server-option", 1)) for (i = 0; i < server_options->nr; i++) @@ -442,15 +488,15 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, if (!for_push) packet_write_fmt(fd_out, "peel\n"); packet_write_fmt(fd_out, "symrefs\n"); - for (i = 0; ref_prefixes && i < ref_prefixes->argc; i++) { + for (i = 0; ref_prefixes && i < ref_prefixes->nr; i++) { packet_write_fmt(fd_out, "ref-prefix %s\n", - ref_prefixes->argv[i]); + ref_prefixes->v[i]); } packet_flush(fd_out); /* Process response from server */ while (packet_reader_read(reader) == PACKET_READ_NORMAL) { - if (!process_ref_v2(reader->line, &list)) + if (!process_ref_v2(reader, &list)) die(_("invalid ls-refs response: %s"), reader->line); } @@ -463,7 +509,7 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, return list; } -static const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp) +const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset) { int len; @@ -471,6 +517,8 @@ static const char *parse_feature_value(const char *feature_list, const char *fea return NULL; len = strlen(feature); + if (offset) + feature_list += *offset; while (*feature_list) { const char *found = strstr(feature_list, feature); if (!found) @@ -485,9 +533,14 @@ static const char *parse_feature_value(const char *feature_list, const char *fea } /* feature with a value (e.g., "agent=git/1.2.3") */ else if (*value == '=') { + int end; + value++; + end = strcspn(value, " \t\n"); if (lenp) - *lenp = strcspn(value, " \t\n"); + *lenp = end; + if (offset) + *offset = value + end - feature_list; return value; } /* @@ -500,14 +553,41 @@ static const char *parse_feature_value(const char *feature_list, const char *fea return NULL; } +int server_supports_hash(const char *desired, int *feature_supported) +{ + int offset = 0; + int len; + const char *hash; + + hash = next_server_feature_value("object-format", &len, &offset); + if (feature_supported) + *feature_supported = !!hash; + if (!hash) { + hash = hash_algos[GIT_HASH_SHA1].name; + len = strlen(hash); + } + while (hash) { + if (!xstrncmpz(desired, hash, len)) + return 1; + + hash = next_server_feature_value("object-format", &len, &offset); + } + return 0; +} + int parse_feature_request(const char *feature_list, const char *feature) { - return !!parse_feature_value(feature_list, feature, NULL); + return !!parse_feature_value(feature_list, feature, NULL, NULL); +} + +static const char *next_server_feature_value(const char *feature, int *len, int *offset) +{ + return parse_feature_value(server_capabilities_v1, feature, len, offset); } const char *server_feature_value(const char *feature, int *len) { - return parse_feature_value(server_capabilities_v1, feature, len); + return parse_feature_value(server_capabilities_v1, feature, len, NULL); } int server_supports(const char *feature) @@ -864,9 +944,9 @@ static struct child_process *git_proxy_connect(int fd[2], char *host) proxy = xmalloc(sizeof(*proxy)); child_process_init(proxy); - argv_array_push(&proxy->args, git_proxy_command); - argv_array_push(&proxy->args, host); - argv_array_push(&proxy->args, port); + strvec_push(&proxy->args, git_proxy_command); + strvec_push(&proxy->args, host); + strvec_push(&proxy->args, port); proxy->in = -1; proxy->out = -1; if (start_command(proxy)) @@ -1119,16 +1199,16 @@ static struct child_process *git_connect_git(int fd[2], char *hostandport, * Append the appropriate environment variables to `env` and options to * `args` for running ssh in Git's SSH-tunneled transport. */ -static void push_ssh_options(struct argv_array *args, struct argv_array *env, +static void push_ssh_options(struct strvec *args, struct strvec *env, enum ssh_variant variant, const char *port, enum protocol_version version, int flags) { if (variant == VARIANT_SSH && version > 0) { - argv_array_push(args, "-o"); - argv_array_push(args, "SendEnv=" GIT_PROTOCOL_ENVIRONMENT); - argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=version=%d", - version); + strvec_push(args, "-o"); + strvec_push(args, "SendEnv=" GIT_PROTOCOL_ENVIRONMENT); + strvec_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=version=%d", + version); } if (flags & CONNECT_IPV4) { @@ -1141,7 +1221,7 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env, case VARIANT_PLINK: case VARIANT_PUTTY: case VARIANT_TORTOISEPLINK: - argv_array_push(args, "-4"); + strvec_push(args, "-4"); } } else if (flags & CONNECT_IPV6) { switch (variant) { @@ -1153,12 +1233,12 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env, case VARIANT_PLINK: case VARIANT_PUTTY: case VARIANT_TORTOISEPLINK: - argv_array_push(args, "-6"); + strvec_push(args, "-6"); } } if (variant == VARIANT_TORTOISEPLINK) - argv_array_push(args, "-batch"); + strvec_push(args, "-batch"); if (port) { switch (variant) { @@ -1167,15 +1247,15 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env, case VARIANT_SIMPLE: die(_("ssh variant 'simple' does not support setting port")); case VARIANT_SSH: - argv_array_push(args, "-p"); + strvec_push(args, "-p"); break; case VARIANT_PLINK: case VARIANT_PUTTY: case VARIANT_TORTOISEPLINK: - argv_array_push(args, "-P"); + strvec_push(args, "-P"); } - argv_array_push(args, port); + strvec_push(args, port); } } @@ -1213,18 +1293,18 @@ static void fill_ssh_args(struct child_process *conn, const char *ssh_host, detect.use_shell = conn->use_shell; detect.no_stdin = detect.no_stdout = detect.no_stderr = 1; - argv_array_push(&detect.args, ssh); - argv_array_push(&detect.args, "-G"); + strvec_push(&detect.args, ssh); + strvec_push(&detect.args, "-G"); push_ssh_options(&detect.args, &detect.env_array, VARIANT_SSH, port, version, flags); - argv_array_push(&detect.args, ssh_host); + strvec_push(&detect.args, ssh_host); variant = run_command(&detect) ? VARIANT_SIMPLE : VARIANT_SSH; } - argv_array_push(&conn->args, ssh); + strvec_push(&conn->args, ssh); push_ssh_options(&conn->args, &conn->env_array, variant, port, version, flags); - argv_array_push(&conn->args, ssh_host); + strvec_push(&conn->args, ssh_host); } /* @@ -1285,7 +1365,7 @@ struct child_process *git_connect(int fd[2], const char *url, /* remove repo-local variables from the environment */ for (var = local_repo_env; *var; var++) - argv_array_push(&conn->env_array, *var); + strvec_push(&conn->env_array, *var); conn->use_shell = 1; conn->in = conn->out = -1; @@ -1317,11 +1397,12 @@ struct child_process *git_connect(int fd[2], const char *url, transport_check_allowed("file"); conn->trace2_child_class = "transport/file"; if (version > 0) { - argv_array_pushf(&conn->env_array, GIT_PROTOCOL_ENVIRONMENT "=version=%d", - version); + strvec_pushf(&conn->env_array, + GIT_PROTOCOL_ENVIRONMENT "=version=%d", + version); } } - argv_array_push(&conn->args, cmd.buf); + strvec_push(&conn->args, cmd.buf); if (start_command(conn)) die(_("unable to fork")); |