Browse Source

uninstall good

David Rose 16 years ago
parent
commit
fd6e2fc82a

+ 2 - 1
direct/src/plugin/p3dHost.I

@@ -70,7 +70,8 @@ get_download_url_prefix() const {
 //     Function: P3DHost::get_descriptive_name
 //       Access: Public
 //  Description: Returns the descriptive name provided for this host,
-//               if any.  This will be available after
+//               if any.  Returns the url if no descriptive name is
+//               provided.  This will be available after
 //               read_contents_file() has been called.
 ////////////////////////////////////////////////////////////////////
 inline const string &P3DHost::

+ 33 - 0
direct/src/plugin/p3dHost.cxx

@@ -37,6 +37,8 @@ P3DHost(const string &host_url) :
   }
   _download_url_prefix = _host_url_prefix;
 
+  _descriptive_name = _host_url;
+
   _xcontents = NULL;
   _contents_seq = 0;
 }
@@ -449,6 +451,37 @@ add_mirror(string mirror_url) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DHost::uninstall
+//       Access: Public
+//  Description: Removes the host directory and all its contents
+//               from the user's hard disk.
+////////////////////////////////////////////////////////////////////
+void P3DHost::
+uninstall() {
+  if (_host_dir.empty()) {
+    nout << "Cannot uninstall " << _descriptive_name << ": host directory not yet known.\n";
+    return;
+  }
+
+  // First, explicitly uninstall each of our packages.
+  Packages::iterator mi;
+  for (mi = _packages.begin(); mi != _packages.end(); ++mi) {
+    PackageMap &package_map = (*mi).second;
+    PackageMap::iterator pi;
+    for (pi = package_map.begin(); pi != package_map.end(); ++pi) {
+      P3DPackage *package = (*pi).second;
+      package->uninstall();
+    }
+  }
+
+  // Then, uninstall the host itself.
+  nout << "Uninstalling " << _descriptive_name << " from " << _host_dir << "\n";
+
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  inst_mgr->delete_directory_recursively(_host_dir);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DHost::determine_host_dir
 //       Access: Private

+ 2 - 0
direct/src/plugin/p3dHost.h

@@ -64,6 +64,8 @@ public:
   void choose_random_mirrors(vector<string> &result, int num_mirrors);
   void add_mirror(string mirror_url);
 
+  void uninstall();
+
 private:
   void determine_host_dir(const string &host_dir_basename);
 

+ 58 - 13
direct/src/plugin/p3dInstance.cxx

@@ -421,6 +421,10 @@ set_p3d_filename(const string &p3d_filename, const int &p3d_offset) {
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 set_wparams(const P3DWindowParams &wparams) {
+  if (is_failed()) {
+    return;
+  }
+
   _got_wparams = true;
   _wparams = wparams;
 
@@ -1080,6 +1084,12 @@ start_download(P3DDownload *download, bool add_request) {
   assert(download->get_download_id() == 0);
   assert(!download->get_url().empty());
 
+  if (is_failed()) {
+    // Can't download anything more after failure.
+    download->cancel();
+    return 0;
+  }
+
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
   int download_id = inst_mgr->get_unique_id();
@@ -1239,6 +1249,11 @@ splash_button_clicked_sub_thread() {
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 splash_button_clicked_main_thread() {
+  if (is_failed()) {
+    // Can't click the button after we've failed.
+    return;
+  }
+
   if (!_p3d_trusted) {
     auth_button_clicked();
   } else if (_session == NULL) {
@@ -1329,7 +1344,7 @@ auth_finished_main_thread() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DInstance::uninstall
+//     Function: P3DInstance::uninstall_packages
 //       Access: Public
 //  Description: Stops the instance (if it is running) and deletes any
 //               packages referenced by the instance.  This is
@@ -1337,23 +1352,39 @@ auth_finished_main_thread() {
 //               P3DMainObject::call_uninstall().
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
-uninstall() {
-  if (_session != NULL) {
-    _session->set_failed();
-    _session->shutdown();
+uninstall_packages() {
+  Packages::const_iterator pi;
+  for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
+    P3DPackage *package = (*pi);
+    package->uninstall();
   }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::uninstall_host
+//       Access: Public
+//  Description: Stops the instance (if it is running) and deletes all
+//               packages downloaded from any of the host(s)
+//               referenced by the instance.  This is a more
+//               aggressive uninstall than uninstall_packages().  This
+//               is normally called by JavaScript, via
+//               P3DMainObject::call_uninstall().
+////////////////////////////////////////////////////////////////////
+void P3DInstance::
+uninstall_host() {
+  set<P3DHost *> hosts;
 
   Packages::const_iterator pi;
   for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
     P3DPackage *package = (*pi);
-    package->uninstall();
-    package->remove_instance(this);
+    hosts.insert(package->get_host());
   }
-  _packages.clear();
 
-  _auto_install = false;
-  _instance_window_opened = false;
-  set_failed();
+  set<P3DHost *>::iterator hi;
+  for (hi = hosts.begin(); hi != hosts.end(); ++hi) {
+    P3DHost *host = (*hi);
+    host->uninstall();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2283,9 +2314,9 @@ handle_script_request(const string &operation, P3D_object *object,
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 set_failed() {
-  _failed = true;
   set_button_image(IT_none);
   set_background_image(IT_failed);
+  _failed = true;
   make_splash_window();
 }
 
@@ -2312,7 +2343,7 @@ make_splash_window() {
     make_visible = false;
   }
 
-  if (_failed) {
+  if (is_failed()) {
     // But, if we've failed to launch somehow, we need to let the user
     // know.
     make_visible = true;
@@ -2423,6 +2454,11 @@ make_splash_window() {
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 set_background_image(ImageType image_type) {
+  if (is_failed()) {
+    // Can't change the background again after we've failed.
+    return;
+  }
+
   if (image_type != _current_background_image) {
     nout << "setting background to " << _image_type_names[image_type]
          << ", splash_window = " << _splash_window << "\n";
@@ -2455,6 +2491,11 @@ set_background_image(ImageType image_type) {
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 set_button_image(ImageType image_type) {
+  if (is_failed()) {
+    // Can't set the button again after we've failed.
+    return;
+  }
+
   if (image_type != _current_button_image) {
     nout << "setting button to " << _image_type_names[image_type] << "\n";
     // Remove the previous image.
@@ -2659,6 +2700,10 @@ start_next_download() {
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 mark_download_complete() {
+  if (_failed) {
+    return;
+  }
+
   if (!_download_complete) {
     _download_complete = true;
     _panda_script_object->set_bool_property("downloadComplete", true);

+ 2 - 1
direct/src/plugin/p3dInstance.h

@@ -116,7 +116,8 @@ public:
   void auth_finished_sub_thread();
   void auth_finished_main_thread();
 
-  void uninstall();
+  void uninstall_packages();
+  void uninstall_host();
 
 private:
   class ImageDownload : public P3DFileDownload {

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

@@ -24,6 +24,21 @@ is_initialized() const {
   return _is_initialized;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::reconsider_runtime_environment
+//       Access: Public
+//  Description: Recreates the runtime environment if a previous call
+//               to uninstall_all() removed it.  Does nothing if the
+//               runtime environment is already correctly set up.
+////////////////////////////////////////////////////////////////////
+inline void P3DInstanceManager::
+reconsider_runtime_environment() {
+  assert(_is_initialized);
+  if (!_created_runtime_environment) {
+    create_runtime_environment();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::verify_contents
 //       Access: Public

+ 259 - 127
direct/src/plugin/p3dInstanceManager.cxx

@@ -58,6 +58,7 @@ P3DInstanceManager() {
   init_xml();
 
   _is_initialized = false;
+  _created_runtime_environment = false;
   _api_version = 0;
   _next_temp_filename_counter = 1;
   _unique_id = 0;
@@ -187,47 +188,28 @@ initialize(int api_version, const string &contents_filename,
            const string &log_basename, bool trusted_environment,
            bool console_environment) {
   _api_version = api_version;
-  _trusted_environment = trusted_environment;
-  _console_environment = console_environment;
+  _host_url = host_url;
   _verify_contents = verify_contents;
   _platform = platform;
-  if (_platform.empty()) {
-    _platform = DTOOL_PLATFORM;
-  }
+  _log_directory = log_directory;
+  _log_basename = log_basename;
+  _trusted_environment = trusted_environment;
+  _console_environment = console_environment;
 
-  _host_url = host_url;
   if (_host_url.empty()) {
     _host_url = PANDA_PACKAGE_HOST_URL;
   }
 
-  _root_dir = find_root_dir();
-
-  // Allow the caller (e.g. panda3d.exe) to specify a log directory.
-  _log_directory = log_directory;
+  if (_platform.empty()) {
+    _platform = DTOOL_PLATFORM;
+  }
 
-  // Or, allow the developer to compile one in.
 #ifdef P3D_PLUGIN_LOG_DIRECTORY
   if (_log_directory.empty()) {
     _log_directory = P3D_PLUGIN_LOG_DIRECTORY;
   }
 #endif
-  
-  // Failing that, we write logfiles to Panda3D/log.
-  if (_log_directory.empty()) {
-    _log_directory = _root_dir + "/log";
-  }
-  mkdir_complete(_log_directory, cerr);
 
-  // Ensure that the log directory ends with a slash.
-  if (!_log_directory.empty() && _log_directory[_log_directory.size() - 1] != '/') {
-#ifdef _WIN32
-    if (_log_directory[_log_directory.size() - 1] != '\\')
-#endif
-      _log_directory += "/";
-  }
-
-  // Construct the logfile pathname.
-  _log_basename = log_basename;
 #ifdef P3D_PLUGIN_LOG_BASENAME2
   if (_log_basename.empty()) {
     _log_basename = P3D_PLUGIN_LOG_BASENAME2;
@@ -236,117 +218,36 @@ initialize(int api_version, const string &contents_filename,
   if (_log_basename.empty()) {
     _log_basename = "p3dcore";
   }
-  _log_pathname = _log_directory;
-  _log_pathname += _log_basename;
-  _log_pathname += ".log";
-
-  logfile.close();
-  logfile.clear();
-  logfile.open(_log_pathname.c_str(), ios::out | ios::trunc);
-  if (logfile) {
-    logfile.setf(ios::unitbuf);
-    nout_stream = &logfile;
-  }
-
-  // Determine the temporary directory.
-#ifdef _WIN32
-  char buffer_1[MAX_PATH];
-
-  // Figuring out the correct path for temporary files is a real mess
-  // on Windows.  We should be able to use GetTempPath(), but that
-  // relies on $TMP or $TEMP being defined, and it appears that
-  // Mozilla clears these environment variables for the plugin, which
-  // forces GetTempPath() into $USERPROFILE instead.  This is really
-  // an inappropriate place for temporary files, so, GetTempPath()
-  // isn't a great choice.
 
-  // We could use SHGetSpecialFolderPath() instead to get us the path
-  // to "Temporary Internet Files", which is acceptable.  The trouble
-  // is, if we happen to be running in "Protected Mode" on Vista, this
-  // folder isn't actually writable by us!  On Vista, we're supposed
-  // to use IEGetWriteableFolderPath() instead, but *this* function
-  // doesn't exist on XP and below.  Good Lord.
-
-  // We could go through a bunch of LoadLibrary() calls to try to find
-  // the right path, like we do in find_root_dir(), but I'm just tired
-  // of doing all that nonsense.  We'll use a two-stage trick instead.
-  // We'll check for $TEMP or $TMP being defined specifically, and if
-  // they are, we'll use GetTempPath(); otherwise, we'll fall back to
-  // SHGetSpecialFolderPath().
-
-  if (getenv("TEMP") != NULL || getenv("TMP") != NULL) {
-    if (GetTempPath(MAX_PATH, buffer_1) != 0) {
-      _temp_directory = buffer_1;
-    }
-  }
-  if (_temp_directory.empty()) {
-    if (SHGetSpecialFolderPath(NULL, buffer_1, CSIDL_INTERNET_CACHE, true)) {
-      _temp_directory = buffer_1;
-
-      // That just *might* return a non-writable folder, if we're in
-      // Protected Mode.  We'll test this with GetTempFileName().
-      char temp_buffer[MAX_PATH];
-      if (!GetTempFileName(_temp_directory.c_str(), "p3d", 0, temp_buffer)) {
-        nout << "GetTempFileName failed on " << _temp_directory
-             << ", switching to GetTempPath\n";
-        _temp_directory.clear();
-      } else {
-        DeleteFile(temp_buffer);
-      }
-    }
-  }
+  _root_dir = find_root_dir();
 
-  // If both of the above failed, we'll fall back to GetTempPath()
-  // once again as a last resort, which is supposed to return
-  // *something* that works, even if $TEMP and $TMP are undefined.
-  if (_temp_directory.empty()) {
-    if (GetTempPath(MAX_PATH, buffer_1) != 0) {
-      _temp_directory = buffer_1;
-    }
+  if (_root_dir.empty()) {
+    cerr << "Could not find root directory.\n";
+    return false;
   }
 
-  // Also insist that the temp directory is fully specified.
-  size_t needs_size_2 = GetFullPathName(_temp_directory.c_str(), 0, NULL, NULL);
-  char *buffer_2 = new char[needs_size_2];
-  if (GetFullPathName(_temp_directory.c_str(), needs_size_2, buffer_2, NULL) != 0) {
-    _temp_directory = buffer_2;
+  // Allow the caller (e.g. panda3d.exe) to specify a log directory.
+  // Or, allow the developer to compile one in.
+  
+  // Failing that, we write logfiles to Panda3D/log.
+  if (_log_directory.empty()) {
+    _log_directory = _root_dir + "/log";
   }
-  delete[] buffer_2;
 
-  // And make sure the directory actually exists.
-  mkdir_complete(_temp_directory, nout);
-
-#else
-  _temp_directory = "/tmp/";
-#endif  // _WIN32
-
-  // Ensure that the temp directory ends with a slash.
-  if (!_temp_directory.empty() && _temp_directory[_temp_directory.size() - 1] != '/') {
+  // Ensure that the log directory ends with a slash.
+  if (!_log_directory.empty() && _log_directory[_log_directory.size() - 1] != '/') {
 #ifdef _WIN32
-    if (_temp_directory[_temp_directory.size() - 1] != '\\')
+    if (_log_directory[_log_directory.size() - 1] != '\\')
 #endif
-      _temp_directory += "/";
-  }
-
-  nout << "_root_dir = " << _root_dir
-       << ", _temp_directory = " << _temp_directory
-       << ", platform = " << _platform
-       << ", contents_filename = " << contents_filename
-       << ", host_url = " << host_url
-       << ", verify_contents = " << verify_contents
-       << "\n";
-
-  if (_root_dir.empty()) {
-    nout << "Could not find root directory.\n";
-    return false;
+      _log_directory += "/";
   }
 
-  // Make the certificate directory.
-  _certs_dir = _root_dir + "/certs";
-  if (!mkdir_complete(_certs_dir, nout)) {
-    nout << "Couldn't mkdir " << _certs_dir << "\n";
-  }
+  // Construct the logfile pathname.
+  _log_pathname = _log_directory;
+  _log_pathname += _log_basename;
+  _log_pathname += ".log";
 
+  create_runtime_environment();
   _is_initialized = true;
 
   if (!_verify_contents &&
@@ -373,6 +274,7 @@ set_plugin_version(int major, int minor, int sequence,
                    bool official, const string &distributor,
                    const string &coreapi_host_url,
                    time_t coreapi_timestamp) {
+  reconsider_runtime_environment();
   _plugin_major_version = major;
   _plugin_minor_version = minor;
   _plugin_sequence_version = sequence;
@@ -389,6 +291,7 @@ set_plugin_version(int major, int minor, int sequence,
 ////////////////////////////////////////////////////////////////////
 void P3DInstanceManager::
 set_super_mirror(const string &super_mirror_url) {
+  reconsider_runtime_environment();
   if (!super_mirror_url.empty()) {
     nout << "super_mirror = " << super_mirror_url << "\n";
   }
@@ -410,6 +313,7 @@ P3DInstance *P3DInstanceManager::
 create_instance(P3D_request_ready_func *func, 
                 const P3D_token tokens[], size_t num_tokens, 
                 int argc, const char *argv[], void *user_data) {
+  reconsider_runtime_environment();
   P3DInstance *inst = new P3DInstance(func, tokens, num_tokens, argc, argv,
                                       user_data);
   inst->ref();
@@ -961,6 +865,43 @@ cert_to_der(X509 *cert) {
   return result;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::uninstall_all
+//       Access: Public
+//  Description: Stops all active instances and removes *all*
+//               downloaded files from all hosts, and empties the
+//               current user's Panda3D directory as much as possible.
+//
+//               This cannot remove the coreapi dll or directory on
+//               Windows.
+////////////////////////////////////////////////////////////////////
+void P3DInstanceManager::
+uninstall_all() {
+  Instances::iterator ii;
+  for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
+    P3DInstance *inst = (*ii);
+    inst->uninstall_host();
+  }
+
+  Hosts::iterator hi;
+  for (hi = _hosts.begin(); hi != _hosts.end(); ++hi) {
+    P3DHost *host = (*hi).second;
+    host->uninstall();
+  }
+
+  // Close the logfile so we can remove that too.
+  logfile.close();
+
+  if (!_root_dir.empty()) {
+    // This won't be able to delete the coreapi directory on Windows,
+    // because we're running that DLL right now.  But it will delete
+    // everything else.
+    delete_directory_recursively(_root_dir);
+  }
+
+  _created_runtime_environment = false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::get_global_ptr
 //       Access: Public, Static
@@ -1112,6 +1053,82 @@ scan_directory_recursively(const string &dirname,
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::delete_directory_recursively
+//       Access: Public, Static
+//  Description: Deletes all of the files and directories in the named
+//               directory and below, like rm -rf.  Use with extreme
+//               caution.
+////////////////////////////////////////////////////////////////////
+void P3DInstanceManager::
+delete_directory_recursively(const string &root_dir) {
+  vector<string> contents, dirname_contents;
+  if (!scan_directory_recursively(root_dir, contents, dirname_contents)) {
+    // Maybe it's just a single file, not a directory.  Delete it.
+#ifdef _WIN32
+    // Windows can't delete a file if it's read-only.
+    chmod(root_dir.c_str(), 0644);
+#endif
+    int result = unlink(root_dir.c_str());
+    if (result == 0) {
+      nout << "Deleted " << root_dir << "\n";
+    } else {
+      if (access(root_dir.c_str(), F_OK) == 0) {
+        nout << "Could not delete " << root_dir << "\n";
+      }
+    }
+    return;
+  }
+
+  vector<string>::iterator ci;
+  for (ci = contents.begin(); ci != contents.end(); ++ci) {
+    string filename = (*ci);
+    string pathname = root_dir + "/" + filename;
+
+#ifdef _WIN32
+    // Windows can't delete a file if it's read-only.
+    chmod(pathname.c_str(), 0644);
+#endif
+    int result = unlink(pathname.c_str());
+    if (result == 0) {
+      nout << "  Deleted " << filename << "\n";
+    } else {
+      nout << "  Could not delete " << filename << "\n";
+    }
+  }
+
+  // Now delete all of the directories too.  They're already in
+  // reverse order, so we remove deeper directories first.
+  for (ci = dirname_contents.begin(); ci != dirname_contents.end(); ++ci) {
+    string filename = (*ci);
+    string pathname = root_dir + "/" + filename;
+
+#ifdef _WIN32
+    chmod(pathname.c_str(), 0755);
+#endif
+    int result = rmdir(pathname.c_str());
+    if (result == 0) {
+      nout << "  Removed directory " << filename << "\n";
+    } else {
+      nout << "  Could not remove directory " << filename << "\n";
+    }
+  }
+
+  // Finally, delete the root directory itself.
+  string pathname = root_dir;
+#ifdef _WIN32
+  chmod(pathname.c_str(), 0755);
+#endif
+  int result = rmdir(pathname.c_str());
+  if (result == 0) {
+    nout << "Removed directory " << root_dir << "\n";
+  } else {
+    if (access(pathname.c_str(), F_OK) == 0) {
+      nout << "Could not remove directory " << root_dir << "\n";
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::remove_file_from_list
 //       Access: Public, Static
@@ -1185,6 +1202,121 @@ append_safe_dir(string &root, const string &basename) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::create_runtime_environment
+//       Access: Private
+//  Description: Called during initialize, or after a previous call to
+//               uninstall_all(), to make sure all needed
+//               directories exist and the logfile is open.
+////////////////////////////////////////////////////////////////////
+void P3DInstanceManager::
+create_runtime_environment() {
+  mkdir_complete(_log_directory, cerr);
+
+  logfile.close();
+  logfile.clear();
+  logfile.open(_log_pathname.c_str(), ios::out | ios::trunc);
+  if (logfile) {
+    logfile.setf(ios::unitbuf);
+    nout_stream = &logfile;
+  }
+
+  // Determine the temporary directory.
+#ifdef _WIN32
+  char buffer_1[MAX_PATH];
+
+  // Figuring out the correct path for temporary files is a real mess
+  // on Windows.  We should be able to use GetTempPath(), but that
+  // relies on $TMP or $TEMP being defined, and it appears that
+  // Mozilla clears these environment variables for the plugin, which
+  // forces GetTempPath() into $USERPROFILE instead.  This is really
+  // an inappropriate place for temporary files, so, GetTempPath()
+  // isn't a great choice.
+
+  // We could use SHGetSpecialFolderPath() instead to get us the path
+  // to "Temporary Internet Files", which is acceptable.  The trouble
+  // is, if we happen to be running in "Protected Mode" on Vista, this
+  // folder isn't actually writable by us!  On Vista, we're supposed
+  // to use IEGetWriteableFolderPath() instead, but *this* function
+  // doesn't exist on XP and below.  Good Lord.
+
+  // We could go through a bunch of LoadLibrary() calls to try to find
+  // the right path, like we do in find_root_dir(), but I'm just tired
+  // of doing all that nonsense.  We'll use a two-stage trick instead.
+  // We'll check for $TEMP or $TMP being defined specifically, and if
+  // they are, we'll use GetTempPath(); otherwise, we'll fall back to
+  // SHGetSpecialFolderPath().
+
+  if (getenv("TEMP") != NULL || getenv("TMP") != NULL) {
+    if (GetTempPath(MAX_PATH, buffer_1) != 0) {
+      _temp_directory = buffer_1;
+    }
+  }
+  if (_temp_directory.empty()) {
+    if (SHGetSpecialFolderPath(NULL, buffer_1, CSIDL_INTERNET_CACHE, true)) {
+      _temp_directory = buffer_1;
+
+      // That just *might* return a non-writable folder, if we're in
+      // Protected Mode.  We'll test this with GetTempFileName().
+      char temp_buffer[MAX_PATH];
+      if (!GetTempFileName(_temp_directory.c_str(), "p3d", 0, temp_buffer)) {
+        nout << "GetTempFileName failed on " << _temp_directory
+             << ", switching to GetTempPath\n";
+        _temp_directory.clear();
+      } else {
+        DeleteFile(temp_buffer);
+      }
+    }
+  }
+
+  // If both of the above failed, we'll fall back to GetTempPath()
+  // once again as a last resort, which is supposed to return
+  // *something* that works, even if $TEMP and $TMP are undefined.
+  if (_temp_directory.empty()) {
+    if (GetTempPath(MAX_PATH, buffer_1) != 0) {
+      _temp_directory = buffer_1;
+    }
+  }
+
+  // Also insist that the temp directory is fully specified.
+  size_t needs_size_2 = GetFullPathName(_temp_directory.c_str(), 0, NULL, NULL);
+  char *buffer_2 = new char[needs_size_2];
+  if (GetFullPathName(_temp_directory.c_str(), needs_size_2, buffer_2, NULL) != 0) {
+    _temp_directory = buffer_2;
+  }
+  delete[] buffer_2;
+
+  // And make sure the directory actually exists.
+  mkdir_complete(_temp_directory, nout);
+
+#else
+  _temp_directory = "/tmp/";
+#endif  // _WIN32
+
+  // Ensure that the temp directory ends with a slash.
+  if (!_temp_directory.empty() && _temp_directory[_temp_directory.size() - 1] != '/') {
+#ifdef _WIN32
+    if (_temp_directory[_temp_directory.size() - 1] != '\\')
+#endif
+      _temp_directory += "/";
+  }
+
+  nout << "_root_dir = " << _root_dir
+       << ", _temp_directory = " << _temp_directory
+       << ", platform = " << _platform
+       << ", host_url = " << _host_url
+       << ", verify_contents = " << _verify_contents
+       << "\n";
+
+  // Make the certificate directory.
+  _certs_dir = _root_dir + "/certs";
+  if (!mkdir_complete(_certs_dir, nout)) {
+    nout << "Couldn't mkdir " << _certs_dir << "\n";
+  }
+
+  _created_runtime_environment = true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::append_safe_dir_component
 //       Access: Private, Static

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

@@ -60,6 +60,7 @@ public:
                   bool console_environment);
 
   inline bool is_initialized() const;
+  inline void reconsider_runtime_environment();
   inline bool get_verify_contents() const;
   inline void reset_verify_contents();
 
@@ -126,6 +127,8 @@ public:
   string get_cert_dir(X509 *cert);
   static string cert_to_der(X509 *cert);
 
+  void uninstall_all();
+  
   static P3DInstanceManager *get_global_ptr();
   static void delete_global_ptr();
 
@@ -135,11 +138,13 @@ public:
                                          vector<string> &filename_contents,
                                          vector<string> &dirname_contents,
                                          const string &prefix = "");
+  static void delete_directory_recursively(const string &root_dir);
   static bool remove_file_from_list(vector<string> &contents, const string &filename);
 
   static void append_safe_dir(string &root, const string &basename);
 
 private:
+  void create_runtime_environment();
   static void append_safe_dir_component(string &root, const string &component);
 
 private:
@@ -151,6 +156,7 @@ private:
 
 private:
   bool _is_initialized;
+  bool _created_runtime_environment;
   int _api_version;
   string _host_url;
   string _root_dir;

+ 22 - 2
direct/src/plugin/p3dMainObject.cxx

@@ -451,9 +451,29 @@ P3D_object *P3DMainObject::
 call_uninstall(P3D_object *params[], int num_params) {
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
+  // Get the first parameter, the uninstall mode.
+  string mode;
+  if (num_params > 0) {
+    int size = P3D_OBJECT_GET_STRING(params[0], NULL, 0);
+    char *buffer = new char[size];
+    P3D_OBJECT_GET_STRING(params[0], buffer, size);
+    mode = string(buffer, size);
+    delete[] buffer;
+  }
+
+  if (mode == "all") {
+    nout << "uninstall all\n";
+    inst_mgr->uninstall_all();
+    return inst_mgr->new_bool_object(true);
+  }
+
   if (_inst != NULL) {
-    nout << "uninstall for " << _inst << "\n";
-    _inst->uninstall();
+    nout << "uninstall " << mode << " for " << _inst << "\n";
+    if (mode == "host") {
+      _inst->uninstall_host();
+    } else {
+      _inst->uninstall_packages();
+    }
     return inst_mgr->new_bool_object(true);
   }
 

+ 14 - 47
direct/src/plugin/p3dPackage.cxx

@@ -217,56 +217,23 @@ uninstall() {
 
   nout << "Uninstalling package " << _package_name << " from " << _package_dir << "\n";
 
-  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  vector<string> contents, dirname_contents;
-  inst_mgr->scan_directory_recursively(_package_dir, contents,
-                                       dirname_contents);
-
-  vector<string>::iterator ci;
-  for (ci = contents.begin(); ci != contents.end(); ++ci) {
-    string filename = (*ci);
-    string pathname = _package_dir + "/" + filename;
-
-#ifdef _WIN32
-    // Windows can't delete a file if it's read-only.
-    chmod(pathname.c_str(), 0644);
-#endif
-    int result = unlink(pathname.c_str());
-    if (result == 0) {
-      nout << "  Deleted " << filename << "\n";
-    } else {
-      nout << "  Could not delete " << filename << "\n";
-    }
-  }
-
-  // Now delete all of the directories too.  They're already in
-  // reverse order, so we remove deeper directories first.
-  for (ci = dirname_contents.begin(); ci != dirname_contents.end(); ++ci) {
-    string filename = (*ci);
-    string pathname = _package_dir + "/" + filename;
-
-#ifdef _WIN32
-    chmod(pathname.c_str(), 0755);
-#endif
-    int result = rmdir(pathname.c_str());
-    if (result == 0) {
-      nout << "  Removed directory " << filename << "\n";
-    } else {
-      nout << "  Could not remove directory " << filename << "\n";
+  // First, make sure that all instances that are sharing this package
+  // are stopped, so there will be no access conflicts preventing us
+  // from removing the files.  This is particularly important on
+  // Windows.
+  Instances::iterator ii;
+  for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
+    P3DInstance *inst = (*ii);
+    P3DSession *session = inst->get_session();
+    if (session != NULL) {
+      nout << "Stopping session " << session << "\n";
+      session->shutdown();
     }
+    inst->set_failed();
   }
 
-  // Finally, delete the package directory itself.
-  string pathname = _package_dir;
-#ifdef _WIN32
-  chmod(pathname.c_str(), 0755);
-#endif
-  int result = rmdir(pathname.c_str());
-  if (result == 0) {
-    nout << "Removed directory " << _package_dir << "\n";
-  } else {
-    nout << "Could not remove directory " << _package_dir << "\n";
-  }
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  inst_mgr->delete_directory_recursively(_package_dir);
 
   _info_ready = false;
   _ready = false;

+ 2 - 0
direct/src/plugin/p3dSession.cxx

@@ -96,6 +96,8 @@ P3DSession::
 ////////////////////////////////////////////////////////////////////
 void P3DSession::
 shutdown() {
+  set_failed();
+
   if (_p3dpython_started) {
     // Tell the process we're going away.
     TiXmlDocument doc;