if (typeof EIB === "undefined") var EIB = { headers: {}, pending: [] }; const Cc = Components.classes, Ci = Components.interfaces, Cu = Components.utils, CC = Components.Constructor; const ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1", "nsIScriptableInputStream", "init"); Cu.import("resource://gre/modules/Services.jsm"); function install() {} function uninstall() {} var ServerListener = { onSocketAccepted: function (socket, transport) { var is = transport.openInputStream(0, 0, 0); var os = transport.openOutputStream(Ci.nsITransport.OPEN_BLOCKING, 0, 0); var sis = new ScriptableInputStream(is); var buf = ""; is.asyncWait({ onInputStreamReady: function () { while (sis.available()) buf += sis.read(2048); if (buf.indexOf("\r\n\r\n") > -1) { sis.close(); // parse out headers var headers = {}; var rx = /\n(EVE_[^:]*): (.*)$/gm, arr; while ((arr = rx.exec(buf)) !== null) headers[arr[1]] = arr[2]; if (headers.EVE_TRUSTED == "Yes") EIB.headers = headers; // figure out what we're responding with var type, file, code; switch (/ ([^ ]*) /.exec(buf)[1]) { case '/': type = "text/html; charset=UTF-8"; file = headers.EVE_TRUSTED === "Yes" ? "trusted.html" : headers.EVE_TRUSTED === "No" ? "untrusted.html" : "unknown.html"; break; case '/trusted.js': type = "application/javascript"; file = "trusted.js"; break; case '/trusted': if (EIB.stream) EIB.stream.close(); EIB.stream = os; return; } // build the response var resp = "HTTP/1.0 " + (type ? "200 OK\r\nContent-Type: " + type + "\r\n\r\n" : "404 Not Found\r\nContent-Type: text/plain\r\n\r\n404 Not Found\r\n"); os.write(resp, resp.length); if (file) { var channel = Services.io.newChannel("chrome://eib/content/igb/" + file, null, null); channel.asyncOpen({ onStartRequest: function () {}, onDataAvailable: function (req, ctx, fis, offset, cnt) { os.writeFrom(fis, cnt); }, onStopRequest: function (req, ctx, status) { os.close(); } }, null); } else { os.close(); } } } }, 0, 0, Services.tm.mainThread); } }; EIB.listen = function () { this.serverSocket = Cc["@mozilla.org/network/server-socket;1"] .createInstance(Ci.nsIServerSocket); this.serverSocket.init(26001, true, -1); this.serverSocket.asyncListen(ServerListener); }; var HttpObserver = { observe: function (subject, topic, data) { var channel = subject.QueryInterface(Ci.nsIHttpChannel); // TODO: is this secure? if (checkTrusted(channel.URI.specIgnoringRef)) { for (var header in EIB.headers) { channel.setRequestHeader(header, EIB.headers[header], false); } channel.setRequestHeader("User-Agent", channel.getRequestHeader("User-Agent") + " EVE-IGB", false); } } }; function forEachOpenWindow(todo) { var windows = Services.wm.getEnumerator("navigator:browser"); while (windows.hasMoreElements()) todo(windows.getNext().QueryInterface(Ci.nsIDOMWindow)); } var WindowListener = { onOpenWindow: function(xulWindow) { var window = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindow); window.addEventListener("load", function onWindowLoad() { window.removeEventListener("load", onWindowLoad); if (window.document.documentElement.getAttribute("windowtype") == "navigator:browser") loadIntoWindow(window); }); }, }; var TrustedReparser = { observe: function () { EIB.trusted = EIB.prefs.prefHasUserValue("trusted") ? JSON.parse(EIB.prefs.getCharPref("trusted")).map(function (v) { return new RegExp(v); }) : []; } }; function startup(data, reason) { EIB.listen(); forEachOpenWindow(loadIntoWindow); Services.wm.addListener(WindowListener); Services.obs.addObserver(HttpObserver, "http-on-modify-request", false); EIB.prefs = Services.prefs.getBranch("extensions.eib."); EIB.prefs.addObserver("trusted", TrustedReparser, false); TrustedReparser.observe(); } function shutdown(data, reason) { if (reason === APP_SHUTDOWN) return; if (EIB.stream) EIB.stream.close(); delete EIB.stream; if (EIB.serverSocket) EIB.serverSocket.close(); delete EIB.serverSocket; forEachOpenWindow(unloadFromWindow); Services.wm.removeListener(WindowListener); Services.obs.removeObserver(HttpObserver, "http-on-modify-request"); EIB.prefs.removeObserver("trusted", TrustedReparser); } function checkTrusted(href) { return EIB.trusted.some(function (v) { return v.test(href); }); } // We can't get this from the IGB because the properties aren't enumerable and it doesn't implement Object.getOwnPropertyNames. // We could test if the functions are available, but since there's only one IGB version, that wouldn't be particularly useful. const exportFunctions = ["openEveMail", "showInfo", "showPreview", "showRouteTo", "showMap", "showFitting", "showContract", "showMarketDetails", "setDestination", "addWaypoint", "joinChannel", "joinMailingList", "createContract", "buyType", "findInContracts", "addToMarketQuickBar", "addContact", "removeContact", "addCorpContact", "removeCorpContact", "block", "addBounty", "inviteToFleet", "startConversation", "showContracts", "showOnMap", "editMember", "awardDecoration", "sendMail", "showContents", "bookmark"]; function injectCCPEVE(e) { var window = e.originalTarget.defaultView; if (checkTrusted(window.location.href)) { var CCPEVE = Cu.createObjectIn(window, {defineAs: "CCPEVE"}); exportFunctions.forEach(function (n) { Object.defineProperty(CCPEVE, n, { value: Cu.exportFunction(function () { if (EIB.stream) { var resp = "HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n"; var str = JSON.stringify([n, [].slice.call(arguments)]) + "\r\n"; resp += "Content-Length: " + str.length + "\r\n\r\n" + str; EIB.stream.write(resp, resp.length); EIB.stream.close(); } delete EIB.stream; return null; }, CCPEVE) }); }); } } function loadIntoWindow(window) { window.document.getElementById("appcontent").addEventListener("DOMContentLoaded", injectCCPEVE); } function unloadFromWindow(window) { window.document.getElementById("appcontent").removeEventListener("DOMContentLoaded", injectCCPEVE); }