From: Junio C Hamano Date: Tue, 18 Oct 2011 04:37:15 +0000 (-0700) Subject: Merge branch 'jk/http-auth' X-Git-Tag: v1.7.8-rc0~50 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/963838402a94e7fcbd1a73019f80aff708972af8?ds=inline;hp=-c Merge branch 'jk/http-auth' * jk/http-auth: http_init: accept separate URL parameter http: use hostname in credential description http: retry authentication failures for all http requests remote-curl: don't retry auth failures with dumb protocol improve httpd auth tests url: decode buffers that are not NUL-terminated --- 963838402a94e7fcbd1a73019f80aff708972af8 diff --combined http-fetch.c index 8c4c5d2224,e341872a6b..69299b7bd2 --- a/http-fetch.c +++ b/http-fetch.c @@@ -56,10 -56,6 +56,10 @@@ int main(int argc, const char **argv commits = 1; } + if (get_all == 0) + warning("http-fetch: use without -a is deprecated.\n" + "In a future release, -a will become the default."); + if (argv[arg]) str_end_url_with_slash(argv[arg], &url); @@@ -67,7 -63,7 +67,7 @@@ git_config(git_default_config, NULL); - http_init(NULL); + http_init(NULL, url); walker = get_http_walker(url); walker->get_tree = get_tree; walker->get_history = get_history; diff --combined http-push.c index 44f814cda2,ecbfae56da..5d01be9344 --- a/http-push.c +++ b/http-push.c @@@ -1606,10 -1606,10 +1606,10 @@@ static void fetch_symref(const char *pa strbuf_release(&buffer); } -static int verify_merge_base(unsigned char *head_sha1, unsigned char *branch_sha1) +static int verify_merge_base(unsigned char *head_sha1, struct ref *remote) { - struct commit *head = lookup_commit(head_sha1); - struct commit *branch = lookup_commit(branch_sha1); + struct commit *head = lookup_commit_or_die(head_sha1, "HEAD"); + struct commit *branch = lookup_commit_or_die(remote->old_sha1, remote->name); struct commit_list *merge_bases = get_merge_bases(head, branch, 1); return (merge_bases && !merge_bases->next && merge_bases->item == branch); @@@ -1655,7 -1655,7 +1655,7 @@@ static int delete_remote_branch(const c return error("Remote HEAD is not a symref"); /* Remote branch must not be the remote HEAD */ - for (i=0; symref && iname, symref)) return error("Remote branch %s is the current HEAD", remote_ref->name); @@@ -1680,7 -1680,7 +1680,7 @@@ return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, sha1_to_hex(remote_ref->old_sha1)); /* Remote branch must be an ancestor of remote HEAD */ - if (!verify_merge_base(head_sha1, remote_ref->old_sha1)) { + if (!verify_merge_base(head_sha1, remote_ref)) { return error("The branch '%s' is not an ancestor " "of your current HEAD.\n" "If you are sure you want to delete it," @@@ -1747,7 -1747,6 +1747,6 @@@ int main(int argc, char **argv int i; int new_refs; struct ref *ref, *local_refs; - struct remote *remote; git_extract_argv0_path(argv[0]); @@@ -1821,14 -1820,7 +1820,7 @@@ memset(remote_dir_exists, -1, 256); - /* - * Create a minimum remote by hand to give to http_init(), - * primarily to allow it to look at the URL. - */ - remote = xcalloc(sizeof(*remote), 1); - ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc); - remote->url[remote->url_nr++] = repo->url; - http_init(remote); + http_init(NULL, repo->url); #ifdef USE_CURL_MULTI is_running_queue = 0; diff --combined http.c index fb3465f50c,00a8553fcc..a4bc770e2d --- a/http.c +++ b/http.c @@@ -42,7 -42,7 +42,7 @@@ static long curl_low_speed_time = -1 static int curl_ftp_no_epsv; static const char *curl_http_proxy; static const char *curl_cookie_file; - static char *user_name, *user_pass; + static char *user_name, *user_pass, *description; static const char *user_agent; #if LIBCURL_VERSION_NUM >= 0x071700 @@@ -139,6 -139,27 +139,27 @@@ static void process_curl_messages(void } #endif + static char *git_getpass_with_description(const char *what, const char *desc) + { + struct strbuf prompt = STRBUF_INIT; + char *r; + + if (desc) + strbuf_addf(&prompt, "%s for '%s': ", what, desc); + else + strbuf_addf(&prompt, "%s: ", what); + /* + * NEEDSWORK: for usernames, we should do something less magical that + * actually echoes the characters. However, we need to read from + * /dev/tty and not stdio, which is not portable (but getpass will do + * it for us). http.c uses the same workaround. + */ + r = git_getpass(prompt.buf); + + strbuf_release(&prompt); + return xstrdup(r); + } + static int http_options(const char *var, const char *value, void *cb) { if (!strcmp("http.sslverify", var)) { @@@ -214,7 -235,7 +235,7 @@@ static void init_curl_http_auth(CURL *r if (user_name) { struct strbuf up = STRBUF_INIT; if (!user_pass) - user_pass = xstrdup(git_getpass("Password: ")); + user_pass = xstrdup(git_getpass_with_description("Password", description)); strbuf_addf(&up, "%s:%s", user_name, user_pass); curl_easy_setopt(result, CURLOPT_USERPWD, strbuf_detach(&up, NULL)); @@@ -229,7 -250,7 +250,7 @@@ static int has_cert_password(void return 0; /* Only prompt the user once. */ ssl_cert_password_required = -1; - ssl_cert_password = git_getpass("Certificate Password: "); + ssl_cert_password = git_getpass_with_description("Certificate Password", description); if (ssl_cert_password != NULL) { ssl_cert_password = xstrdup(ssl_cert_password); return 1; @@@ -307,8 -328,7 +328,7 @@@ static CURL *get_curl_handle(void static void http_auth_init(const char *url) { - char *at, *colon, *cp, *slash, *decoded; - int len; + const char *at, *colon, *cp, *slash, *host; cp = strstr(url, "://"); if (!cp) @@@ -324,34 -344,22 +344,22 @@@ at = strchr(cp, '@'); colon = strchr(cp, ':'); slash = strchrnul(cp, '/'); - if (!at || slash <= at) - return; /* No credentials */ - if (!colon || at <= colon) { + if (!at || slash <= at) { + /* No credentials, but we may have to ask for some later */ + host = cp; + } + else if (!colon || at <= colon) { /* Only username */ - len = at - cp; - user_name = xmalloc(len + 1); - memcpy(user_name, cp, len); - user_name[len] = '\0'; - decoded = url_decode(user_name); - free(user_name); - user_name = decoded; + user_name = url_decode_mem(cp, at - cp); user_pass = NULL; + host = at + 1; } else { - len = colon - cp; - user_name = xmalloc(len + 1); - memcpy(user_name, cp, len); - user_name[len] = '\0'; - decoded = url_decode(user_name); - free(user_name); - user_name = decoded; - len = at - (colon + 1); - user_pass = xmalloc(len + 1); - memcpy(user_pass, colon + 1, len); - user_pass[len] = '\0'; - decoded = url_decode(user_pass); - free(user_pass); - user_pass = decoded; + user_name = url_decode_mem(cp, colon - cp); + user_pass = url_decode_mem(colon + 1, at - (colon + 1)); + host = at + 1; } + + description = url_decode_mem(host, slash - host); } static void set_from_env(const char **var, const char *envname) @@@ -361,7 -369,7 +369,7 @@@ *var = val; } - void http_init(struct remote *remote) + void http_init(struct remote *remote, const char *url) { char *low_speed_limit; char *low_speed_time; @@@ -425,11 -433,11 +433,11 @@@ if (getenv("GIT_CURL_FTP_NO_EPSV")) curl_ftp_no_epsv = 1; - if (remote && remote->url && remote->url[0]) { - http_auth_init(remote->url[0]); + if (url) { + http_auth_init(url); if (!ssl_cert_password_required && getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") && - !prefixcmp(remote->url[0], "https://")) + !prefixcmp(url, "https://")) ssl_cert_password_required = 1; } @@@ -847,17 -855,12 +855,17 @@@ static int http_request(const char *url * but that is non-portable. Using git_getpass() can at least be stubbed * on other platforms with a different implementation if/when necessary. */ - user_name = xstrdup(git_getpass("Username: ")); + user_name = xstrdup(git_getpass_with_description("Username", description)); init_curl_http_auth(slot->curl); ret = HTTP_REAUTH; } - } else + } else { + if (!curl_errorstr[0]) + strlcpy(curl_errorstr, + curl_easy_strerror(results.curl_result), + sizeof(curl_errorstr)); ret = HTTP_ERROR; + } } else { error("Unable to start HTTP request for %s", url); ret = HTTP_START_FAILED; @@@ -870,13 -873,18 +878,18 @@@ return ret; } + static int http_request_reauth(const char *url, void *result, int target, + int options) + { + int ret = http_request(url, result, target, options); + if (ret != HTTP_REAUTH) + return ret; + return http_request(url, result, target, options); + } + int http_get_strbuf(const char *url, struct strbuf *result, int options) { - int http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options); - if (http_ret == HTTP_REAUTH) { - http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options); - } - return http_ret; + return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options); } /* @@@ -899,7 -907,7 +912,7 @@@ static int http_get_file(const char *ur goto cleanup; } - ret = http_request(url, result, HTTP_REQUEST_FILE, options); + ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options); fclose(result); if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename)) @@@ -913,7 -921,7 +926,7 @@@ int http_error(const char *url, int ret { /* http_request has already handled HTTP_START_FAILED. */ if (ret != HTTP_START_FAILED) - error("%s while accessing %s\n", curl_errorstr, url); + error("%s while accessing %s", curl_errorstr, url); return ret; } @@@ -1126,8 -1134,9 +1139,8 @@@ struct http_pack_request *new_http_pack struct strbuf buf = STRBUF_INIT; struct http_pack_request *preq; - preq = xmalloc(sizeof(*preq)); + preq = xcalloc(1, sizeof(*preq)); preq->target = target; - preq->range_header = NULL; end_url_with_slash(&buf, base_url); strbuf_addf(&buf, "objects/pack/pack-%s.pack", @@@ -1219,7 -1228,7 +1232,7 @@@ struct http_object_request *new_http_ob struct curl_slist *range_header = NULL; struct http_object_request *freq; - freq = xmalloc(sizeof(*freq)); + freq = xcalloc(1, sizeof(*freq)); hashcpy(freq->sha1, sha1); freq->localfile = -1; @@@ -1257,6 -1266,8 +1270,6 @@@ goto abort; } - memset(&freq->stream, 0, sizeof(freq->stream)); - git_inflate_init(&freq->stream); git_SHA1_Init(&freq->c); @@@ -1331,6 -1342,7 +1344,6 @@@ return freq; abort: - free(filename); free(freq->url); free(freq); return NULL; diff --combined remote-curl.c index 0aa4bfed30,d4d0910e60..0e720ee8bb --- a/remote-curl.c +++ b/remote-curl.c @@@ -115,7 -115,7 +115,7 @@@ static struct discovery* discover_refs( http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE); /* try again with "plain" url (no ? or & appended) */ - if (http_ret != HTTP_OK) { + if (http_ret != HTTP_OK && http_ret != HTTP_NOAUTH) { free(refs_url); strbuf_reset(&buffer); @@@ -227,8 -227,6 +227,8 @@@ static struct ref *parse_info_refs(stru if (data[i] == '\t') mid = &data[i]; if (data[i] == '\n') { + if (mid - start != 40) + die("%sinfo/refs not valid: is this a git repository?", url); data[i] = 0; ref_name = mid + 1; ref = xmalloc(sizeof(struct ref) + @@@ -573,14 -571,7 +573,14 @@@ static int rpc_service(struct rpc_stat close(client.in); client.in = -1; - strbuf_read(&rpc->result, client.out, 0); + if (!err) { + strbuf_read(&rpc->result, client.out, 0); + } else { + char buf[4096]; + for (;;) + if (xread(client.out, buf, sizeof(buf)) <= 0) + break; + } close(client.out); client.out = -1; @@@ -859,17 -850,10 +859,17 @@@ int main(int argc, const char **argv url = strbuf_detach(&buf, NULL); - http_init(remote); + http_init(remote, url); do { - if (strbuf_getline(&buf, stdin, '\n') == EOF) + if (strbuf_getline(&buf, stdin, '\n') == EOF) { + if (ferror(stdin)) + fprintf(stderr, "Error reading command stream\n"); + else + fprintf(stderr, "Unexpected end of command stream\n"); + return 1; + } + if (buf.len == 0) break; if (!prefixcmp(buf.buf, "fetch ")) { if (nongit) @@@ -909,7 -893,6 +909,7 @@@ printf("\n"); fflush(stdout); } else { + fprintf(stderr, "Unknown command '%s'\n", buf.buf); return 1; } strbuf_reset(&buf); diff --combined url.c index e4262a0d7a,389d9dab10..335d97d3f7 --- a/url.c +++ b/url.c @@@ -18,15 -18,35 +18,15 @@@ int is_urlschemechar(int first_flag, in int is_url(const char *url) { - const char *url2, *first_slash; - - if (!url) - return 0; - url2 = url; - first_slash = strchr(url, '/'); - - /* Input with no slash at all or slash first can't be URL. */ - if (!first_slash || first_slash == url) - return 0; - /* Character before must be : and next must be /. */ - if (first_slash[-1] != ':' || first_slash[1] != '/') + /* Is "scheme" part reasonable? */ + if (!url || !is_urlschemechar(1, *url++)) return 0; - /* There must be something before the :// */ - if (first_slash == url + 1) - return 0; - /* - * Check all characters up to first slash - 1. Only alphanum - * is allowed. - */ - url2 = url; - while (url2 < first_slash - 1) { - if (!is_urlschemechar(url2 == url, (unsigned char)*url2)) + while (*url && *url != ':') { + if (!is_urlschemechar(0, *url++)) return 0; - url2++; } - - /* Valid enough. */ - return 1; + /* We've seen "scheme"; we want colon-slash-slash */ + return (url[0] == ':' && url[1] == '/' && url[2] == '/'); } static int url_decode_char(const char *q) @@@ -48,18 -68,20 +48,20 @@@ return val; } - static char *url_decode_internal(const char **query, const char *stop_at, - struct strbuf *out, int decode_plus) + static char *url_decode_internal(const char **query, int len, + const char *stop_at, struct strbuf *out, + int decode_plus) { const char *q = *query; - do { + while (len) { unsigned char c = *q; if (!c) break; if (stop_at && strchr(stop_at, c)) { q++; + len--; break; } @@@ -68,6 -90,7 +70,7 @@@ if (0 <= val) { strbuf_addch(out, val); q += 3; + len -= 3; continue; } } @@@ -77,34 -100,41 +80,41 @@@ else strbuf_addch(out, c); q++; - } while (1); + len--; + } *query = q; return strbuf_detach(out, NULL); } char *url_decode(const char *url) + { + return url_decode_mem(url, strlen(url)); + } + + char *url_decode_mem(const char *url, int len) { struct strbuf out = STRBUF_INIT; - const char *colon = strchr(url, ':'); + const char *colon = memchr(url, ':', len); /* Skip protocol part if present */ if (colon && url < colon) { strbuf_add(&out, url, colon - url); + len -= colon - url; url = colon; } - return url_decode_internal(&url, NULL, &out, 0); + return url_decode_internal(&url, len, NULL, &out, 0); } char *url_decode_parameter_name(const char **query) { struct strbuf out = STRBUF_INIT; - return url_decode_internal(query, "&=", &out, 1); + return url_decode_internal(query, -1, "&=", &out, 1); } char *url_decode_parameter_value(const char **query) { struct strbuf out = STRBUF_INIT; - return url_decode_internal(query, "&", &out, 1); + return url_decode_internal(query, -1, "&", &out, 1); } void end_url_with_slash(struct strbuf *buf, const char *url)