| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791 | 
							- /*
 
-  * HTTP CONNECT proxy negotiation.
 
-  */
 
- #include "putty.h"
 
- #include "network.h"
 
- #include "proxy.h"
 
- #include "sshcr.h"
 
- static bool read_line(bufchain *input, strbuf *output, bool is_header)
 
- {
 
-     char c;
 
-     while (bufchain_try_fetch(input, &c, 1)) {
 
-         if (is_header && output->len > 0 &&
 
-             output->s[output->len - 1] == '\n') {
 
-             /*
 
-              * A newline terminates the header, provided we're sure it
 
-              * is _not_ followed by a space or a tab.
 
-              */
 
-             if (c != ' ' && c != '\t')
 
-                 goto done;  /* we have a complete header line */
 
-         } else {
 
-             put_byte(output, c);
 
-             bufchain_consume(input, 1);
 
-             if (!is_header && output->len > 0 &&
 
-                 output->s[output->len - 1] == '\n') {
 
-                 /* If we're looking for just a line, not an HTTP
 
-                  * header, then any newline terminates it. */
 
-                 goto done;
 
-             }
 
-         }
 
-     }
 
-     return false;
 
-   done:
 
-     strbuf_chomp(output, '\n');
 
-     strbuf_chomp(output, '\r');
 
-     return true;
 
- }
 
- /* Types of HTTP authentication, in preference order. */
 
- typedef enum HttpAuthType {
 
-     AUTH_ERROR, /* if an HttpAuthDetails was never satisfactorily filled in */
 
-     AUTH_NONE,  /* if no auth header is seen, assume no auth required */
 
-     AUTH_BASIC, /* username + password sent in clear (only keyless base64) */
 
-     AUTH_DIGEST, /* cryptographic hash, most preferred if available */
 
- } HttpAuthType;
 
- typedef struct HttpAuthDetails {
 
-     HttpAuthType auth_type;
 
-     bool digest_nonce_was_stale;
 
-     HttpDigestHash digest_hash;
 
-     strbuf *realm, *nonce, *opaque, *error;
 
-     bool got_opaque;
 
-     bool hash_username;
 
- } HttpAuthDetails;
 
- typedef struct HttpProxyNegotiator {
 
-     int crLine;
 
-     strbuf *response, *header, *token;
 
-     int http_status_pos;
 
-     size_t header_pos;
 
-     strbuf *username, *password;
 
-     int http_status;
 
-     bool connection_close;
 
-     HttpAuthDetails *next_auth;
 
-     bool try_auth_from_conf;
 
-     strbuf *uri;
 
-     uint32_t nonce_count;
 
-     prompts_t *prompts;
 
-     int username_prompt_index, password_prompt_index;
 
-     size_t content_length, chunk_length;
 
-     bool chunked_transfer;
 
-     ProxyNegotiator pn;
 
- } HttpProxyNegotiator;
 
- static inline HttpAuthDetails *auth_error(HttpAuthDetails *d,
 
-                                           const char *fmt, ...)
 
- {
 
-     d->auth_type = AUTH_ERROR;
 
-     put_fmt(d->error, "Unable to parse auth header from HTTP proxy");
 
-     if (fmt) {
 
-         va_list ap;
 
-         va_start(ap, fmt);
 
-         put_datalit(d->error, ": ");
 
-         put_fmtv(d->error, fmt, ap);
 
-         va_end(ap);
 
-     }
 
-     return d;
 
- }
 
- static HttpAuthDetails *http_auth_details_new(void)
 
- {
 
-     HttpAuthDetails *d = snew(HttpAuthDetails);
 
-     memset(d, 0, sizeof(*d));
 
-     d->realm = strbuf_new();
 
-     d->nonce = strbuf_new();
 
-     d->opaque = strbuf_new();
 
-     d->error = strbuf_new();
 
-     return d;
 
- }
 
- static void http_auth_details_free(HttpAuthDetails *d)
 
