@@ -22,6 +22,7 @@ void credential_clear(struct credential *c)
free(c->username);
free(c->password);
string_list_clear(&c->helpers, 0);
+ strvec_clear(&c->wwwauth_headers);
credential_init(c);
}
@@ -2,6 +2,7 @@
#define CREDENTIAL_H
#include "string-list.h"
+#include "strvec.h"
/**
* The credentials API provides an abstracted way of gathering username and
@@ -115,6 +116,19 @@ struct credential {
*/
struct string_list helpers;
+ /**
+ * A `strvec` of WWW-Authenticate header values. Each string
+ * is the value of a WWW-Authenticate header in an HTTP response,
+ * in the order they were received in the response.
+ */
+ struct strvec wwwauth_headers;
+
+ /**
+ * Internal use only. Used to keep track of split header fields
+ * in order to fold multiple lines into one value.
+ */
+ unsigned header_is_last_match:1;
+
unsigned approved:1,
configured:1,
quit:1,
@@ -130,6 +144,7 @@ struct credential {
#define CREDENTIAL_INIT { \
.helpers = STRING_LIST_INIT_DUP, \
+ .wwwauth_headers = STRVEC_INIT, \
}
/* Initialize a credential structure, setting all fields to empty. */
@@ -183,6 +183,82 @@ size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
return nmemb;
}
+static size_t fwrite_wwwauth(char *ptr, size_t eltsize, size_t nmemb, void *p)
+{
+ size_t size = eltsize * nmemb;
+ struct strvec *values = &http_auth.wwwauth_headers;
+ struct strbuf buf = STRBUF_INIT;
+ const char *val;
+ const char *z = NULL;
+
+ /*
+ * Header lines may not come NULL-terminated from libcurl so we must
+ * limit all scans to the maximum length of the header line, or leverage
+ * strbufs for all operations.
+ *
+ * In addition, it is possible that header values can be split over
+ * multiple lines as per RFC 2616 (even though this has since been
+ * deprecated in RFC 7230). A continuation header field value is
+ * identified as starting with a space or horizontal tab.
+ *
+ * The formal definition of a header field as given in RFC 2616 is:
+ *
+ * message-header = field-name ":" [ field-value ]
+ * field-name = token
+ * field-value = *( field-content | LWS )
+ * field-content = <the OCTETs making up the field-value
+ * and consisting of either *TEXT or combinations
+ * of token, separators, and quoted-string>
+ */
+
+ strbuf_add(&buf, ptr, size);
+
+ /* Strip the CRLF that should be present at the end of each field */
+ strbuf_trim_trailing_newline(&buf);
+
+ /* Start of a new WWW-Authenticate header */
+ if (skip_iprefix(buf.buf, "www-authenticate:", &val)) {
+ while (isspace(*val))
+ val++;
+
+ strvec_push(values, val);
+ http_auth.header_is_last_match = 1;
+ goto exit;
+ }
+
+ /*
+ * This line could be a continuation of the previously matched header
+ * field. If this is the case then we should append this value to the
+ * end of the previously consumed value.
+ */
+ if (http_auth.header_is_last_match && isspace(*buf.buf)) {
+ const char **v = values->v + values->nr - 1;
+ char *append = xstrfmt("%s%.*s", *v, (int)(size - 1), ptr + 1);
+
+ free((void*)*v);
+ *v = append;
+
+ goto exit;
+ }
+
+ /* This is the start of a new header we don't care about */
+ http_auth.header_is_last_match = 0;
+
+ /*
+ * If this is a HTTP status line and not a header field, this signals
+ * a different HTTP response. libcurl writes all the output of all
+ * response headers of all responses, including redirects.
+ * We only care about the last HTTP request response's headers so clear
+ * the existing array.
+ */
+ if (skip_iprefix(buf.buf, "http/", &z))
+ strvec_clear(values);
+
+exit:
+ strbuf_release(&buf);
+ return size;
+}
+
size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
{
return nmemb;
@@ -1829,6 +1905,8 @@ static int http_request(const char *url,
fwrite_buffer);
}
+ curl_easy_setopt(slot->curl, CURLOPT_HEADERFUNCTION, fwrite_wwwauth);
+
accept_language = http_get_accept_language_header();
if (accept_language)