tor-browser

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

adb-binary.js (6614B)


      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
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
      8 
      9 const lazy = {};
     10 
     11 ChromeUtils.defineESModuleGetters(lazy, {
     12  ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
     13  FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
     14  NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
     15 });
     16 loader.lazyGetter(this, "UNPACKED_ROOT_PATH", () => {
     17  return PathUtils.join(PathUtils.localProfileDir, "adb");
     18 });
     19 loader.lazyGetter(this, "EXTENSION_ID", () => {
     20  return Services.prefs.getCharPref("devtools.remote.adb.extensionID");
     21 });
     22 loader.lazyGetter(this, "ADB_BINARY_PATH", () => {
     23  let adbBinaryPath = PathUtils.join(UNPACKED_ROOT_PATH, "adb");
     24  if (Services.appinfo.OS === "WINNT") {
     25    adbBinaryPath += ".exe";
     26  }
     27  return adbBinaryPath;
     28 });
     29 
     30 const MANIFEST = "manifest.json";
     31 
     32 /**
     33 * Read contents from a given uri in the devtools-adb-extension and parse the
     34 * contents as JSON.
     35 */
     36 async function readFromExtension(fileUri) {
     37  return new Promise(resolve => {
     38    lazy.NetUtil.asyncFetch(
     39      {
     40        uri: fileUri,
     41        loadUsingSystemPrincipal: true,
     42      },
     43      input => {
     44        try {
     45          const string = lazy.NetUtil.readInputStreamToString(
     46            input,
     47            input.available()
     48          );
     49          resolve(JSON.parse(string));
     50        } catch (e) {
     51          dumpn(`Could not read ${fileUri} in the extension: ${e}`);
     52          resolve(null);
     53        }
     54      }
     55    );
     56  });
     57 }
     58 
     59 /**
     60 * Unpack file from the extension.
     61 * Uses NetUtil to read and write, since it's required for reading.
     62 *
     63 * @param {string} file
     64 *        The path name of the file in the extension.
     65 */
     66 async function unpackFile(file) {
     67  const policy = lazy.ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
     68  if (!policy) {
     69    return;
     70  }
     71 
     72  // Assumes that destination dir already exists.
     73  const basePath = file.substring(file.lastIndexOf("/") + 1);
     74  const filePath = PathUtils.join(UNPACKED_ROOT_PATH, basePath);
     75  await new Promise((resolve, reject) => {
     76    lazy.NetUtil.asyncFetch(
     77      {
     78        uri: policy.getURL(file),
     79        loadUsingSystemPrincipal: true,
     80      },
     81      input => {
     82        try {
     83          // Since we have to use NetUtil to read, probably it's okay to use for
     84          // writing, rather than bouncing to IOUtils...?
     85          const outputFile = new lazy.FileUtils.File(filePath);
     86          const output = lazy.FileUtils.openAtomicFileOutputStream(outputFile);
     87          lazy.NetUtil.asyncCopy(input, output, resolve);
     88        } catch (e) {
     89          dumpn(`Could not unpack file ${file} in the extension: ${e}`);
     90          reject(e);
     91        }
     92      }
     93    );
     94  });
     95  // Mark binaries as executable.
     96  await IOUtils.setPermissions(filePath, 0o744);
     97 }
     98 
     99 /**
    100 * Extract files in the extension into local profile directory and returns
    101 * if it fails.
    102 */
    103 async function extractFiles() {
    104  const policy = lazy.ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
    105  if (!policy) {
    106    return false;
    107  }
    108  const uri = policy.getURL("adb.json");
    109  const adbInfo = await readFromExtension(uri);
    110  if (!adbInfo) {
    111    return false;
    112  }
    113 
    114  let filesForAdb;
    115  try {
    116    // The adbInfo is an object looks like this;
    117    //
    118    //  {
    119    //    "Linux": {
    120    //      "x86": [
    121    //        "linux/adb"
    122    //      ],
    123    //      "x86_64": [
    124    //        "linux64/adb"
    125    //      ]
    126    //    },
    127    // ...
    128 
    129    // XPCOMABI looks this; x86_64-gcc3, so drop the compiler name.
    130    let architecture = Services.appinfo.XPCOMABI.split("-")[0];
    131    if (architecture === "aarch64") {
    132      // Fallback on x86 or x86_64 binaries for aarch64 - See Bug 1522149
    133      const hasX86Binary = !!adbInfo[Services.appinfo.OS].x86;
    134      architecture = hasX86Binary ? "x86" : "x86_64";
    135    }
    136    filesForAdb = adbInfo[Services.appinfo.OS][architecture];
    137  } catch (e) {
    138    return false;
    139  }
    140 
    141  // manifest.json isn't in adb.json but has to be unpacked for version
    142  // comparison
    143  filesForAdb.push(MANIFEST);
    144 
    145  await IOUtils.makeDirectory(UNPACKED_ROOT_PATH);
    146 
    147  for (const file of filesForAdb) {
    148    try {
    149      await unpackFile(file);
    150    } catch (e) {
    151      return false;
    152    }
    153  }
    154 
    155  return true;
    156 }
    157 
    158 /**
    159 * Read the manifest from inside the devtools-adb-extension.
    160 * Uses NetUtil since data is packed inside the extension, not a local file.
    161 */
    162 async function getManifestFromExtension() {
    163  const policy = lazy.ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
    164  if (!policy) {
    165    return null;
    166  }
    167 
    168  const manifestUri = policy.getURL(MANIFEST);
    169  return readFromExtension(manifestUri);
    170 }
    171 
    172 /**
    173 * Returns whether manifest.json has already been unpacked.
    174 */
    175 async function isManifestUnpacked() {
    176  const manifestPath = PathUtils.join(UNPACKED_ROOT_PATH, MANIFEST);
    177  return IOUtils.exists(manifestPath);
    178 }
    179 
    180 /**
    181 * Read the manifest from the unpacked binary directory.
    182 * Uses IOUtils since this is a local file.
    183 */
    184 async function getManifestFromUnpacked() {
    185  if (!(await isManifestUnpacked())) {
    186    throw new Error("Manifest doesn't exist at unpacked path");
    187  }
    188 
    189  const manifestPath = PathUtils.join(UNPACKED_ROOT_PATH, MANIFEST);
    190  const binary = await IOUtils.read(manifestPath);
    191  const json = new TextDecoder().decode(binary);
    192  let data;
    193  try {
    194    data = JSON.parse(json);
    195  } catch (e) {}
    196  return data;
    197 }
    198 
    199 /**
    200 * Check state of binary unpacking, including the location and manifest.
    201 */
    202 async function isUnpacked() {
    203  if (!(await isManifestUnpacked())) {
    204    dumpn("Needs unpacking, no manifest found");
    205    return false;
    206  }
    207 
    208  const manifestInExtension = await getManifestFromExtension();
    209  const unpackedManifest = await getManifestFromUnpacked();
    210  if (manifestInExtension.version != unpackedManifest.version) {
    211    dumpn(
    212      `Needs unpacking, extension version ${manifestInExtension.version} != ` +
    213        `unpacked version ${unpackedManifest.version}`
    214    );
    215    return false;
    216  }
    217  dumpn("Already unpacked");
    218  return true;
    219 }
    220 
    221 /**
    222 * Get a file object for the adb binary from the 'adb@mozilla.org' extension
    223 * which has been already installed.
    224 *
    225 * @return {nsIFile}
    226 *        File object for the binary.
    227 */
    228 async function getFileForBinary() {
    229  if (!(await isUnpacked()) && !(await extractFiles())) {
    230    return null;
    231  }
    232 
    233  const file = new lazy.FileUtils.File(ADB_BINARY_PATH);
    234  if (!file.exists()) {
    235    return null;
    236  }
    237  return file;
    238 }
    239 
    240 exports.getFileForBinary = getFileForBinary;