diff options
Diffstat (limited to 'browser/components/migration/ChromeProfileMigrator.js')
-rw-r--r-- | browser/components/migration/ChromeProfileMigrator.js | 557 |
1 files changed, 0 insertions, 557 deletions
diff --git a/browser/components/migration/ChromeProfileMigrator.js b/browser/components/migration/ChromeProfileMigrator.js deleted file mode 100644 index ec0c8d444..000000000 --- a/browser/components/migration/ChromeProfileMigrator.js +++ /dev/null @@ -1,557 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- - * vim: sw=2 ts=2 sts=2 et */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; - -const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1"; - -const S100NS_FROM1601TO1970 = 0x19DB1DED53E8000; -const S100NS_PER_MS = 10; - -const AUTH_TYPE = { - SCHEME_HTML: 0, - SCHEME_BASIC: 1, - SCHEME_DIGEST: 2 -}; - -Cu.import("resource://gre/modules/AppConstants.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/NetUtil.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); -Cu.import("resource://gre/modules/osfile.jsm"); /* globals OS */ -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */ - -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto", - "resource://gre/modules/OSCrypto.jsm"); -/** - * Get an nsIFile instance representing the expected location of user data - * for this copy of Chrome/Chromium/Canary on different OSes. - * @param subfoldersWin {Array} an array of subfolders to use for Windows - * @param subfoldersOSX {Array} an array of subfolders to use for OS X - * @param subfoldersUnix {Array} an array of subfolders to use for *nix systems - * @returns {nsIFile} the place we expect data to live. Might not actually exist! - */ -function getDataFolder(subfoldersWin, subfoldersOSX, subfoldersUnix) { - let dirServiceID, subfolders; - if (AppConstants.platform == "win") { - dirServiceID = "LocalAppData"; - subfolders = subfoldersWin.concat(["User Data"]); - } else if (AppConstants.platform == "macosx") { - dirServiceID = "ULibDir"; - subfolders = ["Application Support"].concat(subfoldersOSX); - } else { - dirServiceID = "Home"; - subfolders = [".config"].concat(subfoldersUnix); - } - return FileUtils.getDir(dirServiceID, subfolders, false); -} - -/** - * Convert Chrome time format to Date object - * - * @param aTime - * Chrome time - * @return converted Date object - * @note Google Chrome uses FILETIME / 10 as time. - * FILETIME is based on same structure of Windows. - */ -function chromeTimeToDate(aTime) -{ - return new Date((aTime * S100NS_PER_MS - S100NS_FROM1601TO1970) / 10000); -} - -/** - * Convert Date object to Chrome time format - * - * @param aDate - * Date object or integer equivalent - * @return Chrome time - * @note For details on Chrome time, see chromeTimeToDate. - */ -function dateToChromeTime(aDate) { - return (aDate * 10000 + S100NS_FROM1601TO1970) / S100NS_PER_MS; -} - -/** - * Insert bookmark items into specific folder. - * - * @param parentGuid - * GUID of the folder where items will be inserted - * @param items - * bookmark items to be inserted - * @param errorAccumulator - * function that gets called with any errors thrown so we don't drop them on the floor. - */ -function* insertBookmarkItems(parentGuid, items, errorAccumulator) { - for (let item of items) { - try { - if (item.type == "url") { - if (item.url.trim().startsWith("chrome:")) { - // Skip invalid chrome URIs. Creating an actual URI always reports - // messages to the console, so we avoid doing that. - continue; - } - yield MigrationUtils.insertBookmarkWrapper({ - parentGuid, url: item.url, title: item.name - }); - } else if (item.type == "folder") { - let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({ - parentGuid, type: PlacesUtils.bookmarks.TYPE_FOLDER, title: item.name - })).guid; - - yield insertBookmarkItems(newFolderGuid, item.children, errorAccumulator); - } - } catch (e) { - Cu.reportError(e); - errorAccumulator(e); - } - } -} - -function ChromeProfileMigrator() { - let chromeUserDataFolder = - getDataFolder(["Google", "Chrome"], ["Google", "Chrome"], ["google-chrome"]); - this._chromeUserDataFolder = chromeUserDataFolder.exists() ? - chromeUserDataFolder : null; -} - -ChromeProfileMigrator.prototype = Object.create(MigratorPrototype); - -ChromeProfileMigrator.prototype.getResources = - function Chrome_getResources(aProfile) { - if (this._chromeUserDataFolder) { - let profileFolder = this._chromeUserDataFolder.clone(); - profileFolder.append(aProfile.id); - if (profileFolder.exists()) { - let possibleResources = [ - GetBookmarksResource(profileFolder), - GetHistoryResource(profileFolder), - GetCookiesResource(profileFolder), - ]; - if (AppConstants.platform == "win") { - possibleResources.push(GetWindowsPasswordsResource(profileFolder)); - } - return possibleResources.filter(r => r != null); - } - } - return []; - }; - -ChromeProfileMigrator.prototype.getLastUsedDate = - function Chrome_getLastUsedDate() { - let datePromises = this.sourceProfiles.map(profile => { - let basePath = OS.Path.join(this._chromeUserDataFolder.path, profile.id); - let fileDatePromises = ["Bookmarks", "History", "Cookies"].map(leafName => { - let path = OS.Path.join(basePath, leafName); - return OS.File.stat(path).catch(() => null).then(info => { - return info ? info.lastModificationDate : 0; - }); - }); - return Promise.all(fileDatePromises).then(dates => { - return Math.max.apply(Math, dates); - }); - }); - return Promise.all(datePromises).then(dates => { - dates.push(0); - return new Date(Math.max.apply(Math, dates)); - }); - }; - -Object.defineProperty(ChromeProfileMigrator.prototype, "sourceProfiles", { - get: function Chrome_sourceProfiles() { - if ("__sourceProfiles" in this) - return this.__sourceProfiles; - - if (!this._chromeUserDataFolder) - return []; - - let profiles = []; - try { - // Local State is a JSON file that contains profile info. - let localState = this._chromeUserDataFolder.clone(); - localState.append("Local State"); - if (!localState.exists()) - throw new Error("Chrome's 'Local State' file does not exist."); - if (!localState.isReadable()) - throw new Error("Chrome's 'Local State' file could not be read."); - - let fstream = Cc[FILE_INPUT_STREAM_CID].createInstance(Ci.nsIFileInputStream); - fstream.init(localState, -1, 0, 0); - let inputStream = NetUtil.readInputStreamToString(fstream, fstream.available(), - { charset: "UTF-8" }); - let info_cache = JSON.parse(inputStream).profile.info_cache; - for (let profileFolderName in info_cache) { - let profileFolder = this._chromeUserDataFolder.clone(); - profileFolder.append(profileFolderName); - profiles.push({ - id: profileFolderName, - name: info_cache[profileFolderName].name || profileFolderName, - }); - } - } catch (e) { - Cu.reportError("Error detecting Chrome profiles: " + e); - // If we weren't able to detect any profiles above, fallback to the Default profile. - let defaultProfileFolder = this._chromeUserDataFolder.clone(); - defaultProfileFolder.append("Default"); - if (defaultProfileFolder.exists()) { - profiles = [{ - id: "Default", - name: "Default", - }]; - } - } - - // Only list profiles from which any data can be imported - this.__sourceProfiles = profiles.filter(function(profile) { - let resources = this.getResources(profile); - return resources && resources.length > 0; - }, this); - return this.__sourceProfiles; - } -}); - -Object.defineProperty(ChromeProfileMigrator.prototype, "sourceHomePageURL", { - get: function Chrome_sourceHomePageURL() { - let prefsFile = this._chromeUserDataFolder.clone(); - prefsFile.append("Preferences"); - if (prefsFile.exists()) { - // XXX reading and parsing JSON is synchronous. - let fstream = Cc[FILE_INPUT_STREAM_CID]. - createInstance(Ci.nsIFileInputStream); - fstream.init(prefsFile, -1, 0, 0); - try { - return JSON.parse( - NetUtil.readInputStreamToString(fstream, fstream.available(), - { charset: "UTF-8" }) - ).homepage; - } - catch (e) { - Cu.reportError("Error parsing Chrome's preferences file: " + e); - } - } - return ""; - } -}); - -Object.defineProperty(ChromeProfileMigrator.prototype, "sourceLocked", { - get: function Chrome_sourceLocked() { - // There is an exclusive lock on some SQLite databases. Assume they are locked for now. - return true; - }, -}); - -function GetBookmarksResource(aProfileFolder) { - let bookmarksFile = aProfileFolder.clone(); - bookmarksFile.append("Bookmarks"); - if (!bookmarksFile.exists()) - return null; - - return { - type: MigrationUtils.resourceTypes.BOOKMARKS, - - migrate: function(aCallback) { - return Task.spawn(function* () { - let gotErrors = false; - let errorGatherer = function() { gotErrors = true }; - let jsonStream = yield new Promise((resolve, reject) => { - let options = { - uri: NetUtil.newURI(bookmarksFile), - loadUsingSystemPrincipal: true - }; - NetUtil.asyncFetch(options, (inputStream, resultCode) => { - if (Components.isSuccessCode(resultCode)) { - resolve(inputStream); - } else { - reject(new Error("Could not read Bookmarks file")); - } - }); - }); - - // Parse Chrome bookmark file that is JSON format - let bookmarkJSON = NetUtil.readInputStreamToString( - jsonStream, jsonStream.available(), { charset : "UTF-8" }); - let roots = JSON.parse(bookmarkJSON).roots; - - // Importing bookmark bar items - if (roots.bookmark_bar.children && - roots.bookmark_bar.children.length > 0) { - // Toolbar - let parentGuid = PlacesUtils.bookmarks.toolbarGuid; - if (!MigrationUtils.isStartupMigration) { - parentGuid = - yield MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid); - } - yield insertBookmarkItems(parentGuid, roots.bookmark_bar.children, errorGatherer); - } - - // Importing bookmark menu items - if (roots.other.children && - roots.other.children.length > 0) { - // Bookmark menu - let parentGuid = PlacesUtils.bookmarks.menuGuid; - if (!MigrationUtils.isStartupMigration) { - parentGuid = - yield MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid); - } - yield insertBookmarkItems(parentGuid, roots.other.children, errorGatherer); - } - if (gotErrors) { - throw new Error("The migration included errors."); - } - }.bind(this)).then(() => aCallback(true), - () => aCallback(false)); - } - }; -} - -function GetHistoryResource(aProfileFolder) { - let historyFile = aProfileFolder.clone(); - historyFile.append("History"); - if (!historyFile.exists()) - return null; - - return { - type: MigrationUtils.resourceTypes.HISTORY, - - migrate(aCallback) { - Task.spawn(function* () { - const MAX_AGE_IN_DAYS = Services.prefs.getIntPref("browser.migrate.chrome.history.maxAgeInDays"); - const LIMIT = Services.prefs.getIntPref("browser.migrate.chrome.history.limit"); - - let query = "SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0"; - if (MAX_AGE_IN_DAYS) { - let maxAge = dateToChromeTime(Date.now() - MAX_AGE_IN_DAYS * 24 * 60 * 60 * 1000); - query += " AND last_visit_time > " + maxAge; - } - if (LIMIT) { - query += " ORDER BY last_visit_time DESC LIMIT " + LIMIT; - } - - let rows = - yield MigrationUtils.getRowsFromDBWithoutLocks(historyFile.path, "Chrome history", query); - let places = []; - for (let row of rows) { - try { - // if having typed_count, we changes transition type to typed. - let transType = PlacesUtils.history.TRANSITION_LINK; - if (row.getResultByName("typed_count") > 0) - transType = PlacesUtils.history.TRANSITION_TYPED; - - places.push({ - uri: NetUtil.newURI(row.getResultByName("url")), - title: row.getResultByName("title"), - visits: [{ - transitionType: transType, - visitDate: chromeTimeToDate( - row.getResultByName( - "last_visit_time")) * 1000, - }], - }); - } catch (e) { - Cu.reportError(e); - } - } - - if (places.length > 0) { - yield new Promise((resolve, reject) => { - MigrationUtils.insertVisitsWrapper(places, { - _success: false, - handleResult: function() { - // Importing any entry is considered a successful import. - this._success = true; - }, - handleError: function() {}, - handleCompletion: function() { - if (this._success) { - resolve(); - } else { - reject(new Error("Couldn't add visits")); - } - } - }); - }); - } - }).then(() => { aCallback(true) }, - ex => { - Cu.reportError(ex); - aCallback(false); - }); - } - }; -} - -function GetCookiesResource(aProfileFolder) { - let cookiesFile = aProfileFolder.clone(); - cookiesFile.append("Cookies"); - if (!cookiesFile.exists()) - return null; - - return { - type: MigrationUtils.resourceTypes.COOKIES, - - migrate: Task.async(function* (aCallback) { - // We don't support decrypting cookies yet so only import plaintext ones. - let rows = yield MigrationUtils.getRowsFromDBWithoutLocks(cookiesFile.path, "Chrome cookies", - `SELECT host_key, name, value, path, expires_utc, secure, httponly, encrypted_value - FROM cookies - WHERE length(encrypted_value) = 0`).catch(ex => { - Cu.reportError(ex); - aCallback(false); - }); - // If the promise was rejected we will have already called aCallback, - // so we can just return here. - if (!rows) { - return; - } - - for (let row of rows) { - let host_key = row.getResultByName("host_key"); - if (host_key.match(/^\./)) { - // 1st character of host_key may be ".", so we have to remove it - host_key = host_key.substr(1); - } - - try { - let expiresUtc = - chromeTimeToDate(row.getResultByName("expires_utc")) / 1000; - Services.cookies.add(host_key, - row.getResultByName("path"), - row.getResultByName("name"), - row.getResultByName("value"), - row.getResultByName("secure"), - row.getResultByName("httponly"), - false, - parseInt(expiresUtc), - {}); - } catch (e) { - Cu.reportError(e); - } - } - aCallback(true); - }), - }; -} - -function GetWindowsPasswordsResource(aProfileFolder) { - let loginFile = aProfileFolder.clone(); - loginFile.append("Login Data"); - if (!loginFile.exists()) - return null; - - return { - type: MigrationUtils.resourceTypes.PASSWORDS, - - migrate: Task.async(function* (aCallback) { - let rows = yield MigrationUtils.getRowsFromDBWithoutLocks(loginFile.path, "Chrome passwords", - `SELECT origin_url, action_url, username_element, username_value, - password_element, password_value, signon_realm, scheme, date_created, - times_used FROM logins WHERE blacklisted_by_user = 0`).catch(ex => { - Cu.reportError(ex); - aCallback(false); - }); - // If the promise was rejected we will have already called aCallback, - // so we can just return here. - if (!rows) { - return; - } - let crypto = new OSCrypto(); - - for (let row of rows) { - try { - let origin_url = NetUtil.newURI(row.getResultByName("origin_url")); - // Ignore entries for non-http(s)/ftp URLs because we likely can't - // use them anyway. - const kValidSchemes = new Set(["https", "http", "ftp"]); - if (!kValidSchemes.has(origin_url.scheme)) { - continue; - } - let loginInfo = { - username: row.getResultByName("username_value"), - password: crypto. - decryptData(crypto.arrayToString(row.getResultByName("password_value")), - null), - hostname: origin_url.prePath, - formSubmitURL: null, - httpRealm: null, - usernameElement: row.getResultByName("username_element"), - passwordElement: row.getResultByName("password_element"), - timeCreated: chromeTimeToDate(row.getResultByName("date_created") + 0).getTime(), - timesUsed: row.getResultByName("times_used") + 0, - }; - - switch (row.getResultByName("scheme")) { - case AUTH_TYPE.SCHEME_HTML: - let action_url = NetUtil.newURI(row.getResultByName("action_url")); - if (!kValidSchemes.has(action_url.scheme)) { - continue; // This continues the outer for loop. - } - loginInfo.formSubmitURL = action_url.prePath; - break; - case AUTH_TYPE.SCHEME_BASIC: - case AUTH_TYPE.SCHEME_DIGEST: - // signon_realm format is URIrealm, so we need remove URI - loginInfo.httpRealm = row.getResultByName("signon_realm") - .substring(loginInfo.hostname.length + 1); - break; - default: - throw new Error("Login data scheme type not supported: " + - row.getResultByName("scheme")); - } - MigrationUtils.insertLoginWrapper(loginInfo); - } catch (e) { - Cu.reportError(e); - } - } - crypto.finalize(); - aCallback(true); - }), - }; -} - -ChromeProfileMigrator.prototype.classDescription = "Chrome Profile Migrator"; -ChromeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome"; -ChromeProfileMigrator.prototype.classID = Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}"); - - -/** - * Chromium migration - **/ -function ChromiumProfileMigrator() { - let chromiumUserDataFolder = getDataFolder(["Chromium"], ["Chromium"], ["chromium"]); - this._chromeUserDataFolder = chromiumUserDataFolder.exists() ? chromiumUserDataFolder : null; -} - -ChromiumProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype); -ChromiumProfileMigrator.prototype.classDescription = "Chromium Profile Migrator"; -ChromiumProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chromium"; -ChromiumProfileMigrator.prototype.classID = Components.ID("{8cece922-9720-42de-b7db-7cef88cb07ca}"); - -var componentsArray = [ChromeProfileMigrator, ChromiumProfileMigrator]; - -/** - * Chrome Canary - * Not available on Linux - **/ -function CanaryProfileMigrator() { - let chromeUserDataFolder = getDataFolder(["Google", "Chrome SxS"], ["Google", "Chrome Canary"]); - this._chromeUserDataFolder = chromeUserDataFolder.exists() ? chromeUserDataFolder : null; -} -CanaryProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype); -CanaryProfileMigrator.prototype.classDescription = "Chrome Canary Profile Migrator"; -CanaryProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=canary"; -CanaryProfileMigrator.prototype.classID = Components.ID("{4bf85aa5-4e21-46ca-825f-f9c51a5e8c76}"); - -if (AppConstants.platform == "win" || AppConstants.platform == "macosx") { - componentsArray.push(CanaryProfileMigrator); -} - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory(componentsArray); |