|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 const Ci = Components.interfaces; |
|
7 const Cc = Components.classes; |
|
8 const Cu = Components.utils; |
|
9 |
|
10 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
11 Cu.import("resource://gre/modules/FileUtils.jsm"); |
|
12 Cu.import("resource://gre/modules/Services.jsm"); |
|
13 |
|
14 const DIR_UPDATES = "updates"; |
|
15 const FILE_UPDATES_DB = "updates.xml"; |
|
16 const FILE_UPDATE_ACTIVE = "active-update.xml"; |
|
17 const FILE_LAST_LOG = "last-update.log"; |
|
18 const FILE_BACKUP_LOG = "backup-update.log"; |
|
19 const FILE_UPDATE_STATUS = "update.status"; |
|
20 |
|
21 const KEY_UPDROOT = "UpdRootD"; |
|
22 |
|
23 #ifdef XP_WIN |
|
24 |
|
25 const PREF_APP_UPDATE_MIGRATE_APP_DIR = "app.update.migrated.updateDir"; |
|
26 |
|
27 |
|
28 function getTaskbarIDHash(rootKey, exePath, appInfoName) { |
|
29 let registry = Cc["@mozilla.org/windows-registry-key;1"]. |
|
30 createInstance(Ci.nsIWindowsRegKey); |
|
31 try { |
|
32 registry.open(rootKey, "Software\\Mozilla\\" + appInfoName + "\\TaskBarIDs", |
|
33 Ci.nsIWindowsRegKey.ACCESS_READ); |
|
34 if (registry.hasValue(exePath)) { |
|
35 return registry.readStringValue(exePath); |
|
36 } |
|
37 } catch (ex) { |
|
38 } finally { |
|
39 registry.close(); |
|
40 } |
|
41 return undefined; |
|
42 }; |
|
43 |
|
44 /* |
|
45 * Migrates old update directory files to the new update directory |
|
46 * which is based on a hash of the installation. |
|
47 */ |
|
48 function migrateOldUpdateDir() { |
|
49 // Get the old udpate root leaf dir. It is based on the sub directory of |
|
50 // program files, or if the exe path is not inside program files, the appname. |
|
51 var appinfo = Components.classes["@mozilla.org/xre/app-info;1"]. |
|
52 getService(Components.interfaces.nsIXULAppInfo). |
|
53 QueryInterface(Components.interfaces.nsIXULRuntime); |
|
54 var updateLeafName; |
|
55 var programFiles = FileUtils.getFile("ProgF", []); |
|
56 var exeFile = FileUtils.getFile("XREExeF", []); |
|
57 if (exeFile.path.substring(0, programFiles.path.length).toLowerCase() == |
|
58 programFiles.path.toLowerCase()) { |
|
59 updateLeafName = exeFile.parent.leafName; |
|
60 } else { |
|
61 updateLeafName = appinfo.name; |
|
62 } |
|
63 |
|
64 // Get the old update root dir |
|
65 var oldUpdateRoot; |
|
66 if (appinfo.vendor) { |
|
67 oldUpdateRoot = FileUtils.getDir("LocalAppData", [appinfo.vendor, |
|
68 appinfo.name, |
|
69 updateLeafName], false); |
|
70 } else { |
|
71 oldUpdateRoot = FileUtils.getDir("LocalAppData", [appinfo.name, |
|
72 updateLeafName], false); |
|
73 } |
|
74 |
|
75 // Obtain the new update root |
|
76 var newUpdateRoot = FileUtils.getDir("UpdRootD", [], true); |
|
77 |
|
78 // If there is no taskbar ID then we want to retry this migration |
|
79 // at a later time if the application gets a taskbar ID. |
|
80 var taskbarID = getTaskbarIDHash(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, |
|
81 exeFile.parent.path, appinfo.name); |
|
82 if (!taskbarID) { |
|
83 taskbarID = getTaskbarIDHash(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, |
|
84 exeFile.parent.path, appinfo.name); |
|
85 if (!taskbarID) { |
|
86 return; |
|
87 } |
|
88 } |
|
89 |
|
90 Services.prefs.setBoolPref(PREF_APP_UPDATE_MIGRATE_APP_DIR, true); |
|
91 |
|
92 // Sanity checks only to ensure we don't delete something we don't mean to. |
|
93 if (oldUpdateRoot.path.toLowerCase() == newUpdateRoot.path.toLowerCase() || |
|
94 updateLeafName.length == 0) { |
|
95 return; |
|
96 } |
|
97 |
|
98 // If the old update root doesn't exist then we have already migrated |
|
99 // or else there is no work to do. |
|
100 if (!oldUpdateRoot.exists()) { |
|
101 return; |
|
102 } |
|
103 |
|
104 // Get an array of all of the files we want to migrate. |
|
105 // We do this so we don't copy anything extra. |
|
106 var filesToMigrate = [FILE_UPDATES_DB, FILE_UPDATE_ACTIVE, |
|
107 ["updates", FILE_LAST_LOG], ["updates", FILE_BACKUP_LOG], |
|
108 ["updates", "0", FILE_UPDATE_STATUS]]; |
|
109 |
|
110 // Move each of those files to the new directory |
|
111 filesToMigrate.forEach(relPath => { |
|
112 let oldFile = oldUpdateRoot.clone(); |
|
113 let newFile = newUpdateRoot.clone(); |
|
114 if (relPath instanceof Array) { |
|
115 relPath.forEach(relPathPart => { |
|
116 oldFile.append(relPathPart); |
|
117 newFile.append(relPathPart); |
|
118 }); |
|
119 } else { |
|
120 oldFile.append(relPath); |
|
121 newFile.append(relPath); |
|
122 } |
|
123 |
|
124 try { |
|
125 if (!newFile.exists()) { |
|
126 oldFile.moveTo(newFile.parent, newFile.leafName); |
|
127 } |
|
128 } catch (e) { |
|
129 Components.utils.reportError(e); |
|
130 } |
|
131 }); |
|
132 |
|
133 oldUpdateRoot.remove(true); |
|
134 } |
|
135 #endif |
|
136 |
|
137 /** |
|
138 * Gets the specified directory at the specified hierarchy under the update root |
|
139 * directory without creating it if it doesn't exist. |
|
140 * @param pathArray |
|
141 * An array of path components to locate beneath the directory |
|
142 * specified by |key| |
|
143 * @return nsIFile object for the location specified. |
|
144 */ |
|
145 function getUpdateDirNoCreate(pathArray) { |
|
146 return FileUtils.getDir(KEY_UPDROOT, pathArray, false); |
|
147 } |
|
148 |
|
149 function UpdateServiceStub() { |
|
150 #ifdef XP_WIN |
|
151 // Don't attempt this migration more than once for perf reasons |
|
152 var migrated = 0; |
|
153 try { |
|
154 migrated = Services.prefs.getBoolPref(PREF_APP_UPDATE_MIGRATE_APP_DIR); |
|
155 } catch (e) { |
|
156 } |
|
157 |
|
158 if (!migrated) { |
|
159 try { |
|
160 migrateOldUpdateDir(); |
|
161 } catch (e) { |
|
162 Components.utils.reportError(e); |
|
163 } |
|
164 } |
|
165 #endif |
|
166 |
|
167 let statusFile = getUpdateDirNoCreate([DIR_UPDATES, "0"]); |
|
168 statusFile.append(FILE_UPDATE_STATUS); |
|
169 // If the update.status file exists then initiate post update processing. |
|
170 if (statusFile.exists()) { |
|
171 let aus = Components.classes["@mozilla.org/updates/update-service;1"]. |
|
172 getService(Ci.nsIApplicationUpdateService). |
|
173 QueryInterface(Ci.nsIObserver); |
|
174 aus.observe(null, "post-update-processing", ""); |
|
175 } |
|
176 } |
|
177 UpdateServiceStub.prototype = { |
|
178 observe: function(){}, |
|
179 classID: Components.ID("{e43b0010-04ba-4da6-b523-1f92580bc150}"), |
|
180 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) |
|
181 }; |
|
182 |
|
183 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdateServiceStub]); |