add digest authentication

This commit is contained in:
David Rose 2002-10-26 03:09:34 +00:00
parent bb9e38286d
commit b4b34572c7
26 changed files with 1581 additions and 461 deletions

View File

@ -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 \

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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"

View File

@ -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"

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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 .
//
////////////////////////////////////////////////////////////////////

View File

@ -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

View File

@ -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

View File

@ -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);
}
////////////////////////////////////////////////////////////////////

View File

@ -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

View File

@ -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();

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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');
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()) {

View File

@ -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;