- {
 
-     strbuf_free(d->realm);
 
-     strbuf_free(d->nonce);
 
-     strbuf_free(d->opaque);
 
-     strbuf_free(d->error);
 
-     sfree(d);
 
- }
 
- static ProxyNegotiator *proxy_http_new(const ProxyNegotiatorVT *vt)
 
- {
 
-     HttpProxyNegotiator *s = snew(HttpProxyNegotiator);
 
-     memset(s, 0, sizeof(*s));
 
-     s->pn.vt = vt;
 
-     s->response = strbuf_new();
 
-     s->header = strbuf_new();
 
-     s->token = strbuf_new();
 
-     s->username = strbuf_new();
 
-     s->password = strbuf_new_nm();
 
-     s->uri = strbuf_new();
 
-     s->nonce_count = 0;
 
-     /*
 
-      * Always start with a CONNECT request containing no auth. If the
 
-      * proxy rejects that, it will tell us what kind of auth it would
 
-      * prefer.
 
-      */
 
-     s->next_auth = http_auth_details_new();
 
-     s->next_auth->auth_type = AUTH_NONE;
 
-     return &s->pn;
 
- }
 
- static void proxy_http_free(ProxyNegotiator *pn)
 
- {
 
-     HttpProxyNegotiator *s = container_of(pn, HttpProxyNegotiator, pn);
 
-     strbuf_free(s->response);
 
-     strbuf_free(s->header);
 
-     strbuf_free(s->token);
 
-     strbuf_free(s->username);
 
-     strbuf_free(s->password);
 
-     strbuf_free(s->uri);
 
-     http_auth_details_free(s->next_auth);
 
-     if (s->prompts)
 
-         free_prompts(s->prompts);
 
-     sfree(s);
 
- }
 
- #define HTTP_HEADER_LIST(X) \
 
-     X(HDR_CONNECTION, "Connection") \
 
-     X(HDR_CONTENT_LENGTH, "Content-Length") \
 
-     X(HDR_TRANSFER_ENCODING, "Transfer-Encoding") \
 
-     X(HDR_PROXY_AUTHENTICATE, "Proxy-Authenticate") \
 
-     X(HDR_PROXY_CONNECTION, "Proxy-Connection") \
 
-     /* end of list */
 
- typedef enum HttpHeader {
 
-     #define ENUM_DEF(id, string) id,
 
-     HTTP_HEADER_LIST(ENUM_DEF)
 
-     #undef ENUM_DEF
 
-     HDR_UNKNOWN
 
- } HttpHeader;
 
- static inline bool is_whitespace(char c)
 
- {
 
-     return (c == ' ' || c == '\t' || c == '\n');
 
- }
 
- static inline bool is_separator(char c)
 
- {
 
-     return (c == '(' || c == ')' || c == '<' || c == '>' || c == '@' ||
 
-             c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' ||
 
-             c == '/' || c == '[' || c == ']' || c == '?' || c == '=' ||
 
-             c == '{' || c == '}');
 
- }
 
- #define HTTP_SEPARATORS
 
- static bool get_end_of_header(HttpProxyNegotiator *s)
 
- {
 
-     size_t pos = s->header_pos;
 
-     while (pos < s->header->len && is_whitespace(s->header->s[pos]))
 
-         pos++;
 
-     if (pos == s->header->len) {
 
-         s->header_pos = pos;
 
-         return true;
 
-     }
 
-     return false;
 
- }
 
- static bool get_token(HttpProxyNegotiator *s)
 
- {
 
-     size_t pos = s->header_pos;
 
-     while (pos < s->header->len && is_whitespace(s->header->s[pos]))
 
-         pos++;
 
-     if (pos == s->header->len)
 
-         return false;                  /* end of string */
 
-     if (is_separator(s->header->s[pos]))
 
-         return false;
 
-     strbuf_clear(s->token);
 
-     while (pos < s->header->len &&
 
-            !is_whitespace(s->header->s[pos]) &&
 
-            !is_separator(s->header->s[pos]))
 
-         put_byte(s->token, s->header->s[pos++]);
 
-     s->header_pos = pos;
 
-     return true;
 
- }
 
