home *** CD-ROM | disk | FTP | other *** search
/ Freelog 121 / FreelogMagazineJuilletAout2014-No121.iso / Internet / Waterfox / Waterfox.exe / components / IEProfileMigrator.js < prev    next >
Text File  |  2010-01-01  |  18KB  |  535 lines

  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2.  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  3.  * You can obtain one at http://mozilla.org/MPL/2.0/. */
  4.  
  5. "use strict";
  6.  
  7. const Cc = Components.classes;
  8. const Ci = Components.interfaces;
  9. const Cu = Components.utils;
  10. const Cr = Components.results;
  11.  
  12. const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main";
  13. const kRegMultiSz = 7;
  14.  
  15. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  16. Cu.import("resource://gre/modules/Services.jsm");
  17. Cu.import("resource://gre/modules/NetUtil.jsm");
  18. Cu.import("resource:///modules/MigrationUtils.jsm");
  19.  
  20. XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
  21.                                   "resource://gre/modules/PlacesUtils.jsm");
  22. XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
  23.                                   "resource://gre/modules/ctypes.jsm");
  24.  
  25. ////////////////////////////////////////////////////////////////////////////////
  26. //// Helpers.
  27.  
  28. let CtypesHelpers = {
  29.   _structs: {},
  30.   _functions: {},
  31.   _libs: {},
  32.  
  33.   /**
  34.    * Must be invoked once before first use of any of the provided helpers.
  35.    */
  36.   initialize: function CH_initialize() {
  37.     const WORD = ctypes.uint16_t;
  38.     const DWORD = ctypes.uint32_t;
  39.     const BOOL = ctypes.int;
  40.  
  41.     this._structs.SYSTEMTIME = new ctypes.StructType('SYSTEMTIME', [
  42.       {wYear: WORD},
  43.       {wMonth: WORD},
  44.       {wDayOfWeek: WORD},
  45.       {wDay: WORD},
  46.       {wHour: WORD},
  47.       {wMinute: WORD},
  48.       {wSecond: WORD},
  49.       {wMilliseconds: WORD}
  50.     ]);
  51.  
  52.     this._structs.FILETIME = new ctypes.StructType('FILETIME', [
  53.       {dwLowDateTime: DWORD},
  54.       {dwHighDateTime: DWORD}
  55.     ]);
  56.  
  57.     try {
  58.       this._libs.kernel32 = ctypes.open("Kernel32");
  59.       this._functions.FileTimeToSystemTime =
  60.         this._libs.kernel32.declare("FileTimeToSystemTime",
  61.                                     ctypes.default_abi,
  62.                                     BOOL,
  63.                                     this._structs.FILETIME.ptr,
  64.                                     this._structs.SYSTEMTIME.ptr);
  65.     } catch (ex) {
  66.       this.finalize();
  67.     }
  68.   },
  69.  
  70.   /**
  71.    * Must be invoked once after last use of any of the provided helpers.
  72.    */
  73.   finalize: function CH_finalize() {
  74.     this._structs = {};
  75.     this._functions = {};
  76.     for each (let lib in this._libs) {
  77.       try {
  78.         lib.close();
  79.       } catch (ex) {}
  80.     }
  81.     this._libs = {};
  82.   },
  83.  
  84.   /**
  85.    * Converts a FILETIME struct (2 DWORDS), to a SYSTEMTIME struct.
  86.    *
  87.    * @param aTimeHi
  88.    *        Least significant DWORD.
  89.    * @param aTimeLo
  90.    *        Most significant DWORD.
  91.    * @return a Date object representing the converted datetime.
  92.    */
  93.   fileTimeToDate: function CH_fileTimeToDate(aTimeHi, aTimeLo) {
  94.     let fileTime = this._structs.FILETIME();
  95.     fileTime.dwLowDateTime = aTimeLo;
  96.     fileTime.dwHighDateTime = aTimeHi;
  97.     let systemTime = this._structs.SYSTEMTIME();
  98.     let result = this._functions.FileTimeToSystemTime(fileTime.address(),
  99.                                                       systemTime.address());
  100.     if (result == 0)
  101.       throw new Error(ctypes.winLastError);
  102.  
  103.     return new Date(systemTime.wYear,
  104.                     systemTime.wMonth - 1,
  105.                     systemTime.wDay,
  106.                     systemTime.wHour,
  107.                     systemTime.wMinute,
  108.                     systemTime.wSecond,
  109.                     systemTime.wMilliseconds);
  110.   }
  111. };
  112.  
  113. /**
  114.  * Checks whether an host is an IP (v4 or v6) address.
  115.  *
  116.  * @param aHost
  117.  *        The host to check.
  118.  * @return whether aHost is an IP address.
  119.  */
  120. function hostIsIPAddress(aHost) {
  121.   try {
  122.     Services.eTLD.getBaseDomainFromHost(aHost);
  123.   } catch (e if e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {
  124.     return true;
  125.   } catch (e) {}
  126.   return false;
  127. }
  128.  
  129. /**
  130.  * Safely reads a value from the registry.
  131.  *
  132.  * @param aRoot
  133.  *        The root registry to use.
  134.  * @param aPath
  135.  *        The registry path to the key.
  136.  * @param aKey
  137.  *        The key name.
  138.  * @return The key value or undefined if it doesn't exist.  If the key is
  139.  *         a REG_MULTI_SZ, an array is returned.
  140.  */
  141. function readRegKey(aRoot, aPath, aKey) {
  142.   let registry = Cc["@mozilla.org/windows-registry-key;1"].
  143.                  createInstance(Ci.nsIWindowsRegKey);
  144.   try {
  145.     registry.open(aRoot, aPath, Ci.nsIWindowsRegKey.ACCESS_READ);
  146.     if (registry.hasValue(aKey)) {
  147.       let type = registry.getValueType(aKey);
  148.       switch (type) {
  149.         case kRegMultiSz:
  150.           // nsIWindowsRegKey doesn't support REG_MULTI_SZ type out of the box.
  151.           let str = registry.readStringValue(aKey);
  152.           return [v for each (v in str.split("\0")) if (v)];
  153.         case Ci.nsIWindowsRegKey.TYPE_STRING:
  154.           return registry.readStringValue(aKey);
  155.         case Ci.nsIWindowsRegKey.TYPE_INT:
  156.           return registry.readIntValue(aKey);
  157.         default:
  158.           throw new Error("Unsupported registry value.");
  159.       }
  160.     }
  161.   } catch (ex) {
  162.   } finally {
  163.     registry.close();
  164.   }
  165.   return undefined;
  166. };
  167.  
  168. ////////////////////////////////////////////////////////////////////////////////
  169. //// Resources
  170.  
  171. function Bookmarks() {
  172. }
  173.  
  174. Bookmarks.prototype = {
  175.   type: MigrationUtils.resourceTypes.BOOKMARKS,
  176.  
  177.   get exists() !!this._favoritesFolder,
  178.  
  179.   __favoritesFolder: null,
  180.   get _favoritesFolder() {
  181.     if (!this.__favoritesFolder) {
  182.       let favoritesFolder = Services.dirsvc.get("Favs", Ci.nsIFile);
  183.       if (favoritesFolder.exists() && favoritesFolder.isReadable())
  184.         this.__favoritesFolder = favoritesFolder;
  185.     }
  186.     return this.__favoritesFolder;
  187.   },
  188.  
  189.   __toolbarFolderName: null,
  190.   get _toolbarFolderName() {
  191.     if (!this.__toolbarFolderName) {
  192.       // Retrieve the name of IE's favorites subfolder that holds the bookmarks
  193.       // in the toolbar. This was previously stored in the registry and changed
  194.       // in IE7 to always be called "Links".
  195.       let folderName = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
  196.                                   "Software\\Microsoft\\Internet Explorer\\Toolbar",
  197.                                   "LinksFolderName");
  198.       this.__toolbarFolderName = folderName || "Links";
  199.     }
  200.     return this.__toolbarFolderName;
  201.   },
  202.  
  203.   migrate: function B_migrate(aCallback) {
  204.     PlacesUtils.bookmarks.runInBatchMode({
  205.       runBatched: (function migrateBatched() {
  206.         // Import to the bookmarks menu.
  207.         let destFolderId = PlacesUtils.bookmarksMenuFolderId;
  208.         if (!MigrationUtils.isStartupMigration) {
  209.           destFolderId =
  210.             MigrationUtils.createImportedBookmarksFolder("IE", destFolderId);
  211.         }
  212.  
  213.         this._migrateFolder(this._favoritesFolder, destFolderId);
  214.  
  215.         aCallback(true);
  216.       }).bind(this)
  217.     }, null);
  218.   },
  219.  
  220.   _migrateFolder: function B__migrateFolder(aSourceFolder, aDestFolderId) {
  221.     // TODO (bug 741993): the favorites order is stored in the Registry, at
  222.     // HCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Favorites
  223.     // Until we support it, bookmarks are imported in alphabetical order.
  224.     let entries = aSourceFolder.directoryEntries;
  225.     while (entries.hasMoreElements()) {
  226.       let entry = entries.getNext().QueryInterface(Ci.nsIFile);
  227.  
  228.       // Make sure that entry.path == entry.target to not follow .lnk folder
  229.       // shortcuts which could lead to infinite cycles.
  230.       if (entry.isDirectory() && entry.path == entry.target) {
  231.         let destFolderId;
  232.         if (entry.leafName == this._toolbarFolderName &&
  233.             entry.parent.equals(this._favoritesFolder)) {
  234.           // Import to the bookmarks toolbar.
  235.           destFolderId = PlacesUtils.toolbarFolderId;
  236.           if (!MigrationUtils.isStartupMigration) {
  237.             destFolderId =
  238.               MigrationUtils.createImportedBookmarksFolder("IE", destFolderId);
  239.           }
  240.         }
  241.         else {
  242.           // Import to a new folder.
  243.           destFolderId =
  244.             PlacesUtils.bookmarks.createFolder(aDestFolderId, entry.leafName,
  245.                                                PlacesUtils.bookmarks.DEFAULT_INDEX);
  246.         }
  247.  
  248.         if (entry.isReadable()) {
  249.           // Recursively import the folder.
  250.           this._migrateFolder(entry, destFolderId);
  251.         }
  252.       }
  253.       else {
  254.         // Strip the .url extension, to both check this is a valid link file,
  255.         // and get the associated title.
  256.         let matches = entry.leafName.match(/(.+)\.url$/i);
  257.         if (matches) {
  258.           let fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].
  259.                             getService(Ci.nsIFileProtocolHandler);
  260.           let uri = fileHandler.readURLFile(entry);
  261.           let title = matches[1];
  262.  
  263.           PlacesUtils.bookmarks.insertBookmark(aDestFolderId,
  264.                                                uri,
  265.                                                PlacesUtils.bookmarks.DEFAULT_INDEX,
  266.                                                title);
  267.         }
  268.       }
  269.     }
  270.   }
  271. };
  272.  
  273. function History() {
  274. }
  275.  
  276. History.prototype = {
  277.   type: MigrationUtils.resourceTypes.HISTORY,
  278.  
  279.   get exists() true,
  280.  
  281.   __typedURLs: null,
  282.   get _typedURLs() {
  283.     if (!this.__typedURLs) {
  284.       // The list of typed URLs is a sort of annotation stored in the registry.
  285.       // Currently, IE stores 25 entries and this value is not configurable,
  286.       // but we just keep reading up to the first non-existing entry to support
  287.       // possible future bumps of this limit.
  288.       this.__typedURLs = {};
  289.       let registry = Cc["@mozilla.org/windows-registry-key;1"].
  290.                      createInstance(Ci.nsIWindowsRegKey);
  291.       try {
  292.         registry.open(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
  293.                       "Software\\Microsoft\\Internet Explorer\\TypedURLs",
  294.                       Ci.nsIWindowsRegKey.ACCESS_READ);
  295.         for (let entry = 1; registry.hasValue("url" + entry); entry++) {
  296.           let url = registry.readStringValue("url" + entry);
  297.           this.__typedURLs[url] = true;
  298.         }
  299.       } catch (ex) {
  300.       } finally {
  301.         registry.close();
  302.       }
  303.     }
  304.     return this.__typedURLs;
  305.   },
  306.  
  307.   migrate: function H_migrate(aCallback) {
  308.     let places = [];
  309.     let historyEnumerator = Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"].
  310.                             createInstance(Ci.nsISimpleEnumerator);
  311.     while (historyEnumerator.hasMoreElements()) {
  312.       let entry = historyEnumerator.getNext().QueryInterface(Ci.nsIPropertyBag2);
  313.       let uri = entry.get("uri").QueryInterface(Ci.nsIURI);
  314.       // MSIE stores some types of URLs in its history that we don't handle,
  315.       // like HTMLHelp and others.  Since we don't properly map handling for
  316.       // all of them we just avoid importing them.
  317.       if (["http", "https", "ftp", "file"].indexOf(uri.scheme) == -1) {
  318.         continue;
  319.       }
  320.  
  321.       let title = entry.get("title");
  322.       // Embed visits have no title and don't need to be imported.
  323.       if (title.length == 0) {
  324.         continue;
  325.       }
  326.  
  327.       // The typed urls are already fixed-up, so we can use them for comparison.
  328.       let transitionType = this._typedURLs[uri.spec] ?
  329.                              Ci.nsINavHistoryService.TRANSITION_TYPED :
  330.                              Ci.nsINavHistoryService.TRANSITION_LINK;
  331.       let lastVisitTime = entry.get("time");
  332.  
  333.       places.push(
  334.         { uri: uri,
  335.           title: title,
  336.           visits: [{ transitionType: transitionType,
  337.                      visitDate: lastVisitTime }]
  338.         }
  339.       );
  340.     }
  341.  
  342.     // Check whether there is any history to import.
  343.     if (places.length == 0) {
  344.       aCallback(true);
  345.       return;
  346.     }
  347.  
  348.     PlacesUtils.asyncHistory.updatePlaces(places, {
  349.       _success: false,
  350.       handleResult: function() {
  351.         // Importing any entry is considered a successful import.
  352.         this._success = true;
  353.       },
  354.       handleError: function() {},
  355.       handleCompletion: function() {
  356.         aCallback(this._success);
  357.       }
  358.     });
  359.   }
  360. };
  361.  
  362. function Cookies() {
  363. }
  364.  
  365. Cookies.prototype = {
  366.   type: MigrationUtils.resourceTypes.COOKIES,
  367.  
  368.   get exists() !!this._cookiesFolder,
  369.  
  370.   __cookiesFolder: null,
  371.   get _cookiesFolder() {
  372.     // Cookies are stored in txt files, in a Cookies folder whose path varies
  373.     // across the different OS versions.  CookD takes care of most of these
  374.     // cases, though, in Windows Vista/7, UAC makes a difference.
  375.     // If UAC is enabled, the most common destination is CookD/Low.  Though,
  376.     // if the user runs the application in administrator mode or disables UAC,
  377.     // cookies are stored in the original CookD destination.  Cause running the
  378.     // browser in administrator mode is unsafe and discouraged, we just care
  379.     // about the UAC state.
  380.     if (!this.__cookiesFolder) {
  381.       let cookiesFolder = Services.dirsvc.get("CookD", Ci.nsIFile);
  382.       if (cookiesFolder.exists() && cookiesFolder.isReadable()) {
  383.         // Check if UAC is enabled.
  384.         if (Services.appinfo.QueryInterface(Ci.nsIWinAppHelper).userCanElevate) {
  385.           cookiesFolder.append("Low");
  386.         }
  387.         this.__cookiesFolder = cookiesFolder;
  388.       }
  389.     }
  390.     return this.__cookiesFolder;
  391.   },
  392.  
  393.   migrate: function C_migrate(aCallback) {
  394.     CtypesHelpers.initialize();
  395.  
  396.     let cookiesGenerator = (function genCookie() {
  397.       let success = false;
  398.       let entries = this._cookiesFolder.directoryEntries;
  399.       while (entries.hasMoreElements()) {
  400.         let entry = entries.getNext().QueryInterface(Ci.nsIFile);
  401.         // Skip eventual bogus entries.
  402.         if (!entry.isFile() || !/\.txt$/.test(entry.leafName))
  403.           continue;
  404.  
  405.         this._readCookieFile(entry, function(aSuccess) {
  406.           // Importing even a single cookie file is considered a success.
  407.           if (aSuccess)
  408.             success = true;
  409.           try {
  410.             cookiesGenerator.next();
  411.           } catch (ex) {}
  412.         });
  413.  
  414.         yield undefined;
  415.       }
  416.  
  417.       CtypesHelpers.finalize();
  418.  
  419.       aCallback(success);
  420.     }).apply(this);
  421.     cookiesGenerator.next();
  422.   },
  423.  
  424.   _readCookieFile: function C__readCookieFile(aFile, aCallback) {
  425.     let fileReader = Cc["@mozilla.org/files/filereader;1"].
  426.                      createInstance(Ci.nsIDOMFileReader);
  427.     fileReader.addEventListener("loadend", (function onLoadEnd() {
  428.       fileReader.removeEventListener("loadend", onLoadEnd, false);
  429.  
  430.       if (fileReader.readyState != fileReader.DONE) {
  431.         Cu.reportError("Could not read cookie contents: " + fileReader.error);
  432.         aCallback(false);
  433.         return;
  434.       }
  435.  
  436.       let success = true;
  437.       try {
  438.         this._parseCookieBuffer(fileReader.result);
  439.       } catch (ex) {
  440.         Components.utils.reportError("Unable to migrate cookie: " + ex);
  441.         success = false;
  442.       } finally {
  443.         aCallback(success);
  444.       }
  445.     }).bind(this), false);
  446.     fileReader.readAsText(File(aFile));
  447.   },
  448.  
  449.   /**
  450.    * Parses a cookie file buffer and returns an array of the contained cookies.
  451.    *
  452.    * The cookie file format is a newline-separated-values with a "*" used as
  453.    * delimeter between multiple records.
  454.    * Each cookie has the following fields:
  455.    *  - name
  456.    *  - value
  457.    *  - host/path
  458.    *  - flags
  459.    *  - Expiration time most significant integer
  460.    *  - Expiration time least significant integer
  461.    *  - Creation time most significant integer
  462.    *  - Creation time least significant integer
  463.    *  - Record delimiter "*"
  464.    *
  465.    * @note All the times are in FILETIME format.
  466.    */
  467.   _parseCookieBuffer: function C__parseCookieBuffer(aTextBuffer) {
  468.     // Note the last record is an empty string.
  469.     let records = [r for each (r in aTextBuffer.split("*\n")) if (r)];
  470.     for (let record of records) {
  471.       let [name, value, hostpath, flags,
  472.            expireTimeLo, expireTimeHi] = record.split("\n");
  473.  
  474.       // IE stores deleted cookies with a zero-length value, skip them.
  475.       if (value.length == 0)
  476.         continue;
  477.  
  478.       let hostLen = hostpath.indexOf("/");
  479.       let host = hostpath.substr(0, hostLen);
  480.  
  481.       // For a non-null domain, assume it's what Mozilla considers
  482.       // a domain cookie.  See bug 222343.
  483.       if (host.length > 0) {
  484.         // Fist delete any possible extant matching host cookie.
  485.         Services.cookies.remove(host, name, path, false);
  486.         // Now make it a domain cookie.
  487.         if (host[0] != "." && !hostIsIPAddress(host))
  488.           host = "." + host;
  489.       }
  490.  
  491.       let path = hostpath.substr(hostLen);
  492.       let expireTime = CtypesHelpers.fileTimeToDate(Number(expireTimeHi),
  493.                                                     Number(expireTimeLo));
  494.       Services.cookies.add(host,
  495.                            path,
  496.                            name,
  497.                            value,
  498.                            Number(flags) & 0x1, // secure
  499.                            false, // httpOnly
  500.                            false, // session
  501.                            expireTime);
  502.     }
  503.   }
  504. };
  505.  
  506. function Settings() {
  507. }
  508.  
  509. Settings.prototype = {
  510.   type: MigrationUtils.resourceTypes.SETTINGS,
  511.  
  512.   get exists() true,
  513.  
  514.   migrate: function S_migrate(aCallback) {
  515.     // Converts from yes/no to a boolean.
  516.     function yesNoToBoolean(v) v == "yes";
  517.  
  518.     // Converts source format like "en-us,ar-kw;q=0.7,ar-om;q=0.3" into
  519.     // destination format like "en-us, ar-kw, ar-om".
  520.     // Final string is sorted by quality (q=) param.
  521.     function parseAcceptLanguageList(v) {
  522.       return v.match(/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/gi)
  523.               .sort(function (a , b) {
  524.                 let qA = parseFloat(a.split(";q=")[1]) || 1.0;
  525.                 let qB = parseFloat(b.split(";q=")[1]) || 1.0;
  526.                 return qA < qB ? 1 : qA == qB ? 0 : -1;
  527.               })
  528.               .map(function(a) a.split(";")[0]B =BlK qAa\     letens qA let
  529.   i =Bl*(;\s*q\s*=\s*(1|0\.[0-9]+))?/gi)
  530.   (fileReader.result);
  531.       } ca)v, == q,succesan} ca)v, == q,lMcesa;q=0.at\GenookiesFod == ]oSor3.
  532. Y
  533.     }
  534.     thi ii1 return new i1ret?ext().QueryIB ? 1 : qA == qB ? 0 : -1;
  535.