]> cygwin.com Git - cygwin-apps/setup.git/blobdiff - download.cc
Also run stratum 'z' perpetual preremove scripts
[cygwin-apps/setup.git] / download.cc
index daabe1d0755c9d70b388c74bece025fac58eef04..5bfcebf0957d26f122faa07324230946174d6c93 100644 (file)
 /* The purpose of this file is to download all the files we need to
    do the installation. */
 
-static char *cvsid = "\n%%% $Id$\n";
+#include "csu_util/rfc1738.h"
 
+#include "download.h"
+  
 #include "win32.h"
 
 #include <stdio.h>
 #include <unistd.h>
+#include <process.h>
+#include <vector>
 
 #include "resource.h"
 #include "msg.h"
-#include "ini.h"
 #include "dialog.h"
-#include "concat.h"
 #include "geturl.h"
 #include "state.h"
-#include "mkdir.h"
-#include "log.h"
-#include "port.h"
+#include "LogFile.h"
+#include "filemanip.h"
 
-#define pi (package[i].info[package[i].trust])
+#include "io_stream.h"
 
-DWORD
-get_file_size (char *name)
-{
-  HANDLE h;
-  WIN32_FIND_DATA buf;
-  DWORD ret = 0;
+#include "package_db.h"
+#include "package_meta.h"
+#include "package_source.h"
+
+#include "threebar.h"
+
+#include "Exception.h"
 
-  h = FindFirstFileA (name, &buf);
-  if (h != INVALID_HANDLE_VALUE)
+extern ThreeBarProgressPage Progress;
+
+// Return true if selected checks pass, false if they don't and the
+// user chooses to delete the file; otherwise throw an exception.
+static bool
+validateCachedPackage (const std::string& fullname, packagesource & pkgsource,
+                      HWND owner, bool check_hash, bool check_size)
+{
+  try
     {
-      if (buf.nFileSizeHigh == 0)
-       ret = buf.nFileSizeLow;
-      FindClose (h);
+      if (check_size)
+       pkgsource.check_size_and_cache (fullname);
+      if (check_hash)
+       pkgsource.check_hash ();
+      return true;
     }
-  return ret;
+  catch (Exception *e)
+    {
+      pkgsource.set_cached ("");
+      const char *filename = fullname.c_str ();
+      if (strncmp (filename, "file://", 7) == 0)
+       filename += 7;
+      if (e->errNo() == APPERR_CORRUPT_PACKAGE
+         && yesno (owner, IDS_QUERY_CORRUPT, filename) == IDYES)
+       remove (filename);
+      else
+       throw e;
+    }
+  return false;
 }
 
-static int
-download_one (char *name, int expected_size, int action)
+/* 0 if not cached; may throw exception if validation fails.
+ */
+int
+check_for_cached (packagesource & pkgsource, HWND owner, bool mirror_mode,
+                 bool check_hash)
 {
-  char *local = name;
-  int errors = 0;
-
-  DWORD size;
-  if ((size = get_file_size (local)) > 0)
-    if (size == expected_size && action != ACTION_SRC_ONLY
-  && action != ACTION_REDO )
+  /* If the packagesource doesn't have a filename, it can't possibly be in the
+     cache */
+  if (!pkgsource.Canonical())
+    {
       return 0;
+    }
 
-  mkdir_p (0, local);
+  /* Note that the cache dir is represented by a mirror site of file://local_dir */
+  std::string prefix = "file://" + local_dir + "/";
+  std::string fullname = prefix + pkgsource.Canonical();
 
-  if (get_url_to_file (concat (MIRROR_SITE, "/", name, 0),
-                      concat (local, ".tmp", 0),
-                      expected_size))
+  if (mirror_mode)
     {
-      note (IDS_DOWNLOAD_FAILED, name);
+      /* Just assume correctness of mirror. */
+      if (!pkgsource.Cached())
+       pkgsource.set_cached (fullname);
       return 1;
     }
-  else
+
+  // Already found one, which we can assume to have the right size.
+  if (pkgsource.Cached())
+    {
+      if (validateCachedPackage (pkgsource.Cached(), pkgsource, owner,
+                                check_hash, false))
+       return 1;
+      // If we get here, pkgsource.Cached() was corrupt and deleted.
+      pkgsource.set_cached ("");
+    }
+
+  /*
+     1) is there a legacy version in the cache dir available.
+  */
+  if (io_stream::exists (fullname))
+    {
+      if (validateCachedPackage (fullname, pkgsource, owner, check_hash, true))
+       return 1;
+      // If we get here, fullname was corrupt and deleted, but it
+      // might have been cached.
+      pkgsource.set_cached ("");
+    }
+
+  /*
+     2) is there a version from one of the selected mirror sites available ?
+  */
+  for (packagesource::sitestype::const_iterator n = pkgsource.sites.begin();
+       n != pkgsource.sites.end(); ++n)
+  {
+    std::string fullname = prefix + rfc1738_escape_part (n->key) + "/" +
+      pkgsource.Canonical ();
+    if (io_stream::exists(fullname))
+       {
+         if (validateCachedPackage (fullname, pkgsource, owner, check_hash,
+                                    true))
+           return 1;
+         // If we get here, fullname was corrupt and deleted, but it
+         // might have been cached.
+         pkgsource.set_cached ("");
+       }
+  }
+  return 0;
+}
+
+/* download a file from a mirror site to the local cache. */
+static int
+download_one (packagesource & pkgsource, HWND owner)
+{
+  try
     {
-      size = get_file_size (concat (local, ".tmp", 0));
-      if (size == expected_size)
+      if (check_for_cached (pkgsource, owner))
+        return 0;
+    }
+  catch (Exception * e)
+    {
+      // We know what to do with these..
+      if (e->errNo() == APPERR_CORRUPT_PACKAGE)
        {
-         log (0, "Downloaded %s", local);
-    if ( _access (local , 0) == 0)
-       remove ( local );
-         rename (concat (local, ".tmp", 0), local);
+         fatal (owner, IDS_CORRUPT_PACKAGE, pkgsource.Canonical());
+         return 1;
+       }
+      // Unexpected exception.
+      throw e;
+    }
+  /* try the download sites one after another */
+
+  int success = 0;
+  for (packagesource::sitestype::const_iterator n = pkgsource.sites.begin();
+       n != pkgsource.sites.end() && !success; ++n)
+    {
+      const std::string local = local_dir + "/" +
+                                 rfc1738_escape_part (n->key) + "/" +
+                                 pkgsource.Canonical ();
+      io_stream::mkpath_p (PATH_TO_FILE, "file://" + local, 0);
+
+      if (get_url_to_file(n->key + pkgsource.Canonical (),
+                         local + ".tmp", pkgsource.size, owner))
+       {
+         /* FIXME: note new source ? */
+         continue;
        }
       else
        {
-         log (0, "Download %s wrong size (%d actual vs %d expected)",
-              local, size, expected_size);
-         note (IDS_DOWNLOAD_SHORT, local, size, expected_size);
-         return 1;
+         try
+           {
+             if (_access (local.c_str(), 0) == 0)
+               remove (local.c_str());
+             rename ((local + ".tmp").c_str(), local.c_str());
+             pkgsource.check_size_and_cache ("file://" + local);
+             pkgsource.check_hash ();
+             Log (LOG_PLAIN) << "Downloaded " << local << endLog;
+             success = 1;
+             // FIXME: move the downloaded file to the 
+             //  original locations - without the mirror site dir in the way
+             continue;
+           }
+         catch (Exception *e)
+           {
+             remove (local.c_str());
+             pkgsource.set_cached ("");
+             if (e->errNo() == APPERR_CORRUPT_PACKAGE)
+               {
+                 Log (LOG_PLAIN) << "Downloaded file " << local
+                                 << " is corrupt; deleting." << endLog;
+                 continue;
+               }
+             else
+               {
+                 Log (LOG_PLAIN) << "Unexpected exception while validating "
+                                 << "downloaded file " << local
+                                 << "; deleting." << endLog;
+                 throw e;
+               }
+           }
        }
     }
+  if (success)
+    return 0;
+  return 1;
+}
 
-  return 0;
+static std::vector <packageversion> download_failures;
+static std::string download_warn_pkgs;
+
+static INT_PTR CALLBACK
+download_error_proc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  switch (message)
+    {
+    case WM_INITDIALOG:
+      eset (h, IDC_DOWNLOAD_EDIT, download_warn_pkgs);
+      SetFocus (GetDlgItem(h, IDRETRY));
+      return FALSE;
+
+    case WM_COMMAND:
+      switch (LOWORD (wParam))
+       {
+       case IDRETRY:
+       case IDC_BACK:
+       case IDIGNORE:
+       case IDABORT:
+         EndDialog (h, LOWORD (wParam));
+       default:
+         // Not reached.
+         return 0;
+       }
+
+    default:
+      // Not handled.
+      return FALSE;
+    }
+  return TRUE;
 }
 
