add digest authentication
This commit is contained in:
parent
bb9e38286d
commit
b4b34572c7
|
|
@ -6,7 +6,7 @@
|
|||
#begin lib_target
|
||||
#define TARGET downloader
|
||||
|
||||
#define COMBINED_SOURCES $[TARGET]_composite1.cxx \
|
||||
#define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx \
|
||||
$[if $[HAVE_NET], $[TARGET]_composite3.cxx] \
|
||||
$[if $[HAVE_ZLIB], $[TARGET]_composite4.cxx] \
|
||||
|
||||
|
|
@ -18,8 +18,12 @@
|
|||
bioStream.I bioStream.h bioStreamBuf.h \
|
||||
chunkedStream.I chunkedStream.h chunkedStreamBuf.h \
|
||||
extractor.h \
|
||||
httpAuthorization.I httpAuthorization.h \
|
||||
httpBasicAuthorization.I httpBasicAuthorization.h \
|
||||
httpClient.I httpClient.h \
|
||||
httpChannel.I httpChannel.h \
|
||||
httpDigestAuthorization.I httpDigestAuthorization.h \
|
||||
httpEnum.h \
|
||||
identityStream.I identityStream.h identityStreamBuf.h \
|
||||
multiplexStream.I multiplexStream.h \
|
||||
multiplexStreamBuf.I multiplexStreamBuf.h \
|
||||
|
|
@ -37,8 +41,12 @@
|
|||
bioStream.cxx bioStreamBuf.cxx \
|
||||
chunkedStream.cxx chunkedStreamBuf.cxx \
|
||||
extractor.cxx \
|
||||
httpAuthorization.cxx \
|
||||
httpBasicAuthorization.cxx \
|
||||
httpClient.cxx \
|
||||
httpChannel.cxx \
|
||||
httpDigestAuthorization.cxx \
|
||||
httpEnum.cxx \
|
||||
identityStream.cxx identityStreamBuf.cxx \
|
||||
multiplexStream.cxx multiplexStreamBuf.cxx \
|
||||
socketStream.cxx \
|
||||
|
|
@ -57,8 +65,12 @@
|
|||
download_utils.h downloadDb.h downloadDb.I \
|
||||
downloader.h downloader.I \
|
||||
extractor.h \
|
||||
httpAuthorization.I httpAuthorization.h \
|
||||
httpBasicAuthorization.I httpBasicAuthorization.h \
|
||||
httpClient.I httpClient.h \
|
||||
httpChannel.I httpChannel.h \
|
||||
httpDigestAuthorization.I httpDigestAuthorization.h \
|
||||
httpEnum.h \
|
||||
identityStream.I identityStream.h identityStreamBuf.h \
|
||||
multiplexStream.I multiplexStream.h \
|
||||
multiplexStreamBuf.I multiplexStreamBuf.I \
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
// because the socket has genuinely closed, or false if
|
||||
// we can expect more data to come along shortly.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool IBioStream::
|
||||
bool IBioStream::
|
||||
is_closed() {
|
||||
if (_buf._is_closed) {
|
||||
return true;
|
||||
|
|
@ -42,7 +42,7 @@ is_closed() {
|
|||
// false if we can expect to send more data along
|
||||
// shortly.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool OBioStream::
|
||||
bool OBioStream::
|
||||
is_closed() {
|
||||
if (_buf._is_closed) {
|
||||
return true;
|
||||
|
|
@ -59,7 +59,7 @@ is_closed() {
|
|||
// false if we can expect to read or send more data
|
||||
// shortly.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool BioStream::
|
||||
bool BioStream::
|
||||
is_closed() {
|
||||
if (_buf._is_closed) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
// because the socket has genuinely closed, or false if
|
||||
// we can expect more data to come along shortly.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool IChunkedStream::
|
||||
bool IChunkedStream::
|
||||
is_closed() {
|
||||
if (_buf._done || (*_buf._source)->is_closed()) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include "httpChannel.h"
|
||||
|
||||
|
||||
Configure(config_downloader);
|
||||
ConfigureDef(config_downloader);
|
||||
NotifyCategoryDef(downloader, "");
|
||||
|
||||
// How often we write to disk is determined by this ratio which is
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@
|
|||
#ifndef CONFIG_DOWNLOADER_H
|
||||
#define CONFIG_DOWNLOADER_H
|
||||
|
||||
#include <pandabase.h>
|
||||
#include <notifyCategoryProxy.h>
|
||||
#include "pandabase.h"
|
||||
#include "notifyCategoryProxy.h"
|
||||
#include "dconfig.h"
|
||||
|
||||
ConfigureDecl(config_downloader, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
||||
NotifyCategoryDecl(downloader, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
||||
|
||||
extern const int downloader_disk_write_frequency;
|
||||
|
|
|
|||
|
|
@ -7,12 +7,3 @@
|
|||
#include "chunkedStream.cxx"
|
||||
#include "chunkedStreamBuf.cxx"
|
||||
#include "extractor.cxx"
|
||||
#include "httpClient.cxx"
|
||||
#include "httpChannel.cxx"
|
||||
#include "identityStream.cxx"
|
||||
#include "identityStreamBuf.cxx"
|
||||
#include "multiplexStream.cxx"
|
||||
#include "multiplexStreamBuf.cxx"
|
||||
#include "socketStream.cxx"
|
||||
#include "urlSpec.cxx"
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
#include "httpAuthorization.cxx"
|
||||
#include "httpBasicAuthorization.cxx"
|
||||
#include "httpClient.cxx"
|
||||
#include "httpChannel.cxx"
|
||||
#include "httpDigestAuthorization.cxx"
|
||||
#include "httpEnum.cxx"
|
||||
#include "identityStream.cxx"
|
||||
#include "identityStreamBuf.cxx"
|
||||
#include "multiplexStream.cxx"
|
||||
#include "multiplexStreamBuf.cxx"
|
||||
#include "socketStream.cxx"
|
||||
#include "urlSpec.cxx"
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Filename: httpAuthorization.I
|
||||
// Created by: drose (22Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPAuthorization::get_realm
|
||||
// Access: Public
|
||||
// Description: Returns the realm to which this authorization
|
||||
// applies. This is the server-supplied string that may
|
||||
// have meaning to the user, and describes the general
|
||||
// collection of things protected by this password.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
const string &HTTPAuthorization::
|
||||
get_realm() const {
|
||||
return _realm;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPAuthorization::get_domain
|
||||
// Access: Public
|
||||
// Description: Returns the set of domain strings on which this
|
||||
// authorization applies. This is the set of URL
|
||||
// prefixes for which this authorization should be
|
||||
// used.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
const vector_string &HTTPAuthorization::
|
||||
get_domain() const {
|
||||
return _domain;
|
||||
}
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
// Filename: httpAuthorization.cxx
|
||||
// Created by: drose (22Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "httpAuthorization.h"
|
||||
#include "httpChannel.h"
|
||||
#include "urlSpec.h"
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
static const char base64_table[64] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/',
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPAuthorization::Constructor
|
||||
// Access: Protected
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
HTTPAuthorization::
|
||||
HTTPAuthorization(const HTTPAuthorization::Tokens &tokens,
|
||||
const URLSpec &url, bool is_proxy) {
|
||||
Tokens::const_iterator ti;
|
||||
ti = tokens.find("realm");
|
||||
if (ti != tokens.end()) {
|
||||
_realm = (*ti).second;
|
||||
}
|
||||
|
||||
URLSpec canon = get_canonical_url(url);
|
||||
|
||||
ti = tokens.find("domain");
|
||||
if (ti != tokens.end() && !is_proxy) {
|
||||
// Now the domain consists of a series of space-separated URL
|
||||
// prefixes.
|
||||
const string &domain = (*ti).second;
|
||||
size_t p = 0;
|
||||
while (p < domain.length()) {
|
||||
while (p < domain.length() && isspace(domain[p])) {
|
||||
++p;
|
||||
}
|
||||
size_t q = p;
|
||||
while (q < domain.length() && !isspace(domain[q])) {
|
||||
++q;
|
||||
}
|
||||
if (q > p) {
|
||||
string domain_str = domain.substr(p, q - p);
|
||||
URLSpec domain_url(domain_str);
|
||||
if (domain_url.has_server()) {
|
||||
// A fully-qualified URL.
|
||||
_domain.push_back(get_canonical_url(domain_url).get_url());
|
||||
} else {
|
||||
// A relative URL; relative to this path.
|
||||
domain_url = canon;
|
||||
domain_url.set_path(domain_str);
|
||||
_domain.push_back(domain_url.get_url());
|
||||
}
|
||||
}
|
||||
p = q;
|
||||
}
|
||||
|
||||
} else {
|
||||
// If no domain is defined by the server, use the supplied URL.
|
||||
// Truncate it to the rightmost slash.
|
||||
string canon_str = canon.get_url();
|
||||
size_t slash = canon_str.rfind('/');
|
||||
nassertv(slash != string::npos);
|
||||
_domain.push_back(canon_str.substr(0, slash + 1));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPAuthorization::Destructor
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
HTTPAuthorization::
|
||||
~HTTPAuthorization() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPAuthorization::is_valid
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns true if the authorization challenge was
|
||||
// correctly parsed and is usable, or false if there was
|
||||
// some unsupported algorithm or some such requested by
|
||||
// the server, rendering the challenge unmeetable.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPAuthorization::
|
||||
is_valid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPAuthorization::parse_authentication_schemes
|
||||
// Access: Public, Static
|
||||
// Description: Decodes the text following a WWW-Authenticate: or
|
||||
// Proxy-Authenticate: header field.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HTTPAuthorization::
|
||||
parse_authentication_schemes(HTTPAuthorization::AuthenticationSchemes &schemes,
|
||||
const string &field_value) {
|
||||
// This string will consist of one or more records of the form:
|
||||
//
|
||||
// scheme token=value[,token=value[,...]]
|
||||
//
|
||||
// If there are multiple records, they will be comma-delimited,
|
||||
// which makes parsing just a bit tricky.
|
||||
|
||||
// Start by skipping initial whitespace.
|
||||
size_t p = 0;
|
||||
while (p < field_value.length() && isspace(field_value[p])) {
|
||||
++p;
|
||||
}
|
||||
|
||||
if (p < field_value.length()) {
|
||||
size_t q = p;
|
||||
while (q < field_value.length() && !isspace(field_value[q])) {
|
||||
++q;
|
||||
}
|
||||
// Here's our first scheme.
|
||||
string scheme = HTTPChannel::downcase(field_value.substr(p, q - p));
|
||||
Tokens *tokens = &(schemes[scheme]);
|
||||
|
||||
// Now pull off the tokens, one at a time.
|
||||
p = q + 1;
|
||||
while (p < field_value.length()) {
|
||||
q = p;
|
||||
while (q < field_value.length() && field_value[q] != '=' &&
|
||||
field_value[q] != ',' && !isspace(field_value[q])) {
|
||||
++q;
|
||||
}
|
||||
if (field_value[q] == '=') {
|
||||
// This is a token.
|
||||
string token = HTTPChannel::downcase(field_value.substr(p, q - p));
|
||||
string value;
|
||||
p = scan_quoted_or_unquoted_string(value, field_value, q + 1);
|
||||
(*tokens)[token] = value;
|
||||
|
||||
// Skip trailing whitespace and extra commas.
|
||||
while (p < field_value.length() &&
|
||||
(field_value[p] == ',' || isspace(field_value[p]))) {
|
||||
++p;
|
||||
}
|
||||
|
||||
} else {
|
||||
// This is not a token; it must be the start of a new scheme.
|
||||
scheme = HTTPChannel::downcase(field_value.substr(p, q - p));
|
||||
tokens = &(schemes[scheme]);
|
||||
p = q + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPAuthorization::get_canonical_url
|
||||
// Access: Public, Static
|
||||
// Description: Returns the "canonical" URL corresponding to this
|
||||
// URL. This is the same URL with an explicit port
|
||||
// indication, an explicit scheme, and a non-empty path,
|
||||
// etc.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
URLSpec HTTPAuthorization::
|
||||
get_canonical_url(const URLSpec &url) {
|
||||
URLSpec canon = url;
|
||||
canon.set_scheme(canon.get_scheme());
|
||||
canon.set_username(string());
|
||||
canon.set_port(canon.get_port());
|
||||
canon.set_path(canon.get_path());
|
||||
|
||||
return canon;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPAuthorization::base64_encode
|
||||
// Access: Public, Static
|
||||
// Description: Returns the input string encoded using base64. No
|
||||
// respect is paid to maintaining a 76-char line length.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPAuthorization::
|
||||
base64_encode(const string &s) {
|
||||
// Collect the string 3 bytes at a time into 24-bit words, then
|
||||
// output each word using 4 bytes.
|
||||
size_t num_words = (s.size() + 2) / 3;
|
||||
string result;
|
||||
result.reserve(num_words * 4);
|
||||
size_t p;
|
||||
for (p = 0; p + 2 < s.size(); p += 3) {
|
||||
unsigned int word =
|
||||
((unsigned)s[p] << 16) |
|
||||
((unsigned)s[p + 1] << 8) |
|
||||
((unsigned)s[p + 2]);
|
||||
result += base64_table[(word >> 18) & 0x3f];
|
||||
result += base64_table[(word >> 12) & 0x3f];
|
||||
result += base64_table[(word >> 6) & 0x3f];
|
||||
result += base64_table[(word) & 0x3f];
|
||||
}
|
||||
// What's left over?
|
||||
if (p < s.size()) {
|
||||
unsigned int word = ((unsigned)s[p] << 16);
|
||||
p++;
|
||||
if (p < s.size()) {
|
||||
word |= ((unsigned)s[p] << 8);
|
||||
p++;
|
||||
nassertr(p == s.size(), result);
|
||||
|
||||
result += base64_table[(word >> 18) & 0x3f];
|
||||
result += base64_table[(word >> 12) & 0x3f];
|
||||
result += base64_table[(word >> 6) & 0x3f];
|
||||
result += '=';
|
||||
} else {
|
||||
result += base64_table[(word >> 18) & 0x3f];
|
||||
result += base64_table[(word >> 12) & 0x3f];
|
||||
result += '=';
|
||||
result += '=';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPAuthorization::scan_quoted_or_unquoted_string
|
||||
// Access: Protected, Static
|
||||
// Description: Scans the string source beginning at character
|
||||
// position start, to identify either the
|
||||
// (space-delimited) unquoted string there, or the
|
||||
// (quote-delimited) quoted string. In either case,
|
||||
// fills the string found into result, and returns the
|
||||
// next character position after the string (or after
|
||||
// its closing quote mark).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
size_t HTTPAuthorization::
|
||||
scan_quoted_or_unquoted_string(string &result, const string &source,
|
||||
size_t start) {
|
||||
result = string();
|
||||
|
||||
if (start < source.length()) {
|
||||
if (source[start] == '"') {
|
||||
// Quoted string.
|
||||
size_t p = start + 1;
|
||||
while (p < source.length() && source[p] != '"') {
|
||||
if (source[p] == '\\') {
|
||||
// Backslash escapes.
|
||||
++p;
|
||||
if (p < source.length()) {
|
||||
result += source[p];
|
||||
++p;
|
||||
}
|
||||
} else {
|
||||
result += source[p];
|
||||
++p;
|
||||
}
|
||||
}
|
||||
if (p < source.length()) {
|
||||
++p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// Unquoted string.
|
||||
size_t p = start;
|
||||
while (p < source.length() && source[p] != ',' && !isspace(source[p])) {
|
||||
result += source[p];
|
||||
++p;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Empty string.
|
||||
return start;
|
||||
}
|
||||
|
||||
#endif // HAVE_SSL
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
// Filename: httpAuthorization.h
|
||||
// Created by: drose (22Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef HTTPAUTHORIZATION_H
|
||||
#define HTTPAUTHORIZATION_H
|
||||
|
||||
#include "pandabase.h"
|
||||
|
||||
// This module requires OpenSSL to compile, even though it doesn't
|
||||
// actually use any OpenSSL code, because it is a support module for
|
||||
// HTTPChannel, which *does* use OpenSSL code.
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
#include "referenceCount.h"
|
||||
#include "httpEnum.h"
|
||||
|
||||
class URLSpec;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : HTTPAuthorization
|
||||
// Description : A base class for storing information used to fulfill
|
||||
// authorization requests in the past, which can
|
||||
// possibly be re-used for future requests to the same
|
||||
// server.
|
||||
//
|
||||
// This class does not need to be exported from the DLL
|
||||
// because it has no public interface; it is simply a
|
||||
// helper class for HTTPChannel.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class HTTPAuthorization : public ReferenceCount {
|
||||
public:
|
||||
typedef pmap<string, string> Tokens;
|
||||
typedef pmap<string, Tokens> AuthenticationSchemes;
|
||||
|
||||
protected:
|
||||
HTTPAuthorization(const Tokens &tokens, const URLSpec &url,
|
||||
bool is_proxy);
|
||||
public:
|
||||
virtual ~HTTPAuthorization();
|
||||
|
||||
virtual const string &get_mechanism() const=0;
|
||||
virtual bool is_valid();
|
||||
|
||||
INLINE const string &get_realm() const;
|
||||
INLINE const vector_string &get_domain() const;
|
||||
|
||||
virtual string generate(HTTPEnum::Method method, const string &request_path,
|
||||
const string &username, const string &body)=0;
|
||||
|
||||
static void parse_authentication_schemes(AuthenticationSchemes &schemes,
|
||||
const string &field_value);
|
||||
static URLSpec get_canonical_url(const URLSpec &url);
|
||||
static string base64_encode(const string &s);
|
||||
|
||||
protected:
|
||||
static size_t scan_quoted_or_unquoted_string(string &result,
|
||||
const string &source,
|
||||
size_t start);
|
||||
|
||||
protected:
|
||||
string _realm;
|
||||
vector_string _domain;
|
||||
};
|
||||
|
||||
#include "httpAuthorization.I"
|
||||
|
||||
#endif // HAVE_SSL
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Filename: httpBasicAuthorization.I
|
||||
// Created by: drose (22Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// Filename: httpBasicAuthorization.cxx
|
||||
// Created by: drose (22Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "httpBasicAuthorization.h"
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
const string HTTPBasicAuthorization::_mechanism = "basic";
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPBasicAuthorization::Constructor
|
||||
// Access: Protected
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
HTTPBasicAuthorization::
|
||||
HTTPBasicAuthorization(const HTTPAuthorization::Tokens &tokens,
|
||||
const URLSpec &url, bool is_proxy) :
|
||||
HTTPAuthorization(tokens, url, is_proxy)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPBasicAuthorization::Destructor
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
HTTPBasicAuthorization::
|
||||
~HTTPBasicAuthorization() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPBasicAuthorization::get_mechanism
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns the type of authorization mechanism,
|
||||
// represented as a string, e.g. "basic".
|
||||
////////////////////////////////////////////////////////////////////
|
||||
const string &HTTPBasicAuthorization::
|
||||
get_mechanism() const {
|
||||
return _mechanism;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPBasicAuthorization::generate
|
||||
// Access: Public, Virtual
|
||||
// Description: Generates a suitable authorization string to send
|
||||
// to the server, based on the data stored within this
|
||||
// object, for retrieving the indicated URL with the
|
||||
// given username:password.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPBasicAuthorization::
|
||||
generate(HTTPEnum::Method, const string &,
|
||||
const string &username, const string &) {
|
||||
return "Basic " + base64_encode(username);
|
||||
}
|
||||
|
||||
#endif // HAVE_SSL
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// Filename: httpBasicAuthorization.h
|
||||
// Created by: drose (22Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef HTTPBASICAUTHORIZATION_H
|
||||
#define HTTPBASICAUTHORIZATION_H
|
||||
|
||||
#include "pandabase.h"
|
||||
|
||||
// This module requires OpenSSL to compile, even though it doesn't
|
||||
// actually use any OpenSSL code, because it is a support module for
|
||||
// HTTPChannel, which *does* use OpenSSL code.
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
#include "httpAuthorization.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : HTTPBasicAuthorization
|
||||
// Description : Implements the "Basic" type of HTTP authorization.
|
||||
// This authorization sends usernames and passwords over
|
||||
// the net in cleartext, so it's not much in the way of
|
||||
// security, but it's easy to implement and therefore
|
||||
// widely supported.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class HTTPBasicAuthorization : public HTTPAuthorization {
|
||||
public:
|
||||
HTTPBasicAuthorization(const Tokens &tokens, const URLSpec &url,
|
||||
bool is_proxy);
|
||||
virtual ~HTTPBasicAuthorization();
|
||||
|
||||
virtual const string &get_mechanism() const;
|
||||
virtual string generate(HTTPEnum::Method method, const string &request_path,
|
||||
const string &username, const string &body);
|
||||
|
||||
private:
|
||||
static const string _mechanism;
|
||||
};
|
||||
|
||||
#include "httpBasicAuthorization.I"
|
||||
|
||||
#endif // HAVE_SSL
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
INLINE bool HTTPChannel::
|
||||
is_valid() const {
|
||||
return (_state != S_failure && (_status_code / 100) == 2 &&
|
||||
(has_no_body() || !_source.is_null()));
|
||||
(_server_response_has_no_body || !_source.is_null()));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -64,7 +64,7 @@ get_url() const {
|
|||
// server, as one of the HTTPClient enumerated types,
|
||||
// e.g. HTTPClient::HV_11.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE HTTPClient::HTTPVersion HTTPChannel::
|
||||
INLINE HTTPEnum::HTTPVersion HTTPChannel::
|
||||
get_http_version() const {
|
||||
return _http_version;
|
||||
}
|
||||
|
|
@ -113,26 +113,34 @@ get_status_string() const {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::get_realm
|
||||
// Function: HTTPChannel::get_www_realm
|
||||
// Access: Published
|
||||
// Description: If the document failed to connect because of a 401
|
||||
// (Authorization required) or 407 (Proxy authorization
|
||||
// required) error, this method should return the
|
||||
// (Authorization required), this method will return the
|
||||
// "realm" returned by the server in which the requested
|
||||
// document must be authenticated. This string may be
|
||||
// presented to the user to request an associated
|
||||
// username and password (which then should be stored in
|
||||
// HTTPClient::set_username()).
|
||||
//
|
||||
// If the document was retrieved successfully, or failed
|
||||
// to connect for some other reason, this string will be
|
||||
// empty. If it is empty even in the presence of a 401
|
||||
// or 407 error, then the client code and the server do
|
||||
// not have a common scheme for exchanging passwords.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const string &HTTPChannel::
|
||||
get_realm() const {
|
||||
return _realm;
|
||||
get_www_realm() const {
|
||||
return _www_realm;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::get_proxy_realm
|
||||
// Access: Published
|
||||
// Description: If the document failed to connect because of a 407
|
||||
// (Proxy authorization required), this method will
|
||||
// return the "realm" returned by the proxy. This
|
||||
// string may be presented to the user to request an
|
||||
// associated username and password (which then should
|
||||
// be stored in HTTPClient::set_username()).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const string &HTTPChannel::
|
||||
get_proxy_realm() const {
|
||||
return _proxy_realm;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -383,7 +391,7 @@ reset() {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool HTTPChannel::
|
||||
post_form(const URLSpec &url, const string &body) {
|
||||
begin_request(M_post, url, body, false, 0, 0);
|
||||
begin_request(HTTPEnum::M_post, url, body, false, 0, 0);
|
||||
run();
|
||||
return is_valid();
|
||||
}
|
||||
|
|
@ -396,7 +404,7 @@ post_form(const URLSpec &url, const string &body) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool HTTPChannel::
|
||||
get_document(const URLSpec &url) {
|
||||
begin_request(M_get, url, string(), false, 0, 0);
|
||||
begin_request(HTTPEnum::M_get, url, string(), false, 0, 0);
|
||||
run();
|
||||
return is_valid();
|
||||
}
|
||||
|
|
@ -413,7 +421,7 @@ get_document(const URLSpec &url) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool HTTPChannel::
|
||||
get_subdocument(const URLSpec &url, size_t first_byte, size_t last_byte) {
|
||||
begin_request(M_get, url, string(), false, first_byte, last_byte);
|
||||
begin_request(HTTPEnum::M_get, url, string(), false, first_byte, last_byte);
|
||||
run();
|
||||
return is_valid();
|
||||
}
|
||||
|
|
@ -429,7 +437,7 @@ get_subdocument(const URLSpec &url, size_t first_byte, size_t last_byte) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool HTTPChannel::
|
||||
get_header(const URLSpec &url) {
|
||||
begin_request(M_head, url, string(), false, 0, 0);
|
||||
begin_request(HTTPEnum::M_head, url, string(), false, 0, 0);
|
||||
run();
|
||||
return is_valid();
|
||||
}
|
||||
|
|
@ -448,7 +456,7 @@ get_header(const URLSpec &url) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool HTTPChannel::
|
||||
connect_to(const URLSpec &url) {
|
||||
begin_request(M_connect, url, string(), false, 0, 0);
|
||||
begin_request(HTTPEnum::M_connect, url, string(), false, 0, 0);
|
||||
run();
|
||||
return is_connection_ready();
|
||||
}
|
||||
|
|
@ -469,7 +477,7 @@ connect_to(const URLSpec &url) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HTTPChannel::
|
||||
begin_post_form(const URLSpec &url, const string &body) {
|
||||
begin_request(M_post, url, body, true, 0, 0);
|
||||
begin_request(HTTPEnum::M_post, url, body, true, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -488,7 +496,7 @@ begin_post_form(const URLSpec &url, const string &body) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HTTPChannel::
|
||||
begin_get_document(const URLSpec &url) {
|
||||
begin_request(M_get, url, string(), true, 0, 0);
|
||||
begin_request(HTTPEnum::M_get, url, string(), true, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -505,7 +513,7 @@ begin_get_document(const URLSpec &url) {
|
|||
INLINE void HTTPChannel::
|
||||
begin_get_subdocument(const URLSpec &url, size_t first_byte,
|
||||
size_t last_byte) {
|
||||
begin_request(M_get, url, string(), true, first_byte, last_byte);
|
||||
begin_request(HTTPEnum::M_get, url, string(), true, first_byte, last_byte);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -516,7 +524,7 @@ begin_get_subdocument(const URLSpec &url, size_t first_byte,
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HTTPChannel::
|
||||
begin_get_header(const URLSpec &url) {
|
||||
begin_request(M_head, url, string(), true, 0, 0);
|
||||
begin_request(HTTPEnum::M_head, url, string(), true, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -539,7 +547,7 @@ begin_get_header(const URLSpec &url) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HTTPChannel::
|
||||
begin_connect_to(const URLSpec &url) {
|
||||
begin_request(M_connect, url, string(), true, 0, 0);
|
||||
begin_request(HTTPEnum::M_connect, url, string(), true, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -24,22 +24,12 @@
|
|||
#include "config_downloader.h"
|
||||
#include "clockObject.h"
|
||||
#include "buffer.h" // for Ramfile
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
TypeHandle HTTPChannel::_type_handle;
|
||||
|
||||
static const char base64_table[64] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/',
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::Constructor
|
||||
// Access: Private
|
||||
|
|
@ -138,7 +128,7 @@ is_regular_file() const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPChannel::
|
||||
will_close_connection() const {
|
||||
if (get_http_version() < HTTPClient::HV_11) {
|
||||
if (get_http_version() < HTTPEnum::HV_11) {
|
||||
// pre-HTTP 1.1 always closes.
|
||||
return true;
|
||||
}
|
||||
|
|
@ -153,22 +143,6 @@ will_close_connection() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::has_no_body
|
||||
// Access: Public
|
||||
// Description: Returns true if the nature of the request is such
|
||||
// that there is no associated body to read, false if
|
||||
// there should be a body (even if that body might be
|
||||
// empty).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPChannel::
|
||||
has_no_body() const {
|
||||
return (get_status_code() / 100 == 1 ||
|
||||
get_status_code() == 204 ||
|
||||
get_status_code() == 304 ||
|
||||
_method == M_head);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::open_read_file
|
||||
// Access: Public, Virtual
|
||||
|
|
@ -569,6 +543,23 @@ get_connection() {
|
|||
return stream;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::downcase
|
||||
// Access: Public, Static
|
||||
// Description: Returns the input string with all uppercase letters
|
||||
// converted to lowercase.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPChannel::
|
||||
downcase(const string &s) {
|
||||
string result;
|
||||
result.reserve(s.size());
|
||||
string::const_iterator p;
|
||||
for (p = s.begin(); p != s.end(); ++p) {
|
||||
result += tolower(*p);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::reached_done_state
|
||||
// Access: Private
|
||||
|
|
@ -792,14 +783,23 @@ run_proxy_reading_header() {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (get_status_code() == 407 && !_proxy.empty()) {
|
||||
_server_response_has_no_body =
|
||||
(get_status_code() / 100 == 1 ||
|
||||
get_status_code() == 204 ||
|
||||
get_status_code() == 304);
|
||||
|
||||
int last_status = _last_status_code;
|
||||
_last_status_code = get_status_code();
|
||||
|
||||
if (get_status_code() == 407 && last_status != 407 && !_proxy.empty()) {
|
||||
// 407: not authorized to proxy. Try to get the authorization.
|
||||
string authenticate_request = get_header_value("Proxy-Authenticate");
|
||||
string authorization;
|
||||
if (get_authorization(authorization, authenticate_request, _proxy, true)) {
|
||||
if (_client->_proxy_authorization != authorization) {
|
||||
// Change the authorization.
|
||||
_client->_proxy_authorization = authorization;
|
||||
_proxy_auth =
|
||||
_client->generate_auth(_proxy, true, authenticate_request);
|
||||
if (_proxy_auth != (HTTPAuthorization *)NULL) {
|
||||
_proxy_realm = _proxy_auth->get_realm();
|
||||
_proxy_username = _client->select_username(_proxy, false, _proxy_realm);
|
||||
if (!_proxy_username.empty()) {
|
||||
make_proxy_request_text();
|
||||
|
||||
// Roll the state forward to force a new request.
|
||||
|
|
@ -825,7 +825,7 @@ run_proxy_reading_header() {
|
|||
|
||||
// Now we have a tunnel opened through the proxy.
|
||||
_proxy_tunnel = true;
|
||||
make_request_text(string());
|
||||
make_request_text();
|
||||
|
||||
_state = _want_ssl ? S_setup_ssl : S_ready;
|
||||
return false;
|
||||
|
|
@ -1039,7 +1039,11 @@ run_reading_header() {
|
|||
return true;
|
||||
}
|
||||
|
||||
_realm = string();
|
||||
_server_response_has_no_body =
|
||||
(get_status_code() / 100 == 1 ||
|
||||
get_status_code() == 204 ||
|
||||
get_status_code() == 304 ||
|
||||
_method == HTTPEnum::M_head);
|
||||
|
||||
// Look for key properties in the header fields.
|
||||
if (get_status_code() == 206) {
|
||||
|
|
@ -1063,7 +1067,7 @@ run_reading_header() {
|
|||
|
||||
_state = S_read_header;
|
||||
|
||||
if (has_no_body() && will_close_connection()) {
|
||||
if (_server_response_has_no_body && will_close_connection()) {
|
||||
// If the server said it will close the connection, we should
|
||||
// close it too.
|
||||
close_connection();
|
||||
|
|
@ -1076,12 +1080,13 @@ run_reading_header() {
|
|||
if (get_status_code() == 407 && last_status != 407 && !_proxy.empty()) {
|
||||
// 407: not authorized to proxy. Try to get the authorization.
|
||||
string authenticate_request = get_header_value("Proxy-Authenticate");
|
||||
string authorization;
|
||||
if (get_authorization(authorization, authenticate_request, _proxy, true)) {
|
||||
if (_client->_proxy_authorization != authorization) {
|
||||
// Change the authorization.
|
||||
_client->_proxy_authorization = authorization;
|
||||
make_request_text(string());
|
||||
_proxy_auth =
|
||||
_client->generate_auth(_proxy, true, authenticate_request);
|
||||
if (_proxy_auth != (HTTPAuthorization *)NULL) {
|
||||
_proxy_realm = _proxy_auth->get_realm();
|
||||
_proxy_username = _client->select_username(_proxy, false, _proxy_realm);
|
||||
if (!_proxy_username.empty()) {
|
||||
make_request_text();
|
||||
|
||||
// Roll the state forward to force a new request.
|
||||
_state = S_begin_body;
|
||||
|
|
@ -1093,19 +1098,24 @@ run_reading_header() {
|
|||
if (get_status_code() == 401 && last_status != 401) {
|
||||
// 401: not authorized to remote server. Try to get the authorization.
|
||||
string authenticate_request = get_header_value("WWW-Authenticate");
|
||||
string authorization;
|
||||
if (get_authorization(authorization, authenticate_request, _url, false)) {
|
||||
make_request_text(authorization);
|
||||
_www_auth = _client->generate_auth(_url, false, authenticate_request);
|
||||
if (_www_auth != (HTTPAuthorization *)NULL) {
|
||||
_www_realm = _www_auth->get_realm();
|
||||
_www_username = _client->select_username(_url, false, _www_realm);
|
||||
if (!_www_username.empty()) {
|
||||
make_request_text();
|
||||
|
||||
// Roll the state forward to force a new request.
|
||||
_state = S_begin_body;
|
||||
return false;
|
||||
// Roll the state forward to force a new request.
|
||||
_state = S_begin_body;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((get_status_code() / 100) == 3 && get_status_code() != 305) {
|
||||
// Redirect. Should we handle it automatically?
|
||||
if (!get_redirect().empty() && (_method == M_get || _method == M_head)) {
|
||||
if (!get_redirect().empty() && (_method == HTTPEnum::M_get ||
|
||||
_method == HTTPEnum::M_head)) {
|
||||
// Sure!
|
||||
URLSpec new_url = get_redirect();
|
||||
if (!_redirect_trail.insert(new_url).second) {
|
||||
|
|
@ -1122,7 +1132,7 @@ run_reading_header() {
|
|||
}
|
||||
set_url(new_url);
|
||||
make_header();
|
||||
make_request_text(string());
|
||||
make_request_text();
|
||||
|
||||
// Roll the state forward to force a new request.
|
||||
_state = S_begin_body;
|
||||
|
|
@ -1173,9 +1183,9 @@ run_begin_body() {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (has_no_body()) {
|
||||
if (_server_response_has_no_body) {
|
||||
// We have already "read" the nonexistent body.
|
||||
_state = S_ready;
|
||||
_state = S_read_trailer;
|
||||
|
||||
} else if (_file_size > 8192) {
|
||||
// If we know the size of the body we are about to skip and it's
|
||||
|
|
@ -1406,7 +1416,7 @@ run_download_to_ram() {
|
|||
// necessary.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HTTPChannel::
|
||||
begin_request(HTTPChannel::Method method, const URLSpec &url,
|
||||
begin_request(HTTPEnum::Method method, const URLSpec &url,
|
||||
const string &body, bool nonblocking,
|
||||
size_t first_byte, size_t last_byte) {
|
||||
reset_for_new_request();
|
||||
|
|
@ -1440,9 +1450,9 @@ begin_request(HTTPChannel::Method method, const URLSpec &url,
|
|||
_last_byte = last_byte;
|
||||
|
||||
make_header();
|
||||
make_request_text(string());
|
||||
make_request_text();
|
||||
|
||||
if (!_proxy.empty() && (_want_ssl || _method == M_connect)) {
|
||||
if (!_proxy.empty() && (_want_ssl || _method == HTTPEnum::M_connect)) {
|
||||
// Maybe we need to tunnel through the proxy to connect to the
|
||||
// server directly. We need this for HTTPS, or if the user
|
||||
// requested a direct connection somewhere.
|
||||
|
|
@ -1450,7 +1460,7 @@ begin_request(HTTPChannel::Method method, const URLSpec &url,
|
|||
request
|
||||
<< "CONNECT " << _url.get_server() << ":" << _url.get_port()
|
||||
<< " " << _client->get_http_version_string() << "\r\n";
|
||||
if (_client->get_http_version() >= HTTPClient::HV_11) {
|
||||
if (_client->get_http_version() >= HTTPEnum::HV_11) {
|
||||
request
|
||||
<< "Host: " << _url.get_server() << "\r\n";
|
||||
}
|
||||
|
|
@ -1472,7 +1482,7 @@ begin_request(HTTPChannel::Method method, const URLSpec &url,
|
|||
_state = S_begin_body;
|
||||
}
|
||||
|
||||
_done_state = (_method == M_connect) ? S_ready : S_read_header;
|
||||
_done_state = (_method == HTTPEnum::M_connect) ? S_ready : S_read_header;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -2085,7 +2095,14 @@ x509_name_subset(X509_NAME *name_a, X509_NAME *name_b) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void HTTPChannel::
|
||||
make_header() {
|
||||
if (_method == M_connect) {
|
||||
_proxy_auth = _client->select_auth(_proxy, true, _proxy_realm);
|
||||
_proxy_username = string();
|
||||
if (_proxy_auth != (HTTPAuthorization *)NULL) {
|
||||
_proxy_realm = _proxy_auth->get_realm();
|
||||
_proxy_username = _client->select_username(_proxy, true, _proxy_realm);
|
||||
}
|
||||
|
||||
if (_method == HTTPEnum::M_connect) {
|
||||
// This method doesn't require an HTTP header at all; we'll just
|
||||
// open a plain connection. (Except when we're using a proxy; but
|
||||
// in that case, it's the proxy_header we'll need, not the regular
|
||||
|
|
@ -2094,45 +2111,34 @@ make_header() {
|
|||
return;
|
||||
}
|
||||
|
||||
string path;
|
||||
_www_auth = _client->select_auth(_url, false, _www_realm);
|
||||
_www_username = string();
|
||||
if (_www_auth != (HTTPAuthorization *)NULL) {
|
||||
_www_realm = _www_auth->get_realm();
|
||||
_www_username = _client->select_username(_url, false, _www_realm);
|
||||
}
|
||||
|
||||
string request_path;
|
||||
if (_proxy_serves_document) {
|
||||
// If we'll be asking the proxy for the document, we need its full
|
||||
// URL--but we omit the username, which is information just for us.
|
||||
URLSpec url_no_username = _url;
|
||||
url_no_username.set_username(string());
|
||||
path = url_no_username.get_url();
|
||||
request_path = url_no_username.get_url();
|
||||
|
||||
} else {
|
||||
// If we'll be asking the server directly for the document, we
|
||||
// just want its path relative to the server.
|
||||
path = _url.get_path();
|
||||
request_path = _url.get_path();
|
||||
}
|
||||
|
||||
ostringstream stream;
|
||||
|
||||
switch (_method) {
|
||||
case M_get:
|
||||
stream << "GET";
|
||||
break;
|
||||
|
||||
case M_head:
|
||||
stream << "HEAD";
|
||||
break;
|
||||
|
||||
case M_post:
|
||||
stream << "POST";
|
||||
break;
|
||||
|
||||
case M_connect:
|
||||
stream << "CONNECT";
|
||||
break;
|
||||
}
|
||||
|
||||
stream
|
||||
<< " " << path << " "
|
||||
<< _method << " " << request_path << " "
|
||||
<< _client->get_http_version_string() << "\r\n";
|
||||
|
||||
if (_client->get_http_version() >= HTTPClient::HV_11) {
|
||||
if (_client->get_http_version() >= HTTPEnum::HV_11) {
|
||||
stream
|
||||
<< "Host: " << _url.get_server() << "\r\n";
|
||||
if (!get_persistent_connection()) {
|
||||
|
|
@ -2172,9 +2178,14 @@ void HTTPChannel::
|
|||
make_proxy_request_text() {
|
||||
_proxy_request_text = _proxy_header;
|
||||
|
||||
if (!_client->_proxy_authorization.empty()) {
|
||||
if (_proxy_auth != (HTTPAuthorization *)NULL && !_proxy_username.empty()) {
|
||||
ostringstream strm;
|
||||
strm << _url.get_server() << ":" << _url.get_port();
|
||||
|
||||
_proxy_request_text += "Proxy-Authorization: ";
|
||||
_proxy_request_text += _client->_proxy_authorization;
|
||||
_proxy_request_text +=
|
||||
_proxy_auth->generate(HTTPEnum::M_connect, strm.str(),
|
||||
_proxy_username, _body);
|
||||
_proxy_request_text += "\r\n";
|
||||
}
|
||||
|
||||
|
|
@ -2189,19 +2200,22 @@ make_proxy_request_text() {
|
|||
// pass, based on the current header and body.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HTTPChannel::
|
||||
make_request_text(const string &authorization) {
|
||||
make_request_text() {
|
||||
_request_text = _header;
|
||||
|
||||
if (!_proxy.empty() && !_client->_proxy_authorization.empty() &&
|
||||
!_proxy_tunnel) {
|
||||
if (!_proxy.empty() &&
|
||||
_proxy_auth != (HTTPAuthorization *)NULL && !_proxy_username.empty()) {
|
||||
_request_text += "Proxy-Authorization: ";
|
||||
_request_text += _client->_proxy_authorization;
|
||||
_request_text +=
|
||||
_proxy_auth->generate(_method, _url.get_url(), _proxy_username, _body);
|
||||
_request_text += "\r\n";
|
||||
}
|
||||
|
||||
if (!authorization.empty()) {
|
||||
if (_www_auth != (HTTPAuthorization *)NULL && !_www_username.empty()) {
|
||||
string authorization =
|
||||
_request_text += "Authorization: ";
|
||||
_request_text += authorization;
|
||||
_request_text +=
|
||||
_www_auth->generate(_method, _url.get_path(), _www_username, _body);
|
||||
_request_text += "\r\n";
|
||||
}
|
||||
|
||||
|
|
@ -2252,153 +2266,6 @@ store_header_field(const string &field_name, const string &field_value) {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::get_authorization
|
||||
// Access: Private
|
||||
// Description: Looks for a username:password to satisfy the given
|
||||
// authenticate_request string from the server or proxy.
|
||||
// If found, fills in authorization and returns true;
|
||||
// otherwise, returns false.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPChannel::
|
||||
get_authorization(string &authorization, const string &authenticate_request,
|
||||
const URLSpec &url, bool is_proxy) {
|
||||
AuthenticationSchemes schemes;
|
||||
parse_authentication_schemes(schemes, authenticate_request);
|
||||
|
||||
AuthenticationSchemes::iterator si;
|
||||
si = schemes.find("basic");
|
||||
if (si != schemes.end()) {
|
||||
return get_basic_authorization(authorization, (*si).second, url, is_proxy);
|
||||
}
|
||||
|
||||
downloader_cat.warning()
|
||||
<< "Don't know how to use any of the server's available authorization schemes:\n";
|
||||
for (si = schemes.begin(); si != schemes.end(); ++si) {
|
||||
downloader_cat.warning() << (*si).first << "\n";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::downcase
|
||||
// Access: Private, Static
|
||||
// Description: Returns the input string with all uppercase letters
|
||||
// converted to lowercase.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPChannel::
|
||||
downcase(const string &s) {
|
||||
string result;
|
||||
result.reserve(s.size());
|
||||
string::const_iterator p;
|
||||
for (p = s.begin(); p != s.end(); ++p) {
|
||||
result += tolower(*p);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::base64_encode
|
||||
// Access: Private, Static
|
||||
// Description: Returns the input string encoded using base64. No
|
||||
// respect is paid to maintaining a 76-char line length.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPChannel::
|
||||
base64_encode(const string &s) {
|
||||
// Collect the string 3 bytes at a time into 24-bit words, then
|
||||
// output each word using 4 bytes.
|
||||
size_t num_words = (s.size() + 2) / 3;
|
||||
string result;
|
||||
result.reserve(num_words * 4);
|
||||
size_t p;
|
||||
for (p = 0; p + 2 < s.size(); p += 3) {
|
||||
unsigned int word =
|
||||
((unsigned)s[p] << 16) |
|
||||
((unsigned)s[p + 1] << 8) |
|
||||
((unsigned)s[p + 2]);
|
||||
result += base64_table[(word >> 18) & 0x3f];
|
||||
result += base64_table[(word >> 12) & 0x3f];
|
||||
result += base64_table[(word >> 6) & 0x3f];
|
||||
result += base64_table[(word) & 0x3f];
|
||||
}
|
||||
// What's left over?
|
||||
if (p < s.size()) {
|
||||
unsigned int word = ((unsigned)s[p] << 16);
|
||||
p++;
|
||||
if (p < s.size()) {
|
||||
word |= ((unsigned)s[p] << 8);
|
||||
p++;
|
||||
nassertr(p == s.size(), result);
|
||||
|
||||
result += base64_table[(word >> 18) & 0x3f];
|
||||
result += base64_table[(word >> 12) & 0x3f];
|
||||
result += base64_table[(word >> 6) & 0x3f];
|
||||
result += '=';
|
||||
} else {
|
||||
result += base64_table[(word >> 18) & 0x3f];
|
||||
result += base64_table[(word >> 12) & 0x3f];
|
||||
result += '=';
|
||||
result += '=';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::scan_quoted_or_unquoted_string
|
||||
// Access: Private, Static
|
||||
// Description: Scans the string source beginning at character
|
||||
// position start, to identify either the
|
||||
// (spaced-delimited) unquoted string there, or the
|
||||
// (quote-delimited) quoted string. In either case,
|
||||
// fills the string found into result, and returns the
|
||||
// next character position after the string (or after
|
||||
// its closing quote mark).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
size_t HTTPChannel::
|
||||
scan_quoted_or_unquoted_string(string &result, const string &source,
|
||||
size_t start) {
|
||||
result = string();
|
||||
|
||||
if (start < source.length()) {
|
||||
if (source[start] == '"') {
|
||||
// Quoted string.
|
||||
size_t p = start + 1;
|
||||
while (p < source.length() && source[p] != '"') {
|
||||
if (source[p] == '\\') {
|
||||
// Backslash escapes.
|
||||
++p;
|
||||
if (p < source.length()) {
|
||||
result += source[p];
|
||||
++p;
|
||||
}
|
||||
} else {
|
||||
result += source[p];
|
||||
++p;
|
||||
}
|
||||
}
|
||||
if (p < source.length()) {
|
||||
++p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// Unquoted string.
|
||||
size_t p = start;
|
||||
while (p < source.length() && source[p] != ',' && !isspace(source[p])) {
|
||||
result += source[p];
|
||||
++p;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Empty string.
|
||||
return start;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::show_send
|
||||
|
|
@ -2425,68 +2292,6 @@ show_send(const string &message) {
|
|||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::parse_authentication_schemes
|
||||
// Access: Private, Static
|
||||
// Description: Decodes the text following a WWW-Authenticate: or
|
||||
// Proxy-Authenticate: header field.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HTTPChannel::
|
||||
parse_authentication_schemes(HTTPChannel::AuthenticationSchemes &schemes,
|
||||
const string &field_value) {
|
||||
// This string will consist of one or more records of the form:
|
||||
//
|
||||
// scheme token=value[,token=value[,...]]
|
||||
//
|
||||
// If there are multiple records, they will be comma-delimited,
|
||||
// which makes parsing just a bit tricky.
|
||||
|
||||
// Start by skipping initial whitespace.
|
||||
size_t p = 0;
|
||||
while (p < field_value.length() && isspace(field_value[p])) {
|
||||
++p;
|
||||
}
|
||||
|
||||
if (p < field_value.length()) {
|
||||
size_t q = p;
|
||||
while (q < field_value.length() && !isspace(field_value[q])) {
|
||||
++q;
|
||||
}
|
||||
// Here's our first scheme.
|
||||
string scheme = downcase(field_value.substr(p, q - p));
|
||||
Tokens *tokens = &(schemes[scheme]);
|
||||
|
||||
// Now pull off the tokens, one at a time.
|
||||
p = q + 1;
|
||||
while (p < field_value.length()) {
|
||||
q = p;
|
||||
while (q < field_value.length() && field_value[q] != '=' &&
|
||||
field_value[q] != ',' && !isspace(field_value[q])) {
|
||||
++q;
|
||||
}
|
||||
if (field_value[q] == '=') {
|
||||
// This is a token.
|
||||
string token = downcase(field_value.substr(p, q - p));
|
||||
string value;
|
||||
p = scan_quoted_or_unquoted_string(value, field_value, q + 1);
|
||||
(*tokens)[token] = value;
|
||||
|
||||
// Skip trailing whitespace and extra commas.
|
||||
while (p < field_value.length() &&
|
||||
(field_value[p] == ',' || isspace(field_value[p]))) {
|
||||
++p;
|
||||
}
|
||||
|
||||
} else {
|
||||
// This is not a token; it must be the start of a new scheme.
|
||||
scheme = downcase(field_value.substr(p, q - p));
|
||||
tokens = &(schemes[scheme]);
|
||||
p = q + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::reset_download_to
|
||||
// Access: Private
|
||||
|
|
@ -2533,66 +2338,4 @@ close_connection() {
|
|||
_read_index++;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::get_basic_authorization
|
||||
// Access: Private
|
||||
// Description: Looks for a username:password to satisfy the "Basic"
|
||||
// scheme authorization request from the server or
|
||||
// proxy.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPChannel::
|
||||
get_basic_authorization(string &authorization, const HTTPChannel::Tokens &tokens, const URLSpec &url, bool is_proxy) {
|
||||
Tokens::const_iterator ti;
|
||||
ti = tokens.find("realm");
|
||||
if (ti != tokens.end()) {
|
||||
_realm = (*ti).second;
|
||||
}
|
||||
|
||||
string username;
|
||||
|
||||
// Look in several places in order to find the matching username.
|
||||
|
||||
// Fist, if there's a username on the URL, that always wins (except
|
||||
// when we are looking for a proxy username).
|
||||
if (url.has_username() && !is_proxy) {
|
||||
username = url.get_username();
|
||||
}
|
||||
|
||||
// Otherwise, start looking on the HTTPClient.
|
||||
if (is_proxy) {
|
||||
if (username.empty()) {
|
||||
// Try the *proxy/realm.
|
||||
username = _client->get_username("*proxy", _realm);
|
||||
}
|
||||
if (username.empty()) {
|
||||
// Then, try *proxy/any realm.
|
||||
username = _client->get_username("*proxy", string());
|
||||
}
|
||||
}
|
||||
if (username.empty()) {
|
||||
// Try the specific server/realm.
|
||||
username = _client->get_username(url.get_server(), _realm);
|
||||
}
|
||||
if (username.empty()) {
|
||||
// Then, try the specific server/any realm.
|
||||
username = _client->get_username(url.get_server(), string());
|
||||
}
|
||||
if (username.empty()) {
|
||||
// Then, try any server with this realm.
|
||||
username = _client->get_username(string(), _realm);
|
||||
}
|
||||
if (username.empty()) {
|
||||
// Then, take the general password.
|
||||
username = _client->get_username(string(), string());
|
||||
}
|
||||
|
||||
if (username.empty()) {
|
||||
// No username:password available.
|
||||
return false;
|
||||
}
|
||||
|
||||
authorization = "Basic " + base64_encode(username);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // HAVE_SSL
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#ifdef HAVE_SSL
|
||||
|
||||
#include "httpClient.h"
|
||||
#include "httpEnum.h"
|
||||
#include "urlSpec.h"
|
||||
#include "virtualFile.h"
|
||||
#include "bioPtr.h"
|
||||
|
|
@ -71,17 +72,17 @@ public:
|
|||
virtual istream *open_read_file() const;
|
||||
|
||||
bool will_close_connection() const;
|
||||
bool has_no_body() const;
|
||||
|
||||
PUBLISHED:
|
||||
INLINE bool is_valid() const;
|
||||
INLINE bool is_connection_ready() const;
|
||||
INLINE const URLSpec &get_url() const;
|
||||
INLINE HTTPClient::HTTPVersion get_http_version() const;
|
||||
INLINE HTTPEnum::HTTPVersion get_http_version() const;
|
||||
INLINE const string &get_http_version_string() const;
|
||||
INLINE int get_status_code() const;
|
||||
INLINE const string &get_status_string() const;
|
||||
INLINE const string &get_realm() const;
|
||||
INLINE const string &get_www_realm() const;
|
||||
INLINE const string &get_proxy_realm() const;
|
||||
INLINE const URLSpec &get_redirect() const;
|
||||
string get_header_value(const string &key) const;
|
||||
|
||||
|
|
@ -132,14 +133,10 @@ PUBLISHED:
|
|||
INLINE size_t get_bytes_requested() const;
|
||||
INLINE bool is_download_complete() const;
|
||||
|
||||
private:
|
||||
enum Method {
|
||||
M_get,
|
||||
M_head,
|
||||
M_post,
|
||||
M_connect
|
||||
};
|
||||
public:
|
||||
static string downcase(const string &s);
|
||||
|
||||
private:
|
||||
bool reached_done_state();
|
||||
bool run_connecting();
|
||||
bool run_connecting_wait();
|
||||
|
|
@ -160,7 +157,7 @@ private:
|
|||
bool run_download_to_file();
|
||||
bool run_download_to_ram();
|
||||
|
||||
void begin_request(Method method, const URLSpec &url,
|
||||
void begin_request(HTTPEnum::Method method, const URLSpec &url,
|
||||
const string &body, bool nonblocking,
|
||||
size_t first_byte, size_t last_byte);
|
||||
void reset_for_new_request();
|
||||
|
|
@ -181,17 +178,10 @@ private:
|
|||
|
||||
void make_header();
|
||||
void make_proxy_request_text();
|
||||
void make_request_text(const string &authorization);
|
||||
void make_request_text();
|
||||
|
||||
void set_url(const URLSpec &url);
|
||||
void store_header_field(const string &field_name, const string &field_value);
|
||||
bool get_authorization(string &authorization,
|
||||
const string &authenticate_request,
|
||||
const URLSpec &url, bool is_proxy);
|
||||
|
||||
static string downcase(const string &s);
|
||||
static string base64_encode(const string &s);
|
||||
static size_t scan_quoted_or_unquoted_string(string &result, const string &source, size_t start);
|
||||
|
||||
#ifndef NDEBUG
|
||||
static void show_send(const string &message);
|
||||
|
|
@ -216,11 +206,13 @@ private:
|
|||
bool _nonblocking;
|
||||
|
||||
URLSpec _url;
|
||||
Method _method;
|
||||
HTTPEnum::Method _method;
|
||||
string request_path;
|
||||
string _header;
|
||||
string _body;
|
||||
bool _want_ssl;
|
||||
bool _proxy_serves_document;
|
||||
bool _server_response_has_no_body;
|
||||
size_t _first_byte;
|
||||
size_t _last_byte;
|
||||
|
||||
|
|
@ -236,13 +228,20 @@ private:
|
|||
|
||||
int _read_index;
|
||||
|
||||
HTTPClient::HTTPVersion _http_version;
|
||||
HTTPEnum::HTTPVersion _http_version;
|
||||
string _http_version_string;
|
||||
int _status_code;
|
||||
string _status_string;
|
||||
string _realm;
|
||||
URLSpec _redirect;
|
||||
|
||||
string _proxy_realm;
|
||||
string _proxy_username;
|
||||
PT(HTTPAuthorization) _proxy_auth;
|
||||
|
||||
string _www_realm;
|
||||
string _www_username;
|
||||
PT(HTTPAuthorization) _www_auth;
|
||||
|
||||
enum ResponseType {
|
||||
RT_none,
|
||||
RT_hangup,
|
||||
|
|
@ -302,13 +301,6 @@ private:
|
|||
int _last_status_code;
|
||||
double _last_run_time;
|
||||
|
||||
typedef pmap<string, string> Tokens;
|
||||
typedef pmap<string, Tokens> AuthenticationSchemes;
|
||||
static void parse_authentication_schemes(AuthenticationSchemes &schemes,
|
||||
const string &field_value);
|
||||
bool get_basic_authorization(string &authorization, const Tokens &tokens,
|
||||
const URLSpec &url, bool is_proxy);
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
INLINE void HTTPClient::
|
||||
set_proxy(const URLSpec &proxy) {
|
||||
_proxy = proxy;
|
||||
_proxy_authorization = string();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -49,7 +48,7 @@ get_proxy() const {
|
|||
// request the server use the older interface.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void HTTPClient::
|
||||
set_http_version(HTTPClient::HTTPVersion version) {
|
||||
set_http_version(HTTPEnum::HTTPVersion version) {
|
||||
_http_version = version;
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +58,7 @@ set_http_version(HTTPClient::HTTPVersion version) {
|
|||
// Description: Returns the client's current setting for HTTP
|
||||
// version. See set_http_version().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE HTTPClient::HTTPVersion HTTPClient::
|
||||
INLINE HTTPEnum::HTTPVersion HTTPClient::
|
||||
get_http_version() const {
|
||||
return _http_version;
|
||||
}
|
||||
|
|
@ -91,3 +90,4 @@ INLINE HTTPClient::VerifySSL HTTPClient::
|
|||
get_verify_ssl() const {
|
||||
return _verify_ssl;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
#include "config_express.h"
|
||||
#include "virtualFileSystem.h"
|
||||
#include "executionEnvironment.h"
|
||||
#include "httpBasicAuthorization.h"
|
||||
#include "httpDigestAuthorization.h"
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
|
|
@ -51,7 +53,7 @@ X509_STORE *HTTPClient::_x509_store = NULL;
|
|||
////////////////////////////////////////////////////////////////////
|
||||
HTTPClient::
|
||||
HTTPClient() {
|
||||
_http_version = HV_11;
|
||||
_http_version = HTTPEnum::HV_11;
|
||||
_verify_ssl = verify_ssl ? VS_normal : VS_no_verify;
|
||||
_ssl_ctx = (SSL_CTX *)NULL;
|
||||
|
||||
|
|
@ -191,16 +193,16 @@ get_username(const string &server, const string &realm) const {
|
|||
string HTTPClient::
|
||||
get_http_version_string() const {
|
||||
switch (_http_version) {
|
||||
case HV_09:
|
||||
case HTTPEnum::HV_09:
|
||||
return "HTTP/0.9";
|
||||
|
||||
case HV_10:
|
||||
case HTTPEnum::HV_10:
|
||||
return "HTTP/1.0";
|
||||
|
||||
case HV_11:
|
||||
case HTTPEnum::HV_11:
|
||||
return "HTTP/1.1";
|
||||
|
||||
case HV_other:
|
||||
case HTTPEnum::HV_other:
|
||||
// Report the best we can do.
|
||||
return "HTTP/1.1";
|
||||
}
|
||||
|
|
@ -216,16 +218,16 @@ get_http_version_string() const {
|
|||
// the appropriate enumerated value, or HV_other if the
|
||||
// version is unknown.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
HTTPClient::HTTPVersion HTTPClient::
|
||||
HTTPEnum::HTTPVersion HTTPClient::
|
||||
parse_http_version_string(const string &version) {
|
||||
if (version == "HTTP/1.0") {
|
||||
return HV_10;
|
||||
return HTTPEnum::HV_10;
|
||||
} else if (version == "HTTP/1.1") {
|
||||
return HV_11;
|
||||
return HTTPEnum::HV_11;
|
||||
} else if (version.substr(0, 6) == "HTTP/0") {
|
||||
return HV_09;
|
||||
return HTTPEnum::HV_09;
|
||||
} else {
|
||||
return HV_other;
|
||||
return HTTPEnum::HV_other;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -508,6 +510,159 @@ add_http_username(const string &http_username) {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::select_username
|
||||
// Access: Private
|
||||
// Description: Chooses a suitable username:password string for the
|
||||
// given URL and realm.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPClient::
|
||||
select_username(const URLSpec &url, bool is_proxy, const string &realm) const {
|
||||
string username;
|
||||
|
||||
// Look in several places in order to find the matching username.
|
||||
|
||||
// Fist, if there's a username on the URL, that always wins (except
|
||||
// when we are looking for a proxy username).
|
||||
if (url.has_username() && !is_proxy) {
|
||||
username = url.get_username();
|
||||
}
|
||||
|
||||
// Otherwise, start looking on the HTTPClient.
|
||||
if (is_proxy) {
|
||||
if (username.empty()) {
|
||||
// Try the *proxy/realm.
|
||||
username = get_username("*proxy", realm);
|
||||
}
|
||||
if (username.empty()) {
|
||||
// Then, try *proxy/any realm.
|
||||
username = get_username("*proxy", string());
|
||||
}
|
||||
}
|
||||
if (username.empty()) {
|
||||
// Try the specific server/realm.
|
||||
username = get_username(url.get_server(), realm);
|
||||
}
|
||||
if (username.empty()) {
|
||||
// Then, try the specific server/any realm.
|
||||
username = get_username(url.get_server(), string());
|
||||
}
|
||||
if (username.empty()) {
|
||||
// Then, try any server with this realm.
|
||||
username = get_username(string(), realm);
|
||||
}
|
||||
if (username.empty()) {
|
||||
// Then, take the general password.
|
||||
username = get_username(string(), string());
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::select_auth
|
||||
// Access: Private
|
||||
// Description: Chooses a suitable pre-computed authorization for the
|
||||
// indicated URL. Returns NULL if no authorization
|
||||
// matches.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
HTTPAuthorization *HTTPClient::
|
||||
select_auth(const URLSpec &url, bool is_proxy, const string &last_realm) {
|
||||
Domains &domains = is_proxy ? _proxy_domains : _www_domains;
|
||||
string canon = HTTPAuthorization::get_canonical_url(url).get_url();
|
||||
|
||||
// Look for the longest domain string that is a prefix of our
|
||||
// canonical URL. We have to make a linear scan through the list.
|
||||
Domains::const_iterator best_di = domains.end();
|
||||
size_t longest_length = 0;
|
||||
Domains::const_iterator di;
|
||||
for (di = domains.begin(); di != domains.end(); ++di) {
|
||||
const string &domain = (*di).first;
|
||||
size_t length = domain.length();
|
||||
if (domain == canon.substr(0, length)) {
|
||||
// This domain string matches. Is it the longest?
|
||||
if (length > longest_length) {
|
||||
best_di = di;
|
||||
longest_length = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_di != domains.end()) {
|
||||
// Ok, we found a matching domain. Use it.
|
||||
if (downloader_cat.is_spam()) {
|
||||
downloader_cat.spam()
|
||||
<< "Choosing domain " << (*best_di).first << " for " << url << "\n";
|
||||
}
|
||||
const Realms &realms = (*best_di).second._realms;
|
||||
// First, try our last realm.
|
||||
Realms::const_iterator ri;
|
||||
ri = realms.find(last_realm);
|
||||
if (ri != realms.end()) {
|
||||
return (*ri).second;
|
||||
}
|
||||
|
||||
if (!realms.empty()) {
|
||||
// Oh well, just return the first realm.
|
||||
return (*realms.begin()).second;
|
||||
}
|
||||
}
|
||||
|
||||
// No matching domains.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::generate_auth
|
||||
// Access: Private
|
||||
// Description: Generates a new authorization entry in response to a
|
||||
// 401 or 407 challenge from the server or proxy. The
|
||||
// new authorization entry is stored for future
|
||||
// connections to the same server (or, more precisely,
|
||||
// the same domain, which may be a subset of the server,
|
||||
// or it may include multiple servers).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
PT(HTTPAuthorization) HTTPClient::
|
||||
generate_auth(const URLSpec &url, bool is_proxy, const string &challenge) {
|
||||
HTTPAuthorization::AuthenticationSchemes schemes;
|
||||
HTTPAuthorization::parse_authentication_schemes(schemes, challenge);
|
||||
|
||||
PT(HTTPAuthorization) auth;
|
||||
HTTPAuthorization::AuthenticationSchemes::iterator si;
|
||||
|
||||
si = schemes.find("digest");
|
||||
if (si != schemes.end()) {
|
||||
auth = new HTTPDigestAuthorization((*si).second, url, is_proxy);
|
||||
}
|
||||
|
||||
if (auth == (HTTPAuthorization *)NULL || !auth->is_valid()) {
|
||||
si = schemes.find("basic");
|
||||
if (si != schemes.end()) {
|
||||
auth = new HTTPBasicAuthorization((*si).second, url, is_proxy);
|
||||
}
|
||||
}
|
||||
|
||||
if (auth == (HTTPAuthorization *)NULL || !auth->is_valid()) {
|
||||
downloader_cat.warning()
|
||||
<< "Don't know how to use any of the server's available authorization schemes:\n";
|
||||
for (si = schemes.begin(); si != schemes.end(); ++si) {
|
||||
downloader_cat.warning() << (*si).first << "\n";
|
||||
}
|
||||
|
||||
} else {
|
||||
// Now that we've got an authorization, store it under under each
|
||||
// of its suggested domains for future use.
|
||||
Domains &domains = is_proxy ? _proxy_domains : _www_domains;
|
||||
const vector_string &domain = auth->get_domain();
|
||||
vector_string::const_iterator si;
|
||||
for (si = domain.begin(); si != domain.end(); ++si) {
|
||||
domains[(*si)]._realms[auth->get_realm()] = auth;
|
||||
}
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::initialize_ssl
|
||||
// Access: Private, Static
|
||||
|
|
@ -745,4 +900,3 @@ ssl_msg_callback(int write_p, int version, int content_type,
|
|||
#endif // defined(SSL_097) && !defined(NDEBUG)
|
||||
|
||||
#endif // HAVE_SSL
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
#ifdef HAVE_SSL
|
||||
|
||||
#include "urlSpec.h"
|
||||
#include "httpAuthorization.h"
|
||||
#include "httpEnum.h"
|
||||
#include "pointerTo.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
|
@ -63,17 +65,10 @@ PUBLISHED:
|
|||
void set_username(const string &server, const string &realm, const string &username);
|
||||
string get_username(const string &server, const string &realm) const;
|
||||
|
||||
enum HTTPVersion {
|
||||
HV_09, // HTTP 0.9 or older
|
||||
HV_10, // HTTP 1.0
|
||||
HV_11, // HTTP 1.1
|
||||
HV_other,
|
||||
};
|
||||
|
||||
INLINE void set_http_version(HTTPVersion version);
|
||||
INLINE HTTPVersion get_http_version() const;
|
||||
INLINE void set_http_version(HTTPEnum::HTTPVersion version);
|
||||
INLINE HTTPEnum::HTTPVersion get_http_version() const;
|
||||
string get_http_version_string() const;
|
||||
static HTTPVersion parse_http_version_string(const string &version);
|
||||
static HTTPEnum::HTTPVersion parse_http_version_string(const string &version);
|
||||
|
||||
bool load_certificates(const Filename &filename);
|
||||
|
||||
|
|
@ -99,6 +94,13 @@ public:
|
|||
|
||||
private:
|
||||
void add_http_username(const string &http_username);
|
||||
string select_username(const URLSpec &url, bool is_proxy,
|
||||
const string &realm) const;
|
||||
|
||||
HTTPAuthorization *select_auth(const URLSpec &url, bool is_proxy,
|
||||
const string &last_realm);
|
||||
PT(HTTPAuthorization) generate_auth(const URLSpec &url, bool is_proxy,
|
||||
const string &challenge);
|
||||
|
||||
static void initialize_ssl();
|
||||
static int load_verify_locations(SSL_CTX *ctx, const Filename &ca_file);
|
||||
|
|
@ -112,13 +114,20 @@ private:
|
|||
#endif
|
||||
|
||||
URLSpec _proxy;
|
||||
string _proxy_authorization;
|
||||
HTTPVersion _http_version;
|
||||
HTTPEnum::HTTPVersion _http_version;
|
||||
VerifySSL _verify_ssl;
|
||||
|
||||
typedef pmap<string, string> Usernames;
|
||||
Usernames _usernames;
|
||||
|
||||
typedef map<string, PT(HTTPAuthorization) > Realms;
|
||||
class Domain {
|
||||
public:
|
||||
Realms _realms;
|
||||
};
|
||||
typedef pmap<string, Domain> Domains;
|
||||
Domains _proxy_domains, _www_domains;
|
||||
|
||||
// List of allowable SSL servers to connect to. If the list is
|
||||
// empty, any server is acceptable.
|
||||
typedef pvector<X509_NAME *> ExpectedServers;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
// Filename: httpDigestAuthorization.I
|
||||
// Created by: drose (25Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::hexdigit
|
||||
// Access: Private, Static
|
||||
// Description: Returns the ASCII character corresponding to the
|
||||
// hexadecimal representation of the indicated value,
|
||||
// which must be 0 <= value <= 15.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE char HTTPDigestAuthorization::
|
||||
hexdigit(int value) {
|
||||
return (value < 10) ? (value + '0') : (value - 10 + 'a');
|
||||
}
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
// Filename: httpDigestAuthorization.cxx
|
||||
// Created by: drose (25Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "httpDigestAuthorization.h"
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <time.h>
|
||||
|
||||
const string HTTPDigestAuthorization::_mechanism = "digest";
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::Constructor
|
||||
// Access: Protected
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
HTTPDigestAuthorization::
|
||||
HTTPDigestAuthorization(const HTTPAuthorization::Tokens &tokens,
|
||||
const URLSpec &url, bool is_proxy) :
|
||||
HTTPAuthorization(tokens, url, is_proxy)
|
||||
{
|
||||
Tokens::const_iterator ti;
|
||||
ti = tokens.find("nonce");
|
||||
if (ti != tokens.end()) {
|
||||
_nonce = (*ti).second;
|
||||
}
|
||||
_nonce_count = 0;
|
||||
|
||||
ti = tokens.find("opaque");
|
||||
if (ti != tokens.end()) {
|
||||
_opaque = (*ti).second;
|
||||
}
|
||||
|
||||
_algorithm = A_md5;
|
||||
ti = tokens.find("algorithm");
|
||||
if (ti != tokens.end()) {
|
||||
string algo_str = HTTPChannel::downcase((*ti).second);
|
||||
if (algo_str == "md5") {
|
||||
_algorithm = A_md5;
|
||||
} else if (algo_str == "md5-sess") {
|
||||
_algorithm = A_md5_sess;
|
||||
} else {
|
||||
_algorithm = A_unknown;
|
||||
}
|
||||
}
|
||||
|
||||
_qop = 0;
|
||||
ti = tokens.find("qop");
|
||||
if (ti != tokens.end()) {
|
||||
string qop_str = HTTPChannel::downcase((*ti).second);
|
||||
// A comma-delimited list of tokens.
|
||||
|
||||
size_t p = 0;
|
||||
while (p < qop_str.length()) {
|
||||
while (p < qop_str.length() && isspace(qop_str[p])) {
|
||||
++p;
|
||||
}
|
||||
size_t q = p;
|
||||
size_t last_char = p;
|
||||
while (q < qop_str.length() && qop_str[q] != ',') {
|
||||
if (!isspace(qop_str[q])) {
|
||||
qop_str[q] = tolower(qop_str[q]);
|
||||
last_char = q;
|
||||
}
|
||||
++q;
|
||||
}
|
||||
|
||||
if (last_char > p) {
|
||||
_qop |= match_qop_token(qop_str.substr(p, last_char - p + 1));
|
||||
}
|
||||
p = q + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute an arbitrary client nonce.
|
||||
ostringstream strm;
|
||||
strm << time(NULL) << ":" << clock() << ":"
|
||||
<< url.get_url() << ":Panda";
|
||||
|
||||
_cnonce = calc_md5(strm.str());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::Destructor
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
HTTPDigestAuthorization::
|
||||
~HTTPDigestAuthorization() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::is_valid
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns true if the authorization challenge was
|
||||
// correctly parsed and is usable, or false if there was
|
||||
// some unsupported algorithm or some such requested by
|
||||
// the server, rendering the challenge unmeetable.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPDigestAuthorization::
|
||||
is_valid() {
|
||||
return (_algorithm != A_unknown);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::get_mechanism
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns the type of authorization mechanism,
|
||||
// represented as a string, e.g. "digest".
|
||||
////////////////////////////////////////////////////////////////////
|
||||
const string &HTTPDigestAuthorization::
|
||||
get_mechanism() const {
|
||||
return _mechanism;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::generate
|
||||
// Access: Public, Virtual
|
||||
// Description: Generates a suitable authorization string to send
|
||||
// to the server, based on the data stored within this
|
||||
// object, for retrieving the indicated URL with the
|
||||
// given username:password.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPDigestAuthorization::
|
||||
generate(HTTPEnum::Method method, const string &request_path,
|
||||
const string &username, const string &body) {
|
||||
_nonce_count++;
|
||||
|
||||
size_t colon = username.find(':');
|
||||
string username_only = username.substr(0, colon);
|
||||
string password_only = username.substr(colon + 1);
|
||||
|
||||
string digest = calc_request_digest(username_only, password_only,
|
||||
method, request_path, body);
|
||||
|
||||
ostringstream strm;
|
||||
strm << "Digest username=\"" << username.substr(0, colon) << "\""
|
||||
<< ", realm=\"" << get_realm() << "\""
|
||||
<< ", nonce=\"" << _nonce << "\""
|
||||
<< ", uri=" << request_path
|
||||
<< ", response=\"" << digest << "\""
|
||||
<< ", algorithm=" << _algorithm;
|
||||
|
||||
if (!_opaque.empty()) {
|
||||
strm << ", opaque=\"" << _opaque << "\"";
|
||||
}
|
||||
|
||||
if (_chosen_qop != Q_unused) {
|
||||
strm << ", qop=" << _chosen_qop
|
||||
<< ", cnonce=\"" << _cnonce << "\""
|
||||
<< ", nc=" << get_hex_nonce_count();
|
||||
}
|
||||
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::match_qop_token
|
||||
// Access: Private, Static
|
||||
// Description: Returns the bitfield corresponding to the indicated
|
||||
// qop token string, or 0 if the token string is
|
||||
// unrecognized.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int HTTPDigestAuthorization::
|
||||
match_qop_token(const string &token) {
|
||||
if (token == "auth") {
|
||||
return Q_auth;
|
||||
} else if (token == "auth-int") {
|
||||
return Q_auth_int;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::calc_request_digest
|
||||
// Access: Private
|
||||
// Description: Calculates the appropriate digest response, according
|
||||
// to RFC 2617.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPDigestAuthorization::
|
||||
calc_request_digest(const string &username, const string &password,
|
||||
HTTPEnum::Method method, const string &request_path,
|
||||
const string &body) {
|
||||
_chosen_qop = Q_unused;
|
||||
string h_a1 = calc_h(get_a1(username, password));
|
||||
string h_a2 = calc_h(get_a2(method, request_path, body));
|
||||
|
||||
ostringstream strm;
|
||||
|
||||
if (_qop == 0) {
|
||||
_chosen_qop = Q_unused;
|
||||
strm << _nonce << ":" << h_a2;
|
||||
|
||||
} else {
|
||||
strm << _nonce << ":" << get_hex_nonce_count() << ":"
|
||||
<< _cnonce << ":" << _chosen_qop << ":"
|
||||
<< h_a2;
|
||||
}
|
||||
|
||||
return calc_kd(h_a1, strm.str());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::calc_h
|
||||
// Access: Private
|
||||
// Description: Applies the specified checksum algorithm to the data,
|
||||
// according to RFC 2617.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPDigestAuthorization::
|
||||
calc_h(const string &data) const {
|
||||
switch (_algorithm) {
|
||||
case A_unknown:
|
||||
case A_md5:
|
||||
case A_md5_sess:
|
||||
return calc_md5(data);
|
||||
}
|
||||
|
||||
return string();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::calc_kd
|
||||
// Access: Private
|
||||
// Description: Applies the specified digest algorithm to the
|
||||
// indicated data with the indicated secret, according
|
||||
// to RFC 2617.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPDigestAuthorization::
|
||||
calc_kd(const string &secret, const string &data) const {
|
||||
switch (_algorithm) {
|
||||
case A_unknown:
|
||||
case A_md5:
|
||||
case A_md5_sess:
|
||||
return calc_h(secret + ":" + data);
|
||||
}
|
||||
|
||||
return string();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::get_a1
|
||||
// Access: Private
|
||||
// Description: Returns the A1 value, as defined by RFC 2617.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPDigestAuthorization::
|
||||
get_a1(const string &username, const string &password) {
|
||||
switch (_algorithm) {
|
||||
case A_unknown:
|
||||
case A_md5:
|
||||
return username + ":" + get_realm() + ":" + password;
|
||||
|
||||
case A_md5_sess:
|
||||
if (_a1.empty()) {
|
||||
_a1 = calc_h(username + ":" + get_realm() + ":" + password) +
|
||||
":" + _nonce + ":" + _cnonce;
|
||||
}
|
||||
return _a1;
|
||||
}
|
||||
|
||||
return string();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::get_a2
|
||||
// Access: Private
|
||||
// Description: Returns the A2 value, as defined by RFC 2617.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPDigestAuthorization::
|
||||
get_a2(HTTPEnum::Method method, const string &request_path,
|
||||
const string &body) {
|
||||
ostringstream strm;
|
||||
|
||||
if ((_qop & Q_auth_int) != 0 && !body.empty()) {
|
||||
_chosen_qop = Q_auth_int;
|
||||
strm << method << ":" << request_path << ":" << calc_h(body);
|
||||
|
||||
} else {
|
||||
_chosen_qop = Q_auth;
|
||||
strm << method << ":" << request_path;
|
||||
}
|
||||
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::get_hex_nonce_count
|
||||
// Access: Private
|
||||
// Description: Returns the current nonce count (the number of times
|
||||
// we have used the server's nonce value, including this
|
||||
// time) as an eight-digit hexadecimal value.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPDigestAuthorization::
|
||||
get_hex_nonce_count() const {
|
||||
ostringstream strm;
|
||||
strm << hex << setfill('0') << setw(8) << _nonce_count;
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPDigestAuthorization::calc_md5
|
||||
// Access: Private, Static
|
||||
// Description: Computes the MD5 of the indicated source string and
|
||||
// returns it as a hexadecimal string of 32 ASCII
|
||||
// characters.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string HTTPDigestAuthorization::
|
||||
calc_md5(const string &source) {
|
||||
unsigned char binary[MD5_DIGEST_LENGTH];
|
||||
|
||||
MD5((const unsigned char *)source.data(), source.length(), binary);
|
||||
|
||||
string result;
|
||||
result.reserve(MD5_DIGEST_LENGTH * 2);
|
||||
|
||||
for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
|
||||
result += hexdigit((binary[i] >> 4) & 0xf);
|
||||
result += hexdigit(binary[i] & 0xf);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ostream &
|
||||
operator << (ostream &out, HTTPDigestAuthorization::Algorithm algorithm) {
|
||||
switch (algorithm) {
|
||||
case HTTPDigestAuthorization::A_md5:
|
||||
out << "MD5";
|
||||
break;
|
||||
case HTTPDigestAuthorization::A_md5_sess:
|
||||
out << "MD5-sess";
|
||||
break;
|
||||
case HTTPDigestAuthorization::A_unknown:
|
||||
out << "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
ostream &
|
||||
operator << (ostream &out, HTTPDigestAuthorization::Qop qop) {
|
||||
switch (qop) {
|
||||
case HTTPDigestAuthorization::Q_auth:
|
||||
out << "auth";
|
||||
break;
|
||||
case HTTPDigestAuthorization::Q_auth_int:
|
||||
out << "auth-int";
|
||||
break;
|
||||
case HTTPDigestAuthorization::Q_unused:
|
||||
out << "unused";
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif // HAVE_SSL
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// Filename: httpDigestAuthorization.h
|
||||
// Created by: drose (25Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef HTTPDIGESTAUTHORIZATION_H
|
||||
#define HTTPDIGESTAUTHORIZATION_H
|
||||
|
||||
#include "pandabase.h"
|
||||
|
||||
// This module requires OpenSSL to compile, even though it doesn't
|
||||
// actually use any OpenSSL code, because it is a support module for
|
||||
// HTTPChannel, which *does* use OpenSSL code.
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
#include "httpAuthorization.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : HTTPDigestAuthorization
|
||||
// Description : Implements the "Digest" type of HTTP authorization.
|
||||
// This is designed to be an improvement over "Basic"
|
||||
// authorization, in that it does not send passwords
|
||||
// over the net in cleartext, and it is harder to spoof.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class HTTPDigestAuthorization : public HTTPAuthorization {
|
||||
public:
|
||||
HTTPDigestAuthorization(const Tokens &tokens, const URLSpec &url,
|
||||
bool is_proxy);
|
||||
virtual ~HTTPDigestAuthorization();
|
||||
|
||||
virtual const string &get_mechanism() const;
|
||||
virtual bool is_valid();
|
||||
|
||||
virtual string generate(HTTPEnum::Method method, const string &request_path,
|
||||
const string &username, const string &body);
|
||||
|
||||
public:
|
||||
enum Algorithm {
|
||||
A_unknown,
|
||||
A_md5,
|
||||
A_md5_sess,
|
||||
};
|
||||
enum Qop {
|
||||
// These are used as a bitfield.
|
||||
Q_unused = 0x000,
|
||||
Q_auth = 0x001,
|
||||
Q_auth_int = 0x002,
|
||||
};
|
||||
|
||||
private:
|
||||
static int match_qop_token(const string &token);
|
||||
|
||||
string calc_request_digest(const string &username, const string &password,
|
||||
HTTPEnum::Method method,
|
||||
const string &request_path, const string &body);
|
||||
string calc_h(const string &data) const;
|
||||
string calc_kd(const string &secret, const string &data) const;
|
||||
string get_a1(const string &username, const string &password);
|
||||
string get_a2(HTTPEnum::Method method, const string &request_path,
|
||||
const string &body);
|
||||
string get_hex_nonce_count() const;
|
||||
|
||||
static string calc_md5(const string &source);
|
||||
INLINE static char hexdigit(int value);
|
||||
|
||||
string _cnonce;
|
||||
string _nonce;
|
||||
int _nonce_count;
|
||||
string _opaque;
|
||||
|
||||
Algorithm _algorithm;
|
||||
string _a1;
|
||||
|
||||
int _qop;
|
||||
Qop _chosen_qop;
|
||||
|
||||
static const string _mechanism;
|
||||
};
|
||||
|
||||
ostream &operator << (ostream &out, HTTPDigestAuthorization::Algorithm algorithm);
|
||||
ostream &operator << (ostream &out, HTTPDigestAuthorization::Qop qop);
|
||||
|
||||
#include "httpDigestAuthorization.I"
|
||||
|
||||
#endif // HAVE_SSL
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// Filename: httpEnum.cxx
|
||||
// Created by: drose (25Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "httpEnum.h"
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPEnum::Method::output operator
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
ostream &
|
||||
operator << (ostream &out, HTTPEnum::Method method) {
|
||||
switch (method) {
|
||||
case HTTPEnum::M_get:
|
||||
out << "GET";
|
||||
break;
|
||||
|
||||
case HTTPEnum::M_head:
|
||||
out << "HEAD";
|
||||
break;
|
||||
|
||||
case HTTPEnum::M_post:
|
||||
out << "POST";
|
||||
break;
|
||||
|
||||
case HTTPEnum::M_connect:
|
||||
out << "CONNECT";
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif // HAVE_SSL
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// Filename: httpEnum.h
|
||||
// Created by: drose (25Oct02)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
|
||||
//
|
||||
// All use of this software is subject to the terms of the Panda 3d
|
||||
// Software license. You should have received a copy of this license
|
||||
// along with this source code; you will also find a current copy of
|
||||
// the license at http://www.panda3d.org/license.txt .
|
||||
//
|
||||
// To contact the maintainers of this program write to
|
||||
// panda3d@yahoogroups.com .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef HTTPENUM_H
|
||||
#define HTTPENUM_H
|
||||
|
||||
#include "pandabase.h"
|
||||
|
||||
// This module requires OpenSSL to compile, even if you do not intend
|
||||
// to use this to establish https connections; this is because it uses
|
||||
// the OpenSSL library to portably handle all of the socket
|
||||
// communications.
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : HTTPEnum
|
||||
// Description : This class is just used as a namespace wrapper for
|
||||
// some of the enumerated types used by various classes
|
||||
// within the HTTPClient family.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDAEXPRESS HTTPEnum {
|
||||
public:
|
||||
enum HTTPVersion {
|
||||
HV_09, // HTTP 0.9 or older
|
||||
HV_10, // HTTP 1.0
|
||||
HV_11, // HTTP 1.1
|
||||
HV_other,
|
||||
};
|
||||
|
||||
enum Method {
|
||||
M_get,
|
||||
M_head,
|
||||
M_post,
|
||||
M_connect
|
||||
};
|
||||
};
|
||||
|
||||
ostream &operator << (ostream &out, HTTPEnum::Method method);
|
||||
|
||||
#endif // HAVE_SSL
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
// because the socket has genuinely closed, or false if
|
||||
// we can expect more data to come along shortly.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool IIdentityStream::
|
||||
bool IIdentityStream::
|
||||
is_closed() {
|
||||
if ((_buf._has_content_length && _buf._bytes_remaining == 0) ||
|
||||
(*_buf._source)->is_closed()) {
|
||||
|
|
|
|||
|
|
@ -317,16 +317,26 @@ set_path(const string &path) {
|
|||
|
||||
} else if (!has_path()) {
|
||||
// Insert a new path specification.
|
||||
length_adjust = path.length();
|
||||
string cpath = path;
|
||||
if (cpath[0] != '/') {
|
||||
// Paths must always begin with a slash.
|
||||
cpath = '/' + cpath;
|
||||
}
|
||||
length_adjust = cpath.length();
|
||||
|
||||
_url = _url.substr(0, _path_start) + path + _url.substr(_path_end);
|
||||
_url = _url.substr(0, _path_start) + cpath + _url.substr(_path_end);
|
||||
_flags |= F_has_path;
|
||||
|
||||
} else {
|
||||
// Replace an existing path specification.
|
||||
string cpath = path;
|
||||
if (cpath[0] != '/') {
|
||||
// Paths must always begin with a slash.
|
||||
cpath = '/' + cpath;
|
||||
}
|
||||
int old_length = (int)_path_end - (int)_path_start;
|
||||
length_adjust = path.length() - old_length;
|
||||
_url = _url.substr(0, _path_start) + path + _url.substr(_path_end);
|
||||
length_adjust = cpath.length() - old_length;
|
||||
_url = _url.substr(0, _path_start) + cpath + _url.substr(_path_end);
|
||||
}
|
||||
|
||||
_path_end += length_adjust;
|
||||
|
|
|
|||
Loading…
Reference in New Issue