/*
- * Copyright (c) 2000, Red Hat, Inc.
+ * Copyright (c) 2000, 2001, Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
/* The purpose of this file is to download all the files we need to
do the installation. */
+#if 0
+static const char *cvsid =
+ "\n%%% $Id$\n";
+#endif
+
+#include "download.h"
+
#include "win32.h"
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <unistd.h>
+#include <process.h>
#include "resource.h"
#include "msg.h"
-#include "ini.h"
#include "dialog.h"
-#include "concat.h"
+#include "String++.h"
#include "geturl.h"
#include "state.h"
-#include "mkdir.h"
+#include "LogSingleton.h"
+#include "filemanip.h"
-#define pi (package[i].info[package[i].trust])
+#include "io_stream.h"
-void
-do_download (HINSTANCE h)
+#include "package_db.h"
+#include "package_meta.h"
+#include "package_version.h"
+#include "package_source.h"
+
+#include "rfc1738.h"
+
+#include "threebar.h"
+
+#include "md5.h"
+
+#include "Exception.h"
+
+extern ThreeBarProgressPage Progress;
+
+
+bool
+validateCachedPackage (String const &fullname, packagesource & pkgsource)
{
- int i;
+ if (pkgsource.md5.isSet())
+ {
+ // check the MD5 sum of the cached file here
+ io_stream *thefile = io_stream::open (fullname, "rb");
+ if (!thefile)
+ return 0;
+ md5_state_t pns;
+ md5_init (&pns);
+
+ log (LOG_BABBLE) << "Checking MD5 for " << fullname << endLog;
+
+ Progress.SetText1 ((String ("Checking MD5 for ") + pkgsource.Base()).cstr_oneuse());
+ Progress.SetText4 ("Progress:");
+ Progress.SetBar1 (0);
+
+ unsigned char buffer[16384];
+ ssize_t count;
+ while ((count = thefile->read (buffer, 16384)) > 0)
+ {
+ md5_append (&pns, buffer, count);
+ Progress.SetBar1 (thefile->tell(), thefile->get_size());
+ }
+ delete thefile;
+ if (count < 0)
+ throw new Exception ("__LINE__ __FILE__", (String ("IO Error reading ") + pkgsource.Cached()).cstr_oneuse(), APPERR_IO_ERROR);
+
+ md5_byte_t tempdigest[16];
+ md5_finish(&pns, tempdigest);
+ md5 tempMD5;
+ tempMD5.set (tempdigest);
+
+ log (LOG_BABBLE) << "For file '" << fullname <<
+ " ini digest is " << pkgsource.md5.print() <<
+ " file digest is " << tempMD5.print() << endLog;
+
+ if (pkgsource.md5 != tempMD5)
+ return false;
+ }
+ return true;
+}
- for (i=0; i<npackages; i++)
- if (package[i].action != ACTION_SAME)
+/* 0 on failure
+ */
+int
+check_for_cached (packagesource & pkgsource)
+{
+ /* search algo:
+ 1) is there a legacy version in the cache dir available.
+ (Note that the cache dir is represented by a mirror site of
+ file://local_dir
+ */
+
+ // Already found one.
+ if (pkgsource.Cached())
+ return 1;
+
+ String prefix = String ("file://") + local_dir + "/";
+ DWORD size;
+ if ((size = get_file_size (prefix + pkgsource.Canonical ())) > 0)
+ if (size == pkgsource.size)
{
- char *local = pi.install;
-
- struct stat s;
- if (stat (local, &s) >= 0)
- if (s.st_size == pi.install_size)
- continue;
-
- mkdir_p (0, local);
-
- if (get_url_to_file (concat (MIRROR_SITE, "/", pi.install, 0),
- concat (local, ".tmp", 0),
- pi.install_size))
- {
- package[i].action = ACTION_ERROR;
- continue;
- }
+ if (validateCachedPackage (prefix + pkgsource.Canonical (), pkgsource))
+ pkgsource.set_cached (prefix + pkgsource.Canonical ());
else
- {
- stat (concat (local, ".tmp", 0), &s);
- if (s.st_size == pi.install_size)
- {
- rename (concat (local, ".tmp", 0), local);
- }
- else
- {
- note (IDS_DOWNLOAD_SHORT, local, s.st_size, pi.install_size);
- package[i].action = ACTION_ERROR;
- }
- }
+ throw new Exception ("__LINE__ __FILE__", (String ("Package validation failure for ") + prefix + pkgsource.Canonical ()).cstr_oneuse(), APPERR_CORRUPT_PACKAGE);
+ return 1;
}
- dismiss_url_status_dialog ();
+ /*
+ 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)
+ {
+ String fullname = prefix + rfc1738_escape_part (n->key) + "/" +
+ pkgsource.Canonical ();
+ if ((size = get_file_size (fullname)) > 0)
+ if (size == pkgsource.size)
+ {
+ if (validateCachedPackage (fullname, pkgsource))
+ pkgsource.set_cached (fullname );
+ else
+ throw new Exception ("__LINE__ __FILE__", (String ("Package validation failure for ") + fullname).cstr_oneuse(), APPERR_CORRUPT_PACKAGE);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* download a file from a mirror site to the local cache. */
+static int
+download_one (packagesource & pkgsource, HWND owner)
+{
+ try
+ {
+ if (check_for_cached (pkgsource))
+ return 0;
+ }
+ catch (Exception * e)
+ {
+ // We know what to do with these..
+ if (e->errNo() == APPERR_CORRUPT_PACKAGE)
+ {
+ 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)
+ {
+ String const local = local_dir + "/" +
+ rfc1738_escape_part (n->key) + "/" +
+ pkgsource.Canonical ();
+ io_stream::mkpath_p (PATH_TO_FILE, String ("file://") + local);
+
+ if (get_url_to_file(n->key + "/" + pkgsource.Canonical (),
+ local + ".tmp", pkgsource.size, owner))
+ {
+ /* FIXME: note new source ? */
+ continue;
+ }
+ else
+ {
+ size_t size = get_file_size (String("file://") + local + ".tmp");
+ if (size == pkgsource.size)
+ {
+ log (LOG_PLAIN) << "Downloaded " << local << endLog;
+ if (_access (local.cstr_oneuse(), 0) == 0)
+ remove (local.cstr_oneuse());
+ rename ((local + ".tmp").cstr_oneuse(), local.cstr_oneuse());
+ success = 1;
+ pkgsource.set_cached (String ("file://") + local);
+ // FIXME: move the downloaded file to the
+ // original locations - without the mirror site dir in the way
+ continue;
+ }
+ else
+ {
+ log (LOG_PLAIN) << "Download " << local << " wrong size (" <<
+ size << " actual vs " << pkgsource.size << " expected)" <<
+ endLog;
+ remove ((local + ".tmp").cstr_oneuse());
+ continue;
+ }
+ }
+ }
+ if (success)
+ return 0;
+ /* FIXME: Do we want to note this? if so how? */
+ return 1;
+}
+
+static void
+do_download_thread (HINSTANCE h, HWND owner)
+{
+ int errors = 0;
+ total_download_bytes = 0;
+ total_download_bytes_sofar = 0;
+
+ packagedb db;
+ /* calculate the amount needed */
+ for (vector <packagemeta *>::iterator i = db.packages.begin ();
+ i != db.packages.end (); ++i)
+ {
+ packagemeta & pkg = **i;
+ if (pkg.desired.changeRequested())
+ {
+ packageversion version = pkg.desired;
+ packageversion sourceversion = version.sourcePackage();
+ try
+ {
+ if (version.picked())
+ {
+ for (vector<packagesource>::iterator i =
+ version.sources ()->begin();
+ i != version.sources ()->end(); ++i)
+ if (!check_for_cached (*i))
+ total_download_bytes += i->size;
+ }
+ if (sourceversion.picked ())
+ {
+ for (vector<packagesource>::iterator i =
+ sourceversion.sources ()->begin();
+ i != sourceversion.sources ()->end(); ++i)
+ if (!check_for_cached (*i))
+ total_download_bytes += i->size;
+ }
+ }
+ catch (Exception * e)
+ {
+ // We know what to do with these..
+ if (e->errNo() == APPERR_CORRUPT_PACKAGE)
+ fatal (owner, IDS_CORRUPT_PACKAGE, pkg.name.cstr_oneuse());
+ // Unexpected exception.
+ throw e;
+ }
+ }
+ }
+
+ /* and do the download. FIXME: This here we assign a new name for the cached version
+ * and check that above.
+ */
+ for (vector <packagemeta *>::iterator i = db.packages.begin ();
+ i != db.packages.end (); ++i)
+ {
+ packagemeta & pkg = **i;
+ if (pkg.desired.changeRequested())
+ {
+ int e = 0;
+ packageversion version = pkg.desired;
+ packageversion sourceversion = version.sourcePackage();
+ if (version.picked())
+ {
+ for (vector<packagesource>::iterator i =
+ version.sources ()->begin();
+ i != version.sources ()->end(); ++i)
+ e += download_one (*i, owner);
+ }
+ if (sourceversion && sourceversion.picked())
+ {
+ for (vector<packagesource>::iterator i =
+ sourceversion.sources ()->begin();
+ i != sourceversion.sources ()->end(); ++i)
+ e += download_one (*i, owner);
+ }
+ errors += e;
+#if 0
+ if (e)
+ pkg->action = ACTION_ERROR;
+#endif
+ }
+ }
+
+ if (errors)
+ {
+ if (yesno (owner, IDS_DOWNLOAD_INCOMPLETE) == IDYES)
+ {
+ next_dialog = IDD_SITE;
+ return;
+ }
+ }
if (source == IDC_SOURCE_DOWNLOAD)
{
- note (IDS_DOWNLOAD_COMPLETE);
+ if (errors)
+ exit_msg = IDS_DOWNLOAD_INCOMPLETE;
+ else
+ exit_msg = IDS_DOWNLOAD_COMPLETE;
next_dialog = 0;
}
else
next_dialog = IDD_S_INSTALL;
}
+
+static DWORD WINAPI
+do_download_reflector (void *p)
+{
+ HANDLE *context;
+ context = (HANDLE *) p;
+
+ do_download_thread ((HINSTANCE) context[0], (HWND) context[1]);
+
+ // Tell the progress page that we're done downloading
+ Progress.PostMessage (WM_APP_DOWNLOAD_THREAD_COMPLETE, 0, next_dialog);
+
+ 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);
+}