-void
-do_download (HINSTANCE h)
+static int
+query_download_errors (HINSTANCE h, HWND owner)
+{
+  download_warn_pkgs = "";
+  Log (LOG_PLAIN) << "The following package(s) had download errors:" << endLog;
+  for (std::vector <packageversion>::const_iterator i = download_failures.begin (); i != download_failures.end (); i++)
+    {
+      packageversion pv = *i;
+      std::string pvs = pv.Name () + "-" + pv.Canonical_version ();
+      Log (LOG_PLAIN) << "  " << pvs << endLog;
+      download_warn_pkgs += pvs + "\r\n";
+    }
+  return DialogBox (h, MAKEINTRESOURCE (IDD_DOWNLOAD_ERROR), owner,
+                   download_error_proc);
+}
+
+static int
+do_download_thread (HINSTANCE h, HWND owner)
 {
-  int i;
   int errors = 0;
   total_download_bytes = 0;
   total_download_bytes_sofar = 0;
+  download_failures.clear ();
+
+  Progress.SetText1 (IDS_PROGRESS_CHECKING);
+  Progress.SetText2 ("");
+  Progress.SetText3 ("");
+
+  packagedb db;
+  const SolverTransactionList &t = db.solution.transactions();
+
+  /* calculate the total size of the download */
+  for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
+    {
+      if (i->type != SolverTransaction::transInstall)
+        continue;
+      packageversion version = i->version;
 
-  for (i=0; i<npackages; i++)
-    if (package[i].action == ACTION_NEW || package[i].action == ACTION_UPGRADE
-  || package[i].action == ACTION_REDO
-  || package[i].action == ACTION_SRC_ONLY)
-      {
-       DWORD size = get_file_size (pi.install);
-  char *local = pi.install;
-       if (package[i].action != ACTION_SRC_ONLY &&
-     (package[i].action == ACTION_REDO ||
-     size != pi.install_size))
-    total_download_bytes += pi.install_size;
-  local = pi.source;
-  size = get_file_size (pi.source);
-       if (package[i].srcaction == SRCACTION_YES &&
-     (package[i].action == ACTION_SRC_ONLY ||
-     size != pi.source_size))
-         total_download_bytes += pi.source_size;
-      }
-
-  for (i=0; i<npackages; i++)
-    if (package[i].action == ACTION_NEW || package[i].action == ACTION_UPGRADE
-  || package[i].action == ACTION_REDO
-  || package[i].action == ACTION_SRC_ONLY)
-      {
-  int e = 0;
-       if (package[i].action != ACTION_SRC_ONLY)
-    e += download_one (pi.install, pi.install_size, package[i].action);
-       if (package[i].srcaction == SRCACTION_YES && pi.source)
-    e += download_one (pi.source, pi.source_size, package[i].action);
-       errors += e;
-       if (e)
-         package[i].action = ACTION_ERROR;
-      }
-
-  dismiss_url_status_dialog ();
+      try
+        {
+          if (!check_for_cached (*version.source(), owner))
+            total_download_bytes += version.source()->size;
+        }
+      catch (Exception * e)
+        {
+          // We know what to do with these..
+          if (e->errNo() == APPERR_CORRUPT_PACKAGE)
+            fatal (owner, IDS_CORRUPT_PACKAGE, version.Name().c_str());
+          // Unexpected exception.
+          throw e;
+        }
+      Progress.SetBar2(std::distance(t.begin(), i) + 1, t.size());
+    }
+
+  /* and do the download. FIXME: This here we assign a new name for the cached version
+   * and check that above.
+   */
+  for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
+    {
+      if (i->type != SolverTransaction::transInstall)
+        continue;
+      packageversion version = i->version;
+
+       {
+         int e = 0;
+          e += download_one (*version.source(), owner);
+         errors += e;
+         if (e)
+           download_failures.push_back (version);
+#if 0
+         if (e)
+           pkg->action = ACTION_ERROR;
+#endif
+       }
+    }
 
   if (errors)
     {
-      if (yesno (IDS_DOWNLOAD_INCOMPLETE) == IDYES)
+      // In unattended mode we retry the download, but not forever.
+      static int retries = 5;
+      int rc;
+      if (unattended_mode && --retries <= 0)
+        {
+         Log (LOG_PLAIN) << "download error in unattended_mode: out of retries" << endLog;
+         rc = IDABORT;
+       }
+      else if (unattended_mode)
+        {
+         Log (LOG_PLAIN) << "download error in unattended_mode: " << retries
+           << (retries > 1 ? " retries" : " retry") << " remaining." << endLog;
+         rc = IDRETRY;
+       }
+      else
+       rc = query_download_errors (h, owner);
+      switch (rc)
        {
-         next_dialog = IDD_SITE;
-         return;
+       case IDRETRY:
+         Progress.SetActivateTask (WM_APP_START_DOWNLOAD);
+         return IDD_INSTATUS;
+       case IDC_BACK:
+         return IDD_CHOOSE;
+       case IDABORT:
+         Logger ().setExitMsg (IDS_DOWNLOAD_INCOMPLETE_EXIT);
+         Logger ().exit (1);
+       case IDIGNORE:
+         break;
+       default:
+         break;
        }
     }
 
   if (source == IDC_SOURCE_DOWNLOAD)
     {
       if (errors)
-       exit_msg = IDS_DOWNLOAD_INCOMPLETE;
-      else
-       exit_msg = IDS_DOWNLOAD_COMPLETE;
-      next_dialog = 0;
+       Logger ().setExitMsg (IDS_DOWNLOAD_INCOMPLETE_EXIT);
+      else if (!unattended_mode)
+       Logger ().setExitMsg (IDS_DOWNLOAD_COMPLETE);
+      return IDD_DESKTOP;
     }
   else
-    next_dialog = IDD_S_INSTALL;
+    return IDD_S_INSTALL;
+}
+
+static DWORD WINAPI
+do_download_reflector (void *p)
+{
+  HANDLE *context;
+  context = (HANDLE *) p;
+
+  SetThreadUILanguage(langid);
+
+  try
+  {
+    int next_dialog =
+      do_download_thread ((HINSTANCE) context[0], (HWND) context[1]);
+
+    // Tell the progress page that we're done downloading
+    Progress.PostMessageNow (WM_APP_DOWNLOAD_THREAD_COMPLETE, 0, next_dialog);
+  }
+  TOPLEVEL_CATCH((HWND) context[1], "download");
+
+  ExitThread(0);
+}
+
+static HANDLE context[2];
+
+void
+do_download (HINSTANCE h, HWND owner)
+{
+  context[0] = h;
+  context[1] = owner;
+
+  DWORD threadID;
+  CreateThread (NULL, 0, do_download_reflector, context, 0, &threadID);
 }
This page took 0.0314 seconds and 5 git commands to generate.