|
|
@@ -18,6 +18,7 @@
|
|
|
#include <sstream>
|
|
|
#include <iostream>
|
|
|
#include <fstream>
|
|
|
+#include <algorithm>
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
#include <stdio.h>
|
|
|
@@ -39,6 +40,10 @@
|
|
|
#include "find_root_dir.h"
|
|
|
#include "mkdir_complete.h"
|
|
|
|
|
|
+// We can include this header file to get the DTOOL_PLATFORM
|
|
|
+// definition, even though we don't link with dtool.
|
|
|
+#include "dtool_platform.h"
|
|
|
+
|
|
|
#define P3D_CONTENTS_FILENAME "contents.xml"
|
|
|
#define P3D_DEFAULT_PLUGIN_FILENAME "p3d_plugin.dll"
|
|
|
|
|
|
@@ -101,11 +106,11 @@ PPInstance::~PPInstance( )
|
|
|
int PPInstance::DownloadFile( const std::string& from, const std::string& to )
|
|
|
{
|
|
|
int error( 0 );
|
|
|
- PPDownloadRequest p3dContentsDownloadRequest( *this, to );
|
|
|
- PPDownloadCallback dcForContents( p3dContentsDownloadRequest );
|
|
|
+ PPDownloadRequest p3dFileDownloadRequest( *this, to );
|
|
|
+ PPDownloadCallback dcForFile( p3dFileDownloadRequest );
|
|
|
|
|
|
nout << "Downloading " << from << " into " << to << "\n";
|
|
|
- HRESULT hr = ::URLOpenStream( m_parentCtrl.GetControllingUnknown(), from.c_str(), 0, &dcForContents );
|
|
|
+ HRESULT hr = ::URLOpenStream( m_parentCtrl.GetControllingUnknown(), from.c_str(), 0, &dcForFile );
|
|
|
if ( FAILED( hr ) )
|
|
|
{
|
|
|
error = 1;
|
|
|
@@ -140,35 +145,144 @@ int PPInstance::CopyFile( const std::string& from, const std::string& to )
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int PPInstance::ReadContents( const std::string& contentsFilename, FileSpec& p3dDllFile )
|
|
|
-{
|
|
|
- int error(1);
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: PPInstance::read_contents_file
|
|
|
+// Access: Private
|
|
|
+// Description: Reads the contents.xml file and starts the core API
|
|
|
+// DLL downloading, if necessary.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+bool PPInstance::
|
|
|
+read_contents_file(const string &contents_filename) {
|
|
|
+ TiXmlDocument doc(contents_filename.c_str());
|
|
|
+ if (!doc.LoadFile()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- TiXmlDocument doc( contentsFilename.c_str( ) );
|
|
|
- if ( doc.LoadFile( ) )
|
|
|
- {
|
|
|
- TiXmlElement *xcontents = doc.FirstChildElement( "contents" );
|
|
|
- if ( xcontents != NULL )
|
|
|
- {
|
|
|
- TiXmlElement *xpackage = xcontents->FirstChildElement( "package" );
|
|
|
- while ( xpackage != NULL )
|
|
|
- {
|
|
|
- const char *name = xpackage->Attribute( "name" );
|
|
|
- if ( name != NULL && strcmp( name, "coreapi" ) == 0 )
|
|
|
- {
|
|
|
- const char *platform = xpackage->Attribute( "platform" );
|
|
|
- if ( platform != NULL && !strcmp(platform, "win32") )
|
|
|
- {
|
|
|
- p3dDllFile.load_xml(xpackage);
|
|
|
- error = 0;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- xpackage = xpackage->NextSiblingElement( "package" );
|
|
|
- }
|
|
|
+ TiXmlElement *xcontents = doc.FirstChildElement("contents");
|
|
|
+ if (xcontents != NULL) {
|
|
|
+ // Look for the <host> entry; it might point us at a different
|
|
|
+ // download URL, and it might mention some mirrors.
|
|
|
+ string host_url = PANDA_PACKAGE_HOST_URL;
|
|
|
+ TiXmlElement *xhost = xcontents->FirstChildElement("host");
|
|
|
+ if (xhost != NULL) {
|
|
|
+ const char *url = xhost->Attribute("url");
|
|
|
+ if (url != NULL && host_url == string(url)) {
|
|
|
+ // We're the primary host. This is the normal case.
|
|
|
+ read_xhost(xhost);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // We're not the primary host; perhaps we're an alternate host.
|
|
|
+ TiXmlElement *xalthost = xhost->FirstChildElement("alt_host");
|
|
|
+ while (xalthost != NULL) {
|
|
|
+ const char *url = xalthost->Attribute("url");
|
|
|
+ if (url != NULL && host_url == string(url)) {
|
|
|
+ // Yep, we're this alternate host.
|
|
|
+ read_xhost(xhost);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ xalthost = xalthost->NextSiblingElement("alt_host");
|
|
|
}
|
|
|
+ }
|
|
|
}
|
|
|
- return error;
|
|
|
+
|
|
|
+ // Now look for the core API package.
|
|
|
+ TiXmlElement *xpackage = xcontents->FirstChildElement("package");
|
|
|
+ while (xpackage != NULL) {
|
|
|
+ const char *name = xpackage->Attribute("name");
|
|
|
+ if (name != NULL && strcmp(name, "coreapi") == 0) {
|
|
|
+ const char *platform = xpackage->Attribute("platform");
|
|
|
+ if (platform != NULL && strcmp(platform, DTOOL_PLATFORM) == 0) {
|
|
|
+ _core_api_dll.load_xml(xpackage);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ xpackage = xpackage->NextSiblingElement("package");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Couldn't find the coreapi package description.
|
|
|
+ nout << "No coreapi package defined in contents file for "
|
|
|
+ << DTOOL_PLATFORM << "\n";
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: PPInstance::read_xhost
|
|
|
+// Access: Private
|
|
|
+// Description: Reads the host data from the <host> (or <alt_host>)
|
|
|
+// entry in the contents.xml file.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void PPInstance::
|
|
|
+read_xhost(TiXmlElement *xhost) {
|
|
|
+ // Get the "download" URL, which is the source from which we
|
|
|
+ // download everything other than the contents.xml file.
|
|
|
+ const char *download_url = xhost->Attribute("download_url");
|
|
|
+ if (download_url != NULL) {
|
|
|
+ _download_url_prefix = download_url;
|
|
|
+ } else {
|
|
|
+ _download_url_prefix = PANDA_PACKAGE_HOST_URL;
|
|
|
+ }
|
|
|
+ if (!_download_url_prefix.empty()) {
|
|
|
+ if (_download_url_prefix[_download_url_prefix.size() - 1] != '/') {
|
|
|
+ _download_url_prefix += "/";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ TiXmlElement *xmirror = xhost->FirstChildElement("mirror");
|
|
|
+ while (xmirror != NULL) {
|
|
|
+ const char *url = xmirror->Attribute("url");
|
|
|
+ if (url != NULL) {
|
|
|
+ add_mirror(url);
|
|
|
+ }
|
|
|
+ xmirror = xmirror->NextSiblingElement("mirror");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: PPInstance::add_mirror
|
|
|
+// Access: Private
|
|
|
+// Description: Adds a new URL to serve as a mirror for this host.
|
|
|
+// The mirrors will be consulted first, before
|
|
|
+// consulting the host directly.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void PPInstance::
|
|
|
+add_mirror(std::string mirror_url) {
|
|
|
+ // Ensure the URL ends in a slash.
|
|
|
+ if (!mirror_url.empty() && mirror_url[mirror_url.size() - 1] != '/') {
|
|
|
+ mirror_url += '/';
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add it to the _mirrors list, but only if it's not already
|
|
|
+ // there.
|
|
|
+ if (std::find(_mirrors.begin(), _mirrors.end(), mirror_url) == _mirrors.end()) {
|
|
|
+ _mirrors.push_back(mirror_url);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: PPInstance::choose_random_mirrors
|
|
|
+// Access: Public
|
|
|
+// Description: Selects num_mirrors elements, chosen at random, from
|
|
|
+// the _mirrors list. Adds the selected mirrors to
|
|
|
+// result. If there are fewer than num_mirrors elements
|
|
|
+// in the list, adds only as many mirrors as we can get.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void PPInstance::
|
|
|
+choose_random_mirrors(std::vector<std::string> &result, int num_mirrors) {
|
|
|
+ std::vector<size_t> selected;
|
|
|
+
|
|
|
+ size_t num_to_select = min(_mirrors.size(), (size_t)num_mirrors);
|
|
|
+ while (num_to_select > 0) {
|
|
|
+ size_t i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size());
|
|
|
+ while (std::find(selected.begin(), selected.end(), i) != selected.end()) {
|
|
|
+ // Already found this i, find a new one.
|
|
|
+ i = (size_t)(((double)rand() / (double)RAND_MAX) * _mirrors.size());
|
|
|
+ }
|
|
|
+ selected.push_back(i);
|
|
|
+ result.push_back(_mirrors[i]);
|
|
|
+ --num_to_select;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
int PPInstance::DownloadP3DComponents( std::string& p3dDllFilename )
|
|
|
@@ -204,17 +318,18 @@ int PPInstance::DownloadP3DComponents( std::string& p3dDllFilename )
|
|
|
strm << hostUrl << P3D_CONTENTS_FILENAME << "?" << time(NULL);
|
|
|
std::string remoteContentsUrl( strm.str() );
|
|
|
|
|
|
- FileSpec p3dDllFile;
|
|
|
error = DownloadFile( remoteContentsUrl, localContentsFileName );
|
|
|
if ( !error )
|
|
|
{
|
|
|
- error = ReadContents( localContentsFileName, p3dDllFile );
|
|
|
+ if ( !read_contents_file( localContentsFileName ) )
|
|
|
+ error = 1;
|
|
|
}
|
|
|
|
|
|
if ( error ) {
|
|
|
// If we couldn't download or read the contents.xml file, check
|
|
|
// to see if there's a good one on disk already, as a fallback.
|
|
|
- error = ReadContents( finalContentsFileName, p3dDllFile );
|
|
|
+ if ( !read_contents_file( finalContentsFileName ) )
|
|
|
+ error = 1;
|
|
|
|
|
|
} else {
|
|
|
// If we have successfully read the downloaded version,
|
|
|
@@ -226,34 +341,63 @@ int PPInstance::DownloadP3DComponents( std::string& p3dDllFilename )
|
|
|
// We don't need the temporary file any more.
|
|
|
::DeleteFile( localContentsFileName.c_str() );
|
|
|
|
|
|
- if ( !error )
|
|
|
- {
|
|
|
- // OK, at this point we have successfully read contents.xml,
|
|
|
- // and we have a good file spec in p3dDllFile.
|
|
|
- if ( p3dDllFile.quick_verify( m_rootDir ) )
|
|
|
- {
|
|
|
- // The DLL is already on-disk, and is good.
|
|
|
- p3dDllFilename = p3dDllFile.get_pathname( m_rootDir );
|
|
|
+ if (!error) {
|
|
|
+ // OK, at this point we have successfully read contents.xml,
|
|
|
+ // and we have a good file spec in _core_api_dll.
|
|
|
+ if (_core_api_dll.quick_verify(m_rootDir)) {
|
|
|
+ // The DLL is already on-disk, and is good.
|
|
|
+ p3dDllFilename = _core_api_dll.get_pathname(m_rootDir);
|
|
|
+ } else {
|
|
|
+ // The DLL is not already on-disk, or it's stale. Go get it.
|
|
|
+ std::string p3dLocalModuleFileName(_core_api_dll.get_pathname(m_rootDir));
|
|
|
+ mkfile_complete(p3dLocalModuleFileName, nout);
|
|
|
+
|
|
|
+ // Try one of the mirrors first.
|
|
|
+ std::vector<std::string> mirrors;
|
|
|
+ choose_random_mirrors(mirrors, 2);
|
|
|
+
|
|
|
+ error = 1;
|
|
|
+ for (std::vector<std::string>::iterator si = mirrors.begin();
|
|
|
+ si != mirrors.end() && error;
|
|
|
+ ++si) {
|
|
|
+ std::string url = (*si) + _core_api_dll.get_filename();
|
|
|
+ error = DownloadFile(url, p3dLocalModuleFileName);
|
|
|
+ if (!error && !_core_api_dll.full_verify(m_rootDir)) {
|
|
|
+ // If it's not right after downloading, it's an error.
|
|
|
+ error = 1;
|
|
|
+ }
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- // The DLL is not already on-disk, or it's stale.
|
|
|
- std::string p3dLocalModuleFileName( p3dDllFile.get_pathname( m_rootDir ) );
|
|
|
- mkfile_complete( p3dLocalModuleFileName, nout );
|
|
|
- std::string p3dRemoteModuleUrl( hostUrl );
|
|
|
- p3dRemoteModuleUrl += p3dDllFile.get_filename();
|
|
|
- error = DownloadFile( p3dRemoteModuleUrl, p3dLocalModuleFileName );
|
|
|
- if ( !error )
|
|
|
- {
|
|
|
- error = 1;
|
|
|
- if ( p3dDllFile.full_verify( m_rootDir ) )
|
|
|
- {
|
|
|
- // Downloaded successfully.
|
|
|
- p3dDllFilename = p3dDllFile.get_pathname( m_rootDir );
|
|
|
- error = 0;
|
|
|
- }
|
|
|
- }
|
|
|
+
|
|
|
+ // If that failed, go get it from the authoritative host.
|
|
|
+ if (error) {
|
|
|
+ std::string url = _download_url_prefix + _core_api_dll.get_filename();
|
|
|
+ error = DownloadFile(url, p3dLocalModuleFileName);
|
|
|
+ if (!error && !_core_api_dll.full_verify(m_rootDir)) {
|
|
|
+ error = 1;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ // If *that* failed, go get it again from the same URL, this
|
|
|
+ // time with a query prefix to bust through any caches.
|
|
|
+ if (error) {
|
|
|
+ std::ostringstream strm;
|
|
|
+ strm << _download_url_prefix << _core_api_dll.get_filename();
|
|
|
+ strm << "?" << time(NULL);
|
|
|
+
|
|
|
+ std::string url = strm.str();
|
|
|
+ error = DownloadFile(url, p3dLocalModuleFileName);
|
|
|
+ if (!error && !_core_api_dll.full_verify(m_rootDir)) {
|
|
|
+ nout << "After download, " << _core_api_dll.get_filename()
|
|
|
+ << " is no good.\n";
|
|
|
+ error = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!error) {
|
|
|
+ // Downloaded successfully.
|
|
|
+ p3dDllFilename = _core_api_dll.get_pathname(m_rootDir);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return error;
|
|
|
@@ -438,22 +582,18 @@ void PPInstance::HandleRequestGetUrl( void* data )
|
|
|
PPDownloadRequest p3dObjectDownloadRequest( parent->m_instance, request );
|
|
|
PPDownloadCallback bsc( p3dObjectDownloadRequest );
|
|
|
HRESULT hr = ::URLOpenStream( parent->GetControllingUnknown(), url.c_str(), 0, &bsc );
|
|
|
+
|
|
|
+ P3D_result_code result_code = P3D_RC_done;
|
|
|
if ( FAILED( hr ) )
|
|
|
{
|
|
|
nout << "Error handling P3D_RT_get_url request" << " :" << hr << "\n";
|
|
|
- return;
|
|
|
+ result_code = P3D_RC_generic_error;
|
|
|
}
|
|
|
- //inet_InternetSession inet(parent->m_pythonEmbed.m_threadData.m_parent);
|
|
|
- //std::string outdata;
|
|
|
- //if ( !inet.getURLMemory( url, outdata, request ) )
|
|
|
- //{
|
|
|
- // handled = false;
|
|
|
- //}
|
|
|
|
|
|
P3D_instance_feed_url_stream(
|
|
|
request->_instance,
|
|
|
request->_request._get_url._unique_id,
|
|
|
- P3D_RC_done,
|
|
|
+ result_code,
|
|
|
0,
|
|
|
0,
|
|
|
(const void*)NULL,
|