mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-08-25 14:22:54 -06:00
Digest authentication support
git-svn-id: svn://ultimatepp.org/upp/trunk@1962 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
16d45496d3
commit
051004a13c
4 changed files with 116 additions and 1 deletions
|
|
@ -19,6 +19,7 @@ void HttpClient::Init()
|
|||
max_header_size = DEFAULT_MAX_HEADER_SIZE;
|
||||
max_content_size = DEFAULT_MAX_CONTENT_SIZE;
|
||||
keepalive = false;
|
||||
force_digest = false;
|
||||
std_headers = true;
|
||||
hasurlvar = false;
|
||||
method = METHOD_GET;
|
||||
|
|
@ -78,6 +79,14 @@ String HttpClient::ExecuteRedirect(int max_redirect, int retries, Gate2<int, int
|
|||
return String::GetVoid();
|
||||
}
|
||||
String data = Execute(progress);
|
||||
if(status_code == 401 && !IsNull(username) && !IsNull(authenticate)) {
|
||||
if(++nredir > max_redirect) {
|
||||
error = NFormat("Maximum number of digest authentication attempts exceeded: %d", max_redirect);
|
||||
return String::GetVoid();
|
||||
}
|
||||
Digest(CalculateDigest(authenticate));
|
||||
continue;
|
||||
}
|
||||
if(status_code >= 400 && status_code < 500) {
|
||||
error = status_line;
|
||||
return String::GetVoid();
|
||||
|
|
@ -146,6 +155,81 @@ HttpClient& HttpClient::UrlVar(const char *id, const String& data)
|
|||
return *this;
|
||||
}
|
||||
|
||||
String HttpClient::CalculateDigest(String authenticate) const
|
||||
{
|
||||
const char *p = authenticate;
|
||||
String realm, qop, nonce, opaque;
|
||||
while(*p) {
|
||||
if(!IsAlNum(*p)) {
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
const char *b = p;
|
||||
while(IsAlNum(*p))
|
||||
p++;
|
||||
String var = ToLower(String(b, p));
|
||||
String value;
|
||||
while(*p && (byte)*p <= ' ')
|
||||
p++;
|
||||
if(*p == '=') {
|
||||
p++;
|
||||
while(*p && (byte)*p <= ' ')
|
||||
p++;
|
||||
if(*p == '\"') {
|
||||
p++;
|
||||
while(*p && *p != '\"')
|
||||
if(*p != '\\' || *++p)
|
||||
value.Cat(*p++);
|
||||
if(*p == '\"')
|
||||
p++;
|
||||
}
|
||||
else {
|
||||
b = p;
|
||||
while(*p && *p != ',' && (byte)*p > ' ')
|
||||
p++;
|
||||
value = String(b, p);
|
||||
}
|
||||
}
|
||||
if(var == "realm")
|
||||
realm = value;
|
||||
else if(var == "qop")
|
||||
qop = value;
|
||||
else if(var == "nonce")
|
||||
nonce = var;
|
||||
else if(var == "opaque")
|
||||
opaque = var;
|
||||
}
|
||||
}
|
||||
String hv1, hv2;
|
||||
hv1 << username << ':' << realm << ':' << password;
|
||||
String ha1 = MD5String(hv1);
|
||||
hv2 << (method == METHOD_GET ? "GET" : method == METHOD_POST ? "POST" : "READ")
|
||||
<< ':' << path;
|
||||
String ha2 = MD5String(hv2);
|
||||
int nc = 1;
|
||||
String cnonce = FormatIntHex(Random(), 8);
|
||||
String hv;
|
||||
hv << BinhexEncode(ha1)
|
||||
<< ':' << nonce
|
||||
<< ':' << FormatIntHex(nc, 8)
|
||||
<< ':' << cnonce
|
||||
<< ':' << qop << ':' << BinhexEncode(ha2);
|
||||
String ha = MD5String(hv);
|
||||
String auth;
|
||||
auth << "Authorization: Digest "
|
||||
"username=" << AsCString(username)
|
||||
<< ", realm=" << AsCString(realm)
|
||||
<< ", nonce=" << AsCString(nonce)
|
||||
<< ", uri=" << AsCString(path)
|
||||
<< ", qop=" << AsCString(qop)
|
||||
<< ", nc=" << AsCString(FormatIntHex(nc, 8))
|
||||
<< ", cnonce=" << cnonce
|
||||
<< ", response=" << AsCString(BinhexEncode(ha))
|
||||
<< ", opaque=" << AsCString(opaque);
|
||||
return auth;
|
||||
}
|
||||
|
||||
String HttpClient::Execute(Gate2<int, int> progress)
|
||||
{
|
||||
LLOGBLOCK("HttpClient::Execute");
|
||||
|
|
@ -222,7 +306,9 @@ String HttpClient::Execute(Gate2<int, int> progress)
|
|||
}
|
||||
if(use_proxy && !IsNull(proxy_username))
|
||||
request << "Proxy-Authorization: basic " << Base64Encode(proxy_username + ':' + proxy_password) << "\r\n";
|
||||
if(!IsNull(username) || !IsNull(password))
|
||||
if(!IsNull(digest))
|
||||
request << "Authorization: Digest " << digest << "\r\n";
|
||||
else if(!force_digest && (!IsNull(username) || !IsNull(password)))
|
||||
request << "Authorization: basic " << Base64Encode(username + ":" + password) << "\r\n";
|
||||
request << client_headers << "\r\n" << postdata;
|
||||
LLOG("host = " << host << ", port = " << port);
|
||||
|
|
@ -285,10 +371,12 @@ String HttpClient::Execute(Gate2<int, int> progress)
|
|||
static const char ce[] = "content-encoding:";
|
||||
static const char te[] = "transfer-encoding:";
|
||||
static const char lo[] = "location:";
|
||||
static const char au[] = "www-authenticate:";
|
||||
static const int CL_LENGTH = sizeof(cl) - 1;
|
||||
static const int CE_LENGTH = sizeof(ce) - 1;
|
||||
static const int TE_LENGTH = sizeof(te) - 1;
|
||||
static const int LO_LENGTH = sizeof(lo) - 1;
|
||||
static const int AU_LENGTH = sizeof(au) - 1;
|
||||
if(!MemICmp(p, cl, CL_LENGTH)) {
|
||||
for(p += CL_LENGTH; *p == ' '; p++)
|
||||
;
|
||||
|
|
@ -323,6 +411,11 @@ String HttpClient::Execute(Gate2<int, int> progress)
|
|||
if(p >= 0 && q < 0)
|
||||
redirect_url.Cat(path.GetIter(p));
|
||||
}
|
||||
else if(!MemICmp(p, au, AU_LENGTH)) {
|
||||
for(p += LO_LENGTH; *p == ' '; p++)
|
||||
;
|
||||
authenticate = String(p, e);
|
||||
}
|
||||
if(server_headers.GetLength() + (e - b) + 2 > max_header_size) {
|
||||
error = NFormat(t_("%s:%d: maximum header length exceeded (%d B)"), host, port, max_header_size);
|
||||
return String::GetVoid();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ public:
|
|||
HttpClient& Port(int p) { port = p; return *this; }
|
||||
HttpClient& Path(String p) { path = p; return *this; }
|
||||
HttpClient& User(String u, String p) { username = u; password = p; return *this; }
|
||||
HttpClient& Digest() { force_digest = true; return *this; }
|
||||
HttpClient& Digest(String d) { digest = d; return *this; }
|
||||
HttpClient& URL(const char *url);
|
||||
HttpClient& Url(const char *id, const String& data);
|
||||
HttpClient& KeepAlive(bool k) { keepalive = k; return *this; }
|
||||
|
|
@ -60,6 +62,8 @@ public:
|
|||
bool IsRedirect() const { return is_redirect; }
|
||||
String GetRedirectURL() const { return redirect_url; }
|
||||
|
||||
String CalculateDigest(String authenticate) const;
|
||||
|
||||
void Close() { socket.Close(); }
|
||||
|
||||
static void Trace(bool b = true);
|
||||
|
|
@ -68,6 +72,7 @@ public:
|
|||
Socket socket;
|
||||
bool keepalive;
|
||||
bool aborted;
|
||||
bool force_digest;
|
||||
String error;
|
||||
|
||||
int timeout_msecs;
|
||||
|
|
@ -84,6 +89,7 @@ public:
|
|||
String path;
|
||||
String username;
|
||||
String password;
|
||||
String digest;
|
||||
String client_headers;
|
||||
String accept;
|
||||
String agent;
|
||||
|
|
@ -100,6 +106,7 @@ public:
|
|||
String server_headers;
|
||||
|
||||
String redirect_url;
|
||||
String authenticate;
|
||||
|
||||
enum {
|
||||
DEFAULT_PORT = 80,
|
||||
|
|
|
|||
|
|
@ -217,6 +217,19 @@ String BinHexEncode(const char *s, const char *e)
|
|||
return out;
|
||||
}
|
||||
|
||||
String BinhexEncode(const char *s, const char *e)
|
||||
{
|
||||
static const char bh[] = "0123456789abcdef";
|
||||
int l = int(e - s);
|
||||
StringBuffer out(2 * l);
|
||||
char *p = out;
|
||||
for(; s < e; s++) {
|
||||
*p++ = bh[(*s >> 4) & 0xF];
|
||||
*p++ = bh[*s & 0xF];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
String BinHexDecode(const char *p, const char *e)
|
||||
{
|
||||
StringBuffer out;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ String OtpEncode(String password, String otp_key);
|
|||
String EncryptString(String password, String otp_key);
|
||||
String BinHexEncode(const char *b, const char *e);
|
||||
inline String BinHexEncode(String data) { return BinHexEncode(data.Begin(), data.End()); }
|
||||
String BinhexEncode(const char *b, const char *e);
|
||||
inline String BinhexEncode(String data) { return BinhexEncode(data.Begin(), data.End()); }
|
||||
String BinHexDecode(const char *b, const char *e);
|
||||
inline String BinHexDecode(String data) { return BinHexDecode(data.Begin(), data.End()); }
|
||||
String Base64Encode(const char *b, const char *e);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue