more towards self-downloading
This commit is contained in:
parent
739ca67e1f
commit
652ca28703
|
|
@ -1,9 +1,9 @@
|
|||
// This directory is still experimental. Define HAVE_P3D_PLUGIN in
|
||||
// your Config.pp to build it.
|
||||
#define BUILD_DIRECTORY $[and $[HAVE_P3D_PLUGIN],$[HAVE_PYTHON],$[HAVE_TINYXML]]
|
||||
#define BUILD_DIRECTORY $[and $[HAVE_P3D_PLUGIN],$[HAVE_PYTHON],$[HAVE_TINYXML],$[HAVE_OPENSSL],$[HAVE_ZLIB]]
|
||||
|
||||
#begin lib_target
|
||||
#define USE_PACKAGES tinyxml
|
||||
#define USE_PACKAGES tinyxml openssl zlib
|
||||
#define TARGET p3d_plugin
|
||||
|
||||
#define COMBINED_SOURCES \
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
p3dFileDownload.h p3dFileDownload.I \
|
||||
p3dInstance.h p3dInstance.I \
|
||||
p3dInstanceManager.h p3dInstanceManager.I \
|
||||
p3dPackage.h p3dPackage.I \
|
||||
p3dSession.h p3dSession.I
|
||||
|
||||
#define INCLUDED_SOURCES \
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
p3dFileDownload.cxx \
|
||||
p3dInstance.cxx \
|
||||
p3dInstanceManager.cxx \
|
||||
p3dPackage.cxx \
|
||||
p3dSession.cxx
|
||||
|
||||
#define INSTALL_HEADERS \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
#! /bin/env python
|
||||
|
||||
"""
|
||||
This command is used to build a downloadable package for the p3d
|
||||
plugin to retrieve and install. It examines the files in the current
|
||||
directory, assumes they are all intended to be part of the package,
|
||||
and constructs the necessary package xml file and archive file for
|
||||
hosting on the web serevr.
|
||||
|
||||
make_package.py [opts]
|
||||
|
||||
Options:
|
||||
|
||||
-d stage_dir
|
||||
|
||||
Specify the staging directory. This is a temporary directory on
|
||||
the local machine that will be filled with the contents of the
|
||||
package directory for the web server.
|
||||
|
||||
-p package_name
|
||||
-v package_version
|
||||
|
||||
Specify the name and version of the package to build.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import getopt
|
||||
import os
|
||||
import tarfile
|
||||
import gzip
|
||||
import stat
|
||||
import md5
|
||||
|
||||
import direct
|
||||
from pandac.PandaModules import *
|
||||
|
||||
class ArgumentError(AttributeError):
|
||||
pass
|
||||
|
||||
class FileSpec:
|
||||
def __init__(self, filename, pathname):
|
||||
self.filename = filename
|
||||
self.pathname = pathname
|
||||
self.size = 0
|
||||
self.timestamp = 0
|
||||
self.hash = None
|
||||
|
||||
s = os.stat(self.pathname)
|
||||
self.size = s[stat.ST_SIZE]
|
||||
self.timestamp = s[stat.ST_MTIME]
|
||||
|
||||
m = md5.new()
|
||||
f = open(self.pathname, 'rb')
|
||||
data = f.read(4096)
|
||||
while data:
|
||||
m.update(data)
|
||||
data = f.read(4096)
|
||||
f.close()
|
||||
|
||||
self.hash = m.hexdigest()
|
||||
|
||||
def get_params(self):
|
||||
return 'filename="%s" size=%s timestamp=%s hash="%s"' % (
|
||||
self.filename, self.size, self.timestamp, self.hash)
|
||||
|
||||
class PackageMaker:
|
||||
def __init__(self):
|
||||
self.startDir = None
|
||||
self.stageDir = None
|
||||
self.packageName = None
|
||||
self.packageVersion = None
|
||||
|
||||
|
||||
def build(self):
|
||||
if not self.startDir:
|
||||
raise ArgumentError, "Start directory not specified."
|
||||
if not self.stageDir:
|
||||
raise ArgumentError, "Stage directory not specified."
|
||||
if not self.packageName or not self.packageVersion:
|
||||
raise ArgumentError, "Package name and version not specified."
|
||||
|
||||
self.packageFullname = '%s_%s' % (
|
||||
self.packageName, self.packageVersion)
|
||||
|
||||
self.cleanDir(self.stageDir)
|
||||
|
||||
uncompressedArchiveBasename = '%s.tar' % (self.packageFullname)
|
||||
uncompressedArchivePathname = os.path.join(self.stageDir, uncompressedArchiveBasename)
|
||||
self.archive = tarfile.open(uncompressedArchivePathname, 'w')
|
||||
|
||||
self.components = []
|
||||
|
||||
self.addComponents()
|
||||
self.archive.close()
|
||||
|
||||
uncompressedArchive = FileSpec(uncompressedArchiveBasename, uncompressedArchivePathname)
|
||||
|
||||
compressedArchiveBasename = '%s.tgz' % (self.packageFullname)
|
||||
compressedArchivePathname = os.path.join(self.stageDir, compressedArchiveBasename)
|
||||
|
||||
print "\ncompressing"
|
||||
f = open(uncompressedArchivePathname, 'rb')
|
||||
gz = gzip.open(compressedArchivePathname, 'w', 9)
|
||||
data = f.read(4096)
|
||||
while data:
|
||||
gz.write(data)
|
||||
data = f.read(4096)
|
||||
gz.close()
|
||||
f.close()
|
||||
|
||||
compressedArchive = FileSpec(compressedArchiveBasename, compressedArchivePathname)
|
||||
|
||||
os.unlink(uncompressedArchivePathname)
|
||||
|
||||
descFileBasename = '%s.xml' % (self.packageFullname)
|
||||
descFilePathname = os.path.join(self.stageDir, descFileBasename)
|
||||
|
||||
f = open(descFilePathname, 'w')
|
||||
print >> f, '<?xml version="1.0" ?>'
|
||||
print >> f, ''
|
||||
print >> f, '<package name="%s" version="%s">' % (self.packageName, self.packageVersion)
|
||||
print >> f, ' <uncompressed_archive %s />' % (uncompressedArchive.get_params())
|
||||
print >> f, ' <compressed_archive %s />' % (compressedArchive.get_params())
|
||||
for file in self.components:
|
||||
print >> f, ' <component %s />' % (file.get_params())
|
||||
print >> f, '</package>'
|
||||
f.close()
|
||||
|
||||
|
||||
def cleanDir(self, dirname):
|
||||
""" Remove all the files in the named directory. Does not
|
||||
operate recursively. """
|
||||
|
||||
for filename in os.listdir(dirname):
|
||||
pathname = os.path.join(dirname, filename)
|
||||
try:
|
||||
os.unlink(pathname)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def addComponents(self):
|
||||
""" Walks through all the files in the start directory and
|
||||
adds them to the archive. Recursively visits
|
||||
sub-directories. """
|
||||
|
||||
startDir = self.startDir
|
||||
if startDir.endswith(os.altsep) or startDir.endswith(os.sep):
|
||||
startDir = startDir[:-1]
|
||||
prefix = startDir + os.sep
|
||||
for dirpath, dirnames, filenames in os.walk(startDir):
|
||||
if dirpath == startDir:
|
||||
localpath = ''
|
||||
else:
|
||||
assert dirpath.startswith(prefix)
|
||||
localpath = dirpath[len(prefix):]
|
||||
|
||||
for basename in filenames:
|
||||
file = FileSpec(os.path.join(localpath, basename),
|
||||
os.path.join(startDir, basename))
|
||||
print file.filename
|
||||
self.components.append(file)
|
||||
self.archive.add(file.pathname, file.filename, recursive = False)
|
||||
|
||||
def makePackage(args):
|
||||
opts, args = getopt.getopt(args, 'd:p:v:h')
|
||||
|
||||
pm = PackageMaker()
|
||||
pm.startDir = '.'
|
||||
for option, value in opts:
|
||||
if option == '-d':
|
||||
pm.stageDir = Filename.fromOsSpecific(value).toOsSpecific()
|
||||
elif option == '-p':
|
||||
pm.packageName = value
|
||||
elif option == '-v':
|
||||
pm.packageVersion = value
|
||||
|
||||
elif option == '-h':
|
||||
print __doc__
|
||||
sys.exit(1)
|
||||
|
||||
pm.build()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
makePackage(sys.argv[1:])
|
||||
except ArgumentError, e:
|
||||
print e.args[0]
|
||||
sys.exit(1)
|
||||
|
|
@ -25,7 +25,8 @@ P3DDownload() {
|
|||
_http_status_code = 0;
|
||||
_total_data = 0;
|
||||
_total_expected_data = 0;
|
||||
|
||||
|
||||
_canceled = false;
|
||||
_download_id = 0;
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +49,21 @@ set_url(const string &url) {
|
|||
_url = url;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DDownload::cancel
|
||||
// Access: Public
|
||||
// Description: Cancels a running download. download_finished() will
|
||||
// not be called, but the P3DDownload object itself will
|
||||
// eventually be deleted by its owning P3DInstance.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DDownload::
|
||||
cancel() {
|
||||
_canceled = true;
|
||||
if (_status == P3D_RC_in_progress) {
|
||||
_status = P3D_RC_generic_error;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DDownload::feed_url_stream
|
||||
// Access: Public
|
||||
|
|
@ -60,12 +76,16 @@ feed_url_stream(P3D_result_code result_code,
|
|||
size_t total_expected_data,
|
||||
const unsigned char *this_data,
|
||||
size_t this_data_size) {
|
||||
if (_canceled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_status = result_code;
|
||||
_http_status_code = http_status_code;
|
||||
|
||||
if (this_data_size != 0) {
|
||||
if (!receive_data(this_data, this_data_size)) {
|
||||
// Failure writing to disk.
|
||||
// Failure writing to disk or some such.
|
||||
_status = P3D_RC_generic_error;
|
||||
download_finished(false);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ public:
|
|||
inline bool get_download_finished() const;
|
||||
inline bool get_download_success() const;
|
||||
|
||||
void cancel();
|
||||
|
||||
public:
|
||||
// These are intended to be called only by P3DInstance.
|
||||
inline void set_download_id(int download_id);
|
||||
|
|
@ -62,6 +64,7 @@ protected:
|
|||
size_t _total_expected_data;
|
||||
|
||||
private:
|
||||
bool _canceled;
|
||||
int _download_id;
|
||||
string _url;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,13 +13,51 @@
|
|||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::get_root_dir
|
||||
// Access: Public
|
||||
// Description: Returns the root directory into which all the P3D
|
||||
// runtime files are downloaded and installed. This
|
||||
// must be a writable directory or nothing will work.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline const string &P3DInstanceManager::
|
||||
get_root_dir() const {
|
||||
return _root_dir;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::get_download_url
|
||||
// Access: Public
|
||||
// Description: Returns the URL of the download server. All
|
||||
// downloadable files will be retrieved from various
|
||||
// subdirectories of this URL root.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline const string &P3DInstanceManager::
|
||||
get_download_url() const {
|
||||
return _download_url;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::get_command_instance
|
||||
// Access: Public
|
||||
// Description: Returns a global P3DInstance object that exists for
|
||||
// the lifetime of the instance manager. This instance
|
||||
// object has no reality onscreen, but can be used as a
|
||||
// download host for downloading files that aren't
|
||||
// specifically needed by any user-created instance.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline P3DInstance *P3DInstanceManager::
|
||||
get_command_instance() const {
|
||||
return _command_instance;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::get_num_instances
|
||||
// Access: Public
|
||||
// Description: Returns the number of instances currently running
|
||||
// within the world.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE int P3DInstanceManager::
|
||||
inline int P3DInstanceManager::
|
||||
get_num_instances() const {
|
||||
return _instances.size();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "p3dInstanceManager.h"
|
||||
#include "p3dInstance.h"
|
||||
#include "p3dSession.h"
|
||||
#include "p3dPackage.h"
|
||||
|
||||
P3DInstanceManager *P3DInstanceManager::_global_ptr;
|
||||
|
||||
|
|
@ -35,6 +36,8 @@ P3DInstanceManager() {
|
|||
INIT_LOCK(_request_ready_lock);
|
||||
pthread_cond_init(&_request_ready_cvar, NULL);
|
||||
#endif
|
||||
|
||||
_command_instance = NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -44,7 +47,14 @@ P3DInstanceManager() {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
P3DInstanceManager::
|
||||
~P3DInstanceManager() {
|
||||
// Actually, the destructor is never called.
|
||||
// Actually, this destructor is never called, since this is a global
|
||||
// object that never gets deleted.
|
||||
|
||||
assert(_instances.empty());
|
||||
assert(_sessions.empty());
|
||||
|
||||
delete _command_instance;
|
||||
|
||||
#ifdef _WIN32
|
||||
CloseHandle(_request_ready);
|
||||
#else
|
||||
|
|
@ -63,6 +73,8 @@ P3DInstanceManager::
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DInstanceManager::
|
||||
initialize() {
|
||||
_root_dir = "c:/cygwin/home/drose/p3ddir";
|
||||
_download_url = "http://10.196.143.118/~drose/";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +121,8 @@ create_instance(P3D_request_ready_func *func,
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstanceManager::
|
||||
finish_instance(P3DInstance *inst) {
|
||||
assert(inst != _command_instance);
|
||||
|
||||
Instances::iterator ii;
|
||||
ii = _instances.find(inst);
|
||||
assert(ii != _instances.end());
|
||||
|
|
@ -145,6 +159,10 @@ check_request() {
|
|||
}
|
||||
}
|
||||
|
||||
if (_command_instance->has_request()) {
|
||||
return _command_instance;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -164,10 +182,10 @@ wait_request() {
|
|||
int seq = _request_seq;
|
||||
|
||||
while (true) {
|
||||
if (_instances.empty()) {
|
||||
if (check_request() != (P3DInstance *)NULL) {
|
||||
return;
|
||||
}
|
||||
if (check_request() != (P3DInstance *)NULL) {
|
||||
if (_instances.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -187,6 +205,27 @@ wait_request() {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::get_package
|
||||
// Access: Public
|
||||
// Description: Returns a (possibly shared) pointer to the indicated
|
||||
// package.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3DPackage *P3DInstanceManager::
|
||||
get_package(const string &package_name, const string &package_version) {
|
||||
string key = package_name + "_" + package_version;
|
||||
Packages::iterator pi = _packages.find(key);
|
||||
if (pi != _packages.end()) {
|
||||
return (*pi).second;
|
||||
}
|
||||
|
||||
P3DPackage *package = new P3DPackage(package_name, package_version);
|
||||
bool inserted = _packages.insert(Packages::value_type(key, package)).second;
|
||||
assert(inserted);
|
||||
|
||||
return package;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::get_unique_session_index
|
||||
// Access: Public
|
||||
|
|
@ -229,6 +268,22 @@ P3DInstanceManager *P3DInstanceManager::
|
|||
get_global_ptr() {
|
||||
if (_global_ptr == NULL) {
|
||||
_global_ptr = new P3DInstanceManager;
|
||||
_global_ptr->create_command_instance();
|
||||
}
|
||||
return _global_ptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::create_command_instance;
|
||||
// Access: Private
|
||||
// Description: Create a command instance. This is used to handle
|
||||
// requests that have nothing to do with any particular
|
||||
// host-created instance.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstanceManager::
|
||||
create_command_instance() {
|
||||
P3D_window_handle dummy_handle;
|
||||
_command_instance =
|
||||
new P3DInstance(NULL, "", P3D_WT_hidden, 0, 0, 0, 0,
|
||||
dummy_handle, NULL, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
class P3DInstance;
|
||||
class P3DSession;
|
||||
class P3DPackage;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : P3DInstanceManager
|
||||
|
|
@ -36,6 +37,9 @@ private:
|
|||
public:
|
||||
bool initialize();
|
||||
|
||||
inline const string &get_root_dir() const;
|
||||
inline const string &get_download_url() const;
|
||||
|
||||
P3DInstance *
|
||||
create_instance(P3D_request_ready_func *func,
|
||||
const string &p3d_filename,
|
||||
|
|
@ -51,7 +55,11 @@ public:
|
|||
P3DInstance *check_request();
|
||||
void wait_request();
|
||||
|
||||
INLINE int get_num_instances() const;
|
||||
P3DPackage *get_package(const string &package_name,
|
||||
const string &package_version);
|
||||
|
||||
inline P3DInstance *get_command_instance() const;
|
||||
inline int get_num_instances() const;
|
||||
|
||||
int get_unique_session_index();
|
||||
void signal_request_ready();
|
||||
|
|
@ -59,7 +67,13 @@ public:
|
|||
static P3DInstanceManager *get_global_ptr();
|
||||
|
||||
private:
|
||||
string _p3d_root_directory;
|
||||
void create_command_instance();
|
||||
|
||||
private:
|
||||
string _root_dir;
|
||||
string _download_url;
|
||||
|
||||
P3DInstance *_command_instance;
|
||||
|
||||
typedef set<P3DInstance *> Instances;
|
||||
Instances _instances;
|
||||
|
|
@ -67,6 +81,9 @@ private:
|
|||
typedef map<string, P3DSession *> Sessions;
|
||||
Sessions _sessions;
|
||||
|
||||
typedef map<string, P3DPackage *> Packages;
|
||||
Packages _packages;
|
||||
|
||||
int _unique_session_index;
|
||||
|
||||
// Implements a condition-var like behavior.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
// Filename: p3dPackage.I
|
||||
// Created by: drose (12Jun09)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
//
|
||||
// All use of this software is subject to the terms of the revised BSD
|
||||
// license. You should have received a copy of this license along
|
||||
// with this source code in a file named "LICENSE."
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::get_ready
|
||||
// Access: Public
|
||||
// Description: Returns true if the package has been downloaded and
|
||||
// verified and is ready to be used, false if it has
|
||||
// not.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline bool P3DPackage::
|
||||
get_ready() const {
|
||||
return _ready;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::get_failed
|
||||
// Access: Public
|
||||
// Description: Returns true if the package cannot be made ready, for
|
||||
// instance because the download server is down.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline bool P3DPackage::
|
||||
get_failed() const {
|
||||
return _failed;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::decode_hexdigit
|
||||
// Access: Private
|
||||
// Description: Returns the integer value corresponding to the
|
||||
// indicated hex digit. Returns -1 if it is not a hex
|
||||
// digit.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline int P3DPackage::
|
||||
decode_hexdigit(char c) {
|
||||
if (isdigit(c)) {
|
||||
return c - '0';
|
||||
}
|
||||
c = tolower(c);
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::encode_hexdigit
|
||||
// Access: Private
|
||||
// Description: Returns the hex digit corresponding to the
|
||||
// indicated integer value.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline char P3DPackage::
|
||||
encode_hexdigit(int c) {
|
||||
if (c >= 10) {
|
||||
return c - 10 + 'a';
|
||||
}
|
||||
return c + '0';
|
||||
}
|
||||
|
|
@ -0,0 +1,695 @@
|
|||
// Filename: p3dPackage.cxx
|
||||
// Created by: drose (12Jun09)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
//
|
||||
// All use of this software is subject to the terms of the revised BSD
|
||||
// license. You should have received a copy of this license along
|
||||
// with this source code in a file named "LICENSE."
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "p3dPackage.h"
|
||||
#include "p3dInstanceManager.h"
|
||||
|
||||
#include "openssl/md5.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/utime.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#define stat _stat
|
||||
#define utime _utime
|
||||
#define utimbuf _utimbuf
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3DPackage::
|
||||
P3DPackage(const string &package_name, const string &package_version) :
|
||||
_package_name(package_name),
|
||||
_package_version(package_version)
|
||||
{
|
||||
_package_fullname = _package_name + "_" + _package_version;
|
||||
_ready = false;
|
||||
_failed = false;
|
||||
_active_download = NULL;
|
||||
_partial_download = false;
|
||||
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
|
||||
// Ensure the package directory exists; create it if it does not.
|
||||
_package_dir = inst_mgr->get_root_dir() + string("/") + _package_name;
|
||||
#ifdef _WIN32
|
||||
_mkdir(_package_dir.c_str());
|
||||
#else
|
||||
mkdir(_package_dir.c_str(), 0777);
|
||||
#endif
|
||||
|
||||
_package_dir += string("/") + _package_version;
|
||||
#ifdef _WIN32
|
||||
_mkdir(_package_dir.c_str());
|
||||
#else
|
||||
mkdir(_package_dir.c_str(), 0777);
|
||||
#endif
|
||||
|
||||
_desc_file_basename = _package_fullname + ".xml";
|
||||
_desc_file_pathname = _package_dir + "/" + _desc_file_basename;
|
||||
|
||||
// TODO: we should check the desc file for updates with the server.
|
||||
// Perhaps this should be done in a parent class.
|
||||
|
||||
// Load the desc file, if it exists.
|
||||
TiXmlDocument doc(_desc_file_pathname.c_str());
|
||||
if (!doc.LoadFile()) {
|
||||
download_desc_file();
|
||||
} else {
|
||||
got_desc_file(&doc, false);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::Destructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3DPackage::
|
||||
~P3DPackage() {
|
||||
// Tell any pending callbacks that we're no good any more.
|
||||
report_done(false);
|
||||
|
||||
// Cancel any pending download.
|
||||
if (_active_download != NULL) {
|
||||
_active_download->cancel();
|
||||
delete _active_download;
|
||||
_active_download = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::set_callback
|
||||
// Access: Public
|
||||
// Description: Registers a callback on the package. The callback
|
||||
// object will be notified when the package is ready for
|
||||
// use (or when it has failed to download properly).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
set_callback(Callback *callback) {
|
||||
if (_ready || _failed) {
|
||||
// Actually, we're already done. Signal the callback immediately.
|
||||
callback->package_ready(this, _ready);
|
||||
} else {
|
||||
// Bootstrap still in progress. Save the callback.
|
||||
_callbacks.push_back(callback);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::cancel_callback
|
||||
// Access: Public
|
||||
// Description: Unregisters a particular callback object. This
|
||||
// object will no longer be notified when the package is
|
||||
// ready.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
cancel_callback(Callback *callback) {
|
||||
Callbacks::iterator ci;
|
||||
ci = find(_callbacks.begin(), _callbacks.end(), callback);
|
||||
if (ci != _callbacks.end()) {
|
||||
_callbacks.erase(ci);
|
||||
} else {
|
||||
cerr << "Canceling unknown callback on " << _package_fullname << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::download_desc_file
|
||||
// Access: Private
|
||||
// Description: Starts downloading the desc file for the package.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
download_desc_file() {
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
string url = inst_mgr->get_download_url();
|
||||
url += _package_name + "/" + _package_version + "/" + _desc_file_basename;
|
||||
|
||||
start_download(DT_desc_file, url, _desc_file_pathname, false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::desc_file_download_finished
|
||||
// Access: Private
|
||||
// Description: Called when the desc file has been fully downloaded.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
desc_file_download_finished(bool success) {
|
||||
if (!success) {
|
||||
report_done(false);
|
||||
return;
|
||||
}
|
||||
|
||||
TiXmlDocument doc(_desc_file_pathname.c_str());
|
||||
if (!doc.LoadFile()) {
|
||||
cerr << "Couldn't read " << _desc_file_pathname << "\n";
|
||||
report_done(false);
|
||||
return;
|
||||
}
|
||||
|
||||
got_desc_file(&doc, true);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::got_desc_file
|
||||
// Access: Private
|
||||
// Description: Reads the desc file and begins verifying the files.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
|
||||
cerr << "got desc file\n";
|
||||
|
||||
TiXmlElement *xpackage = doc->FirstChildElement("package");
|
||||
TiXmlElement *uncompressed_archive = NULL;
|
||||
TiXmlElement *compressed_archive = NULL;
|
||||
|
||||
if (xpackage != NULL) {
|
||||
uncompressed_archive = xpackage->FirstChildElement("uncompressed_archive");
|
||||
compressed_archive = xpackage->FirstChildElement("compressed_archive");
|
||||
}
|
||||
|
||||
if (uncompressed_archive == NULL || compressed_archive == NULL) {
|
||||
// The desc file didn't include the archive file itself, weird.
|
||||
if (!freshly_downloaded) {
|
||||
download_desc_file();
|
||||
return;
|
||||
}
|
||||
report_done(false);
|
||||
return;
|
||||
}
|
||||
|
||||
_uncompressed_archive.load_xml(uncompressed_archive);
|
||||
_compressed_archive.load_xml(compressed_archive);
|
||||
|
||||
// Now get all the components.
|
||||
_components.clear();
|
||||
TiXmlElement *component = xpackage->FirstChildElement("component");
|
||||
while (component != NULL) {
|
||||
FileSpec file;
|
||||
file.load_xml(component);
|
||||
_components.push_back(file);
|
||||
component = component->NextSiblingElement("component");
|
||||
}
|
||||
|
||||
cerr << "got " << _components.size() << " components\n";
|
||||
|
||||
// Verify all of the components.
|
||||
bool all_components_ok = true;
|
||||
Components::iterator ci;
|
||||
for (ci = _components.begin(); ci != _components.end(); ++ci) {
|
||||
if (!(*ci).quick_verify(_package_dir)) {
|
||||
all_components_ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_components_ok) {
|
||||
// Great, we're ready to begin.
|
||||
report_done(true);
|
||||
|
||||
} else if (_uncompressed_archive.quick_verify(_package_dir)) {
|
||||
// We need to re-extract the archive.
|
||||
extract_archive();
|
||||
|
||||
} else if (_compressed_archive.quick_verify(_package_dir)) {
|
||||
// We need to uncompress the archive.
|
||||
uncompress_archive();
|
||||
|
||||
} else {
|
||||
// Shoot, we need to download the archive.
|
||||
download_compressed_archive(true);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::download_compressed_archive
|
||||
// Access: Private
|
||||
// Description: Starts downloading the archive file for the package.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
download_compressed_archive(bool allow_partial) {
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
string url = inst_mgr->get_download_url();
|
||||
url += _package_name + "/" + _package_version + "/" + _compressed_archive._filename;
|
||||
|
||||
string target_pathname = _package_dir + "/" + _compressed_archive._filename;
|
||||
|
||||
start_download(DT_compressed_archive, url, target_pathname, allow_partial);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::compressed_archive_download_finished
|
||||
// Access: Private
|
||||
// Description: Called when the desc file has been fully downloaded.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
compressed_archive_download_finished(bool success) {
|
||||
if (!success) {
|
||||
report_done(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_compressed_archive.full_verify(_package_dir)) {
|
||||
// Go on to uncompress the archive.
|
||||
uncompress_archive();
|
||||
return;
|
||||
}
|
||||
|
||||
// Oof, didn't download it correctly.
|
||||
if (_partial_download) {
|
||||
// Go back and get the whole file this time.
|
||||
download_compressed_archive(false);
|
||||
}
|
||||
|
||||
cerr << _compressed_archive._filename
|
||||
<< " failed hash check after download\n";
|
||||
report_done(false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::uncompress_archive
|
||||
// Access: Private
|
||||
// Description: Uncompresses the archive file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
uncompress_archive() {
|
||||
cerr << "uncompressing " << _compressed_archive._filename << "\n";
|
||||
|
||||
string source_pathname = _package_dir + "/" + _compressed_archive._filename;
|
||||
string target_pathname = _package_dir + "/" + _uncompressed_archive._filename;
|
||||
|
||||
gzFile source = gzopen(source_pathname.c_str(), "rb");
|
||||
if (source == NULL) {
|
||||
cerr << "Couldn't open " << source_pathname << "\n";
|
||||
report_done(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ofstream target(target_pathname.c_str(), ios::out | ios::binary);
|
||||
if (!target) {
|
||||
cerr << "Couldn't write to " << target_pathname << "\n";
|
||||
report_done(false);
|
||||
}
|
||||
|
||||
static const int buffer_size = 1024;
|
||||
char buffer[buffer_size];
|
||||
|
||||
int count = gzread(source, buffer, buffer_size);
|
||||
while (count > 0) {
|
||||
target.write(buffer, count);
|
||||
count = gzread(source, buffer, buffer_size);
|
||||
}
|
||||
|
||||
if (count < 0) {
|
||||
cerr << "gzip error decompressing " << source_pathname << "\n";
|
||||
int errnum;
|
||||
cerr << gzerror(source, &errnum) << "\n";
|
||||
gzclose(source);
|
||||
report_done(false);
|
||||
return;
|
||||
}
|
||||
|
||||
gzclose(source);
|
||||
|
||||
if (!target) {
|
||||
cerr << "Couldn't write entire file to " << target_pathname << "\n";
|
||||
report_done(false);
|
||||
return;
|
||||
}
|
||||
|
||||
target.close();
|
||||
|
||||
if (!_uncompressed_archive.full_verify(_package_dir)) {
|
||||
cerr << "after uncompressing " << target_pathname << ", failed hash check\n";
|
||||
report_done(false);
|
||||
return;
|
||||
}
|
||||
|
||||
unlink(source_pathname.c_str());
|
||||
|
||||
// All done uncompressing.
|
||||
extract_archive();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::extract_archive
|
||||
// Access: Private
|
||||
// Description: Extracts the components from the archive file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
extract_archive() {
|
||||
cerr << "extracting " << _uncompressed_archive._filename << "\n";
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::report_done
|
||||
// Access: Private
|
||||
// Description: Transitions the package to "ready" or "failure"
|
||||
// state, and reports this change to all the listening
|
||||
// callbacks.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
report_done(bool success) {
|
||||
if (success) {
|
||||
_ready = true;
|
||||
_failed = false;
|
||||
} else {
|
||||
_ready = false;
|
||||
_failed = true;
|
||||
}
|
||||
|
||||
Callbacks orig_callbacks;
|
||||
orig_callbacks.swap(_callbacks);
|
||||
Callbacks::iterator ci;
|
||||
for (ci = orig_callbacks.begin(); ci != orig_callbacks.end(); ++ci) {
|
||||
(*ci)->package_ready(this, _ready);
|
||||
}
|
||||
|
||||
// We shouldn't have added any more callbacks during the above loop.
|
||||
assert(_callbacks.empty());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::start_download
|
||||
// Access: Private
|
||||
// Description: Initiates a download of the indicated file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
start_download(P3DPackage::DownloadType dtype, const string &url,
|
||||
const string &pathname, bool allow_partial) {
|
||||
// Only one download should be active at a time
|
||||
assert(_active_download == NULL);
|
||||
|
||||
Download *download = new Download(this, dtype);
|
||||
download->set_url(url);
|
||||
download->set_filename(pathname);
|
||||
|
||||
// TODO: implement partial file re-download.
|
||||
allow_partial = false;
|
||||
|
||||
if (!allow_partial) {
|
||||
unlink(pathname.c_str());
|
||||
}
|
||||
|
||||
_active_download = download;
|
||||
_partial_download = false;
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
inst_mgr->get_command_instance()->start_download(download);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::decode_hex
|
||||
// Access: Private, Static
|
||||
// Description: Decodes the hex string in source into the character
|
||||
// array in dest. dest must have has least size bytes;
|
||||
// source must have size * 2 bytes.
|
||||
//
|
||||
// Returns true on success, false if there was a non-hex
|
||||
// digit in the string.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DPackage::
|
||||
decode_hex(unsigned char *dest, const char *source, size_t size) {
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
int high = decode_hexdigit(source[i * 2]);
|
||||
int low = decode_hexdigit(source[i * 2 + 1]);
|
||||
if (high < 0 || low < 0) {
|
||||
return false;
|
||||
}
|
||||
dest[i] = (high << 4) | low;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::encode_hex
|
||||
// Access: Private, Static
|
||||
// Description: Encodes a character array into a hex string for
|
||||
// output. dest must have at least size * 2 bytes;
|
||||
// source must have size bytes. The result is not
|
||||
// null-terminated.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
encode_hex(char *dest, const unsigned char *source, size_t size) {
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
int high = (source[i] >> 4) & 0xf;
|
||||
int low = source[i] & 0xf;
|
||||
dest[2 * i] = encode_hexdigit(high);
|
||||
dest[2 * i + 1] = encode_hexdigit(low);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::stream_hex
|
||||
// Access: Private, Static
|
||||
// Description: Writes the indicated buffer as a string of hex
|
||||
// characters to the given ostream.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::
|
||||
stream_hex(ostream &out, const unsigned char *source, size_t size) {
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
int high = (source[i] >> 4) & 0xf;
|
||||
int low = source[i] & 0xf;
|
||||
out.put(encode_hexdigit(high));
|
||||
out.put(encode_hexdigit(low));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::Download::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3DPackage::Download::
|
||||
Download(P3DPackage *package, DownloadType dtype) :
|
||||
_package(package),
|
||||
_dtype(dtype)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::Download::download_finished
|
||||
// Access: Protected, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::Download::
|
||||
download_finished(bool success) {
|
||||
P3DFileDownload::download_finished(success);
|
||||
assert(_package->_active_download == this);
|
||||
_package->_active_download = NULL;
|
||||
|
||||
switch (_dtype) {
|
||||
case DT_desc_file:
|
||||
_package->desc_file_download_finished(success);
|
||||
break;
|
||||
|
||||
case DT_compressed_archive:
|
||||
_package->compressed_archive_download_finished(success);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::FileSpec::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
P3DPackage::FileSpec::
|
||||
FileSpec() {
|
||||
_size = 0;
|
||||
_timestamp = 0;
|
||||
memset(_hash, 0, sizeof(_hash));
|
||||
_got_hash = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::FileSpec::load_xml
|
||||
// Access: Public
|
||||
// Description: Reads the data from the indicated XML file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DPackage::FileSpec::
|
||||
load_xml(TiXmlElement *element) {
|
||||
const char *filename = element->Attribute("filename");
|
||||
if (filename != NULL) {
|
||||
_filename = filename;
|
||||
}
|
||||
|
||||
const char *size = element->Attribute("size");
|
||||
if (size != NULL) {
|
||||
char *endptr;
|
||||
_size = strtoul(size, &endptr, 10);
|
||||
}
|
||||
|
||||
const char *timestamp = element->Attribute("timestamp");
|
||||
if (timestamp != NULL) {
|
||||
char *endptr;
|
||||
_timestamp = strtoul(timestamp, &endptr, 10);
|
||||
}
|
||||
|
||||
_got_hash = false;
|
||||
const char *hash = element->Attribute("hash");
|
||||
if (hash != NULL && strlen(hash) == (hash_size * 2)) {
|
||||
// Decode the hex hash string.
|
||||
_got_hash = decode_hex(_hash, hash, hash_size);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::FileSpec::quick_verify
|
||||
// Access: Public
|
||||
// Description: Performs a quick test to ensure the file has not been
|
||||
// modified. This test is vulnerable to people
|
||||
// maliciously attempting to fool the program (by
|
||||
// setting datestamps etc.).
|
||||
//
|
||||
// Returns true if it is intact, false if it needs to be
|
||||
// redownloaded.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DPackage::FileSpec::
|
||||
quick_verify(const string &package_dir) const {
|
||||
string pathname = package_dir + "/" + _filename;
|
||||
struct stat st;
|
||||
if (stat(pathname.c_str(), &st) != 0) {
|
||||
cerr << "file not found: " << _filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st.st_size != _size) {
|
||||
// If the size is wrong, the file fails.
|
||||
cerr << "size wrong: " << _filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st.st_mtime == _timestamp) {
|
||||
// If the size is right and the timestamp is right, the file passes.
|
||||
return true;
|
||||
}
|
||||
|
||||
cerr << "modification time wrong: " << _filename << "\n";
|
||||
|
||||
// If the size is right but the timestamp is wrong, the file
|
||||
// soft-fails. We follow this up with a hash check.
|
||||
if (!check_hash(pathname)) {
|
||||
// Hard fail, the hash is wrong.
|
||||
cerr << "hash check wrong: " << _filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
cerr << "hash check ok: " << _filename << "\n";
|
||||
|
||||
// The hash is OK after all. Change the file's timestamp back to
|
||||
// what we expect it to be, so we can quick-verify it successfully
|
||||
// next time.
|
||||
utimbuf utb;
|
||||
utb.actime = st.st_atime;
|
||||
utb.modtime = _timestamp;
|
||||
utime(pathname.c_str(), &utb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::FileSpec::quick_verify
|
||||
// Access: Public
|
||||
// Description: Performs a more thorough test to ensure the file has
|
||||
// not been modified. This test is less vulnerable to
|
||||
// malicious attacks, since it reads and verifies the
|
||||
// entire file.
|
||||
//
|
||||
// Returns true if it is intact, false if it needs to be
|
||||
// redownloaded.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DPackage::FileSpec::
|
||||
full_verify(const string &package_dir) const {
|
||||
string pathname = package_dir + "/" + _filename;
|
||||
struct stat st;
|
||||
if (stat(pathname.c_str(), &st) != 0) {
|
||||
cerr << "file not found: " << _filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st.st_size != _size) {
|
||||
// If the size is wrong, the file fails.
|
||||
cerr << "size wrong: " << _filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the size is right but the timestamp is wrong, the file
|
||||
// soft-fails. We follow this up with a hash check.
|
||||
if (!check_hash(pathname)) {
|
||||
// Hard fail, the hash is wrong.
|
||||
cerr << "hash check wrong: " << _filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
cerr << "hash check ok: " << _filename << "\n";
|
||||
|
||||
// The hash is OK. If the timestamp is wrong, change it back to
|
||||
// what we expect it to be, so we can quick-verify it successfully
|
||||
// next time.
|
||||
|
||||
if (st.st_mtime != _timestamp) {
|
||||
utimbuf utb;
|
||||
utb.actime = st.st_atime;
|
||||
utb.modtime = _timestamp;
|
||||
utime(pathname.c_str(), &utb);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::FileSpec::check_hash
|
||||
// Access: Public
|
||||
// Description: Returns true if the file has the expected md5 hash,
|
||||
// false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DPackage::FileSpec::
|
||||
check_hash(const string &pathname) const {
|
||||
ifstream stream(pathname.c_str(), ios::in | ios::binary);
|
||||
if (!stream) {
|
||||
cerr << "unable to read " << pathname << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char md[hash_size];
|
||||
|
||||
MD5_CTX ctx;
|
||||
MD5_Init(&ctx);
|
||||
|
||||
static const int buffer_size = 1024;
|
||||
char buffer[buffer_size];
|
||||
|
||||
stream.read(buffer, buffer_size);
|
||||
size_t count = stream.gcount();
|
||||
while (count != 0) {
|
||||
MD5_Update(&ctx, buffer, count);
|
||||
stream.read(buffer, buffer_size);
|
||||
count = stream.gcount();
|
||||
}
|
||||
|
||||
MD5_Final(md, &ctx);
|
||||
|
||||
return (memcmp(md, _hash, hash_size) == 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
// Filename: p3dPackage.h
|
||||
// Created by: drose (12Jun09)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PANDA 3D SOFTWARE
|
||||
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
//
|
||||
// All use of this software is subject to the terms of the revised BSD
|
||||
// license. You should have received a copy of this license along
|
||||
// with this source code in a file named "LICENSE."
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef P3DPACKAGE_H
|
||||
#define P3DPACKAGE_H
|
||||
|
||||
#include "p3d_plugin_common.h"
|
||||
#include "p3dFileDownload.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : P3DPackage
|
||||
// Description : This corresponds to a downloadable, patchable
|
||||
// package, and all its constituent files. For
|
||||
// instance, a particular version of the Panda3D
|
||||
// runtime, which consists of a bunch of dll's
|
||||
// downloaded in a single tar file, is a package.
|
||||
//
|
||||
// The plugin is responsible for managing these packages
|
||||
// on disk, downloading new versions when needed, and
|
||||
// removing stale versions to limit disk space waste.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class P3DPackage {
|
||||
public:
|
||||
P3DPackage(const string &package_name, const string &package_version);
|
||||
~P3DPackage();
|
||||
|
||||
class Callback {
|
||||
public:
|
||||
virtual void package_ready(P3DPackage *package, bool success);
|
||||
};
|
||||
|
||||
inline bool get_ready() const;
|
||||
inline bool get_failed() const;
|
||||
|
||||
void set_callback(Callback *callback);
|
||||
void cancel_callback(Callback *callback);
|
||||
|
||||
private:
|
||||
enum DownloadType {
|
||||
DT_desc_file,
|
||||
DT_compressed_archive
|
||||
};
|
||||
|
||||
class Download : public P3DFileDownload {
|
||||
public:
|
||||
Download(P3DPackage *package, DownloadType dtype);
|
||||
|
||||
protected:
|
||||
virtual void download_finished(bool success);
|
||||
|
||||
private:
|
||||
P3DPackage *_package;
|
||||
DownloadType _dtype;
|
||||
};
|
||||
|
||||
void download_desc_file();
|
||||
void desc_file_download_finished(bool success);
|
||||
void got_desc_file(TiXmlDocument *doc, bool freshly_downloaded);
|
||||
|
||||
void download_compressed_archive(bool allow_partial);
|
||||
void compressed_archive_download_finished(bool success);
|
||||
|
||||
void uncompress_archive();
|
||||
void extract_archive();
|
||||
|
||||
void report_done(bool success);
|
||||
void start_download(DownloadType dtype, const string &url,
|
||||
const string &pathname, bool allow_partial);
|
||||
|
||||
static inline int decode_hexdigit(char c);
|
||||
static inline char encode_hexdigit(int c);
|
||||
|
||||
static bool decode_hex(unsigned char *dest, const char *source, size_t size);
|
||||
static void encode_hex(char *dest, const unsigned char *source, size_t size);
|
||||
static void stream_hex(ostream &out, const unsigned char *source, size_t size);
|
||||
|
||||
private:
|
||||
string _package_name;
|
||||
string _package_version;
|
||||
string _package_fullname;
|
||||
string _package_dir;
|
||||
|
||||
string _desc_file_basename;
|
||||
string _desc_file_pathname;
|
||||
|
||||
bool _ready;
|
||||
bool _failed;
|
||||
Download *_active_download;
|
||||
bool _partial_download;
|
||||
|
||||
typedef vector<Callback *> Callbacks;
|
||||
Callbacks _callbacks;
|
||||
|
||||
enum { hash_size = 16 };
|
||||
|
||||
class FileSpec {
|
||||
public:
|
||||
FileSpec();
|
||||
void load_xml(TiXmlElement *element);
|
||||
|
||||
bool quick_verify(const string &package_dir) const;
|
||||
bool full_verify(const string &package_dir) const;
|
||||
|
||||
bool check_hash(const string &pathname) const;
|
||||
|
||||
string _filename;
|
||||
size_t _size;
|
||||
time_t _timestamp;
|
||||
unsigned char _hash[hash_size];
|
||||
bool _got_hash;
|
||||
};
|
||||
|
||||
FileSpec _compressed_archive;
|
||||
FileSpec _uncompressed_archive;
|
||||
|
||||
typedef vector<FileSpec> Components;
|
||||
Components _components;
|
||||
|
||||
friend class Download;
|
||||
};
|
||||
|
||||
#include "p3dPackage.I"
|
||||
|
||||
#endif
|
||||
|
|
@ -131,9 +131,9 @@ start_instance(P3DInstance *inst) {
|
|||
|
||||
send_command(doc);
|
||||
|
||||
if (_python_state == PS_init) {
|
||||
download_p3dpython(inst);
|
||||
}
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
P3DPackage *panda = inst_mgr->get_package("panda3d", "dev");
|
||||
// start_p3dpython();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -209,7 +209,7 @@ download_p3dpython(P3DInstance *inst) {
|
|||
inst->start_download(download);
|
||||
*/
|
||||
|
||||
start_p3dpython();
|
||||
// start_p3dpython();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@
|
|||
#include "p3dFileDownload.cxx"
|
||||
#include "p3dInstance.cxx"
|
||||
#include "p3dInstanceManager.cxx"
|
||||
#include "p3dPackage.cxx"
|
||||
#include "p3dSession.cxx"
|
||||
|
|
|
|||
|
|
@ -143,14 +143,15 @@ thread_main() {
|
|||
} else {
|
||||
status = P3D_RC_generic_error;
|
||||
}
|
||||
cerr << "Error getting URL " << _url << "\n";
|
||||
} else {
|
||||
cerr << "Done getting URL " << _url << ", got " << bytes_sent << " bytes\n";
|
||||
}
|
||||
|
||||
P3D_instance_feed_url_stream
|
||||
(_instance, _unique_id, status,
|
||||
channel->get_status_code(),
|
||||
bytes_sent, NULL, 0);
|
||||
|
||||
cerr << "Done getting URL " << _url << ", got " << bytes_sent << " bytes\n";
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
Loading…
Reference in New Issue