frida-gadget-ios
Version:
Gadget for iOS
148 lines (116 loc) • 3.37 kB
JavaScript
const fs = require('fs');
const gadget = require('.');
const https = require('https');
const path = require('path');
const util = require('util');
const zlib = require('zlib');
const access = util.promisify(fs.access);
const readdir = util.promisify(fs.readdir);
const rename = util.promisify(fs.rename);
const unlink = util.promisify(fs.unlink);
async function run() {
await pruneOldVersions();
if (await alreadyDownloaded())
return;
await download();
}
async function alreadyDownloaded() {
try {
await access(gadget.path, fs.constants.F_OK);
return true;
} catch (e) {
return false;
}
}
async function download() {
const response = await httpsGet(`https://github.com/frida/frida/releases/download/${gadget.version}/frida-gadget-${gadget.version}-ios-universal.dylib.gz`);
const tempGadgetPath = gadget.path + '.download';
const tempGadgetStream = fs.createWriteStream(tempGadgetPath);
await pump(response, zlib.createGunzip(), tempGadgetStream);
await rename(tempGadgetPath, gadget.path);
}
async function pruneOldVersions() {
const gadgetDir = path.dirname(gadget.path);
const currentName = path.basename(gadget.path);
for (const name of await readdir(gadgetDir)) {
if (name.startsWith('frida-gadget-') && name.endsWith('-ios-universal.dylib') && name !== currentName) {
await unlink(path.join(gadgetDir, name));
}
}
}
function httpsGet(url) {
return new Promise((resolve, reject) => {
let redirects = 0;
tryGet(url);
function tryGet(url) {
const request = https.get(url, response => {
tearDown();
const {statusCode, headers} = response;
if (statusCode === 200) {
resolve(response);
} else {
response.resume();
if (statusCode >= 300 && statusCode < 400 && headers.location !== undefined) {
if (redirects === 10) {
reject(new Error('Too many redirects'));
return;
}
redirects++;
tryGet(headers.location);
} else {
reject(new Error(`Download failed (code=${statusCode})`));
}
}
});
request.addListener('error', onError);
function onError(error) {
tearDown();
reject(error);
}
function tearDown() {
request.removeListener('error', onError);
}
}
});
}
function pump(...streams) {
return new Promise((resolve, reject) => {
let done = false;
streams.forEach(stream => {
stream.addListener('error', onError);
});
for (let i = 0; i !== streams.length - 1; i++) {
const cur = streams[i];
const next = streams[i + 1];
cur.pipe(next);
}
const last = streams[streams.length - 1];
last.addListener('finish', onFinish);
function onFinish() {
if (done)
return;
done = true;
tearDown();
resolve();
}
function onError(error) {
if (done)
return;
done = true;
tearDown();
reject(error);
}
function tearDown() {
last.removeListener('finish', onFinish);
streams.forEach(stream => {
stream.removeListener('error', onError);
stream.destroy();
});
}
});
}
run().catch(onError);
function onError(error) {
console.error(error.message);
process.exitCode = 1;
}