{"thread":{"id":"23086","subject":"[PATCH] Prompt for a username when an HTTP request 401s","startedAt":"2010-03-19T19:17:58Z","lastAt":"2010-03-19T19:17:58Z","messageCount":1,"participants":["Scott Chacon"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"137259","messageId":"d411cc4a1003191217k406e7f0fyd144d8a77e73a60c@mail.gmail.com","threadId":"23086","inReplyTo":null,"subject":"[PATCH] Prompt for a username when an HTTP request 401s","fromName":"Scott Chacon","fromEmail":"schacon@gmail.com","sentAt":"2010-03-19T19:17:58Z","receivedAt":"2010-03-19T19:17:58Z","isPatch":true,"sender":{"key":"schacon@gmail.com","avatar":"https://gravatar.com/avatar/9b13a8a078e1dcf8588c4eea9554445d51ebed6c41b51f56f4d96738130b05c6?d=mp&s=160"},"body":"When an HTTP request returns a 401, Git will currently fail with a\nconfusing message saying that it got a 401.  This changes\nhttp_request to prompt for the username and password, then return\nHTTP_REAUTH so http_get_strbuf can try again.  If it gets a 401 even\nwhen a user/pass is supplied, http_request will now return HTTP_NOAUTH\nwhich remote_curl can then use to display a more intelligent error\nmessage that is less confusing.\n\nSigned-off-by: Scott Chacon <schacon@gmail.com>\n---\n\nThird version of this with Shawn's comments on why getpass() is needed.\n\n http.c        |   20 ++++++++++++++++++--\n http.h        |    2 ++\n remote-curl.c |    2 ++\n 3 files changed, 22 insertions(+), 2 deletions(-)\n\ndiff --git a/http.c b/http.c\nindex deab595..62db1f0 100644\n--- a/http.c\n+++ b/http.c\n@@ -815,7 +815,19 @@ static int http_request(const char *url, void\n*result, int target, int options)\n \t\t\tret = HTTP_OK;\n \t\telse if (missing_target(&results))\n \t\t\tret = HTTP_MISSING_TARGET;\n-\t\telse\n+\t\telse if (results.http_code == 401) {\n+\t\t\tif (user_name) {\n+\t\t\t\tret = HTTP_NOAUTH;\n+\t\t\t} else {\n+\t\t\t\t// getpass is needed here because its very likely stdin/stdout are\n+\t\t\t\t// pipes to our parent process.  So we instead need to use /dev/tty,\n+\t\t\t\t// but that is non-portable.  Using getpass() can at least be stubbed\n+\t\t\t\t// on other platforms with a different implementation if/when necessary.\n+\t\t\t\tuser_name = xstrdup(getpass(\"Username: \"));\n+\t\t\t\tinit_curl_http_auth(slot->curl);\n+\t\t\t\tret = HTTP_REAUTH;\n+\t\t\t}\n+\t\t} else\n \t\t\tret = HTTP_ERROR;\n \t} else {\n \t\terror(\"Unable to start HTTP request for %s\", url);\n@@ -831,7 +843,11 @@ static int http_request(const char *url, void\n*result, int target, int options)\n\n int http_get_strbuf(const char *url, struct strbuf *result, int options)\n {\n-\treturn http_request(url, result, HTTP_REQUEST_STRBUF, options);\n+\tint http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);\n+\tif (http_ret == HTTP_REAUTH) {\n+\t\thttp_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);\n+\t}\n+\treturn http_ret;\n }\n\n /*\ndiff --git a/http.h b/http.h\nindex 5c9441c..2dd03e8 100644\n--- a/http.h\n+++ b/http.h\n@@ -126,6 +126,8 @@ extern char *get_remote_object_url(const char\n*url, const char *hex,\n #define HTTP_MISSING_TARGET\t1\n #define HTTP_ERROR\t\t2\n #define HTTP_START_FAILED\t3\n+#define HTTP_REAUTH\t4\n+#define HTTP_NOAUTH\t5\n\n /*\n  * Requests an url and stores the result in a strbuf.\ndiff --git a/remote-curl.c b/remote-curl.c\nindex b76bfcb..0782756 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -132,6 +132,8 @@ static struct discovery* discover_refs(const char *service)\n \tcase HTTP_MISSING_TARGET:\n \t\tdie(\"%s not found: did you run git update-server-info on the\"\n \t\t    \" server?\", refs_url);\n+\tcase HTTP_NOAUTH:\n+\t\tdie(\"Authentication failed\");\n \tdefault:\n \t\thttp_error(refs_url, http_ret);\n \t\tdie(\"HTTP request failed\");\n-- \n1.7.0.1\n"}]}