- static bool get_separator(HttpProxyNegotiator *s, char sep)
 
- {
 
-     size_t pos = s->header_pos;
 
-     while (pos < s->header->len && is_whitespace(s->header->s[pos]))
 
-         pos++;
 
-     if (pos == s->header->len)
 
-         return false;                  /* end of string */
 
-     if (s->header->s[pos] != sep)
 
-         return false;
 
-     s->header_pos = ++pos;
 
-     return true;
 
- }
 
- static bool get_quoted_string(HttpProxyNegotiator *s)
 
- {
 
-     size_t pos = s->header_pos;
 
-     while (pos < s->header->len && is_whitespace(s->header->s[pos]))
 
-         pos++;
 
-     if (pos == s->header->len)
 
-         return false;                  /* end of string */
 
-     if (s->header->s[pos] != '"')
 
-         return false;
 
-     pos++;
 
-     strbuf_clear(s->token);
 
-     while (pos < s->header->len && s->header->s[pos] != '"') {
 
-         if (s->header->s[pos] == '\\') {
 
-             /* Backslash makes the next char literal, even if it's " or \ */
 
-             pos++;
 
-             if (pos == s->header->len)
 
-                 return false;          /* unexpected end of string */
 
-         }
 
-         put_byte(s->token, s->header->s[pos++]);
 
-     }
 
-     if (pos == s->header->len)
 
-         return false;                  /* no closing quote */
 
-     pos++;
 
-     s->header_pos = pos;
 
-     return true;
 
- }
 
- static HttpAuthDetails *parse_http_auth_header(HttpProxyNegotiator *s)
 
