tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MSMigrationUtils.sys.mjs (24019B)


      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 import { ctypes } from "resource://gre/modules/ctypes.sys.mjs";
      6 import { MigrationUtils } from "resource:///modules/MigrationUtils.sys.mjs";
      7 
      8 const lazy = {};
      9 
     10 ChromeUtils.defineESModuleGetters(lazy, {
     11  PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
     12  WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
     13 });
     14 
     15 const EDGE_FAVORITES = "AC\\MicrosoftEdge\\User\\Default\\Favorites";
     16 const FREE_CLOSE_FAILED = 0;
     17 const INTERNET_EXPLORER_EDGE_GUID = [
     18  0x3ccd5499, 0x4b1087a8, 0x886015a2, 0x553bdd88,
     19 ];
     20 const RESULT_SUCCESS = 0;
     21 const VAULT_ENUMERATE_ALL_ITEMS = 512;
     22 const WEB_CREDENTIALS_VAULT_ID = [
     23  0x4bf4c442, 0x41a09b8a, 0x4add80b3, 0x28db4d70,
     24 ];
     25 
     26 const wintypes = {
     27  BOOL: ctypes.int,
     28  DWORD: ctypes.uint32_t,
     29  DWORDLONG: ctypes.uint64_t,
     30  CHAR: ctypes.char,
     31  PCHAR: ctypes.char.ptr,
     32  LPCWSTR: ctypes.char16_t.ptr,
     33  PDWORD: ctypes.uint32_t.ptr,
     34  VOIDP: ctypes.voidptr_t,
     35  WORD: ctypes.uint16_t,
     36 };
     37 
     38 // TODO: Bug 1202978 - Refactor MSMigrationUtils ctypes helpers
     39 function CtypesKernelHelpers() {
     40  this._structs = {};
     41  this._functions = {};
     42  this._libs = {};
     43 
     44  this._structs.SYSTEMTIME = new ctypes.StructType("SYSTEMTIME", [
     45    { wYear: wintypes.WORD },
     46    { wMonth: wintypes.WORD },
     47    { wDayOfWeek: wintypes.WORD },
     48    { wDay: wintypes.WORD },
     49    { wHour: wintypes.WORD },
     50    { wMinute: wintypes.WORD },
     51    { wSecond: wintypes.WORD },
     52    { wMilliseconds: wintypes.WORD },
     53  ]);
     54 
     55  this._structs.FILETIME = new ctypes.StructType("FILETIME", [
     56    { dwLowDateTime: wintypes.DWORD },
     57    { dwHighDateTime: wintypes.DWORD },
     58  ]);
     59 
     60  try {
     61    this._libs.kernel32 = ctypes.open("Kernel32");
     62 
     63    this._functions.FileTimeToSystemTime = this._libs.kernel32.declare(
     64      "FileTimeToSystemTime",
     65      ctypes.winapi_abi,
     66      wintypes.BOOL,
     67      this._structs.FILETIME.ptr,
     68      this._structs.SYSTEMTIME.ptr
     69    );
     70  } catch (ex) {
     71    this.finalize();
     72  }
     73 }
     74 
     75 CtypesKernelHelpers.prototype = {
     76  /**
     77   * Must be invoked once after last use of any of the provided helpers.
     78   */
     79  finalize() {
     80    this._structs = {};
     81    this._functions = {};
     82    for (let key in this._libs) {
     83      let lib = this._libs[key];
     84      try {
     85        lib.close();
     86      } catch (ex) {}
     87    }
     88    this._libs = {};
     89  },
     90 
     91  /**
     92   * Converts a FILETIME struct (2 DWORDS), to a SYSTEMTIME struct,
     93   * and then deduces the number of seconds since the epoch (which
     94   * is the data we want for the cookie expiry date).
     95   *
     96   * @param {number} aTimeHi
     97   *        Least significant DWORD.
     98   * @param {number} aTimeLo
     99   *        Most significant DWORD.
    100   * @returns {number} the number of seconds since the epoch
    101   */
    102  fileTimeToSecondsSinceEpoch(aTimeHi, aTimeLo) {
    103    let fileTime = this._structs.FILETIME();
    104    fileTime.dwLowDateTime = aTimeLo;
    105    fileTime.dwHighDateTime = aTimeHi;
    106    let systemTime = this._structs.SYSTEMTIME();
    107    let result = this._functions.FileTimeToSystemTime(
    108      fileTime.address(),
    109      systemTime.address()
    110    );
    111    if (result == 0) {
    112      throw new Error(ctypes.winLastError);
    113    }
    114 
    115    // System time is in UTC, so we use Date.UTC to get milliseconds from epoch,
    116    // then divide by 1000 to get seconds, and round down:
    117    return Math.floor(
    118      Date.UTC(
    119        systemTime.wYear,
    120        systemTime.wMonth - 1,
    121        systemTime.wDay,
    122        systemTime.wHour,
    123        systemTime.wMinute,
    124        systemTime.wSecond,
    125        systemTime.wMilliseconds
    126      ) / 1000
    127    );
    128  },
    129 };
    130 
    131 function CtypesVaultHelpers() {
    132  this._structs = {};
    133  this._functions = {};
    134 
    135  this._structs.GUID = new ctypes.StructType("GUID", [
    136    { id: wintypes.DWORD.array(4) },
    137  ]);
    138 
    139  this._structs.VAULT_ITEM_ELEMENT = new ctypes.StructType(
    140    "VAULT_ITEM_ELEMENT",
    141    [
    142      // not documented
    143      { schemaElementId: wintypes.DWORD },
    144      // not documented
    145      { unknown1: wintypes.DWORD },
    146      // vault type
    147      { type: wintypes.DWORD },
    148      // not documented
    149      { unknown2: wintypes.DWORD },
    150      // value of the item
    151      { itemValue: wintypes.LPCWSTR },
    152      // not documented
    153      { unknown3: wintypes.CHAR.array(12) },
    154    ]
    155  );
    156 
    157  this._structs.VAULT_ELEMENT = new ctypes.StructType("VAULT_ELEMENT", [
    158    // vault item schemaId
    159    { schemaId: this._structs.GUID },
    160    // a pointer to the name of the browser VAULT_ITEM_ELEMENT
    161    { pszCredentialFriendlyName: wintypes.LPCWSTR },
    162    // a pointer to the url VAULT_ITEM_ELEMENT
    163    { pResourceElement: this._structs.VAULT_ITEM_ELEMENT.ptr },
    164    // a pointer to the username VAULT_ITEM_ELEMENT
    165    { pIdentityElement: this._structs.VAULT_ITEM_ELEMENT.ptr },
    166    // not documented
    167    { pAuthenticatorElement: this._structs.VAULT_ITEM_ELEMENT.ptr },
    168    // not documented
    169    { pPackageSid: this._structs.VAULT_ITEM_ELEMENT.ptr },
    170    // time stamp in local format
    171    { lowLastModified: wintypes.DWORD },
    172    { highLastModified: wintypes.DWORD },
    173    // not documented
    174    { flags: wintypes.DWORD },
    175    // not documented
    176    { dwPropertiesCount: wintypes.DWORD },
    177    // not documented
    178    { pPropertyElements: this._structs.VAULT_ITEM_ELEMENT.ptr },
    179  ]);
    180 
    181  try {
    182    this._vaultcliLib = ctypes.open("vaultcli.dll");
    183 
    184    this._functions.VaultOpenVault = this._vaultcliLib.declare(
    185      "VaultOpenVault",
    186      ctypes.winapi_abi,
    187      wintypes.DWORD,
    188      // GUID
    189      this._structs.GUID.ptr,
    190      // Flags
    191      wintypes.DWORD,
    192      // Vault Handle
    193      wintypes.VOIDP.ptr
    194    );
    195    this._functions.VaultEnumerateItems = this._vaultcliLib.declare(
    196      "VaultEnumerateItems",
    197      ctypes.winapi_abi,
    198      wintypes.DWORD,
    199      // Vault Handle
    200      wintypes.VOIDP,
    201      // Flags
    202      wintypes.DWORD,
    203      // Items Count
    204      wintypes.PDWORD,
    205      // Items
    206      ctypes.voidptr_t
    207    );
    208    this._functions.VaultCloseVault = this._vaultcliLib.declare(
    209      "VaultCloseVault",
    210      ctypes.winapi_abi,
    211      wintypes.DWORD,
    212      // Vault Handle
    213      wintypes.VOIDP
    214    );
    215    this._functions.VaultGetItem = this._vaultcliLib.declare(
    216      "VaultGetItem",
    217      ctypes.winapi_abi,
    218      wintypes.DWORD,
    219      // Vault Handle
    220      wintypes.VOIDP,
    221      // Schema Id
    222      this._structs.GUID.ptr,
    223      // Resource
    224      this._structs.VAULT_ITEM_ELEMENT.ptr,
    225      // Identity
    226      this._structs.VAULT_ITEM_ELEMENT.ptr,
    227      // Package Sid
    228      this._structs.VAULT_ITEM_ELEMENT.ptr,
    229      // HWND Owner
    230      wintypes.DWORD,
    231      // Flags
    232      wintypes.DWORD,
    233      // Items
    234      this._structs.VAULT_ELEMENT.ptr.ptr
    235    );
    236    this._functions.VaultFree = this._vaultcliLib.declare(
    237      "VaultFree",
    238      ctypes.winapi_abi,
    239      wintypes.DWORD,
    240      // Memory
    241      this._structs.VAULT_ELEMENT.ptr
    242    );
    243  } catch (ex) {
    244    this.finalize();
    245  }
    246 }
    247 
    248 CtypesVaultHelpers.prototype = {
    249  /**
    250   * Must be invoked once after last use of any of the provided helpers.
    251   */
    252  finalize() {
    253    this._structs = {};
    254    this._functions = {};
    255    try {
    256      this._vaultcliLib.close();
    257    } catch (ex) {}
    258    this._vaultcliLib = null;
    259  },
    260 };
    261 
    262 var gEdgeDir;
    263 function getEdgeLocalDataFolder() {
    264  if (gEdgeDir) {
    265    return gEdgeDir.clone();
    266  }
    267  let packages = Services.dirsvc.get("LocalAppData", Ci.nsIFile);
    268  packages.append("Packages");
    269  let edgeDir = packages.clone();
    270  edgeDir.append("Microsoft.MicrosoftEdge_8wekyb3d8bbwe");
    271  try {
    272    if (edgeDir.exists() && edgeDir.isReadable() && edgeDir.isDirectory()) {
    273      gEdgeDir = edgeDir;
    274      return edgeDir.clone();
    275    }
    276 
    277    // Let's try the long way:
    278    let dirEntries = packages.directoryEntries;
    279    while (dirEntries.hasMoreElements()) {
    280      let subDir = dirEntries.nextFile;
    281      if (
    282        subDir.leafName.startsWith("Microsoft.MicrosoftEdge") &&
    283        subDir.isReadable() &&
    284        subDir.isDirectory()
    285      ) {
    286        gEdgeDir = subDir;
    287        return subDir.clone();
    288      }
    289    }
    290  } catch (ex) {
    291    console.error(
    292      "Exception trying to find the Edge favorites directory: ",
    293      ex
    294    );
    295  }
    296  return null;
    297 }
    298 
    299 function Bookmarks(migrationType) {
    300  this._migrationType = migrationType;
    301 }
    302 
    303 Bookmarks.prototype = {
    304  type: MigrationUtils.resourceTypes.BOOKMARKS,
    305 
    306  get exists() {
    307    return !!this._favoritesFolder;
    308  },
    309 
    310  get importedAppLabel() {
    311    return this._migrationType == MSMigrationUtils.MIGRATION_TYPE_IE
    312      ? "IE"
    313      : "Edge";
    314  },
    315 
    316  __favoritesFolder: null,
    317  get _favoritesFolder() {
    318    if (!this.__favoritesFolder) {
    319      if (this._migrationType == MSMigrationUtils.MIGRATION_TYPE_IE) {
    320        let favoritesFolder = Services.dirsvc.get("Favs", Ci.nsIFile);
    321        if (favoritesFolder.exists() && favoritesFolder.isReadable()) {
    322          this.__favoritesFolder = favoritesFolder;
    323        }
    324      } else if (this._migrationType == MSMigrationUtils.MIGRATION_TYPE_EDGE) {
    325        let edgeDir = getEdgeLocalDataFolder();
    326        if (edgeDir) {
    327          edgeDir.appendRelativePath(EDGE_FAVORITES);
    328          if (
    329            edgeDir.exists() &&
    330            edgeDir.isReadable() &&
    331            edgeDir.isDirectory()
    332          ) {
    333            this.__favoritesFolder = edgeDir;
    334          }
    335        }
    336      }
    337    }
    338    return this.__favoritesFolder;
    339  },
    340 
    341  __toolbarFolderName: null,
    342  get _toolbarFolderName() {
    343    if (!this.__toolbarFolderName) {
    344      if (this._migrationType == MSMigrationUtils.MIGRATION_TYPE_IE) {
    345        // Retrieve the name of IE's favorites subfolder that holds the bookmarks
    346        // in the toolbar. This was previously stored in the registry and changed
    347        // in IE7 to always be called "Links".
    348        let folderName = lazy.WindowsRegistry.readRegKey(
    349          Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
    350          "Software\\Microsoft\\Internet Explorer\\Toolbar",
    351          "LinksFolderName"
    352        );
    353        this.__toolbarFolderName = folderName || "Links";
    354      } else {
    355        this.__toolbarFolderName = "Links";
    356      }
    357    }
    358    return this.__toolbarFolderName;
    359  },
    360 
    361  migrate: function B_migrate(aCallback) {
    362    return (async () => {
    363      // Import to the bookmarks menu.
    364      let folderGuid = lazy.PlacesUtils.bookmarks.menuGuid;
    365      await this._migrateFolder(this._favoritesFolder, folderGuid);
    366    })().then(
    367      () => aCallback(true),
    368      e => {
    369        console.error(e);
    370        aCallback(false);
    371      }
    372    );
    373  },
    374 
    375  async _migrateFolder(aSourceFolder, aDestFolderGuid) {
    376    let { bookmarks, favicons } =
    377      await this._getBookmarksInFolder(aSourceFolder);
    378    if (!bookmarks.length) {
    379      return;
    380    }
    381 
    382    await MigrationUtils.insertManyBookmarksWrapper(bookmarks, aDestFolderGuid);
    383    MigrationUtils.insertManyFavicons(favicons).catch(console.error);
    384  },
    385 
    386  /**
    387   * Iterates through a bookmark folder to obtain whatever information from each bookmark is needed elsewhere. This function also recurses into child folders.
    388   *
    389   * @param {nsIFile} aSourceFolder the folder to search for bookmarks and subfolders.
    390   * @returns {Promise<object>} An object with the following properties:
    391   * {Object[]} bookmarks:
    392   *   An array of Objects with these properties:
    393   *     {number} type: A type mapping to one of the types in nsINavBookmarksService
    394   *     {string} title: The title of the bookmark
    395   *     {Object[]} children: An array of objects with the same structure as this one.
    396   *
    397   * {Object[]} favicons
    398   *   An array of Objects with these properties:
    399   *     {Uint8Array} faviconData: The binary data of a favicon
    400   *     {nsIURI} uri: The URI of the associated bookmark
    401   */
    402  async _getBookmarksInFolder(aSourceFolder) {
    403    // TODO (bug 741993): the favorites order is stored in the Registry, at
    404    // HCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Favorites
    405    // for IE, and in a similar location for Edge.
    406    // Until we support it, bookmarks are imported in alphabetical order.
    407    let entries = aSourceFolder.directoryEntries;
    408    let rv = [];
    409    let favicons = [];
    410    while (entries.hasMoreElements()) {
    411      let entry = entries.nextFile;
    412      try {
    413        // Make sure that entry.path == entry.target to not follow .lnk folder
    414        // shortcuts which could lead to infinite cycles.
    415        // Don't use isSymlink(), since it would throw for invalid
    416        // lnk files pointing to URLs or to unresolvable paths.
    417        if (entry.path == entry.target && entry.isDirectory()) {
    418          let isBookmarksFolder =
    419            entry.leafName == this._toolbarFolderName &&
    420            entry.parent.equals(this._favoritesFolder);
    421          if (isBookmarksFolder && entry.isReadable()) {
    422            // Import to the bookmarks toolbar.
    423            let folderGuid = lazy.PlacesUtils.bookmarks.toolbarGuid;
    424            await this._migrateFolder(entry, folderGuid);
    425          } else if (entry.isReadable()) {
    426            let { bookmarks: childBookmarks, favicons: childFavicons } =
    427              await this._getBookmarksInFolder(entry);
    428            favicons = favicons.concat(childFavicons);
    429            rv.push({
    430              type: lazy.PlacesUtils.bookmarks.TYPE_FOLDER,
    431              title: entry.leafName,
    432              children: childBookmarks,
    433            });
    434          }
    435        } else {
    436          // Strip the .url extension, to both check this is a valid link file,
    437          // and get the associated title.
    438          let matches = entry.leafName.match(/(.+)\.url$/i);
    439          if (matches) {
    440            let fileHandler = Cc[
    441              "@mozilla.org/network/protocol;1?name=file"
    442            ].getService(Ci.nsIFileProtocolHandler);
    443            let uri = fileHandler.readURLFile(entry);
    444            // Silently failing in the event that the alternative data stream for the favicon doesn't exist
    445            try {
    446              let faviconData = await IOUtils.read(entry.path + ":favicon");
    447              favicons.push({ faviconData, uri });
    448            } catch {}
    449 
    450            rv.push({ url: uri, title: matches[1] });
    451          }
    452        }
    453      } catch (ex) {
    454        console.error(
    455          "Unable to import ",
    456          this.importedAppLabel,
    457          " favorite (",
    458          entry.leafName,
    459          "): ",
    460          ex
    461        );
    462      }
    463    }
    464    return { bookmarks: rv, favicons };
    465  },
    466 };
    467 
    468 function getTypedURLs(registryKeyPath) {
    469  // The list of typed URLs is a sort of annotation stored in the registry.
    470  // The number of entries stored is not UI-configurable, but has changed
    471  // between different Windows versions. We just keep reading up to the first
    472  // non-existing entry to support different limits / states of the registry.
    473  let typedURLs = new Map();
    474  let typedURLKey = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
    475    Ci.nsIWindowsRegKey
    476  );
    477  let typedURLTimeKey = Cc[
    478    "@mozilla.org/windows-registry-key;1"
    479  ].createInstance(Ci.nsIWindowsRegKey);
    480  let cTypes = new CtypesKernelHelpers();
    481  try {
    482    try {
    483      typedURLKey.open(
    484        Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
    485        registryKeyPath + "\\TypedURLs",
    486        Ci.nsIWindowsRegKey.ACCESS_READ
    487      );
    488    } catch (ex) {
    489      // Ignore errors opening this registry key - if it doesn't work, there's
    490      // no way we can get useful info here.
    491      return typedURLs;
    492    }
    493    try {
    494      typedURLTimeKey.open(
    495        Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
    496        registryKeyPath + "\\TypedURLsTime",
    497        Ci.nsIWindowsRegKey.ACCESS_READ
    498      );
    499    } catch (ex) {
    500      typedURLTimeKey = null;
    501    }
    502    let entryName;
    503    for (
    504      let entry = 1;
    505      typedURLKey.hasValue((entryName = "url" + entry));
    506      entry++
    507    ) {
    508      let url = typedURLKey.readStringValue(entryName);
    509      // If we can't get a date for whatever reason, default to 6 months ago
    510      let timeTyped = Date.now() - 31536000 / 2;
    511      if (typedURLTimeKey && typedURLTimeKey.hasValue(entryName)) {
    512        let urlTime = "";
    513        try {
    514          urlTime = typedURLTimeKey.readBinaryValue(entryName);
    515        } catch (ex) {
    516          console.error("Couldn't read url time for ", entryName);
    517        }
    518        if (urlTime.length == 8) {
    519          let urlTimeHex = [];
    520          for (let i = 0; i < 8; i++) {
    521            let c = urlTime.charCodeAt(i).toString(16);
    522            if (c.length == 1) {
    523              c = "0" + c;
    524            }
    525            urlTimeHex.unshift(c);
    526          }
    527          try {
    528            let hi = parseInt(urlTimeHex.slice(0, 4).join(""), 16);
    529            let lo = parseInt(urlTimeHex.slice(4, 8).join(""), 16);
    530            // Convert to seconds since epoch:
    531            let secondsSinceEpoch = cTypes.fileTimeToSecondsSinceEpoch(hi, lo);
    532 
    533            // If the date is very far in the past, just use the default
    534            if (secondsSinceEpoch > Date.now() / 1000000) {
    535              // Callers expect PRTime, which is microseconds since epoch:
    536              timeTyped = secondsSinceEpoch * 1000;
    537            }
    538          } catch (ex) {
    539            // Ignore conversion exceptions. Callers will have to deal
    540            // with the fallback value.
    541          }
    542        }
    543      }
    544      typedURLs.set(url, timeTyped * 1000);
    545    }
    546  } catch (ex) {
    547    console.error("Error reading typed URL history: ", ex);
    548  } finally {
    549    if (typedURLKey) {
    550      typedURLKey.close();
    551    }
    552    if (typedURLTimeKey) {
    553      typedURLTimeKey.close();
    554    }
    555    cTypes.finalize();
    556  }
    557  return typedURLs;
    558 }
    559 
    560 // Migrator for form passwords
    561 function WindowsVaultFormPasswords() {}
    562 
    563 WindowsVaultFormPasswords.prototype = {
    564  type: MigrationUtils.resourceTypes.PASSWORDS,
    565 
    566  get exists() {
    567    // check if there are passwords available for migration.
    568    return this.migrate(() => {}, true);
    569  },
    570 
    571  /**
    572   * If aOnlyCheckExists is false, import the form passwords from the vault
    573   * and then call the aCallback.
    574   * Otherwise, check if there are passwords in the vault.
    575   *
    576   * @param {Function} aCallback - a callback called when the migration is done.
    577   * @param {boolean} [aOnlyCheckExists=false] - if aOnlyCheckExists is true, just check if there are some
    578   * passwords to migrate. Import the passwords from the vault and call aCallback otherwise.
    579   * @returns {boolean} true if there are passwords in the vault and aOnlyCheckExists is set to true,
    580   * false if there is no password in the vault and aOnlyCheckExists is set to true, undefined if
    581   * aOnlyCheckExists is set to false.
    582   */
    583  async migrate(aCallback, aOnlyCheckExists = false) {
    584    // check if the vault item is an IE/Edge one
    585    function _isIEOrEdgePassword(id) {
    586      return (
    587        id[0] == INTERNET_EXPLORER_EDGE_GUID[0] &&
    588        id[1] == INTERNET_EXPLORER_EDGE_GUID[1] &&
    589        id[2] == INTERNET_EXPLORER_EDGE_GUID[2] &&
    590        id[3] == INTERNET_EXPLORER_EDGE_GUID[3]
    591      );
    592    }
    593 
    594    let ctypesVaultHelpers = new CtypesVaultHelpers();
    595    let ctypesKernelHelpers = new CtypesKernelHelpers();
    596    let migrationSucceeded = true;
    597    let successfulVaultOpen = false;
    598    let error, vault;
    599    try {
    600      // web credentials vault id
    601      let vaultGuid = new ctypesVaultHelpers._structs.GUID(
    602        WEB_CREDENTIALS_VAULT_ID
    603      );
    604      error = new wintypes.DWORD();
    605      // web credentials vault
    606      vault = new wintypes.VOIDP();
    607      // open the current vault using the vaultGuid
    608      error = ctypesVaultHelpers._functions.VaultOpenVault(
    609        vaultGuid.address(),
    610        0,
    611        vault.address()
    612      );
    613      if (error != RESULT_SUCCESS) {
    614        throw new Error("Unable to open Vault: " + error);
    615      }
    616      successfulVaultOpen = true;
    617 
    618      let item = new ctypesVaultHelpers._structs.VAULT_ELEMENT.ptr();
    619      let itemCount = new wintypes.DWORD();
    620      // enumerate all the available items. This api is going to return a table of all the
    621      // available items and item is going to point to the first element of this table.
    622      error = ctypesVaultHelpers._functions.VaultEnumerateItems(
    623        vault,
    624        VAULT_ENUMERATE_ALL_ITEMS,
    625        itemCount.address(),
    626        item.address()
    627      );
    628      if (error != RESULT_SUCCESS) {
    629        throw new Error("Unable to enumerate Vault items: " + error);
    630      }
    631 
    632      let logins = [];
    633      for (let j = 0; j < itemCount.value; j++) {
    634        try {
    635          // if it's not an ie/edge password, skip it
    636          if (!_isIEOrEdgePassword(item.contents.schemaId.id)) {
    637            continue;
    638          }
    639          let url =
    640            item.contents.pResourceElement.contents.itemValue.readString();
    641          let realURL = URL.parse(url);
    642          if (
    643            !realURL ||
    644            !["http:", "https:", "ftp:"].includes(realURL.protocol)
    645          ) {
    646            // Ignore items for non-URLs or URLs that aren't HTTP(S)/FTP
    647            continue;
    648          }
    649 
    650          // if aOnlyCheckExists is set to true, the purpose of the call is to return true if there is at
    651          // least a password which is true in this case because a password was by now already found
    652          if (aOnlyCheckExists) {
    653            return true;
    654          }
    655          let username =
    656            item.contents.pIdentityElement.contents.itemValue.readString();
    657          // the current login credential object
    658          let credential = new ctypesVaultHelpers._structs.VAULT_ELEMENT.ptr();
    659          error = ctypesVaultHelpers._functions.VaultGetItem(
    660            vault,
    661            item.contents.schemaId.address(),
    662            item.contents.pResourceElement,
    663            item.contents.pIdentityElement,
    664            null,
    665            0,
    666            0,
    667            credential.address()
    668          );
    669          if (error != RESULT_SUCCESS) {
    670            throw new Error("Unable to get item: " + error);
    671          }
    672 
    673          let password =
    674            credential.contents.pAuthenticatorElement.contents.itemValue.readString();
    675          let creation = Date.now();
    676          try {
    677            // login manager wants time in milliseconds since epoch, so convert
    678            // to seconds since epoch and multiply to get milliseconds:
    679            creation =
    680              ctypesKernelHelpers.fileTimeToSecondsSinceEpoch(
    681                item.contents.highLastModified,
    682                item.contents.lowLastModified
    683              ) * 1000;
    684          } catch (ex) {
    685            // Ignore exceptions in the dates and just create the login for right now.
    686          }
    687          // create a new login
    688          logins.push({
    689            username,
    690            password,
    691            origin: realURL.URI.prePath,
    692            timeCreated: creation,
    693          });
    694 
    695          // close current item
    696          error = ctypesVaultHelpers._functions.VaultFree(credential);
    697          if (error == FREE_CLOSE_FAILED) {
    698            throw new Error("Unable to free item: " + error);
    699          }
    700        } catch (e) {
    701          migrationSucceeded = false;
    702          console.error(e);
    703        } finally {
    704          // move to next item in the table returned by VaultEnumerateItems
    705          item = item.increment();
    706        }
    707      }
    708 
    709      if (logins.length) {
    710        await MigrationUtils.insertLoginsWrapper(logins);
    711      }
    712    } catch (e) {
    713      console.error(e);
    714      migrationSucceeded = false;
    715    } finally {
    716      if (successfulVaultOpen) {
    717        // close current vault
    718        error = ctypesVaultHelpers._functions.VaultCloseVault(vault);
    719        if (error == FREE_CLOSE_FAILED) {
    720          console.error("Unable to close vault: ", error);
    721        }
    722      }
    723      ctypesKernelHelpers.finalize();
    724      ctypesVaultHelpers.finalize();
    725      aCallback(migrationSucceeded);
    726    }
    727    if (aOnlyCheckExists) {
    728      return false;
    729    }
    730    return undefined;
    731  },
    732 };
    733 
    734 export var MSMigrationUtils = {
    735  MIGRATION_TYPE_IE: 1,
    736  MIGRATION_TYPE_EDGE: 2,
    737  CtypesKernelHelpers,
    738  getBookmarksMigrator(migrationType = this.MIGRATION_TYPE_IE) {
    739    return new Bookmarks(migrationType);
    740  },
    741  getWindowsVaultFormPasswordsMigrator() {
    742    return new WindowsVaultFormPasswords();
    743  },
    744  getTypedURLs,
    745  getEdgeLocalDataFolder,
    746 };