verify_contents

This commit is contained in:
David Rose 2009-08-21 19:00:50 +00:00
parent c30ae4fc99
commit df978c88b2
17 changed files with 212 additions and 37 deletions

View File

@ -134,7 +134,7 @@ quick_verify(const string &package_dir) const {
}
////////////////////////////////////////////////////////////////////
// Function: FileSpec::quick_verify
// Function: FileSpec::full_verify
// Access: Public
// Description: Performs a more thorough test to ensure the file has
// not been modified. This test is less vulnerable to

View File

@ -123,8 +123,9 @@ static void unload_dso();
// path.
////////////////////////////////////////////////////////////////////
bool
load_plugin(const string &p3d_plugin_filename, const string &contents_filename,
const string &download_url, const string &platform,
load_plugin(const string &p3d_plugin_filename,
const string &contents_filename, const string &download_url,
bool verify_contents, const string &platform,
const string &log_directory, const string &log_basename) {
string filename = p3d_plugin_filename;
if (filename.empty()) {
@ -295,7 +296,7 @@ load_plugin(const string &p3d_plugin_filename, const string &contents_filename,
plugin_loaded = true;
if (!P3D_initialize(P3D_API_VERSION, contents_filename.c_str(),
download_url.c_str(), platform.c_str(),
download_url.c_str(), verify_contents, platform.c_str(),
log_directory.c_str(), log_basename.c_str())) {
// Oops, failure to initialize.
cerr << "Failed to initialize plugin (wrong API version?)\n";

View File

@ -59,8 +59,9 @@ extern P3D_instance_handle_event_func *P3D_instance_handle_event;
string get_plugin_basename();
bool
load_plugin(const string &p3d_plugin_filename, const string &contents_filename,
const string &download_url, const string &platform,
load_plugin(const string &p3d_plugin_filename,
const string &contents_filename, const string &download_url,
bool verify_contents, const string &platform,
const string &log_directory, const string &log_basename);
void unload_plugin();
bool is_plugin_loaded();

View File

@ -71,3 +71,17 @@ inline bool P3DHost::
has_contents_file() const {
return (_xcontents != NULL);
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::encode_hexdigit
// Access: Private
// Description: Returns the hex digit corresponding to the
// indicated integer value.
////////////////////////////////////////////////////////////////////
inline char P3DHost::
encode_hexdigit(int c) {
if (c >= 10) {
return c - 10 + 'a';
}
return c + '0';
}

View File

@ -15,6 +15,7 @@
#include "p3dHost.h"
#include "p3dInstanceManager.h"
#include "p3dPackage.h"
#include "openssl/md5.h"
////////////////////////////////////////////////////////////////////
// Function: P3DHost::Constructor
@ -23,12 +24,9 @@
// P3DHost.
////////////////////////////////////////////////////////////////////
P3DHost::
P3DHost(P3DInstanceManager *inst_mgr, const string &host_url) :
P3DHost(const string &host_url) :
_host_url(host_url)
{
_host_dir = inst_mgr->get_root_dir();
_host_dir += "/host"; // TODO.
// Ensure that the download URL ends with a slash.
_host_url_prefix = _host_url;
if (!_host_url_prefix.empty() && _host_url_prefix[_host_url_prefix.size() - 1] != '/') {
@ -36,6 +34,8 @@ P3DHost(P3DInstanceManager *inst_mgr, const string &host_url) :
}
_xcontents = NULL;
fill_host_dir();
}
////////////////////////////////////////////////////////////////////
@ -56,6 +56,20 @@ P3DHost::
_packages.clear();
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::read_contents_file
// Access: Public
// Description: Reads the contents.xml file in the standard
// filename, if possible.
//
// Returns true on success, false on failure.
////////////////////////////////////////////////////////////////////
bool P3DHost::
read_contents_file() {
string standard_filename = _host_dir + "/contents.xml";
return read_contents_file(standard_filename);
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::read_contents_file
// Access: Public
@ -181,6 +195,82 @@ get_package_desc_file(FileSpec &desc_file, // out
return false;
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::fill_host_dir
// Access: Private
// Description: Hashes the host_url into a (mostly) unique directory
// string for this particular host. Stores the result
// in _host_dir.
////////////////////////////////////////////////////////////////////
void P3DHost::
fill_host_dir() {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
_host_dir = inst_mgr->get_root_dir();
_host_dir += "/";
string hostname;
// Look for a server name in the URL. Including this string in the
// directory name makes it friendlier for people browsing the
// directory.
size_t p = _host_url.find("://");
if (p != string::npos) {
size_t start = p + 3;
size_t end = _host_url.find("/", start);
// Now start .. end is something like "username@host:port".
size_t at = _host_url.find("@", start);
if (at < end) {
start = at + 1;
}
size_t colon = _host_url.find(":", start);
if (colon < end) {
end = colon;
}
// Now start .. end is just the hostname.
hostname = _host_url.substr(start, end - start);
}
// Now build a hash string of the whole URL. We'll use MD5 to get a
// pretty good hash, with a minimum chance of collision. Even if
// there is a hash collision, though, it's not the end of the world;
// it just means that both hosts will dump their packages into the
// same directory, and they'll fight over the toplevel contents.xml
// file. Assuming they use different version numbers (which should
// be safe since they have the same hostname), there will be minimal
// redownloading.
static const size_t hash_size = 16;
unsigned char md[hash_size];
size_t keep_hash = hash_size;
if (!hostname.empty()) {
_host_dir += hostname;
_host_dir += "_";
// If we successfully got a hostname, we don't really need the
// full hash. We'll keep half of it.
keep_hash = hash_size / 2;
}
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, _host_url.data(), _host_url.size());
MD5_Final(md, &ctx);
for (size_t i = 0; i < keep_hash; ++i) {
int high = (md[i] >> 4) & 0xf;
int low = md[i] & 0xf;
_host_dir += encode_hexdigit(high);
_host_dir += encode_hexdigit(low);
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::standardize_filename
// Access: Private, Static

View File

@ -30,7 +30,7 @@ class P3DPackage;
////////////////////////////////////////////////////////////////////
class P3DHost {
private:
P3DHost(P3DInstanceManager *inst_mgr, const string &host_url);
P3DHost(const string &host_url);
~P3DHost();
public:
@ -40,6 +40,7 @@ public:
inline const string &get_descriptive_name() const;
inline bool has_contents_file() const;
bool read_contents_file();
bool read_contents_file(const string &contents_filename);
P3DPackage *get_package(const string &package_name,
@ -50,8 +51,11 @@ public:
const string &package_version);
private:
void fill_host_dir();
static string standardize_filename(const string &filename);
static bool copy_file(const string &from_filename, const string &to_filename);
static inline char encode_hexdigit(int c);
private:
string _host_dir;

View File

@ -778,6 +778,11 @@ start_download(P3DDownload *download) {
assert(download->get_download_id() == 0);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
// Since we're downloading something, we might as well check all
// contents files from this point on.
inst_mgr->reset_verify_contents();
int download_id = inst_mgr->get_unique_id();
download->set_download_id(download_id);

View File

@ -24,6 +24,33 @@ is_initialized() const {
return _is_initialized;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::verify_contents
// Access: Public
// Description: Returns the verify_contents flag. When this is set
// false, it indicates that we don't need to contact the
// server to verify that a contents.xml file is fresh
// before using it; we should just use it as it is.
////////////////////////////////////////////////////////////////////
inline bool P3DInstanceManager::
get_verify_contents() const {
return _verify_contents;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::reset_verify_contents
// Access: Public
// Description: Resets the verify_contents flag to true. This should
// be done whenever we discover anything needs to be
// downloaded. At this point, we might as well verify
// everything.
////////////////////////////////////////////////////////////////////
inline void P3DInstanceManager::
reset_verify_contents() {
_verify_contents = true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::get_root_dir
// Access: Public

View File

@ -153,11 +153,12 @@ P3DInstanceManager::
////////////////////////////////////////////////////////////////////
bool P3DInstanceManager::
initialize(const string &contents_filename, const string &download_url,
bool verify_contents,
const string &platform, const string &log_directory,
const string &log_basename) {
_root_dir = find_root_dir();
_verify_contents = verify_contents;
_platform = platform;
if (_platform.empty()) {
_platform = DTOOL_PLATFORM;
@ -252,7 +253,8 @@ initialize(const string &contents_filename, const string &download_url,
_is_initialized = true;
if (!download_url.empty() && !contents_filename.empty()) {
if (!_verify_contents &&
!download_url.empty() && !contents_filename.empty()) {
// Attempt to pre-read the supplied contents.xml file, to avoid an
// unnecessary download later.
P3DHost *host = get_host(download_url);
@ -440,7 +442,7 @@ get_host(const string &host_url) {
return (*pi).second;
}
P3DHost *host = new P3DHost(this, host_url);
P3DHost *host = new P3DHost(host_url);
bool inserted = _hosts.insert(Hosts::value_type(host_url, host)).second;
assert(inserted);

View File

@ -45,11 +45,14 @@ private:
public:
bool initialize(const string &contents_filename,
const string &download_url,
bool verify_contents,
const string &platform,
const string &log_directory,
const string &log_basename);
inline bool is_initialized() const;
inline bool get_verify_contents() const;
inline void reset_verify_contents();
inline const string &get_root_dir() const;
inline const string &get_platform() const;
@ -99,6 +102,7 @@ private:
private:
bool _is_initialized;
string _root_dir;
bool _verify_contents;
string _platform;
string _log_directory;
string _log_basename;

View File

@ -162,6 +162,13 @@ begin_info_download() {
////////////////////////////////////////////////////////////////////
void P3DPackage::
download_contents_file() {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (!_host->has_contents_file() && !inst_mgr->get_verify_contents()) {
// If we're allowed to read a contents file without checking the
// server first, try it now.
_host->read_contents_file();
}
if (_host->has_contents_file()) {
// We've already got a contents.xml file; go straight to the
// package desc file.

View File

@ -34,7 +34,8 @@ LOCK _api_lock;
bool
P3D_initialize(int api_version, const char *contents_filename,
const char *download_url, const char *platform,
const char *download_url, bool verify_contents,
const char *platform,
const char *log_directory, const char *log_basename) {
if (api_version != P3D_API_VERSION) {
// Can't accept an incompatible version.
@ -69,7 +70,8 @@ P3D_initialize(int api_version, const char *contents_filename,
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
bool result = inst_mgr->initialize(contents_filename, download_url,
platform, log_directory, log_basename);
verify_contents, platform,
log_directory, log_basename);
RELEASE_LOCK(_api_lock);
return result;
}

View File

@ -96,7 +96,14 @@ extern "C" {
If this is NULL, a new file will be downloaded as needed.
If download_url is not NULL or empty, it specifies the root URL of
the download server; otherwise, the compiled-in default is used.
the download server that provided the contents_filename.
If verify_contents is true, it means that the download server will
be contacted to verify that contents.xml is current, before
continuing. If it is false, it means that contents.xml will be
loaded without checking the download server, if possible. This can
be used to minimize unnecessary network operations for standalone
applications. For a web plugin, it should be set true.
If platform is not NULL or empty, it specifies the current platform
string; otherwise, the compiled-in default is used.
@ -119,7 +126,8 @@ extern "C" {
immediately unload the DLL and (if possible) download a new one. */
typedef bool
P3D_initialize_func(int api_version, const char *contents_filename,
const char *download_url, const char *platform,
const char *download_url, bool verify_contents,
const char *platform,
const char *log_directory, const char *log_basename);
/* This function should be called to unload the core API. It will

View File

@ -928,7 +928,7 @@ do_load_plugin() {
#endif // P3D_PLUGIN_P3D_PLUGIN
nout << "Attempting to load core API from " << pathname << "\n";
if (!load_plugin(pathname, "", "", "", "", "")) {
if (!load_plugin(pathname, "", "", true, "", "", "")) {
nout << "Unable to launch core API in " << pathname << "\n";
return;
}

View File

@ -66,7 +66,7 @@ run(int argc, char *argv[]) {
bool allow_multiple = false;
string download_url = PANDA_PACKAGE_HOST_URL;
string this_platform = DTOOL_PLATFORM;
bool force_download = false;
bool verify_contents = false;
P3D_window_type window_type = P3D_WT_toplevel;
int win_x = 0, win_y = 0;
@ -89,7 +89,7 @@ run(int argc, char *argv[]) {
break;
case 'f':
force_download = true;
verify_contents = true;
break;
case 't':
@ -149,7 +149,7 @@ run(int argc, char *argv[]) {
download_url += '/';
}
if (!get_plugin(download_url, this_platform, force_download)) {
if (!get_plugin(download_url, this_platform, verify_contents)) {
cerr << "Unable to load Panda3D plugin.\n";
return 1;
}
@ -315,10 +315,11 @@ run(int argc, char *argv[]) {
// true on success, false on failure.
////////////////////////////////////////////////////////////////////
bool Panda3D::
get_plugin(const string &download_url, const string &this_platform, bool force_download) {
get_plugin(const string &download_url, const string &this_platform,
bool verify_contents) {
// First, look for the existing contents.xml file.
Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
if (!force_download && read_contents_file(contents_filename, download_url, this_platform)) {
if (!verify_contents && read_contents_file(contents_filename, download_url, this_platform, verify_contents)) {
// Got the file, and it's good.
return true;
}
@ -345,7 +346,11 @@ get_plugin(const string &download_url, const string &this_platform, bool force_d
tempfile.rename_to(contents_filename);
}
return read_contents_file(contents_filename, download_url, this_platform);
// Since we had to download some of it, might as well ask the core
// API to check all of it.
verify_contents = true;
return read_contents_file(contents_filename, download_url, this_platform, verify_contents);
}
////////////////////////////////////////////////////////////////////
@ -357,7 +362,7 @@ get_plugin(const string &download_url, const string &this_platform, bool force_d
////////////////////////////////////////////////////////////////////
bool Panda3D::
read_contents_file(Filename contents_filename, const string &download_url,
const string &this_platform) {
const string &this_platform, bool verify_contents) {
ifstream in;
contents_filename.set_text();
if (!contents_filename.open_read(in)) {
@ -376,8 +381,8 @@ read_contents_file(Filename contents_filename, const string &download_url,
if (name != NULL && strcmp(name, "coreapi") == 0) {
const char *xplatform = xplugin->Attribute("platform");
if (xplatform != NULL && strcmp(xplatform, this_platform.c_str()) == 0) {
return get_core_api(contents_filename, download_url, this_platform,
xplugin);
return get_core_api(contents_filename, download_url,
this_platform, verify_contents, xplugin);
}
}
@ -401,7 +406,8 @@ read_contents_file(Filename contents_filename, const string &download_url,
////////////////////////////////////////////////////////////////////
bool Panda3D::
get_core_api(const Filename &contents_filename, const string &download_url,
const string &this_platform, TiXmlElement *xplugin) {
const string &this_platform, bool verify_contents,
TiXmlElement *xplugin) {
_core_api_dll.load_xml(xplugin);
if (!_core_api_dll.quick_verify(_root_dir)) {
@ -418,10 +424,14 @@ get_core_api(const Filename &contents_filename, const string &download_url,
return false;
}
if (!_core_api_dll.quick_verify(_root_dir)) {
if (!_core_api_dll.full_verify(_root_dir)) {
cerr << "Mismatched download for " << url << "\n";
return false;
}
// Since we had to download some of it, might as well ask the core
// API to check all of it.
verify_contents = true;
}
// Now we've got the DLL. Load it.
@ -440,7 +450,7 @@ get_core_api(const Filename &contents_filename, const string &download_url,
#endif // P3D_PLUGIN_P3D_PLUGIN
if (!load_plugin(pathname, contents_filename.to_os_specific(),
download_url, this_platform, _log_dirname,
download_url, verify_contents, this_platform, _log_dirname,
_log_basename)) {
cerr << "Unable to launch core API in " << pathname << "\n" << flush;
return false;
@ -746,7 +756,7 @@ usage() {
<< " the application output to the console.\n\n"
<< " -f\n"
<< " Force a HTTP contact to the Panda3D download server, to check\n"
<< " Force an initial contact of the Panda3D download server, to check\n"
<< " if a new version is available. Normally, this is done only\n"
<< " if contents.xml cannot be read.\n\n"

View File

@ -43,13 +43,13 @@ public:
private:
bool get_plugin(const string &download_url, const string &this_platform,
bool force_download);
bool verify_contents);
bool read_contents_file(Filename contents_filename,
const string &download_url,
const string &this_platform);
const string &this_platform, bool verify_contents);
bool get_core_api(const Filename &contents_filename,
const string &download_url, const string &this_platform,
TiXmlElement *xplugin);
bool verify_contents, TiXmlElement *xplugin);
void run_getters();
void handle_request(P3D_request *request);
void make_parent_window(P3D_window_handle &parent_window,

View File

@ -105,7 +105,7 @@ class ContentsMaker:
try:
f = open(contentsFilePathname, 'r')
except OSError:
except IOError:
return None
for line in f.readlines():