- {
 
-     HttpAuthDetails *d = http_auth_details_new();
 
-     /* Default hash for HTTP Digest is MD5, if none specified explicitly */
 
-     d->digest_hash = HTTP_DIGEST_MD5;
 
-     if (!get_token(s))
 
-         return auth_error(d, "parse error");
 
-     if (!stricmp(s->token->s, "Basic")) {
 
-         /* For Basic authentication, we don't need anything else. The
 
-          * realm string is not required for the protocol. */
 
-         d->auth_type = AUTH_BASIC;
 
-         return d;
 
-     }
 
-     if (!stricmp(s->token->s, "Digest")) {
 
-         /* Parse all the additional parts of the Digest header. */
 
-         if (!http_digest_available)
 
-             return auth_error(d, "Digest authentication not supported");
 
-         /* Parse the rest of the Digest header */
 
-         while (true) {
 
-             if (!get_token(s))
 
-                 return auth_error(d, "parse error in Digest header");
 
-             if (!stricmp(s->token->s, "realm")) {
 
-                 if (!get_separator(s, '=') ||
 
-                     !get_quoted_string(s))
 
-                     return auth_error(d, "parse error in Digest realm field");
 
-                 put_datapl(d->realm, ptrlen_from_strbuf(s->token));
 
-             } else if (!stricmp(s->token->s, "nonce")) {
 
-                 if (!get_separator(s, '=') ||
 
-                     !get_quoted_string(s))
 
-                     return auth_error(d, "parse error in Digest nonce field");
 
-                 put_datapl(d->nonce, ptrlen_from_strbuf(s->token));
 
-             } else if (!stricmp(s->token->s, "opaque")) {
 
-                 if (!get_separator(s, '=') ||
 
-                     !get_quoted_string(s))
 
-                     return auth_error(d, "parse error in Digest opaque field");
 
-                 put_datapl(d->opaque,
 
-                            ptrlen_from_strbuf(s->token));
 
-                 d->got_opaque = true;
 
-             } else if (!stricmp(s->token->s, "stale")) {
 
-                 if (!get_separator(s, '=') ||
 
-                     !get_token(s))
 
-                     return auth_error(d, "parse error in Digest stale field");
 
-                 d->digest_nonce_was_stale = !stricmp(
 
-                     s->token->s, "true");
 
-             } else if (!stricmp(s->token->s, "userhash")) {
 
-                 if (!get_separator(s, '=') ||
 
-                     !get_token(s))
 
-                     return auth_error(d, "parse error in Digest userhash "
 
-                                       "field");
 
-                 d->hash_username = !stricmp(s->token->s, "true");
 
-             } else if (!stricmp(s->token->s, "algorithm")) {
 
-                 if (!get_separator(s, '=') ||
 
-                     (!get_token(s) && !get_quoted_string(s)))
 
-                     return auth_error(d, "parse error in Digest algorithm "
 
-                                       "field");
 
-                 { // WINSCP
 
-                 bool found = false;
 
-                 size_t i;
 
-                 for (i = 0; i < N_HTTP_DIGEST_HASHES; i++) {
 
-                     if (!stricmp(s->token->s, httphashnames[i])) {
 
-                         found = true;
 
-                         break;
 
-                     }
 
-                 }
 
-                 if (!found) {
 
-                     /* We don't even recognise the name */
 
-                     return auth_error(d, "Digest hash algorithm '%s' not "
 
-                                       "recognised", s->token->s);
 
-                 }
 
-                 if (!httphashaccepted[i]) {
 
-                     /* We do recognise the name but we
 
-                      * don't like it (see comment in cproxy.h) */
 
-                     return auth_error(d, "Digest hash algorithm '%s' not "
 
-                                       "supported", s->token->s);
 
-                 }
 
-                 d->digest_hash = i;
 
-                 } // WINSCP
 
-             } else if (!stricmp(s->token->s, "qop")) {
 
-                 if (!get_separator(s, '=') ||
 
-                     !get_quoted_string(s))
 
-                     return auth_error(d, "parse error in Digest qop field");
 
-                 if (stricmp(s->token->s, "auth"))
 
-                     return auth_error(d, "quality-of-protection type '%s' not "
 
-                                       "supported", s->token->s);
 
-             } else {
 
-                 /* Ignore any other auth-param */
 
-                 if (!get_separator(s, '=') ||
 
-                     (!get_quoted_string(s) && !get_token(s)))
 
-                     return auth_error(d, "parse error in Digest header");
 
-             }
 
-             if (get_end_of_header(s))
 
-                 break;
 
-             if (!get_separator(s, ','))
 
-                 return auth_error(d, "parse error in Digest header");
 
-         }
 
-         d->auth_type = AUTH_DIGEST;
 
-         return d;
 
-     }
 
-     return auth_error(d, "authentication type '%s' not supported",
 
-                       s->token->s);
 
- }
 
- static void proxy_http_process_queue(ProxyNegotiator *pn)
 
