1 /* coded by Ketmar // Invisible Vector (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://www.wtfpl.net/txt/copying/ for more details.
10 ////////////////////////////////////////////////////////////////////////////////
11 let {parseMeta} = require("utils/metaparser");
14 ////////////////////////////////////////////////////////////////////////////////
15 function genFileObj (fname) {
16 if (typeof(fname) !== "string") throw new Error("non-string file name in `genFileObj()`");
17 if (fname.length == 0) throw new Error("empty file name in `genFileObj()`");
18 if (fname[0] == '/') {
19 let fl = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
20 fl.initWithPath(fname);
23 let fl = getUserPkgTempDir();
24 for (let p of fname.split("/")) if (p && p != ".") fl.append(p);
30 ////////////////////////////////////////////////////////////////////////////////
32 // this downloads all package files and creates package object:
39 // ResInfo[] resources
51 // 2: aborted with error
53 function PkgDownloader (aurl) {
56 this.reslist = []; // resources to download: {name, url}; if `name` is null, this is js file
60 this.onError = function () {};
61 this.onComplete = function () {};
62 this.onCancel = function () {};
63 // return `false` from `onMainReceived` to abort downloading and call `onCancel`
64 this.onMainReceived = function () { return true; };
69 PkgDownloader.prototype.clearTempDir = function () {
70 let fl = getUserPkgTempDir();
71 try { fl.remove(true); } catch (e) {}
72 try { if (!fl.exists()) fl.create(fl.DIRECTORY_TYPE, 0700); } catch (e) {}
76 PkgDownloader.prototype.__defineGetter__("started", function () { return (this.state > 0); }); // was started, not necessarily running
77 PkgDownloader.prototype.__defineGetter__("running", function () { return (this.state == 1); });
78 PkgDownloader.prototype.__defineGetter__("cancelled", function () { return (this.state < 0); });
79 PkgDownloader.prototype.__defineGetter__("aborted", function () { return (this.state == 2); });
80 PkgDownloader.prototype.__defineGetter__("complete", function () { return (this.state == 3); });
83 PkgDownloader.prototype.genFileName = function (ext) {
84 let fl = getUserPkgTempDir();
85 if (typeof(ext) === "undefined" || !ext) {
86 fl.append("pkgmain.js");
88 if (ext[0] != ".") ext = "."+ext;
89 fl.append("pkgres"+ext);
91 fl.createUnique(fl.NORMAL_FILE_TYPE, 0600);
93 //conlog("temp file: [", res, "]");
99 // https://raw.githubusercontent.com/adsbypasser/adsbypasser/v5.31.0/css/align_center.css
100 PkgDownloader.prototype.getExtFromUrl = function (url) {
101 let ioSvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
103 function uriFromUrl (url, base) {
105 if (typeof(base) === "string") {
106 baseUri = uriFromUrl(base);
111 return ioSvc.newURI(url, null, baseUri);
118 let uo = uriFromUrl(url);
120 if (!path || path[path.length-1] == "/") return null;
121 let mt = path.match(/(\.[^.]+)$/);
122 if (mt) return mt[1];
129 // nsIHttpChannel chan
134 PkgDownloader.prototype.downloadFile = function (destfname, aurl, callback) {
135 conlog("downloading: ", aurl);
136 let ioSrv = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService)
137 let done = false, cancelled = false;
138 let fl = genFileObj(destfname);
140 onDownloadComplete: function (dldr, request, ctxt, status, fl) {
142 let cc = request.QueryInterface(Ci.nsIHttpChannel);
143 if (!cancelled && callback) {
145 callback(cc, (status == 0 && !cancelled), destfname, aurl, cancelled);
147 logException("downloadFile error", e);
152 let dldr = Cc["@mozilla.org/network/downloader;1"].createInstance(Ci.nsIDownloader);
153 dldr.init(downObserver, fl);
154 let httpChan = ioSrv.newChannel(aurl, "", null).QueryInterface(Ci.nsIHttpChannel);
155 httpChan.setRequestHeader("User-Agent", "GuerillaScript/0.666", false);
156 httpChan.redirectionLimit = 16;
157 httpChan.asyncOpen(dldr, fl);
158 //conlog("downloading...");
161 get filename () destfname,
163 cancel: function () {
164 if (!done && !cancelled) {
166 httpChan.cancel(Cr.NS_BINDING_ABORTED);
173 PkgDownloader.prototype.cancel = function () {
174 if (this.state != 1) throw new Error("can't cancel from invalid state");
175 if (this.state == 1 && this.curdown) {
176 this.curdown.cancel();
180 if ("pkg" in this) delete this.pkg;
181 if ("curres" in this) delete this.pkg;
186 PkgDownloader.prototype.start = function () {
187 if (this.state != 0) throw new Error("can't start from invalid state");
189 let fname = this.genFileName();
190 //conlog("fname: [", fname, "]");
192 this.curdown = this.downloadFile(fname, this.mainURL, hitch(this, "downCBMain"));
194 try { let fl = genFileObj(fname); fl.remove(false); } catch (e) {}
201 PkgDownloader.prototype.__abort = function (skipOnError) {
203 if ("pkg" in this) delete this.pkg;
204 if ("curres" in this) delete this.pkg;
205 if (!skipOnError) try { this.onError(); } catch (e) { logException("onError", e); }
209 PkgDownloader.prototype._abort = function (filename, cancelled) {
210 if (typeof(filename) === "string") {
211 try { let fl = genFileObj(fname); fl.remove(false); } catch (e) {}
213 this.__abort(cancelled);
217 PkgDownloader.prototype.downCBMain = function (chan, success, filename, url, cancelled) {
218 //conlog("downCBMain: [", filename, "] : ", success, " : ", chan.responseStatus, " : ", chan.requestSucceeded);
221 if (!success || !chan.requestSucceeded) { this._abort(filename, cancelled); return; } // failed to download main file
222 // main file downloaded
223 let ct = (chan.getResponseHeader("Content-Type")||"");
224 if (!(/^text\/javascript(?:;|$)/.test(ct)) && !(/^text\/plain(?:;|$)/.test(ct))) {
225 // invalid content type
226 this._abort(filename, cancelled);
230 let meta = parseMeta(fileReadText(genFileObj(filename)));
231 if (meta.length == 0) { this._abort(filename, cancelled); return; }
233 this.pkg.downurl = this.mainURL;
234 this.pkg.meta = meta;
236 let name = meta.getField("name");
238 this.pkg.name = name.value;
240 name = diskname.split("/");
241 this.pkg.name = name[name.length-1];
243 // get package version
244 let ver = meta.getField("version");
245 this.pkg.version = (ver ? ver.value : "");
247 this.pkg.diskname = filename;
248 this.pkg.sha512 = calcFileSha512(this.pkg.diskname);
249 // now build download list
250 this.pkg.resources = new Array();
251 let resList = new Array(); // resources to download: {name, url}
254 meta.forEachField("require", function (kv) {
255 if (!rqf[kv.value]) {
256 if (/^https?:/.test(kv.value)) resList.push({name:null, url:kv.value});
257 rqf[kv.value] = true;
262 meta.forEachField("resource", function (kv) {
263 let rn = kv.value.name;
264 let ru = kv.value.url;
265 if (!rqf[rn] && (/^https?:/.test(ru))) rqf[rn] = ru;
267 for (let [rn, ru] in Iterator(rqf)) resList.push({name:rn, url:ru});
268 this.reslist = resList;
270 if (!this.onMainReceived()) {
271 // cancel downloading
276 this.downloadNextResource();
278 logException("DOWN", e);
284 PkgDownloader.prototype.downloadNextResource = function () {
286 if (this.reslist.length == 0) {
287 conlog("package download complete");
289 try { this.onComplete(); } catch (e) { logException("DOWN", e); }
292 // we have some resource to download, do it
294 let res = this.reslist[0];
295 conlog("downloading resource '", res.name, "' from: ", res.url);
296 let ext = this.getExtFromUrl(res.url);
297 if (!ext) { this._abort(filename, cancelled); return; }
299 this.reslist = this.reslist.slice(1);
300 let fname = this.genFileName(ext);
301 this.curdown = this.downloadFile(fname, res.url, hitch(this, "downCBNextRes"));
303 logException("DOWN", e);
310 PkgDownloader.prototype.downCBNextRes = function (chan, success, filename, url, cancelled) {
312 if (!success || !chan.requestSucceeded) { this._abort(filename, cancelled); return; } // failed to download resource file
316 resobj.diskname = filename;
317 resobj.resname = this.curres.name; // string or null
318 resobj.sha512 = calcFileSha512(resobj.diskname);
319 this.pkg.resources.push(resobj);
320 this.downloadNextResource();
322 logException("DOWN", e);
329 ////////////////////////////////////////////////////////////////////////////////
330 exports.PkgDownloader = PkgDownloader;