@@ -62,6 +62,26 @@ static long curl_low_speed_limit = -1;
6262static long curl_low_speed_time = -1 ;
6363static int curl_ftp_no_epsv ;
6464static const char * curl_http_proxy ;
65+ static const char * http_proxy_authmethod ;
66+ static struct {
67+ const char * name ;
68+ long curlauth_param ;
69+ } proxy_authmethods [] = {
70+ { "basic" , CURLAUTH_BASIC },
71+ { "digest" , CURLAUTH_DIGEST },
72+ { "negotiate" , CURLAUTH_GSSNEGOTIATE },
73+ { "ntlm" , CURLAUTH_NTLM },
74+ #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
75+ { "anyauth" , CURLAUTH_ANY },
76+ #endif
77+ /*
78+ * CURLAUTH_DIGEST_IE has no corresponding command-line option in
79+ * curl(1) and is not included in CURLAUTH_ANY, so we leave it out
80+ * here, too
81+ */
82+ };
83+ static struct credential proxy_auth = CREDENTIAL_INIT ;
84+ static const char * curl_proxyuserpwd ;
6585static const char * curl_cookie_file ;
6686static int curl_save_cookies ;
6787struct credential http_auth = CREDENTIAL_INIT ;
@@ -159,6 +179,9 @@ static void finish_active_slot(struct active_request_slot *slot)
159179#else
160180 slot -> results -> auth_avail = 0 ;
161181#endif
182+
183+ curl_easy_getinfo (slot -> curl , CURLINFO_HTTP_CONNECTCODE ,
184+ & slot -> results -> http_connectcode );
162185 }
163186
164187 /* Run callback if appropriate */
@@ -256,6 +279,9 @@ static int http_options(const char *var, const char *value, void *cb)
256279 if (!strcmp ("http.proxy" , var ))
257280 return git_config_string (& curl_http_proxy , var , value );
258281
282+ if (!strcmp ("http.proxyauthmethod" , var ))
283+ return git_config_string (& http_proxy_authmethod , var , value );
284+
259285 if (!strcmp ("http.cookiefile" , var ))
260286 return git_config_string (& curl_cookie_file , var , value );
261287 if (!strcmp ("http.savecookies" , var )) {
@@ -304,6 +330,64 @@ static void init_curl_http_auth(CURL *result)
304330#endif
305331}
306332
333+ /* *var must be free-able */
334+ static void var_override (const char * * var , char * value )
335+ {
336+ if (value ) {
337+ free ((void * )* var );
338+ * var = xstrdup (value );
339+ }
340+ }
341+
342+ static void set_proxyauth_name_password (CURL * result )
343+ {
344+ #if LIBCURL_VERSION_NUM >= 0x071301
345+ curl_easy_setopt (result , CURLOPT_PROXYUSERNAME ,
346+ proxy_auth .username );
347+ curl_easy_setopt (result , CURLOPT_PROXYPASSWORD ,
348+ proxy_auth .password );
349+ #else
350+ struct strbuf s = STRBUF_INIT ;
351+
352+ strbuf_addstr_urlencode (& s , proxy_auth .username , 1 );
353+ strbuf_addch (& s , ':' );
354+ strbuf_addstr_urlencode (& s , proxy_auth .password , 1 );
355+ curl_proxyuserpwd = strbuf_detach (& s , NULL );
356+ curl_easy_setopt (result , CURLOPT_PROXYUSERPWD , curl_proxyuserpwd );
357+ #endif
358+ }
359+
360+ static void init_curl_proxy_auth (CURL * result )
361+ {
362+ if (proxy_auth .username ) {
363+ if (!proxy_auth .password )
364+ credential_fill (& proxy_auth );
365+ set_proxyauth_name_password (result );
366+ }
367+
368+ var_override (& http_proxy_authmethod , getenv ("GIT_HTTP_PROXY_AUTHMETHOD" ));
369+
370+ #if LIBCURL_VERSION_NUM >= 0x070a07 /* CURLOPT_PROXYAUTH and CURLAUTH_ANY */
371+ if (http_proxy_authmethod ) {
372+ int i ;
373+ for (i = 0 ; i < ARRAY_SIZE (proxy_authmethods ); i ++ ) {
374+ if (!strcmp (http_proxy_authmethod , proxy_authmethods [i ].name )) {
375+ curl_easy_setopt (result , CURLOPT_PROXYAUTH ,
376+ proxy_authmethods [i ].curlauth_param );
377+ break ;
378+ }
379+ }
380+ if (i == ARRAY_SIZE (proxy_authmethods )) {
381+ warning ("unsupported proxy authentication method %s: using anyauth" ,
382+ http_proxy_authmethod );
383+ curl_easy_setopt (result , CURLOPT_PROXYAUTH , CURLAUTH_ANY );
384+ }
385+ }
386+ else
387+ curl_easy_setopt (result , CURLOPT_PROXYAUTH , CURLAUTH_ANY );
388+ #endif
389+ }
390+
307391static int has_cert_password (void )
308392{
309393 if (ssl_cert == NULL || ssl_cert_password_required != 1 )
@@ -462,6 +546,31 @@ static CURL *get_curl_handle(void)
462546 curl_easy_setopt (result , CURLOPT_USE_SSL , CURLUSESSL_TRY );
463547#endif
464548
549+ /*
550+ * CURL also examines these variables as a fallback; but we need to query
551+ * them here in order to decide whether to prompt for missing password (cf.
552+ * init_curl_proxy_auth()).
553+ *
554+ * Unlike many other common environment variables, these are historically
555+ * lowercase only. It appears that CURL did not know this and implemented
556+ * only uppercase variants, which was later corrected to take both - with
557+ * the exception of http_proxy, which is lowercase only also in CURL. As
558+ * the lowercase versions are the historical quasi-standard, they take
559+ * precedence here, as in CURL.
560+ */
561+ if (!curl_http_proxy ) {
562+ if (!strcmp (http_auth .protocol , "https" )) {
563+ var_override (& curl_http_proxy , getenv ("HTTPS_PROXY" ));
564+ var_override (& curl_http_proxy , getenv ("https_proxy" ));
565+ } else {
566+ var_override (& curl_http_proxy , getenv ("http_proxy" ));
567+ }
568+ if (!curl_http_proxy ) {
569+ var_override (& curl_http_proxy , getenv ("ALL_PROXY" ));
570+ var_override (& curl_http_proxy , getenv ("all_proxy" ));
571+ }
572+ }
573+
465574 if (curl_http_proxy ) {
466575 curl_easy_setopt (result , CURLOPT_PROXY , curl_http_proxy );
467576#if LIBCURL_VERSION_NUM >= 0x071800
@@ -475,10 +584,18 @@ static CURL *get_curl_handle(void)
475584 curl_easy_setopt (result ,
476585 CURLOPT_PROXYTYPE , CURLPROXY_SOCKS4 );
477586#endif
587+ if (strstr (curl_http_proxy , "://" ))
588+ credential_from_url (& proxy_auth , curl_http_proxy );
589+ else {
590+ struct strbuf url = STRBUF_INIT ;
591+ strbuf_addf (& url , "http://%s" , curl_http_proxy );
592+ credential_from_url (& proxy_auth , url .buf );
593+ strbuf_release (& url );
594+ }
595+
596+ curl_easy_setopt (result , CURLOPT_PROXY , proxy_auth .host );
478597 }
479- #if LIBCURL_VERSION_NUM >= 0x070a07
480- curl_easy_setopt (result , CURLOPT_PROXYAUTH , CURLAUTH_ANY );
481- #endif
598+ init_curl_proxy_auth (result );
482599
483600 set_curl_keepalive (result );
484601
@@ -519,6 +636,9 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
519636 if (remote && remote -> http_proxy )
520637 curl_http_proxy = xstrdup (remote -> http_proxy );
521638
639+ if (remote )
640+ var_override (& http_proxy_authmethod , remote -> http_proxy_authmethod );
641+
522642 pragma_header = curl_slist_append (pragma_header , "Pragma: no-cache" );
523643 no_pragma_header = curl_slist_append (no_pragma_header , "Pragma:" );
524644
@@ -617,6 +737,18 @@ void http_cleanup(void)
617737 curl_http_proxy = NULL ;
618738 }
619739
740+ if (proxy_auth .password ) {
741+ memset (proxy_auth .password , 0 , strlen (proxy_auth .password ));
742+ free (proxy_auth .password );
743+ proxy_auth .password = NULL ;
744+ }
745+
746+ free ((void * )curl_proxyuserpwd );
747+ curl_proxyuserpwd = NULL ;
748+
749+ free ((void * )http_proxy_authmethod );
750+ http_proxy_authmethod = NULL ;
751+
620752 if (cert_auth .password != NULL ) {
621753 memset (cert_auth .password , 0 , strlen (cert_auth .password ));
622754 free (cert_auth .password );
@@ -946,6 +1078,8 @@ static int handle_curl_result(struct slot_results *results)
9461078
9471079 if (results -> curl_result == CURLE_OK ) {
9481080 credential_approve (& http_auth );
1081+ if (proxy_auth .password )
1082+ credential_approve (& proxy_auth );
9491083 return HTTP_OK ;
9501084 } else if (missing_target (results ))
9511085 return HTTP_MISSING_TARGET ;
@@ -960,6 +1094,8 @@ static int handle_curl_result(struct slot_results *results)
9601094 return HTTP_REAUTH ;
9611095 }
9621096 } else {
1097+ if (results -> http_connectcode == 407 )
1098+ credential_reject (& proxy_auth );
9631099#if LIBCURL_VERSION_NUM >= 0x070c00
9641100 if (!curl_errorstr [0 ])
9651101 strlcpy (curl_errorstr ,
0 commit comments