"\n%%% $Id$\n";
#endif
+#include "download.h"
+
#include "win32.h"
#include <stdio.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 "log.h"
-#include "port.h"
+#include "LogSingleton.h"
+#include "filemanip.h"
+
+#include "io_stream.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;
-DWORD get_file_size (const char *name)
-{
- HANDLE h;
- WIN32_FIND_DATA buf;
- DWORD ret = 0;
- h = FindFirstFileA (name, &buf);
- if (h != INVALID_HANDLE_VALUE)
+bool
+validateCachedPackage (String const &fullname, packagesource & pkgsource)
+{
+ if (pkgsource.md5.isSet())
{
- if (buf.nFileSizeHigh == 0)
- ret = buf.nFileSizeLow;
- FindClose (h);
+ // 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 ret;
+ return true;
}
-static int
-download_one (char *name, unsigned int expected_size, int action)
+/* 0 on failure
+ */
+int
+check_for_cached (packagesource & pkgsource)
{
- char *local = name;
+ /* 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 (local)) > 0)
- if (size == expected_size && action != ACTION_SRC_ONLY
- && action != ACTION_REDO)
- return 0;
+ if ((size = get_file_size (prefix + pkgsource.Canonical ())) > 0)
+ if (size == pkgsource.size)
+ {
+ if (validateCachedPackage (prefix + pkgsource.Canonical (), pkgsource))
+ pkgsource.set_cached (prefix + pkgsource.Canonical ());
+ else
+ throw new Exception ("__LINE__ __FILE__", (String ("Package validation failure for ") + prefix + pkgsource.Canonical ()).cstr_oneuse(), APPERR_CORRUPT_PACKAGE);
+ return 1;
+ }
- mkdir_p (0, local);
+ /*
+ 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;
+}
- if (get_url_to_file (concat (MIRROR_SITE, "/", name, 0),
- concat (local, ".tmp", 0), expected_size))
+/* download a file from a mirror site to the local cache. */
+static int
+download_one (packagesource & pkgsource, HWND owner)
+{
+ try
{
- note (IDS_DOWNLOAD_FAILED, name);
- return 1;
+ if (check_for_cached (pkgsource))
+ return 0;
}
- else
+ 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)
{
- size = get_file_size (concat (local, ".tmp", 0));
- if (size == expected_size)
+ 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))
{
- log (0, "Downloaded %s", local);
- if (_access (local, 0) == 0)
- remove (local);
- rename (concat (local, ".tmp", 0), local);
+ /* FIXME: note new source ? */
+ continue;
}
else
{
- log (0, "Download %s wrong size (%ld actual vs %d expected)",
- local, size, expected_size);
- note (IDS_DOWNLOAD_SHORT, local, size, expected_size);
- return 1;
+ 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;
+ }
}
}
-
- return 0;
+ if (success)
+ return 0;
+ /* FIXME: Do we want to note this? if so how? */
+ return 1;
}
-void
-do_download (HINSTANCE h)
+static void
+do_download_thread (HINSTANCE h, HWND owner)
{
int errors = 0;
total_download_bytes = 0;
total_download_bytes_sofar = 0;
- for (Package * pkg = package; pkg->name; pkg++)
- if (is_download_action (pkg))
- {
- Info *pi = pkg->info + pkg->trust;
- DWORD size = get_file_size (pi->install);
- char *local = pi->install;
- if (pkg->action != ACTION_SRC_ONLY &&
- (pkg->action == ACTION_REDO || size != pi->install_size))
- total_download_bytes += pi->install_size;
- local = pi->source;
- size = get_file_size (pi->source);
- if (pkg->srcpicked &&
- (pkg->action == ACTION_SRC_ONLY || size != pi->source_size))
- total_download_bytes += pi->source_size;
- }
-
- for (Package * pkg = package; pkg->name; pkg++)
- if (is_download_action (pkg))
- {
- int e = 0;
- Info *pi = pkg->info + pkg->trust;
- if (pkg->action != ACTION_SRC_ONLY)
- e += download_one (pi->install, pi->install_size, pkg->action);
- if (pkg->srcpicked && pi->source)
- e += download_one (pi->source, pi->source_size, pkg->action);
- errors += e;
- if (e)
- pkg->action = ACTION_ERROR;
- }
+ 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;
+ }
+ }
+ }
- dismiss_url_status_dialog ();
+ /* 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 (IDS_DOWNLOAD_INCOMPLETE) == IDYES)
+ if (yesno (owner, IDS_DOWNLOAD_INCOMPLETE) == IDYES)
{
next_dialog = IDD_SITE;
return;
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);
+}