- {
 
-     HttpProxyNegotiator *s = container_of(pn, HttpProxyNegotiator, pn);
 
-     crBegin(s->crLine);
 
-     /*
 
-      * Initialise our username and password strbufs from the Conf.
 
-      */
 
-     put_dataz(s->username, conf_get_str(pn->ps->conf, CONF_proxy_username));
 
-     put_dataz(s->password, conf_get_str(pn->ps->conf, CONF_proxy_password));
 
-     if (s->username->len || s->password->len)
 
-         s->try_auth_from_conf = true;
 
-     /*
 
-      * Set up the host:port string we're trying to connect to, also
 
-      * used as the URI string in HTTP Digest auth.
 
-      */
 
-     {
 
-         char dest[512];
 
-         sk_getaddr(pn->ps->remote_addr, dest, lenof(dest));
 
-         put_fmt(s->uri, "%s:%d", dest, pn->ps->remote_port);
 
-     }
 
-     while (true) {
 
-         /*
 
-          * Standard prefix for the HTTP CONNECT request.
 
-          */
 
-         put_fmt(pn->output,
 
-                 "CONNECT %s HTTP/1.1\r\n"
 
-                 "Host: %s\r\n", s->uri->s, s->uri->s);
 
-         /*
 
-          * Add an auth header, if we're planning to this time round.
 
-          */
 
-         if (s->next_auth->auth_type == AUTH_BASIC) {
 
-             put_datalit(pn->output, "Proxy-Authorization: Basic ");
 
-             { // WINSCP
 
-             strbuf *base64_input = strbuf_new_nm();
 
-             put_datapl(base64_input, ptrlen_from_strbuf(s->username));
 
-             put_byte(base64_input, ':');
 
-             put_datapl(base64_input, ptrlen_from_strbuf(s->password));
 
-             { // WINSCP
 
-             char base64_output[4];
 
-             size_t i, e; // WINSCP
 
-             for (i = 0, e = base64_input->len; i < e; i += 3) {
 
-                 base64_encode_atom(base64_input->u + i,
 
-                                    e-i > 3 ? 3 : e-i, base64_output);
 
-                 put_data(pn->output, base64_output, 4);
 
-             }
 
-             strbuf_free(base64_input);
 
-             smemclr(base64_output, sizeof(base64_output));
 
-             put_datalit(pn->output, "\r\n");
 
-             } // WINSCP
 
-             } // WINSCP
 
-         } else if (s->next_auth->auth_type == AUTH_DIGEST) {
 
-             put_datalit(pn->output, "Proxy-Authorization: Digest ");
 
-             /* If we have a fresh nonce, reset the
 
-              * nonce count. Otherwise, keep incrementing it. */
 
-             if (!ptrlen_eq_ptrlen(ptrlen_from_strbuf(s->token),
 
-                                   ptrlen_from_strbuf(s->next_auth->nonce)))
 
-                 s->nonce_count = 0;
 
-             http_digest_response(BinarySink_UPCAST(pn->output),
 
-                                  ptrlen_from_strbuf(s->username),
 
-                                  ptrlen_from_strbuf(s->password),
 
-                                  ptrlen_from_strbuf(s->next_auth->realm),
 
-                                  PTRLEN_LITERAL("CONNECT"),
 
-                                  ptrlen_from_strbuf(s->uri),
 
-                                  PTRLEN_LITERAL("auth"),
 
-                                  ptrlen_from_strbuf(s->next_auth->nonce),
 
-                                  (s->next_auth->got_opaque ?
 
-                                   ptrlen_from_strbuf(s->next_auth->opaque) :
 
-                                   make_ptrlen(NULL, 0)),
 
-                                  ++s->nonce_count, s->next_auth->digest_hash,
 
-                                  s->next_auth->hash_username);
 
-             put_datalit(pn->output, "\r\n");
 
-         }
 
-         /*
 
-          * Blank line to terminate the HTTP request.
 
-          */
 
-         put_datalit(pn->output, "\r\n");
 
-         crReturnV;
 
-         s->content_length = 0;
 
-         s->chunked_transfer = false;
 
-         s->connection_close = false;
 
-         /*
 
-          * Read and parse the HTTP status line, and check if it's a 2xx
 
-          * for success.
 
-          */
 
-         strbuf_clear(s->response);
 
-         crMaybeWaitUntilV(read_line(pn->input, s->response, false));
 
-         {
 
-             int maj_ver, min_ver, n_scanned;
 
-             n_scanned = sscanf(
 
-                 s->response->s, "HTTP/%d.%d %n%d",
 
-                 &maj_ver, &min_ver, &s->http_status_pos, &s->http_status);
 
-             if (n_scanned < 3) {
 
-                 pn->error = dupstr("HTTP response was absent or malformed");
 
-                 crStopV;
 
-             }
 
-             if (maj_ver < 1 || (maj_ver == 1 && min_ver < 1)) {
 
-                 /* Before HTTP/1.1, connections close by default */
 
-                 s->connection_close = true;
 
-             }
 
-         }
 
-         if (s->http_status == 407) {
 
-             /*
 
-              * If this is going to be an auth request, we expect to
 
-              * see at least one Proxy-Authorization header offering us
 
-              * auth options. Start by preloading s->next_auth with a
 
-              * fallback error message, which will be used if nothing
 
-              * better is available.
 
-              */
 
-             http_auth_details_free(s->next_auth);
 
-             s->next_auth = http_auth_details_new();
 
-             auth_error(s->next_auth, "no Proxy-Authorization header seen in "
 
-                        "HTTP 407 Proxy Authentication Required response");
 
-         }
 
-         /*
 
-          * Read the HTTP response header section.
 
-          */
 
-         do {
 
-             strbuf_clear(s->header);
 
-             crMaybeWaitUntilV(read_line(pn->input, s->header, true));
 
-             s->header_pos = 0;
 
-             if (!get_token(s)) {
 
-                 /* Possibly we ought to panic if we see an HTTP header
 
-                  * we can't make any sense of at all? But whatever,
 
-                  * ignore it and hope the next one makes more sense */
 
-                 continue;
 
-             }
 
-             /* Parse the header name */
 
-             { // WINSCP
 
-             HttpHeader hdr = HDR_UNKNOWN;
 
-             {
 
-                 #define CHECK_HEADER(id, string) \
 
-                     if (!stricmp(s->token->s, string)) hdr = id;
 
-                 HTTP_HEADER_LIST(CHECK_HEADER);
 
-                 #undef CHECK_HEADER
 
-             }
 
-             if (!get_separator(s, ':'))
 
-                 continue;
 
-             if (hdr == HDR_CONTENT_LENGTH) {
 
-                 if (!get_token(s))
 
-                     continue;
 
-                 s->content_length = strtoumax(s->token->s, NULL, 10);
 
-             } else if (hdr == HDR_TRANSFER_ENCODING) {
 
-                 /*
 
-                  * The Transfer-Encoding header value should be a
 
-                  * comma-separated list of keywords including
 
-                  * "chunked", "deflate" and "gzip". We parse it in the
 
-                  * most superficial way, by just looking for "chunked"
 
-                  * and ignoring everything else.
 
-                  *
 
-                  * It's OK to do that because we're not actually
 
-                  * _using_ the error document - we only have to skip
 
-                  * over it to find the end of the HTTP response. So we
 
-                  * don't care if it's gzipped or not.
 
-                  */
 
-                 while (get_token(s)) {
 
-                     if (!stricmp(s->token->s, "chunked"))
 
-                         s->chunked_transfer = true;
 
-                 }
 
-             } else if (hdr == HDR_CONNECTION ||
 
-                        hdr == HDR_PROXY_CONNECTION) {
 
-                 if (!get_token(s))
 
-                     continue;
 
-                 if (!stricmp(s->token->s, "close"))
 
-                     s->connection_close = true;
 
-                 else if (!stricmp(s->token->s, "keep-alive"))
 
-                     s->connection_close = false;
 
-             } else if (hdr == HDR_PROXY_AUTHENTICATE) {
 
-                 HttpAuthDetails *auth = parse_http_auth_header(s);
 
-                 /*
 
-                  * See if we prefer this set of auth details to the
 
-                  * previous one we had (either from a previous auth
 
-                  * header, or the fallback when no auth header is
 
-                  * provided at all).
 
-                  */
 
-                 bool change;
 
-                 if (auth->auth_type != s->next_auth->auth_type) {
 
-                     /* Use the preference order implied by the enum */
 
-                     change = auth->auth_type > s->next_auth->auth_type;
 
-                 } else if (auth->auth_type == AUTH_DIGEST &&
 
-                            auth->digest_hash != s->next_auth->digest_hash) {
 
-                     /* Choose based on the hash functions */
 
-                     change = auth->digest_hash > s->next_auth->digest_hash;
 
-                 } else {
 
-                     /*
 
-                      * If in doubt, go with the later one of the
 
-                      * headers.
 
-                      *
 
-                      * The main reason for this is so that an error in
 
-                      * interpreting an auth header will supersede the
 
-                      * default error we preload saying 'no header
 
-                      * found', because that would be a particularly
 
-                      * bad error to report if there _was_ one.
 
-                      *
 
-                      * But we're in a tie-breaking situation by now,
 
-                      * so there's no other reason to choose - we might
 
-                      * as well apply the same policy everywhere else
 
-                      * too.
 
-                      */
 
-                     change = true;
 
-                 }
 
-                 if (change) {
 
-                     http_auth_details_free(s->next_auth);
 
-                     s->next_auth = auth;
 
-                 } else {
 
-                     http_auth_details_free(auth);
 
-                 }
 
-             }
 
-             } // WINSCP
 
-         } while (s->header->len > 0);
 
-         /* Read and ignore the entire response document */
 
-         if (!s->chunked_transfer) {
 
-             /* Simple approach: read exactly Content-Length bytes */
 
-             crMaybeWaitUntilV(bufchain_try_consume(
 
-                                   pn->input, s->content_length));
 
-         } else {
 
-             /* Chunked transfer: read a sequence of
 
-              * <hex length>\r\n<data>\r\n chunks, terminating in one with
 
-              * zero length */
 
-             do {
 
-                 /*
 
-                  * Expect a chunk length
 
-                  */
 
-                 s->chunk_length = 0;
 
-                 while (true) {
 
-                     char c;
 
-                     crMaybeWaitUntilV(bufchain_try_fetch_consume(
 
-                                           pn->input, &c, 1));
 
-                     if (c == '\r') {
 
-                         continue;
 
-                     } else if (c == '\n') {
 
-                         break;
 
-                     } else if ('0' <= c && c <= '9') {
 
-                         s->chunk_length = s->chunk_length*16 + (c-'0');
 
-                     } else if ('A' <= c && c <= 'F') {
 
-                         s->chunk_length = s->chunk_length*16 + (c-'A'+10);
 
-                     } else if ('a' <= c && c <= 'f') {
 
-                         s->chunk_length = s->chunk_length*16 + (c-'a'+10);
 
-                     } else {
 
-                         pn->error = dupprintf(
 
-                             "Received bad character 0x%02X in chunk length "
 
-                             "during HTTP chunked transfer encoding",
 
-                             (unsigned)(unsigned char)c);
 
-                         crStopV;
 
-                     }
 
-                 }
 
-                 /*
 
-                  * Expect that many bytes of chunked data
 
-                  */
 
-                 crMaybeWaitUntilV(bufchain_try_consume(
 
-                                       pn->input, s->chunk_length));
 
-                 /* Now expect \r\n */
 
-                 {
 
-                     char buf[2];
 
-                     crMaybeWaitUntilV(bufchain_try_fetch_consume(
 
-                                           pn->input, buf, 2));
 
-                     if (memcmp(buf, "\r\n", 2)) {
 
-                         pn->error = dupprintf(
 
-                             "Missing CRLF after chunk "
 
-                             "during HTTP chunked transfer encoding");
 
-                         crStopV;
 
-                     }
 
-                 }
 
-             } while (s->chunk_length);
 
-         }
 
-         if (200 <= s->http_status && s->http_status < 300) {
 
-             /* Any 2xx HTTP response means we're done */
 
-             goto authenticated;
 
-         } else if (s->http_status == 407) {
 
-             /* 407 is Proxy Authentication Required, which we may be
 
-              * able to do something about. */
 
-             if (s->connection_close) {
 
-                 /* If we got 407 + connection closed, reconnect before
 
-                  * sending our next request. */
 
-                 pn->reconnect = true;
 
-             }
 
-             /* If the best we can do is report some kind of error from
 
-              * a Proxy-Auth header (or an error saying there wasn't
 
-              * one at all), and no successful parsing of an auth
 
-              * header superseded that, then just throw that error and
 
-              * die. */
 
-             if (s->next_auth->auth_type == AUTH_ERROR) {
 
-                 pn->error = dupstr(s->next_auth->error->s);
 
-                 crStopV;
 
-             }
 
-             /* If we have auth details from the Conf and haven't tried
 
-              * them yet, that's our first step. */
 
-             if (s->try_auth_from_conf) {
 
-                 s->try_auth_from_conf = false;
 
-                 continue;
 
-             }
 
-             /* If the server sent us stale="true" in a Digest auth
 
-              * header, that means we _don't_ need to request a new
 
-              * password yet; just try again with the existing details
 
-              * and the fresh nonce it sent us. */
 
-             if (s->next_auth->digest_nonce_was_stale)
 
-                 continue;
 
-             /* Either we never had a password in the first place, or
 
-              * the one we already presented was rejected. We can only
 
-              * proceed from here if we have a way to ask the user
 
-              * questions. */
 
-             if (!pn->itr) {
 
-                 pn->error = dupprintf("HTTP proxy requested authentication "
 
-                                       "which we do not have");
 
-                 crStopV;
 
-             }
 
-             /*
 
-              * Send some prompts to the user. We'll assume the
 
-              * password is always required (since it's just been
 
-              * rejected, even if we did send one before), and we'll
 
-              * prompt for the username only if we don't have one from
 
-              * the Conf.
 
-              */
 
-             s->prompts = proxy_new_prompts(pn->ps);
 
-             s->prompts->to_server = true;
 
-             s->prompts->from_server = false;
 
-             s->prompts->name = dupstr("HTTP proxy authentication");
 
-             if (!s->username->len) {
 
-                 s->username_prompt_index = s->prompts->n_prompts;
 
-                 add_prompt(s->prompts, dupstr("Proxy username: "), true);
 
-             } else {
 
-                 s->username_prompt_index = -1;
 
-             }
 
-             s->password_prompt_index = s->prompts->n_prompts;
 
-             add_prompt(s->prompts, dupstr("Proxy password: "), false);
 
-             while (true) {
 
-                 SeatPromptResult spr = seat_get_userpass_input(
 
-                     interactor_announce(pn->itr), s->prompts);
 
-                 if (spr.kind == SPRK_OK) {
 
-                     break;
 
-                 } else if (spr_is_abort(spr)) {
 
-                     proxy_spr_abort(pn, spr);
 
-                     crStopV;
 
-                 }
 
-                 crReturnV;
 
-             }
 
-             if (s->username_prompt_index != -1) {
 
-                 strbuf_clear(s->username);
 
-                 put_dataz(s->username,
 
-                           prompt_get_result_ref(
 
-                               s->prompts->prompts[s->username_prompt_index]));
 
-             }
 
-             strbuf_clear(s->password);
 
-             put_dataz(s->password,
 
-                       prompt_get_result_ref(
 
-                           s->prompts->prompts[s->password_prompt_index]));
 
-             free_prompts(s->prompts);
 
-             s->prompts = NULL;
 
-         } else {
 
-             /* Any other HTTP response is treated as permanent failure */
 
-             pn->error = dupprintf("HTTP response %s",
 
-                                   s->response->s + s->http_status_pos);
 
-             crStopV;
 
-         }
 
-     }
 
-   authenticated:
 
-     /*
 
-      * Success! Hand over to the main connection.
 
-      */
 
-     pn->done = true;
 
-     crFinishV;
 
- }
 
- const struct ProxyNegotiatorVT http_proxy_negotiator_vt = {
 
-     // WINSCP
 
-     /*.new =*/ proxy_http_new,
 
-     /*.process_queue =*/ proxy_http_process_queue,
 
-     /*.free =*/ proxy_http_free,
 
-     /*.type =*/ "HTTP",
 
- };
 
 
  |