Browse Source

pre-download core API dll

David Rose 16 years ago
parent
commit
596e6e1482

+ 8 - 1
direct/src/plugin/Sources.pp

@@ -11,8 +11,11 @@
     $[TARGET]_composite1.cxx
 
   #define SOURCES \
+    fileSpec.cxx fileSpec.h fileSpec.I \
+    find_root_dir.cxx find_root_dir.h \
     handleStream.cxx handleStream.h handleStream.I \
     handleStreamBuf.cxx handleStreamBuf.h \
+    mkdir_complete.cxx mkdir_complete.h \
     p3d_lock.h p3d_plugin.h \
     p3d_plugin_config.h \
     p3d_plugin_common.h \
@@ -74,9 +77,13 @@
 
 #begin static_lib_target
   #define TARGET plugin_common
+  #define USE_PACKAGES tinyxml openssl
 
   #define SOURCES \
-     load_plugin.cxx load_plugin.h
+    load_plugin.cxx load_plugin.h \
+    fileSpec.cxx fileSpec.h fileSpec.I \
+    find_root_dir.cxx find_root_dir.h \
+    mkdir_complete.cxx mkdir_complete.h
 
 #end static_lib_target
 

+ 80 - 0
direct/src/plugin/fileSpec.I

@@ -0,0 +1,80 @@
+// Filename: fileSpec.I
+// Created by:  drose (29Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: FileSpec::get_filename
+//       Access: Private
+//  Description: Returns the relative path to this file on disk,
+//               within the package root directory.
+////////////////////////////////////////////////////////////////////
+inline const string &FileSpec::
+get_filename() const {
+  return _filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FileSpec::get_pathname
+//       Access: Private
+//  Description: Returns the full path to this file on disk.
+////////////////////////////////////////////////////////////////////
+inline string FileSpec::
+get_pathname(const string &package_dir) const {
+  return package_dir + "/" + _filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FileSpec::get_size
+//       Access: Private
+//  Description: Returns the expected size of this file on disk, in
+//               bytes.
+////////////////////////////////////////////////////////////////////
+inline size_t FileSpec::
+get_size() const {
+  return _size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FileSpec::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 FileSpec::
+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: FileSpec::encode_hexdigit
+//       Access: Private
+//  Description: Returns the hex digit corresponding to the
+//               indicated integer value.
+////////////////////////////////////////////////////////////////////
+inline char FileSpec::
+encode_hexdigit(int c) {
+  if (c >= 10) {
+    return c - 10 + 'a';
+  }
+  return c + '0';
+}

+ 277 - 0
direct/src/plugin/fileSpec.cxx

@@ -0,0 +1,277 @@
+// Filename: fileSpec.cxx
+// Created by:  drose (29Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "fileSpec.h"
+#include "openssl/md5.h"
+
+#include <fstream>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+#include <sys/utime.h>
+#include <direct.h>
+#define stat _stat
+#define utime _utime
+#define utimbuf _utimbuf
+
+#else
+#include <utime.h>
+
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: FileSpec::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FileSpec::
+FileSpec() {
+  _size = 0;
+  _timestamp = 0;
+  memset(_hash, 0, sizeof(_hash));
+  _got_hash = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FileSpec::load_xml
+//       Access: Public
+//  Description: Reads the data from the indicated XML file.
+////////////////////////////////////////////////////////////////////
+void 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: 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 FileSpec::
+quick_verify(const string &package_dir) const {
+  string pathname = get_pathname(package_dir);
+  struct stat st;
+  if (stat(pathname.c_str(), &st) != 0) {
+    //nout << "file not found: " << _filename << "\n";
+    return false;
+  }
+
+  if (st.st_size != _size) {
+    // If the size is wrong, the file fails.
+    //nout << "size wrong: " << _filename << "\n";
+    return false;
+  }
+
+  if (st.st_mtime == _timestamp) {
+    // If the size is right and the timestamp is right, the file passes.
+    //nout << "file ok: " << _filename << "\n";
+    return true;
+  }
+
+  //nout << "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.
+    //nout << "hash check wrong: " << _filename << "\n";
+    return false;
+  }
+
+  //nout << "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: 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 FileSpec::
+full_verify(const string &package_dir) const {
+  string pathname = get_pathname(package_dir);
+  struct stat st;
+  if (stat(pathname.c_str(), &st) != 0) {
+    //nout << "file not found: " << _filename << "\n";
+    return false;
+  }
+
+  if (st.st_size != _size) {
+    // If the size is wrong, the file fails.
+    //nout << "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.
+    //nout << "hash check wrong: " << _filename << "\n";
+    return false;
+  }
+
+  //nout << "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: FileSpec::check_hash
+//       Access: Public
+//  Description: Returns true if the file has the expected md5 hash,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool FileSpec::
+check_hash(const string &pathname) const {
+  ifstream stream(pathname.c_str(), ios::in | ios::binary);
+  if (!stream) {
+    //nout << "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);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FileSpec::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 FileSpec::
+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: FileSpec::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 FileSpec::
+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: FileSpec::stream_hex
+//       Access: Private, Static
+//  Description: Writes the indicated buffer as a string of hex
+//               characters to the given ostream.
+////////////////////////////////////////////////////////////////////
+void FileSpec::
+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));
+  }
+}

+ 62 - 0
direct/src/plugin/fileSpec.h

@@ -0,0 +1,62 @@
+// Filename: fileSpec.h
+// Created by:  drose (29Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 FILESPEC_H
+#define FILESPEC_H
+
+#include <tinyxml.h>
+#include <string>
+using namespace std;
+
+////////////////////////////////////////////////////////////////////
+//       Class : FileSpec
+// Description : This simple class is used both within the core API in
+//               this module, as well as within the plugin_npapi
+//               plugin implementation, to represent a file on disk
+//               that may need to be verified or (re)downloaded.
+////////////////////////////////////////////////////////////////////
+class FileSpec {
+public:
+  FileSpec();
+  void load_xml(TiXmlElement *element);
+
+  inline const string &get_filename() const;
+  inline string get_pathname(const string &package_dir) const;
+  inline size_t get_size() const;
+  
+  bool quick_verify(const string &package_dir) const;
+  bool full_verify(const string &package_dir) const;
+  
+  bool check_hash(const string &pathname) const;
+
+private:
+  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);
+
+  enum { hash_size = 16 };
+
+  string _filename;
+  size_t _size;
+  time_t _timestamp;
+  unsigned char _hash[hash_size];
+  bool _got_hash;
+};
+
+#include "fileSpec.I"
+
+#endif

+ 69 - 0
direct/src/plugin/find_root_dir.cxx

@@ -0,0 +1,69 @@
+// Filename: find_root_dir.cxx
+// Created by:  drose (29Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "find_root_dir.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <shlobj.h>
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: find_root_dir
+//  Description: Returns the path to the installable Panda3D directory
+//               on the user's machine.
+////////////////////////////////////////////////////////////////////
+string
+find_root_dir() {
+#ifdef _WIN32
+  // e.g., c:/Documents and Settings/<username>/Panda3D
+
+  char buffer[MAX_PATH];
+  if (SHGetSpecialFolderPath(NULL, buffer, CSIDL_APPDATA, true)) {
+    bool isdir = false;
+    DWORD results = GetFileAttributes(buffer);
+    if (results != -1) {
+      isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
+    }
+
+    if (isdir) {
+      // The user prefix exists; do we have a Panda3D child?
+      string root = buffer;
+      root += string("/Panda3D");
+
+      // Attempt to make it first, if possible.
+      CreateDirectory(root.c_str(), NULL);
+
+      isdir = false;
+      results = GetFileAttributes(root.c_str());
+      if (results != -1) {
+        isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
+      }
+
+      if (isdir) {
+        // The directory exists!
+        return root;
+      }
+    }
+  }
+
+  // Couldn't find a directory.  Bail.
+  return ".";
+
+#else  // _WIN32
+  // TODO.
+  return "/Users/drose/p3ddir";
+
+#endif
+}

+ 23 - 0
direct/src/plugin/find_root_dir.h

@@ -0,0 +1,23 @@
+// Filename: find_root_dir.h
+// Created by:  drose (29Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 FIND_ROOT_DIR_H
+#define FIND_ROOT_DIR_H
+
+#include <string>
+using namespace std;
+
+string find_root_dir();
+
+#endif

+ 11 - 6
direct/src/plugin/make_contents.py

@@ -75,21 +75,26 @@ class ContentsMaker:
                 localpath = dirpath[len(prefix):].replace(os.sep, '/') + '/'
                 xml = dirpath[len(prefix):].replace(os.sep, '_') + '.xml'
 
+            # A special case: the "plugin" and "coreapi" directories
+            # don't have xml files, just dll's.
+            if xml.startswith('plugin_') or xml.startswith('coreapi_'):
+                if filenames:
+                    assert len(filenames) == 1
+                    xml = filenames[0]
+
             if xml not in filenames:
                 continue
             
-            if xml.count('_') == 1:
-                basename = xml.split('.')[0]
-                packageName, packageVersion = basename.split('_')
+            if localpath.count('/') == 2:
+                packageName, packageVersion, junk = localpath.split('/')
                 packagePlatform = None
                 file = FileSpec(localpath + xml,
                                 Filename(self.stageDir, localpath + xml))
                 print file.filename
                 self.packages.append((packageName, packageVersion, packagePlatform, file))
 
-            if xml.count('_') == 2:
-                basename = xml.split('.')[0]
-                packageName, packageVersion, packagePlatform = basename.split('_')
+            if localpath.count('/') == 3:
+                packageName, packageVersion, packagePlatform, junk = localpath.split('/')
                 file = FileSpec(localpath + xml,
                                 Filename(self.stageDir, localpath + xml))
                 print file.filename

+ 175 - 0
direct/src/plugin/mkdir_complete.cxx

@@ -0,0 +1,175 @@
+// Filename: mkdir_complete.cxx
+// Created by:  drose (29Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "mkdir_complete.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <fcntl.h>
+#include <errno.h>
+#endif
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: is_pathsep
+//  Description: Returns true if the indicated character is a path
+//               separator character (e.g. slash or backslash), false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+static inline bool
+is_pathsep(char ch) {
+  if (ch == '/') {
+    return true;
+  }
+#ifdef _WIN32
+  if (ch == '\\') {
+    return true;
+  }
+#endif
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: get_dirname
+//  Description: Returns the directory component of the indicated
+//               pathname, or the empty string if there is no
+//               directory prefix.
+////////////////////////////////////////////////////////////////////
+static string
+get_dirname(const string &filename) {
+  size_t p = filename.length();
+  while (p > 0) {
+    --p;
+    if (is_pathsep(filename[p])) {
+      return filename.substr(0, p);
+    }
+  }
+
+  return string();
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: mkdir_complete
+//  Description: Creates a new directory, with normal access
+//               privileges.  Returns true on success, false on
+//               failure.  Will create intervening directories if
+//               necessary.
+////////////////////////////////////////////////////////////////////
+bool
+mkdir_complete(const string &dirname) {
+#ifdef _WIN32
+  if (CreateDirectory(dirname.c_str(), NULL) != 0) {
+    // Success!
+    return true;
+  }
+
+  // Failed.
+  DWORD last_error = GetLastError();
+  if (last_error == ERROR_ALREADY_EXISTS) {
+    // Not really an error: the directory is already there.
+    return true;
+  }
+
+  if (last_error == ERROR_PATH_NOT_FOUND) {
+    // We need to make the parent directory first.
+    string parent = get_dirname(dirname);
+    if (!parent.empty() && mkdir_complete(parent)) {
+      // Parent successfully created.  Try again to make the child.
+      if (CreateDirectory(dirname.c_str(), NULL) != 0) {
+        // Got it!
+        return true;
+      }
+    }
+  }
+  return false;
+
+#else  //_WIN32
+  if (mkdir(dirname.c_str(), 0777) == 0) {
+    // Success!
+    return true;
+  }
+
+  // Failed.
+  if (errno == EEXIST) {
+    // Not really an error: the directory is already there.
+    return true;
+  }
+
+  if (errno == ENOENT) {
+    // We need to make the parent directory first.
+    string parent = get_dirname(dirname);
+    if (!parent.empty() && mkdir_complete(parent)) {
+      // Parent successfully created.  Try again to make the child.
+      if (mkdir(dirname.c_str(), 0777) == 0) {
+        // Got it!
+        return true;
+      }
+    }
+  }
+  return false;
+
+#endif  // _WIN32
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: mkfile_complete
+//  Description: Creates a new file with normal access
+//               priviledges.  Returns true on success, false on
+//               failure.  This will create intervening directories if
+//               needed.
+////////////////////////////////////////////////////////////////////
+bool
+mkfile_complete(const string &filename) {
+#ifdef _WIN32
+  HANDLE file = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE,
+                           FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+                           NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (file == INVALID_HANDLE_VALUE) {
+    // Try to make the parent directory first.
+    string parent = get_dirname(filename);
+    if (!parent.empty() && mkdir_complete(parent)) {
+      // Parent successfully created.  Try again to make the file.
+      file = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE,
+                        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+                        NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    }
+    if (file == INVALID_HANDLE_VALUE) {
+      return false;
+    }
+  }
+  CloseHandle(file);
+  return true;
+
+#else  // _WIN32
+  int fd = creat(filename.c_str(), 0777);
+  if (fd == -1) {
+    // Try to make the parent directory first.
+    string parent = get_dirname(filename);
+    if (!parent.empty() && mkdir_complete(parent)) {
+      // Parent successfully created.  Try again to make the file.
+      fd = creat(filename.c_str(), 0777);
+    }
+    if (fd == -1) {
+      return false;
+    }
+  }
+  close(fd);
+  return true;
+
+#endif  // _WIN32
+}

+ 26 - 0
direct/src/plugin/mkdir_complete.h

@@ -0,0 +1,26 @@
+// Filename: mkdir_complete.h
+// Created by:  drose (29Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 MKDIR_COMPLETE_H
+#define MKDIR_COMPLETE_H
+
+#include <string>
+using namespace std;
+
+bool mkdir_complete(const string &dirname);
+bool mkfile_complete(const string &dirname);
+
+#endif
+
+

+ 2 - 2
direct/src/plugin/p3dFileDownload.cxx

@@ -14,6 +14,7 @@
 
 #include "p3dFileDownload.h"
 #include "p3dInstanceManager.h"
+#include "mkdir_complete.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DFileDownload::Constructor
@@ -45,8 +46,7 @@ set_filename(const string &filename) {
 ////////////////////////////////////////////////////////////////////
 bool P3DFileDownload::
 open_file() {
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  if (!inst_mgr->mkfile_public(_filename)) {
+  if (!mkfile_complete(_filename)) {
     nout << "Unable to create " << _filename << "\n";
     return false;
   }

+ 0 - 21
direct/src/plugin/p3dInstanceManager.I

@@ -70,24 +70,3 @@ inline int P3DInstanceManager::
 get_num_instances() const {
   return _instances.size();
 }
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: P3DInstanceManager::is_pathsep
-//       Access: Private, Static
-//  Description: Returns true if the indicated character is a path
-//               separator character (e.g. slash or backslash), false
-//               otherwise.
-////////////////////////////////////////////////////////////////////
-inline bool P3DInstanceManager::
-is_pathsep(char ch) {
-  if (ch == '/') {
-    return true;
-  }
-#ifdef _WIN32
-  if (ch == '\\') {
-    return true;
-  }
-#endif
-  return false;
-}

+ 3 - 267
direct/src/plugin/p3dInstanceManager.cxx

@@ -17,13 +17,13 @@
 #include "p3dSession.h"
 #include "p3dPackage.h"
 #include "p3d_plugin_config.h"
+#include "find_root_dir.h"
+#include "mkdir_complete.h"
 
 #ifdef _WIN32
 #include <shlobj.h>
 #else
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <errno.h>
+//#include <sys/stat.h>
 #endif
 
 P3DInstanceManager *P3DInstanceManager::_global_ptr;
@@ -294,134 +294,6 @@ signal_request_ready() {
 #endif
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: P3DInstanceManager::mkdir_public
-//       Access: Public, Static
-//  Description: Creates a new directory with wide-open access
-//               priviledges.  Returns true on success, false on
-//               failure.  Will create intervening directories if
-//               necessary.
-////////////////////////////////////////////////////////////////////
-bool P3DInstanceManager::
-mkdir_public(const string &dirname) {
-#ifdef _WIN32
-  SECURITY_DESCRIPTOR sd;
-  InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
-  SetSecurityDescriptorDacl(&sd, true, NULL, false);
-  
-  SECURITY_ATTRIBUTES sa;
-  sa.nLength = sizeof(sa);
-  sa.lpSecurityDescriptor = &sd;
-  if (CreateDirectory(dirname.c_str(), &sa) != 0) {
-    // Success!
-    return true;
-  }
-
-  // Failed.
-  DWORD last_error = GetLastError();
-  if (last_error == ERROR_ALREADY_EXISTS) {
-    // Not really an error: the directory is already there.
-    return true;
-  }
-
-  if (last_error == ERROR_PATH_NOT_FOUND) {
-    // We need to make the parent directory first.
-    string parent = get_dirname(dirname);
-    if (!parent.empty() && mkdir_public(parent)) {
-      // Parent successfully created.  Try again to make the child.
-      if (CreateDirectory(dirname.c_str(), &sa) != 0) {
-        // Got it!
-        return true;
-      }
-    }
-  }
-  return false;
-
-#else  //_WIN32
-  if (mkdir(dirname.c_str(), 0777) == 0) {
-    // Success!
-    return true;
-  }
-
-  // Failed.
-  if (errno == EEXIST) {
-    // Not really an error: the directory is already there.
-    return true;
-  }
-
-  if (errno == ENOENT) {
-    // We need to make the parent directory first.
-    string parent = get_dirname(dirname);
-    if (!parent.empty() && mkdir_public(parent)) {
-      // Parent successfully created.  Try again to make the child.
-      if (mkdir(dirname.c_str(), 0777) == 0) {
-        // Got it!
-        return true;
-      }
-    }
-  }
-  return false;
-
-#endif  // _WIN32
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: P3DInstanceManager::mkfile_public
-//       Access: Public, Static
-//  Description: Creates a new file with wide-open access
-//               priviledges.  Returns true on success, false on
-//               failure.  This will create intervening directories if
-//               needed.
-////////////////////////////////////////////////////////////////////
-bool P3DInstanceManager::
-mkfile_public(const string &filename) {
-#ifdef _WIN32
-  SECURITY_DESCRIPTOR sd;
-  InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
-  SetSecurityDescriptorDacl(&sd, true, NULL, false);
-  
-  SECURITY_ATTRIBUTES sa;
-  sa.nLength = sizeof(sa);
-  sa.lpSecurityDescriptor = &sd;
-
-  HANDLE file = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE,
-                           FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
-                           &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-  if (file == INVALID_HANDLE_VALUE) {
-    // Try to make the parent directory first.
-    string parent = get_dirname(filename);
-    if (!parent.empty() && mkdir_public(parent)) {
-      // Parent successfully created.  Try again to make the file.
-      file = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE,
-                        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
-                        &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-    }
-    if (file == INVALID_HANDLE_VALUE) {
-      return false;
-    }
-  }
-  CloseHandle(file);
-  return true;
-
-#else  // _WIN32
-  int fd = creat(filename.c_str(), 0777);
-  if (fd == -1) {
-    // Try to make the parent directory first.
-    string parent = get_dirname(filename);
-    if (!parent.empty() && mkdir_public(parent)) {
-      // Parent successfully created.  Try again to make the file.
-      fd = creat(filename.c_str(), 0777);
-    }
-    if (fd == -1) {
-      return false;
-    }
-  }
-  close(fd);
-  return true;
-
-#endif  // _WIN32
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::get_global_ptr
 //       Access: Public, Static
@@ -434,139 +306,3 @@ get_global_ptr() {
   }
   return _global_ptr;
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: P3DInstanceManager::find_root_dir
-//       Access: Private
-//  Description: Returns the path to the installable Panda3D directory
-//               on the user's machine.
-////////////////////////////////////////////////////////////////////
-string P3DInstanceManager::
-find_root_dir() const {
-#ifdef _WIN32
-  // Try to locate a writable directory to install Panda files into.
-  char buffer[MAX_PATH];
-
-  // First, check for a user-appdata Panda3D folder.  If it already
-  // exists, use it (but don't create it yet).
-  if (SHGetSpecialFolderPath(NULL, buffer, CSIDL_APPDATA, true)) {
-    bool isdir = false;
-    DWORD results = GetFileAttributes(buffer);
-    if (results != -1) {
-      isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
-    }
-
-    if (isdir) {
-      // The user prefix exists; do we have a Panda3D child?
-      string root = buffer;
-      root += string("/Panda3D");
-
-      // Don't attempt to create the Panda3D folder yet.  Just see if
-      // it exists.
-      isdir = false;
-      results = GetFileAttributes(root.c_str());
-      if (results != -1) {
-        isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
-      }
-
-      if (isdir) {
-        // The directory exists!
-        return root;
-      }
-    }
-  }
-
-  // If there's no user-appdata Panda3D folder, look for a common one.
-  // This time we'll create it if it doesn't exist, if we can.  (We'd
-  // prefer to create the folder in a common space if we can, since
-  // that way it can be shared by multiple users.)
-  if (SHGetSpecialFolderPath(NULL, buffer, CSIDL_COMMON_APPDATA, true)) {
-    bool isdir = false;
-    DWORD results = GetFileAttributes(buffer);
-    if (results != -1) {
-      isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
-    }
-
-    if (isdir) {
-      // The common prefix exists; do we have a Panda3D child?
-      string root = buffer;
-      root += string("/Panda3D");
-
-      // Attempt to make it first, if possible.  Make sure the
-      // security attributes are wide open; this is a shared resource.
-      mkdir_public(root);
-
-      isdir = false;
-      results = GetFileAttributes(root.c_str());
-      if (results != -1) {
-        isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
-      }
-
-      if (isdir) {
-        // The directory exists!
-        return root;
-      }
-    }
-  }
-
-  // Now try again to create a Panda3D folder in user-specific
-  // directory.  Presumably we'll have write permission to this one.
-  if (SHGetSpecialFolderPath(NULL, buffer, CSIDL_APPDATA, true)) {
-    bool isdir = false;
-    DWORD results = GetFileAttributes(buffer);
-    if (results != -1) {
-      isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
-    }
-
-    if (isdir) {
-      // The user prefix exists; do we have a Panda3D child?
-      string root = buffer;
-      root += string("/Panda3D");
-
-      // Attempt to make it first, if possible.  This time we don't
-      // attempt to make it public, since this is a private space.
-      CreateDirectory(root.c_str(), NULL);
-
-      isdir = false;
-      results = GetFileAttributes(root.c_str());
-      if (results != -1) {
-        isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
-      }
-
-      if (isdir) {
-        // The directory exists!
-        return root;
-      }
-    }
-  }
-
-  // Couldn't find a directory.  Bail.
-  nout << "Couldn't find a root directory.\n" << flush;
-  return string();
-
-#else  // _WIN32
-  return "/Users/drose/p3ddir";
-
-#endif
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: P3DInstanceManager::get_dirname
-//       Access: Private, Static
-//  Description: Returns the directory component of the indicated
-//               pathname, or the empty string if there is no
-//               directory prefix.
-////////////////////////////////////////////////////////////////////
-string P3DInstanceManager::
-get_dirname(const string &filename) {
-  size_t p = filename.length();
-  while (p > 0) {
-    --p;
-    if (is_pathsep(filename[p])) {
-      return filename.substr(0, p);
-    }
-  }
-
-  return string();
-}
-

+ 0 - 8
direct/src/plugin/p3dInstanceManager.h

@@ -62,16 +62,8 @@ public:
   int get_unique_session_index();
   void signal_request_ready();
 
-  static bool mkdir_public(const string &dirname);
-  static bool mkfile_public(const string &dirname);
-
   static P3DInstanceManager *get_global_ptr();
 
-private:
-  string find_root_dir() const;
-  static inline bool is_pathsep(char ch);
-  static string get_dirname(const string &filename);
-
 private:
   bool _is_initialized;
   string _root_dir;

+ 2 - 4
direct/src/plugin/p3dMultifileReader.cxx

@@ -13,8 +13,8 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "p3dMultifileReader.h"
-#include "p3dInstanceManager.h"
 #include "p3dPackage.h"
+#include "mkdir_complete.h"
 
 #include <time.h>
 
@@ -99,8 +99,6 @@ extract(const string &pathname, const string &to_dir,
     return false;
   }
 
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-
   // Now walk through all of the files.
   size_t num_processed = 0;
   Subfiles::iterator si;
@@ -109,7 +107,7 @@ extract(const string &pathname, const string &to_dir,
     nout << s._filename << "\n";
 
     string output_pathname = to_dir + "/" + s._filename;
-    if (!inst_mgr->mkfile_public(output_pathname)) {
+    if (!mkfile_complete(output_pathname)) {
       nout << "Unable to create " << output_pathname << "\n";
       return false;
     }

+ 0 - 34
direct/src/plugin/p3dPackage.I

@@ -83,37 +83,3 @@ inline const string &P3DPackage::
 get_package_display_name() const {
   return _package_display_name;
 }
-
-////////////////////////////////////////////////////////////////////
-//     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';
-}

+ 15 - 276
direct/src/plugin/p3dPackage.cxx

@@ -16,28 +16,13 @@
 #include "p3dInstanceManager.h"
 #include "p3dInstance.h"
 #include "p3dMultifileReader.h"
+#include "mkdir_complete.h"
 
-#include "openssl/md5.h"
 #include "zlib.h"
 
 #include <algorithm>
 #include <fstream>
-#include <fcntl.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifdef _WIN32
-#include <sys/utime.h>
-#include <direct.h>
-#define stat _stat
-#define utime _utime
-#define utimbuf _utimbuf
-
-#else
-#include <utime.h>
-
-#endif
+//#include <fcntl.h>
 
 // The relative breakdown of the full install process.  Each phase is
 // worth this fraction of the total movement of the progress bar.
@@ -75,10 +60,8 @@ P3DPackage(const string &package_name, const string &package_version,
   _partial_download = false;
 
   // Ensure the package directory exists; create it if it does not.
-  inst_mgr->mkdir_public(_package_dir);
-
   _package_dir += string("/") + _package_version;
-  inst_mgr->mkdir_public(_package_dir);
+  mkdir_complete(_package_dir);
 
   _desc_file_basename = _package_fullname + ".xml";
   _desc_file_pathname = _package_dir + "/" + _desc_file_basename;
@@ -348,9 +331,9 @@ download_compressed_archive(bool allow_partial) {
     url += "/" + _package_platform;
   }
 
-  url += "/" + _compressed_archive._filename;
+  url += "/" + _compressed_archive.get_filename();
 
-  string target_pathname = _package_dir + "/" + _compressed_archive._filename;
+  string target_pathname = _package_dir + "/" + _compressed_archive.get_filename();
 
   start_download(DT_compressed_archive, url, target_pathname, allow_partial);
 }
@@ -389,7 +372,7 @@ compressed_archive_download_finished(bool success) {
     download_compressed_archive(false);
   }
 
-  nout << _compressed_archive._filename
+  nout << _compressed_archive.get_filename()
        << " failed hash check after download\n";
   report_done(false);
 }
@@ -401,10 +384,10 @@ compressed_archive_download_finished(bool success) {
 ////////////////////////////////////////////////////////////////////
 void P3DPackage::
 uncompress_archive() {
-  nout << "uncompressing " << _compressed_archive._filename << "\n";
+  nout << "uncompressing " << _compressed_archive.get_filename() << "\n";
 
-  string source_pathname = _package_dir + "/" + _compressed_archive._filename;
-  string target_pathname = _package_dir + "/" + _uncompressed_archive._filename;
+  string source_pathname = _package_dir + "/" + _compressed_archive.get_filename();
+  string target_pathname = _package_dir + "/" + _uncompressed_archive.get_filename();
 
   ifstream source(source_pathname.c_str(), ios::in | ios::binary);
   if (!source) {
@@ -413,8 +396,7 @@ uncompress_archive() {
     return;
   }
 
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  if (!inst_mgr->mkfile_public(target_pathname)) {
+  if (!mkfile_complete(target_pathname)) {
     nout << "Unable to create " << target_pathname << "\n";
     report_done(false);
     return;
@@ -481,8 +463,8 @@ uncompress_archive() {
         return;
       }
       total_out += (write_buffer_size - z.avail_out);
-      if (_uncompressed_archive._size != 0) {
-        double progress = (double)total_out / (double)_uncompressed_archive._size;
+      if (_uncompressed_archive.get_size() != 0) {
+        double progress = (double)total_out / (double)_uncompressed_archive.get_size();
         progress = min(progress, 1.0);
         report_progress(download_portion + uncompress_portion * progress);
       }
@@ -536,13 +518,13 @@ uncompress_archive() {
 ////////////////////////////////////////////////////////////////////
 void P3DPackage::
 extract_archive() {
-  nout << "extracting " << _uncompressed_archive._filename << "\n";
+  nout << "extracting " << _uncompressed_archive.get_filename() << "\n";
 
-  string source_pathname = _package_dir + "/" + _uncompressed_archive._filename;
+  string source_pathname = _package_dir + "/" + _uncompressed_archive.get_filename();
   P3DMultifileReader reader;
   if (!reader.extract(source_pathname, _package_dir,
                       this, download_portion + uncompress_portion, extract_portion)) {
-    nout << "Failure extracting " << _uncompressed_archive._filename
+    nout << "Failure extracting " << _uncompressed_archive.get_filename()
          << "\n";
     report_done(false);
     return;
@@ -627,64 +609,6 @@ start_download(P3DPackage::DownloadType dtype, const string &url,
   _instances[0]->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::Callback::Destructor
 //       Access: Public, Virtual
@@ -765,188 +689,3 @@ download_finished(bool 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) {
-    nout << "file not found: " << _filename << "\n";
-    return false;
-  }
-
-  if (st.st_size != _size) {
-    // If the size is wrong, the file fails.
-    nout << "size wrong: " << _filename << "\n";
-    return false;
-  }
-
-  if (st.st_mtime == _timestamp) {
-    // If the size is right and the timestamp is right, the file passes.
-    nout << "file ok: " << _filename << "\n";
-    return true;
-  }
-
-  nout << "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.
-    nout << "hash check wrong: " << _filename << "\n";
-    return false;
-  }
-
-  nout << "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) {
-    nout << "file not found: " << _filename << "\n";
-    return false;
-  }
-
-  if (st.st_size != _size) {
-    // If the size is wrong, the file fails.
-    nout << "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.
-    nout << "hash check wrong: " << _filename << "\n";
-    return false;
-  }
-
-  nout << "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) {
-    nout << "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);
-}

+ 2 - 26
direct/src/plugin/p3dPackage.h

@@ -17,6 +17,8 @@
 
 #include "p3d_plugin_common.h"
 #include "p3dFileDownload.h"
+#include "fileSpec.h"
+#include <tinyxml.h>
 
 class P3DInstance;
 
@@ -95,13 +97,6 @@ private:
   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;
@@ -124,25 +119,6 @@ private:
   typedef vector<P3DInstance *> Instances;
   Instances _instances;
 
-  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;
 

+ 0 - 1
direct/src/plugin/p3dWinSplashWindow.cxx

@@ -568,7 +568,6 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
     break;
 
   case WM_PAINT:
-    nout << "WM_PAINT, _bitmap = " << _bitmap << "\n";
     {
       PAINTSTRUCT ps;
       HDC dc = BeginPaint(hwnd, &ps);

+ 25 - 0
direct/src/plugin/p3d_plugin_config.h

@@ -0,0 +1,25 @@
+/* p3d_plugin_config.h.  Generated automatically by ppremake 1.22 from p3d_plugin_config.h.pp. */
+/********************************** DO NOT EDIT ****************************/
+
+/* The URL that is the root of the download server that this plugin
+   should contact.  The nppanda3d.dll file should be found at this
+   location; as well as the contents.xml file that defines where the
+   various Panda3D packages will be found. */
+#define P3D_PLUGIN_DOWNLOAD "http://www.ddrose.com/~drose/p3d/"
+
+/* The filename(s) to generate output to when the plugin is running.
+   For debugging purposes only. */
+#define P3D_PLUGIN_LOGFILE1 "C:\\cygwin\\home\\drose\\t1.log"
+#define P3D_PLUGIN_LOGFILE2 "C:\\cygwin\\home\\drose\\t2.log"
+
+/* Temporary: the location at which p3dpython.exe can be found.  Empty
+   string for the default. */
+#define P3D_PLUGIN_P3DPYTHON "C:\\cygwin\\home\\drose\\player\\direct\\built\\bin\\p3dpython.exe"
+
+/* Temporary: the location at which p3d_plugin.dll can be found.  Empty
+   string for the default. */
+#define P3D_PLUGIN_P3D_PLUGIN "C:\\cygwin\\home\\drose\\player\\direct\\built\\lib\\p3d_plugin.dll"
+
+/* The string that corresponds to this particular platform. */
+#define P3D_PLUGIN_PLATFORM "win32"
+

+ 1 - 1
direct/src/plugin_npapi/Sources.pp

@@ -30,7 +30,7 @@
   #if $[WINDOWS_PLATFORM]
     #define WIN_RESOURCE_FILE nppanda3d.rc
     #define LINKER_DEF_FILE nppanda3d.def
-    #define WIN_SYS_LIBS user32.lib
+    #define WIN_SYS_LIBS user32.lib shell32.lib
   #endif
 
   // Mac-specific options.

+ 2 - 1
direct/src/plugin_npapi/ppDownloadRequest.h

@@ -26,8 +26,9 @@
 class PPDownloadRequest {
 public:
   enum RequestType {
-    RT_instance_data,
+    RT_contents_file,
     RT_core_dll,
+    RT_instance_data,
     RT_user
   };
 

+ 171 - 23
direct/src/plugin_npapi/ppInstance.cxx

@@ -15,8 +15,11 @@
 #include "ppInstance.h"
 #include "startup.h"
 #include "p3d_plugin_config.h"
+#include "find_root_dir.h"
+#include "mkdir_complete.h"
 
-#include <string.h>
+#include <fstream>
+#include <string.h>  // strcmp()
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::Constructor
@@ -51,13 +54,10 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16 mode,
   _got_window = false;
 
   if (!is_plugin_loaded()) {
-    // Start the plugin DLL downloading.
+    // Go download the contents file, so we can download the core DLL.
     string url = P3D_PLUGIN_DOWNLOAD;
-    url += P3D_PLUGIN_PLATFORM;
-    url += "/";
-    url += get_plugin_basename();
-    
-    PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_core_dll);
+    url += "contents.xml";
+    PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_contents_file);
     browser->geturlnotify(_npp_instance, url.c_str(), NULL, req);
   }
 }
@@ -137,9 +137,15 @@ new_stream(NPMIMEType type, NPStream *stream, bool seekable, uint16 *stype) {
 
   PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
   switch (req->_rtype) {
+  case PPDownloadRequest::RT_contents_file:
+    // This is the initial contents.xml file.  We'll just download
+    // this directoy to a file, since it is small and this is easy.
+    *stype = NP_ASFILEONLY;
+    return NPERR_NO_ERROR;
+
   case PPDownloadRequest::RT_core_dll:
     // This is the core API DLL (or dylib or whatever).  We want to
-    // download this to file so we can run it directly.
+    // download this to file for convenience.
     *stype = NP_ASFILEONLY;
     return NPERR_NO_ERROR;
 
@@ -296,24 +302,22 @@ stream_as_file(NPStream *stream, const char *fname) {
 
   PPDownloadRequest *req = (PPDownloadRequest *)(stream->notifyData);
   switch (req->_rtype) {
-  case PPDownloadRequest::RT_core_dll:
-    {
-      // This is the core API DLL (or dylib or whatever).  Now that
-      // we've downloaded it, we can load it.
-      string override_filename = P3D_PLUGIN_P3D_PLUGIN;
-      if (!override_filename.empty()) {
-        filename = override_filename;
-      }
-      logfile << "got plugin " << filename << "\n" << flush;
-      if (!load_plugin(filename)) {
-        logfile << "Unable to launch core API.\n";
-        break;
-      }
-      logfile << "loaded core API\n";
-      create_instance();
+  case PPDownloadRequest::RT_contents_file:
+    // Now we have the contents.xml file.  Read this to get the
+    // filename and md5 hash of our core API DLL.
+    logfile << "got contents file " << filename << "\n" << flush;
+    if (!read_contents_file(filename)) {
+      logfile << "Unable to read contents file\n";
+      // TODO: fail
     }
     break;
 
+  case PPDownloadRequest::RT_core_dll:
+    // This is the core API DLL (or dylib or whatever).  Now that
+    // we've downloaded it, we can load it.
+    downloaded_plugin(filename);
+    break;
+
   case PPDownloadRequest::RT_instance_data:
     // This is the instance data, e.g. the p3d filename.  Now we can
     // launch the instance.
@@ -378,6 +382,150 @@ handle_request(P3D_request *request) {
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     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 &filename) {
+  TiXmlDocument doc(filename.c_str());
+  if (!doc.LoadFile()) {
+    return false;
+  }
+
+  TiXmlElement *xpackage = doc.FirstChildElement("package");
+  while (xpackage != NULL) {
+    const char *name = xpackage->Attribute("name");
+    if (name != NULL && strcmp(name, "coreapi") == 0) {
+      get_core_api(xpackage);
+      return true;
+    }
+    
+    xpackage = xpackage->NextSiblingElement("package");
+  }
+
+  // Couldn't find the core package description.
+  logfile << "No core package defined in contents file.\n" << flush;
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::get_core_api
+//       Access: Private
+//  Description: Checks the core API DLL file against the
+//               specification in the contents file, and downloads it
+//               if necessary.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+get_core_api(TiXmlElement *xpackage) {
+  _core_api_dll.load_xml(xpackage);
+
+  _root_dir = find_root_dir();
+
+  if (_core_api_dll.quick_verify(_root_dir)) {
+    // The DLL file is good.  Just load it.
+    do_load_plugin();
+
+  } else {
+    // The DLL file needs to be downloaded.  Go get it.
+    string url = P3D_PLUGIN_DOWNLOAD;
+    url += _core_api_dll.get_filename();
+    
+    PPDownloadRequest *req = new PPDownloadRequest(PPDownloadRequest::RT_core_dll);
+    browser->geturlnotify(_npp_instance, url.c_str(), NULL, req);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::downloaded_plugin
+//       Access: Private
+//  Description: The core API DLL has been successfully downloaded;
+//               copy it into place.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+downloaded_plugin(const string &filename) {
+  // We could have been downloading this file as a stream, but that
+  // would cause problems with multiple instances downloading the
+  // plugin at the same time.  Instead, we let them all download the
+  // file asfile, and then only one of them is allowed to copy it into
+  // place.
+
+  if (is_plugin_loaded()) {
+    // Some other instance got there first.  Just get started.
+    create_instance();
+    return;
+  }
+
+  // Copy the file onto the target.
+  string pathname = _core_api_dll.get_pathname(_root_dir);
+  mkfile_complete(pathname);
+
+  ifstream in(filename.c_str(), ios::in | ios::binary);
+  ofstream out(pathname.c_str(), ios::out | ios::binary);
+
+  static const size_t buffer_size = 4096;
+  static char buffer[buffer_size];
+
+  in.read(buffer, buffer_size);
+  size_t count = in.gcount();
+  while (count != 0) {
+    out.write(buffer, count);
+    in.read(buffer, buffer_size);
+    count = in.gcount();
+  }
+
+  if (!out) {
+    logfile << "Could not write " << pathname << "\n";
+    // TODO: fail
+    return;
+  }
+  in.close();
+  out.close();
+
+  if (_core_api_dll.quick_verify(_root_dir)) {
+    // We downloaded and installed it successfully.  Now load it.
+    logfile << "Successfully downloaded " << pathname << "\n";
+    do_load_plugin();
+    return;
+  }
+
+  logfile << "After download, " << pathname << " is no good.\n";
+  // TODO: fail
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::do_load_plugin
+//       Access: Private
+//  Description: Once the core API DLL has been downloaded, loads it
+//               into memory and starts the instance.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+do_load_plugin() {
+  string pathname = _core_api_dll.get_pathname(_root_dir);
+
+#ifdef P3D_PLUGIN_P3D_PLUGIN
+  // This is a convenience macro for development.  If defined and
+  // nonempty, it indicates the name of the plugin DLL that we will
+  // actually run, even after downloading a possibly different
+  // (presumably older) version.  Its purpose is to simplify iteration
+  // on the plugin DLL.
+  string override_filename = P3D_PLUGIN_P3D_PLUGIN;
+  if (!override_filename.empty()) {
+    pathname = override_filename;
+  }
+#endif  // P3D_PLUGIN_P3D_PLUGIN
+
+  if (!load_plugin(pathname)) {
+    logfile << "Unable to launch core API in " << pathname << "\n" << flush;
+    return;
+  }
+  logfile << "loaded core API from " << pathname << "\n" << flush;
+  create_instance();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::create_instance
 //       Access: Private

+ 10 - 0
direct/src/plugin_npapi/ppInstance.h

@@ -16,8 +16,10 @@
 #define PPINSTANCE_H
 
 #include "nppanda3d_common.h"
+#include "fileSpec.h"
 
 #include <vector>
+#include <tinyxml.h>
 
 ////////////////////////////////////////////////////////////////////
 //       Class : PPInstance
@@ -43,6 +45,11 @@ public:
   void handle_request(P3D_request *request);
 
 private:
+  bool read_contents_file(const string &filename);
+  void get_core_api(TiXmlElement *xpackage);
+  void downloaded_plugin(const string &filename);
+  void do_load_plugin();
+
   void create_instance();
   void send_window();
 
@@ -52,6 +59,9 @@ private:
   typedef vector<P3D_token> Tokens;
   Tokens _tokens;
 
+  string _root_dir;
+  FileSpec _core_api_dll;
+
   bool _started_instance_data;
   bool _got_instance_data;
   string _p3d_filename;

+ 0 - 13
direct/src/plugin_npapi/startup.cxx

@@ -145,19 +145,6 @@ NP_Initialize(NPNetscapeFuncs *browserFuncs,
 
   logfile << "browserFuncs = " << browserFuncs << "\n" << flush;
 
-  /*
-#ifdef _WIN32
-  string plugin_location = "c:/cygwin/home/drose/player/direct/built/lib/p3d_plugin.dll";
-#else
-  string plugin_location = "/Users/drose/player/direct/built/lib/p3d_plugin.dylib";
-#endif
-
-  if (!load_plugin(plugin_location)) {
-    logfile << "couldn't load plugin\n" << flush;
-    return NPERR_INVALID_PLUGIN_ERROR;
-  }
-  */
-
   return NPERR_NO_ERROR;
 }
 

+ 1 - 0
direct/src/plugin_standalone/panda3d.cxx

@@ -263,6 +263,7 @@ create_instance(const string &arg, P3D_window_type window_type,
   Filename p3d_filename = Filename::from_os_specific(arg);
   string os_p3d_filename;
   if (p3d_filename.exists()) {
+    p3d_filename.make_absolute();
     os_p3d_filename = p3d_filename.to_os_specific();
   }