--- wpkg.js	Mon Apr 24 15:55:19 2006
+++ ../wpkg_0.9.8-test3+patch.js	Tue May  2 15:43:30 2006
@@ -137,6 +137,9 @@
 
 var settings_file_name = "wpkg.xml";
 
+// default value for WPKG_SOURCE temporary path
+var DEFAULT_WPKG_SOURCE = "%TEMP%\\wpkg";
+
 var sRegPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
 // here we indicate our running state
 var sRegWPKG_Running = "HKLM\\Software\\WPKG\\running";
@@ -333,6 +336,26 @@
             ".");
     }
     
+    // set the %SOURCE% environment 
+    var shell = new ActiveXObject("WScript.Shell");
+    var ProcEnv = shell.Environment("PROCESS");
+    if (ProcEnv("WPKG_SOURCE") == "") {
+    	var wpkg_source = DEFAULT_WPKG_SOURCE;
+	// try to loop through the environment string, until all environment
+	// vars are resolved. Not very nice as it could end up looping
+	// endless.
+    	do {
+	    wpkg_source = shell.ExpandEnvironmentStrings(wpkg_source);
+	} while (wpkg_source.indexOf("%") != -1 );
+	// if the folder does not exist, create it.
+	if (!fso.FolderExists(wpkg_source)) {
+	    fso.CreateFolder(wpkg_source);
+	}
+	dinfo("WPKG_SOURCE environment not set, using default: " + wpkg_source);
+	ProcEnv("WPKG_SOURCE") = wpkg_source;
+    }
+
+    
     // process command line arguments to determine course of action
     
     if (argn("query") != null) {
@@ -463,8 +486,8 @@
 /**
  * Sends a message to the system console notifying of impending action.
  */
-function notifyUserStart() {
-    if (!was_notified) {
+function notifyUserStart(doNotify) {
+    if (!was_notified || doNotify ) {
         var msg = "";
         msg += "The automated software installation utility has or is ";
         msg += "currently applying software updates to your system. Please ";
@@ -525,86 +548,16 @@
 function synchronizeProfile() {
     // accquire packages that should be present
     var packageArray = getAvailablePackages();
+    dinfo("number of available packages: " + packageArray.length); 
 
-    if (debug) { info("number of available packages: " + packageArray.length); }
-
-    
-    // grab currently installed package nodes
-    var installedPackages = settings.selectNodes("package");
-    
-    // loop over each installed package and check whether it still applies
-    for (var i = 0; i < installedPackages.length; i++) {
-        var installedPackageNode = installedPackages(i);
-        if (debug) { info("found installed package: " + installedPackageNode.getAttribute("id")); }
-        
-        // search for the installed package in available packages
-        var found = false;
-        for (j in packageArray) {
-            if (debug) { info("testing available package: " + packageArray[j].getAttribute("id")); }
-
-
-            if (packageArray[j].getAttribute("id") ==
-                installedPackageNode.getAttribute("id")) {
-                if (debug) { info("package: " + installedPackageNode.getAttribute("id") + " found in available packages."); }
-
-                found = true;
-                break;
-            }
-        }
-        
-        // if package is no longer present, remove it
-        if (!found) {
-            if (debug) { info("removing package: " + installedPackageNode.getAttribute("id")); }
-
-            notifyUserStart();
-            removePackage(installedPackageNode);
-        }
-    }
-    
-    // create a native jscript array to do the sorting on
-    var sortedPackages = new Array(packageArray.length);
-    for (var i = 0; i < packageArray.length; i++) {
-        sortedPackages[i] = packageArray[i];
-    }
-    
-    // classic bubble-sort algorithm on the "priority" attribute
-    var len = packageArray.length;
-    for (var i = 0; i < len - 1; i++) {
-        for (var j = 0; j < len - 1 - i; j++) {
-            var pri1;
-            var pri2;
-            var szpri1 = sortedPackages[j].getAttribute("priority");
-            var szpri2 = sortedPackages[j + 1].getAttribute("priority");
-            
-            // if a priority is not set, we assume 0
-            
-            if (szpri1 == null) {
-                pri1 = 0;
-            } else {
-                pri1 = parseInt(szpri1);
-            }
-            
-            if (szpri2 == null) {
-                pri2 = 0;
-            } else {
-                pri2 = parseInt(szpri2);
-            }
-            
-            // if the priority of the first one in the list exceeds the second,
-            // swap the packages
-            if (pri1 < pri2) {
-                var tmp = sortedPackages[j];
-                sortedPackages[j] = sortedPackages[j + 1];
-                sortedPackages[j + 1] = tmp;
-            }
-        }
-    }
+    // handle installed packages
+    checkInstalledPackages(packageArray);
     
     // sorting complete
-    packageArray = sortedPackages;
+    packageArray = sortPackageArray(packageArray);
+    listPackagePriorityList(packageArray);
     
-    // loop over each available package and determine whether to install or
-    // upgrade
+    // loop over each available package and determine whether to install or upgrade
     for (var i = 0; i < packageArray.length; i++) {
         var packageNode = packageArray[i];
         var packageId   = packageNode.getAttribute("id");
@@ -618,15 +571,13 @@
         var installedPackage = settings.selectSingleNode("package[@id='" +
             packageId + "']");
             
-        if (executeAttr == "once") {
+	switch (executeAttr) {
+	    case "once":
             if ((null == installedPackage) |
                ((null != installedPackage) &&
 	       (parseInt(installedPackage.getAttribute("revision")) < packageRev )) ) {
                 try {
-                    if (notifyAttr != "false") {
-                        notifyUserStart();
-                    }
-
+			notifyUserStart(notifyAttr);
                     executeOnce(packageNode);
                 } catch (e) {
                     throw new Error("Installation error while synchronizing " +
@@ -634,26 +585,22 @@
                         "\n\n" + e.description);
                 }
             }
-        } else if (executeAttr == "always") {
-           // do not look if package is installed
+		break;
+	    case "always":
             try {
-                if (notifyAttr != "false") {
-                    notifyUserStart();
-                }
+		    notifyUserStart(notifyAttr);
                 executeOnce(packageNode);
             } catch (e) {
                 throw new Error("Installation error while synchronizing " +
                     "package " + packageName + ", synchronization aborting." +
                     "\n\n" + e.description);
             }
-	    
-        } else {
+		break;
+	    default:
             // if the package is not installed, install it
             if (installedPackage == null) {
                 try {
-                    if (notifyAttr != "false") {
-                        notifyUserStart();
-                    }
+			notifyUserStart(notifyAttr);
                     installPackage(packageNode);
                 } catch (e) {
                     throw new Error("Installation error while synchronizing " +
@@ -663,9 +610,7 @@
             } else if (parseInt(installedPackage.getAttribute("revision")) <
                 packageRev) {
                 try {
-                    if (notifyAttr != "false") {
-                        notifyUserStart();
-                    }
+			notifyUserStart(notifyAttr);
                     upgradePackage(installedPackage, packageNode);
                 } catch (e) {
                     throw new Error("Upgrade error while synchronizing " +
@@ -673,6 +618,7 @@
                         "\n\n" + e.description);
                 }
             }
+		break;
         }
     }
     
@@ -683,6 +629,37 @@
     }
 }
 
+function checkInstalledPackages(packageArray) {
+    // grab currently installed package nodes
+    var installedPackages = settings.selectNodes("package");
+
+    // loop over each installed package and check whether it still applies
+    for (var i = 0; i < installedPackages.length; i++) {
+        var installedPackageNode = installedPackages(i);
+        dinfo("found installed package: " + installedPackageNode.getAttribute("id")); 
+        
+        // search for the installed package in available packages
+        var found = false;
+        for (j in packageArray) {
+            if (packageArray[j].getAttribute("id") ==
+                installedPackageNode.getAttribute("id")) {
+                if (debug) { info("package: " + installedPackageNode.getAttribute("id") + " found in available packages."); }
+
+                found = true;
+                break;
+            }
+        }
+        
+        // if package is no longer present, remove it
+        if (!found) {
+            dinfo("removing package: " + installedPackageNode.getAttribute("id")); 
+            notifyUserStart();
+            removePackage(installedPackageNode);
+        }
+    }
+    dinfo("number of installed packages: " + installedPackages.length);
+}
+
 function queryAllPackages() {
     // retrieve packages
     var settingsNodes = settings.selectNodes("package");
@@ -721,6 +698,116 @@
     info(message);
 }
 
+function downloadPackage(packageNode, downloadList) {
+    var shell = new ActiveXObject("WScript.Shell");
+    var ProcEnv = shell.Environment("PROCESS");
+    var packageName = packageNode.getAttribute("name");
+    var files = packageNode.selectNodes("file");
+
+    if (files.length) info("Downloading files for " + packageName + "...");
+
+    for (var x = 0; x < files.length; x++) {
+	var downloaded = false;
+	var fileNode = files(x);
+	var timeout = parseInt(fileNode.getAttribute("timeout"));
+	if (timeout == null) timeout = 7200;	// default timeout 7200
+	var md5hash = fileNode.getAttribute("md5");
+	var sourceNodes = fileNode.selectNodes("source");
+
+	var sourcefile = fileNode.getAttribute("name");
+	if (sourcefile == null) throw new Error(0, "Wrong download source filename defined for " + packageID);
+	var dest = ProcEnv("WPKG_SOURCE") + "\\" + sourcefile ; 
+
+	// loop through sources and try to fetch the file
+	for (var z = 0; z < sourceNodes.length ; z++) {
+	    var sourceNode = sourceNodes(z);
+	    var url = sourceNode.getAttribute("url");
+	    if (url == null) throw new Error(0, "Invalid URL in package source.");
+
+	    if (downloadFile(url, dest, timeout)) {
+		// MD5 checksum checking (if any)
+		if (md5hash != null) {
+		    if (!md5SumOk(dest, md5hash)) {
+			throw new Error(0, "MD5 checksum WRONG, original is " + md5hash);
+		    } else {
+			info ("MD5 checksum OK for \"" + sourcefile + "\"");
+			// save downloaded filenames for later removal
+			downloadList.push(dest);
+			downloaded = true;	// indicate success on this file
+			break;
+		    }
+		}
+	    }
+	}
+	if (!downloaded) throw new Error(0, "Could not download required files.");
+    }
+}
+
+function purgeDownloadList(downloadList) {
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    dinfo("Purging downloaded packages");
+    while (downloadList.length) {
+	var name = downloadList.pop();
+	dinfo("Purging: " + name);
+	// force delete of temporary file 
+	fso.DeleteFile(name, true);
+    }
+}
+
+function sortPackageArray(packageArray) {
+    // create a native jscript array to do the sorting on
+    var sortedPackages = new Array(packageArray.length);
+    for (var i = 0; i < packageArray.length; i++) {
+        sortedPackages[i] = packageArray[i];
+    }
+
+    // classic bubble-sort algorithm on the "priority" attribute
+    var len = packageArray.length;
+    for (var i = 0; i < len - 1; i++) {
+        for (var j = 0; j < len - 1 - i; j++) {
+            var pri1;
+            var pri2;
+            var szpri1 = sortedPackages[j].getAttribute("priority");
+            var szpri2 = sortedPackages[j + 1].getAttribute("priority");
+            
+            // if a priority is not set, we assume 0
+            
+            if (szpri1 == null) {
+                pri1 = 0;
+            } else {
+                pri1 = parseInt(szpri1);
+            }
+            
+            if (szpri2 == null) {
+                pri2 = 0;
+            } else {
+                pri2 = parseInt(szpri2);
+            }
+            
+            // if the priority of the first one in the list exceeds the second,
+            // swap the packages
+            if (pri1 < pri2) {
+                var tmp = sortedPackages[j];
+                sortedPackages[j] = sortedPackages[j + 1];
+                sortedPackages[j + 1] = tmp;
+            }
+        }
+    }
+    return sortedPackages;
+}
+
+function listPackagePriorityList(packageArray) {
+    if (debug && packageArray.length) {
+    	info("Package priority listing after sorting: ");
+	info("Number\tPrio\tPackageId");
+	for (var f = 0; f < packageArray.length; f++) {
+	    var packageId   = packageArray[f].getAttribute("id");
+	    var packageprio = parseInt(packageArray[f].getAttribute("priority"));
+	    info(f + ":\t" + packageprio + "\t" + packageId);
+	}
+    }
+}
+
 /**
  * Show the user a list of packages that are currently installed.
  */
@@ -1344,9 +1431,12 @@
  * Executes command of the package and registers this fact.
  */
 function executeOnce(packageNode) {
+    var packageFiles = new Array;
     var packageName = packageNode.getAttribute("name");
     var packageId = packageNode.getAttribute("id");
     
+    downloadPackage(packageNode, packageFiles);
+
     info("Executing commands for " + packageName + "...");
         
     // select command lines to install
@@ -1418,6 +1508,8 @@
     settings.appendChild(packageNode);
     saveXml(settings, settings_file);
     
+    purgeDownloadList(packageFiles);
+    
     // reboot the system if this package is suppose to
     if (packageNode.getAttribute("reboot") == "true") {
         info("Execution of commands of " + packageName + " successful, system " +
@@ -1441,13 +1533,13 @@
  * Installs the specified package node to the system.
  */
 function installPackage(packageNode) {
+    var packageFiles = new Array;
     var packageName = packageNode.getAttribute("name");
     
     // get a list of checks to perform before installation.
     var checkNodes = packageNode.selectNodes("check");
     var bypass = false;
 
-
     // when "/forceinstall" say "not installed"
     if (!forceInstall) {
         bypass = checkInstalled(packageNode);
@@ -1459,7 +1551,6 @@
 		var nodeInst = settings.selectSingleNode("package[@id='" + packageID + "']");
 
 		if (nodeInst == null) {
-		  
 		  if (debug) { 
 		    info("Package " + packageName +
 			 " missing from settings file, adding it now.");
@@ -1474,6 +1565,9 @@
 
     
     if (!bypass) {
+
+	downloadPackage(packageNode, packageFiles);
+
         info("Installing " + packageName + "...");
         
         // select command lines to install
@@ -1540,11 +1634,12 @@
                             "Failed checking after installation.");
         }
 
-    
 	// append new node to local xml
 	settings.appendChild(packageNode);
 	saveXml(settings, settings_file);
     
+	purgeDownloadList(packageFiles);
+    
 	// reboot the system if this package is suppose to
 	if (packageNode.getAttribute("reboot") == "true") {
 	  info("Installation of " + packageName + " successful, system " +
@@ -1560,6 +1655,10 @@
  * Upgrades the old package node to the new package node.
  */
 function upgradePackage(oldPackageNode, newPackageNode) {
+    var packageFiles = new Array;
+
+    downloadPackage(packageNode, packageFiles);
+
     info("Upgrading " + newPackageNode.getAttribute("name") + "...");
     var packageName = newPackageNode.getAttribute("name");
     
@@ -1647,6 +1746,7 @@
         saveXml(settings, settings_file);
     }
 
+    purgeDownloadList(packageFiles);
 
     info("Upgrade of " + newPackageNode.getAttribute("name") + " successful.");
     
@@ -1801,6 +1901,7 @@
             }
         }
             }
+
     }
 
     return packageArray;
@@ -1815,6 +1916,10 @@
 function appendDependencies(appendArray, appendNode, sourceArray, sourceName) {
     // search for package tags in each profile
     var dependsNodes = appendNode.selectNodes("depends");
+
+    var nodePrio = parseInt(appendNode.getAttribute("priority"));
+    if (nodePrio == null) nodePrio = 0;
+
     if (dependsNodes != null) {
 	for (var i = 0; i < dependsNodes.length; i++) {
 	    var dependsId = dependsNodes(i).getAttribute(sourceName + "-id");
@@ -1834,6 +1939,20 @@
 	    if (searchArray(appendArray, dependsNode)) {
                         continue;
                     } else {
+	    	// packages get special priority handling
+		if (sourceName == "package") {
+		    var dependsPrio = parseInt(dependsNode.getAttribute("priority"));
+		    // if no priority given, start with original node priority
+		    if (dependsPrio == null) dependsPrio = nodePrio;
+		    // increase priority until its higher than the one for the
+		    // original package
+		    if (dependsPrio <= nodePrio) newPrio = nodePrio + 1;
+		    dependsNode.setAttribute("priority", newPrio);
+		    /*
+		    dinfo("nodePrio=" + nodePrio + " dependsPrio=" +
+			    dependsPrio + " newPrio=" + newPrio);
+		     */
+		}
 		if (debug) info("Add " + sourceName + " dependecy: " + dependsId);
 		appendArray.push(dependsNode);
 		appendDependencies(appendArray, dependsNode, sourceArray, sourceName);
@@ -2420,3 +2539,71 @@
     info(message);
 }
 
+// check MD5 checksum of given file, using external program
+function md5SumOk(sourceFile, checkSum) {
+    var sourceSum;
+    var cmdline;
+    var MD5PROG = "md5.exe";
+    var MD5PROGARGS = "-l -n -c" 
+
+    var fso = new ActiveXObject("Scripting.FileSystemObject");
+    var path = fso.GetParentFolderName(WScript.ScriptFullName);
+
+    cmdline = path + "\\" + MD5PROG + " " + MD5PROGARGS + checkSum + " \"" + sourceFile + "\"";
+    if (debug) info("MD5 check cmdline: " + cmdline);
+
+    if (exec (cmdline, 0) == 0) return true
+    else return false;
+}
+
+// download a file via HTTP
+function downloadFile(sourceFile, targetFile, timeout)
+{
+    try {
+	var fso = new ActiveXObject("Scripting.FileSystemObject");
+	var stream = new ActiveXObject("ADODB.Stream");
+	var xmlHttp = new ActiveXObject("Microsoft.XMLHttp");
+
+	if (debug) info("Downloading \"" + sourceFile + "\" to \"" + targetFile + "\"")
+	else info("Downloading: \"" + sourceFile + "\"");
+
+	xmlHttp.Open("GET", sourceFile, true);
+	xmlHttp.Send();
+
+	for (var t = 0; t < timeout * 10; t++) {
+	    //info("sleeping t=" + t + " timeout=" + timeout + " readystate=" + xmlHttp.ReadyState);
+	    if (xmlHttp.ReadyState == 4) break;
+	    WScript.sleep(100);
+	}
+
+	// abort download on timeout
+	if (xmlHttp.ReadyState != 4) {
+	    xmlHttp.abort();
+	    throw new Error(0, "HTTP Timeout after " + timeout + " seconds.");
+	}
+
+	if (xmlHttp.status != 200) {
+	    throw new Error(0, "HTTP Error: " + xmlHttp.status + " " + xmlHttp.StatusText);
+	}
+
+	stream.open();
+	stream.type = 1;
+
+	stream.write(xmlHttp.responseBody);
+	stream.position = 0;
+
+	if (fso.fileExists(targetFile)) {
+	    fso.deleteFile(targetFile);
+	}
+
+	stream.saveToFile(targetFile);
+	stream.close();
+
+	fso = stream = xmlHttp = null;
+    } catch (e) {
+	info("Download failed: " + e.description);
+	return false;
+    }
+
+    return true;
+}
