michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const Ci = Components.interfaces; michael@0: const Cc = Components.classes; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/FileUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: const DIR_UPDATES = "updates"; michael@0: const FILE_UPDATES_DB = "updates.xml"; michael@0: const FILE_UPDATE_ACTIVE = "active-update.xml"; michael@0: const FILE_LAST_LOG = "last-update.log"; michael@0: const FILE_BACKUP_LOG = "backup-update.log"; michael@0: const FILE_UPDATE_STATUS = "update.status"; michael@0: michael@0: const KEY_UPDROOT = "UpdRootD"; michael@0: michael@0: #ifdef XP_WIN michael@0: michael@0: const PREF_APP_UPDATE_MIGRATE_APP_DIR = "app.update.migrated.updateDir"; michael@0: michael@0: michael@0: function getTaskbarIDHash(rootKey, exePath, appInfoName) { michael@0: let registry = Cc["@mozilla.org/windows-registry-key;1"]. michael@0: createInstance(Ci.nsIWindowsRegKey); michael@0: try { michael@0: registry.open(rootKey, "Software\\Mozilla\\" + appInfoName + "\\TaskBarIDs", michael@0: Ci.nsIWindowsRegKey.ACCESS_READ); michael@0: if (registry.hasValue(exePath)) { michael@0: return registry.readStringValue(exePath); michael@0: } michael@0: } catch (ex) { michael@0: } finally { michael@0: registry.close(); michael@0: } michael@0: return undefined; michael@0: }; michael@0: michael@0: /* michael@0: * Migrates old update directory files to the new update directory michael@0: * which is based on a hash of the installation. michael@0: */ michael@0: function migrateOldUpdateDir() { michael@0: // Get the old udpate root leaf dir. It is based on the sub directory of michael@0: // program files, or if the exe path is not inside program files, the appname. michael@0: var appinfo = Components.classes["@mozilla.org/xre/app-info;1"]. michael@0: getService(Components.interfaces.nsIXULAppInfo). michael@0: QueryInterface(Components.interfaces.nsIXULRuntime); michael@0: var updateLeafName; michael@0: var programFiles = FileUtils.getFile("ProgF", []); michael@0: var exeFile = FileUtils.getFile("XREExeF", []); michael@0: if (exeFile.path.substring(0, programFiles.path.length).toLowerCase() == michael@0: programFiles.path.toLowerCase()) { michael@0: updateLeafName = exeFile.parent.leafName; michael@0: } else { michael@0: updateLeafName = appinfo.name; michael@0: } michael@0: michael@0: // Get the old update root dir michael@0: var oldUpdateRoot; michael@0: if (appinfo.vendor) { michael@0: oldUpdateRoot = FileUtils.getDir("LocalAppData", [appinfo.vendor, michael@0: appinfo.name, michael@0: updateLeafName], false); michael@0: } else { michael@0: oldUpdateRoot = FileUtils.getDir("LocalAppData", [appinfo.name, michael@0: updateLeafName], false); michael@0: } michael@0: michael@0: // Obtain the new update root michael@0: var newUpdateRoot = FileUtils.getDir("UpdRootD", [], true); michael@0: michael@0: // If there is no taskbar ID then we want to retry this migration michael@0: // at a later time if the application gets a taskbar ID. michael@0: var taskbarID = getTaskbarIDHash(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, michael@0: exeFile.parent.path, appinfo.name); michael@0: if (!taskbarID) { michael@0: taskbarID = getTaskbarIDHash(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, michael@0: exeFile.parent.path, appinfo.name); michael@0: if (!taskbarID) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: Services.prefs.setBoolPref(PREF_APP_UPDATE_MIGRATE_APP_DIR, true); michael@0: michael@0: // Sanity checks only to ensure we don't delete something we don't mean to. michael@0: if (oldUpdateRoot.path.toLowerCase() == newUpdateRoot.path.toLowerCase() || michael@0: updateLeafName.length == 0) { michael@0: return; michael@0: } michael@0: michael@0: // If the old update root doesn't exist then we have already migrated michael@0: // or else there is no work to do. michael@0: if (!oldUpdateRoot.exists()) { michael@0: return; michael@0: } michael@0: michael@0: // Get an array of all of the files we want to migrate. michael@0: // We do this so we don't copy anything extra. michael@0: var filesToMigrate = [FILE_UPDATES_DB, FILE_UPDATE_ACTIVE, michael@0: ["updates", FILE_LAST_LOG], ["updates", FILE_BACKUP_LOG], michael@0: ["updates", "0", FILE_UPDATE_STATUS]]; michael@0: michael@0: // Move each of those files to the new directory michael@0: filesToMigrate.forEach(relPath => { michael@0: let oldFile = oldUpdateRoot.clone(); michael@0: let newFile = newUpdateRoot.clone(); michael@0: if (relPath instanceof Array) { michael@0: relPath.forEach(relPathPart => { michael@0: oldFile.append(relPathPart); michael@0: newFile.append(relPathPart); michael@0: }); michael@0: } else { michael@0: oldFile.append(relPath); michael@0: newFile.append(relPath); michael@0: } michael@0: michael@0: try { michael@0: if (!newFile.exists()) { michael@0: oldFile.moveTo(newFile.parent, newFile.leafName); michael@0: } michael@0: } catch (e) { michael@0: Components.utils.reportError(e); michael@0: } michael@0: }); michael@0: michael@0: oldUpdateRoot.remove(true); michael@0: } michael@0: #endif michael@0: michael@0: /** michael@0: * Gets the specified directory at the specified hierarchy under the update root michael@0: * directory without creating it if it doesn't exist. michael@0: * @param pathArray michael@0: * An array of path components to locate beneath the directory michael@0: * specified by |key| michael@0: * @return nsIFile object for the location specified. michael@0: */ michael@0: function getUpdateDirNoCreate(pathArray) { michael@0: return FileUtils.getDir(KEY_UPDROOT, pathArray, false); michael@0: } michael@0: michael@0: function UpdateServiceStub() { michael@0: #ifdef XP_WIN michael@0: // Don't attempt this migration more than once for perf reasons michael@0: var migrated = 0; michael@0: try { michael@0: migrated = Services.prefs.getBoolPref(PREF_APP_UPDATE_MIGRATE_APP_DIR); michael@0: } catch (e) { michael@0: } michael@0: michael@0: if (!migrated) { michael@0: try { michael@0: migrateOldUpdateDir(); michael@0: } catch (e) { michael@0: Components.utils.reportError(e); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: let statusFile = getUpdateDirNoCreate([DIR_UPDATES, "0"]); michael@0: statusFile.append(FILE_UPDATE_STATUS); michael@0: // If the update.status file exists then initiate post update processing. michael@0: if (statusFile.exists()) { michael@0: let aus = Components.classes["@mozilla.org/updates/update-service;1"]. michael@0: getService(Ci.nsIApplicationUpdateService). michael@0: QueryInterface(Ci.nsIObserver); michael@0: aus.observe(null, "post-update-processing", ""); michael@0: } michael@0: } michael@0: UpdateServiceStub.prototype = { michael@0: observe: function(){}, michael@0: classID: Components.ID("{e43b0010-04ba-4da6-b523-1f92580bc150}"), michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) michael@0: }; michael@0: michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdateServiceStub]);