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 require("utils/utils");
12 let {parseMeta
} = require("utils/metaparser");
15 ////////////////////////////////////////////////////////////////////////////////
16 function genFileObj (fname
) {
17 if (typeof(fname
) !== "string") throw new Error("non-string file name in `genFileObj()`");
18 if (fname
.length
== 0) throw new Error("empty file name in `genFileObj()`");
19 if (fname
[0] == '/') {
20 let fl
= Cc
["@mozilla.org/file/local;1"].createInstance(Ci
.nsILocalFile
);
21 fl
.initWithPath(fname
);
24 let fl
= getUserPkgTempDir();
25 for (let p
of fname
.split("/")) if (p
&& p
!= ".") fl
.append(p
);
31 ////////////////////////////////////////////////////////////////////////////////
32 function calcFileSha512 (fname
) {
33 let fl
= Cc
["@mozilla.org/file/local;1"].createInstance(Ci
.nsILocalFile
);
34 fl
.initWithPath(fname
);
35 let istream
= Cc
["@mozilla.org/network/file-input-stream;1"].createInstance(Ci
.nsIFileInputStream
);
37 istream
.init(fl
, 0x01, 0400, istream
.CLOSE_ON_EOF
); // 0x01: PR_RDONLY
38 let chan
= Cc
["@mozilla.org/security/hash;1"].createInstance(Ci
.nsICryptoHash
);
39 chan
.init(chan
.SHA512
);
40 chan
.updateFromStream(istream
, 0xffffffff);
41 // pass false here to get binary data back
42 let hash
= chan
.finish(false);
43 return [("0"+hash
.charCodeAt(i
).toString(16)).slice(-2) for (i
in hash
)].join("");
47 ////////////////////////////////////////////////////////////////////////////////
49 // this downloads all package files and creates package object:
56 // ResInfo[] resources
68 // 2: aborted with error
70 function PkgDownloader (aurl
) {
73 this.reslist
= []; // resources to download: {name, url}; if `name` is null, this is js file
77 this.onError = function () {};
78 this.onComplete = function () {};
79 this.onCancel = function () {};
80 // return `false` from `onMainReceived` to abort downloading and call `onCancel`
81 this.onMainReceived = function () { return true; };
86 PkgDownloader
.prototype.clearTempDir = function () {
87 let fl
= getUserPkgTempDir();
88 try { fl
.remove(true); } catch (e
) {}
89 try { if (!fl
.exists()) fl
.create(fl
.DIRECTORY_TYPE
, 0700); } catch (e
) {}
93 PkgDownloader
.prototype.__defineGetter__("started", function () { return (this.state
> 0); }); // was started, not necessarily running
94 PkgDownloader
.prototype.__defineGetter__("running", function () { return (this.state
== 1); });
95 PkgDownloader
.prototype.__defineGetter__("cancelled", function () { return (this.state
< 0); });
96 PkgDownloader
.prototype.__defineGetter__("aborted", function () { return (this.state
== 2); });
97 PkgDownloader
.prototype.__defineGetter__("complete", function () { return (this.state
== 3); });
100 PkgDownloader
.prototype.genFileName = function (ext
) {
101 let fl
= getUserPkgTempDir();
102 if (typeof(ext
) === "undefined" || !ext
) {
103 fl
.append("pkgmain.js");
105 if (ext
[0] != ".") ext
= "."+ext
;
106 fl
.append("pkgres"+ext
);
108 fl
.createUnique(fl
.NORMAL_FILE_TYPE
, 0600);
110 //conlog("temp file: [", res, "]");
116 // https://raw.githubusercontent.com/adsbypasser/adsbypasser/v5.31.0/css/align_center.css
117 PkgDownloader
.prototype.getExtFromUrl = function (url
) {
118 let ioSvc
= Cc
["@mozilla.org/network/io-service;1"].getService(Ci
.nsIIOService
);
120 function uriFromUrl (url
, base
) {
122 if (typeof(base
) === "string") {
123 baseUri
= uriFromUrl(base
);
128 return ioSvc
.newURI(url
, null, baseUri
);
135 let uo
= uriFromUrl(url
);
137 if (!path
|| path
[path
.length
-1] == "/") return null;
138 let mt
= path
.match(/(\.[^.]+)$/);
139 if (mt
) return mt
[1];
146 // nsIHttpChannel chan
151 PkgDownloader
.prototype.downloadFile = function (destfname
, aurl
, callback
) {
152 conlog("downloading: ", aurl
);
153 let ioSrv
= Cc
["@mozilla.org/network/io-service;1"].getService(Ci
.nsIIOService
)
154 let done
= false, cancelled
= false;
155 let fl
= genFileObj(destfname
);
157 onDownloadComplete: function (dldr
, request
, ctxt
, status
, fl
) {
159 let cc
= request
.QueryInterface(Ci
.nsIHttpChannel
);
160 if (!cancelled
&& callback
) {
162 callback(cc
, (status
== 0 && !cancelled
), destfname
, aurl
, cancelled
);
164 logException("downloadFile error", e
);
169 let dldr
= Cc
["@mozilla.org/network/downloader;1"].createInstance(Ci
.nsIDownloader
);
170 dldr
.init(downObserver
, fl
);
171 let httpChan
= ioSrv
.newChannel(aurl
, "", null).QueryInterface(Ci
.nsIHttpChannel
);
172 httpChan
.setRequestHeader("User-Agent", "GuerillaScript/0.666", false);
173 httpChan
.redirectionLimit
= 16;
174 httpChan
.asyncOpen(dldr
, fl
);
175 //conlog("downloading...");
178 get filename () destfname
,
180 cancel: function () {
181 if (!done
&& !cancelled
) {
183 httpChan
.cancel(Cr
.NS_BINDING_ABORTED
);
190 PkgDownloader
.prototype.cancel = function () {
191 if (this.state
!= 1) throw new Error("can't cancel from invalid state");
192 if (this.state
== 1 && this.curdown
) {
193 this.curdown
.cancel();
197 if ("pkg" in this) delete this.pkg
;
198 if ("curres" in this) delete this.pkg
;
203 PkgDownloader
.prototype.start = function () {
204 if (this.state
!= 0) throw new Error("can't start from invalid state");
206 let fname
= this.genFileName();
207 //conlog("fname: [", fname, "]");
209 this.curdown
= this.downloadFile(fname
, this.mainURL
, tieto(this, "downCBMain"));
211 try { let fl
= genFileObj(fname
); fl
.remove(false); } catch (e
) {}
218 PkgDownloader
.prototype.__abort = function (skipOnError
) {
220 if ("pkg" in this) delete this.pkg
;
221 if ("curres" in this) delete this.pkg
;
222 if (!skipOnError
) try { this.onError(); } catch (e
) { logException("onError", e
); }
226 PkgDownloader
.prototype._abort = function (filename
, cancelled
) {
227 if (typeof(filename
) === "string") {
228 try { let fl
= genFileObj(fname
); fl
.remove(false); } catch (e
) {}
230 this.__abort(cancelled
);
234 PkgDownloader
.prototype.downCBMain = function (chan
, success
, filename
, url
, cancelled
) {
235 //conlog("downCBMain: [", filename, "] : ", success, " : ", chan.responseStatus, " : ", chan.requestSucceeded);
238 if (!success
|| !chan
.requestSucceeded
) { this._abort(filename
, cancelled
); return; } // failed to download main file
239 // main file downloaded
240 let ct
= (chan
.getResponseHeader("Content-Type")||"");
241 if (!(/^text\/javascript(?:;|$)/.test(ct
)) && !(/^text\/plain(?:;|$)/.test(ct
))) {
242 // invalid content type
243 this._abort(filename
, cancelled
);
247 let meta
= parseMeta(fileReadText(genFileObj(filename
)));
248 if (meta
.length
== 0) { this._abort(filename
, cancelled
); return; }
250 this.pkg
.downurl
= this.mainURL
;
251 this.pkg
.meta
= meta
;
253 let name
= meta
.getField("name");
255 this.pkg
.name
= name
.value
;
257 name
= diskname
.split("/");
258 this.pkg
.name
= name
[name
.length
-1];
260 // get package version
261 let ver
= meta
.getField("version");
262 this.pkg
.version
= (ver
? ver
.value
: "");
264 this.pkg
.diskname
= filename
;
265 this.pkg
.sha512
= calcFileSha512(this.pkg
.diskname
);
266 // now build download list
267 this.pkg
.resources
= new Array();
268 let resList
= new Array(); // resources to download: {name, url}
271 meta
.forEachField("require", function (kv
) {
272 if (!rqf
[kv
.value
]) {
273 if (/^https?:/.test(kv
.value
)) resList
.push({name
:null, url
:kv
.value
});
274 rqf
[kv
.value
] = true;
279 meta
.forEachField("resource", function (kv
) {
280 let rn
= kv
.value
.name
;
281 let ru
= kv
.value
.url
;
282 if (!rqf
[rn
] && (/^https?:/.test(ru
))) rqf
[rn
] = ru
;
284 for (let [rn
, ru
] in Iterator(rqf
)) resList
.push({name
:rn
, url
:ru
});
285 this.reslist
= resList
;
287 if (!this.onMainReceived()) {
288 // cancel downloading
293 this.downloadNextResource();
295 logException("DOWN", e
);
301 PkgDownloader
.prototype.downloadNextResource = function () {
303 if (this.reslist
.length
== 0) {
304 conlog("package download complete");
306 try { this.onComplete(); } catch (e
) { logException("DOWN", e
); }
309 // we have some resource to download, do it
311 let res
= this.reslist
[0];
312 conlog("downloading resource '", res
.name
, "' from: ", res
.url
);
313 let ext
= this.getExtFromUrl(res
.url
);
314 if (!ext
) { this._abort(filename
, cancelled
); return; }
316 this.reslist
= this.reslist
.slice(1);
317 let fname
= this.genFileName(ext
);
318 this.curdown
= this.downloadFile(fname
, res
.url
, tieto(this, "downCBNextRes"));
320 logException("DOWN", e
);
327 PkgDownloader
.prototype.downCBNextRes = function (chan
, success
, filename
, url
, cancelled
) {
329 if (!success
|| !chan
.requestSucceeded
) { this._abort(filename
, cancelled
); return; } // failed to download resource file
331 let ct
= (chan
.getResponseHeader("Content-Type")||"application/octet-stream");
334 resobj
.diskname
= filename
;
335 resobj
.resname
= this.curres
.name
; // string or null
336 resobj
.sha512
= calcFileSha512(resobj
.diskname
);
337 resobj
.contenttype
= ct
;
338 this.pkg
.resources
.push(resobj
);
339 this.downloadNextResource();
341 logException("DOWN", e
);
348 ////////////////////////////////////////////////////////////////////////////////
349 exports
.PkgDownloader
= PkgDownloader
;