From 74ea762eef32df4bc459fe48247ab32049138e8a Mon Sep 17 00:00:00 2001
From: Alex Xu <alex_y_xu@yahoo.ca>
Date: Tue, 9 Dec 2014 19:18:15 -0500
Subject: Add features, XHR long polling.

---
 src/.jshintrc                         |   3 +
 src/bootstrap.js                      | 143 +++++++++++++++++++++-------------
 src/chrome/content/igb/trusted.html   |  10 +++
 src/chrome/content/igb/trusted.js     |  15 ++++
 src/chrome/content/igb/unknown.html   |   9 +++
 src/chrome/content/igb/untrusted.html |  12 +++
 6 files changed, 137 insertions(+), 55 deletions(-)
 create mode 100644 src/.jshintrc
 create mode 100644 src/chrome/content/igb/trusted.html
 create mode 100644 src/chrome/content/igb/trusted.js
 create mode 100644 src/chrome/content/igb/unknown.html
 create mode 100644 src/chrome/content/igb/untrusted.html

(limited to 'src')

diff --git a/src/.jshintrc b/src/.jshintrc
new file mode 100644
index 0000000..0460aba
--- /dev/null
+++ b/src/.jshintrc
@@ -0,0 +1,3 @@
+{
+    "moz": true
+}
diff --git a/src/bootstrap.js b/src/bootstrap.js
index 162c857..0fbc56e 100644
--- a/src/bootstrap.js
+++ b/src/bootstrap.js
@@ -6,70 +6,88 @@ if (typeof EIB === "undefined")
 
 const Cc = Components.classes, Ci = Components.interfaces, Cu = Components.utils, CC = Components.Constructor, Cr = Components.results;
 const ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1", "nsIScriptableInputStream", "init");
+const FileInputStream = CC("@mozilla.org/network/file-input-stream;1", "nsIFileInputStream", "init");
+const LocalFileFromPath = CC("@mozilla.org/file/local;1", "nsILocalFile", "initWithPath");
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 function install() {}
 function uninstall() {}
 
-EIB.listen = function () {
-    this.serverSocket = Cc["@mozilla.org/network/server-socket;1"]
-                       .createInstance(Ci.nsIServerSocket);
-    this.serverSocket.init(26001, true, -1);
-    const tm = Cc["@mozilla.org/thread-manager;1"].getService();
-    this.serverSocket.asyncListen({
-        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 = "";
-            // TODO: run on separate thread
-            is.asyncWait({
-                onInputStreamReady: function () {
-                    while (sis.available()) {
-                        buf += sis.read(2048);
-                    }
-                    if (buf.indexOf("\n\n") !== 2) {
-                        var headers = {};
-                        var rx = /\n(EVE_[^:]*): (.*)$/gm, arr;
-                        while ((arr = rx.exec(buf)) !== null) {
-                            headers[arr[1]] = arr[2];
-                        }
+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) {
+                    is.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;
 
-                        var resp = "HTTP/1.0 200 OK\r\n";
-                        resp += "Content-Type: text/html\r\n\r\n";
-                        resp += "<!DOCTYPE html><html><head><title>EVE-IGB Bridge</title>";
-                        if (headers["EVE_TRUSTED"]) {
-                            resp += '<script>';
-                            if (headers["EVE_TRUSTED"] == "Yes") {
-                                var delay = 100;
-                                if (EIB.pending.length) {
-                                    resp += EIB.pending.shift();
-                                    delay = 1000;
-                                }
-                                resp += 'setTimeout(function () { location.reload(); }, ' + delay + ');';
-                            } else {
-                                resp += 'CCPEVE.requestTrust("http://127.0.0.1:' + EIB.serverSocket.port + '");';
+                    // 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();
                             }
-                            resp += "</script></head><body>";
-                            resp += headers["EVE_TRUSTED"] == "Yes" ?
-                                    "Connected to the EVE-IGB Bridge. Please do not close this window. You may minimize it, however. Note that this window will refresh repeatedly; this does not indicate a malfunction and will not count towards your internet data usage if you have one." :
-                                    "Please grant trust to this site, then refresh the page.";
-                            resp += "</body></html>";
-                        } else {
-                            resp += "</head><body>Please open this page in the EVE in-game browser.</body></html>";
-                        }
-                        os.write(resp, resp.length);
-
-                        is.close();
+                        }, null);
+                    } else {
                         os.close();
                     }
                 }
-            }, 0, 0, tm.mainThread);
-        }
-    });
-}
+            }
+        }, 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) {
@@ -108,7 +126,7 @@ var WindowListener = {
 var TrustedReparser = {
     observe: function () {
         EIB.trusted = EIB.prefs.prefHasUserValue("trusted") ?
-                      JSON.parse(EIB.prefs.getCharPref("trusted").map(function (v) { return new RegExp(v); }) :
+                      JSON.parse(EIB.prefs.getCharPref("trusted")).map(function (v) { return new RegExp(v); }) :
                       [];
     }
 };
@@ -130,6 +148,10 @@ 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;
@@ -148,14 +170,25 @@ function checkTrusted(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"});
-        ["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"].forEach(function (n) {
+        exportFunctions.forEach(function (n) {
             Object.defineProperty(CCPEVE, n, {
                 value: Cu.exportFunction(function () {
-                    EIB.pending.push('CCPEVE.' + n + '(' + JSON.stringify([].slice.call(arguments)).slice(1, -1) + ');');
+                    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)
             });
diff --git a/src/chrome/content/igb/trusted.html b/src/chrome/content/igb/trusted.html
new file mode 100644
index 0000000..8867b73
--- /dev/null
+++ b/src/chrome/content/igb/trusted.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>EVE-IGB Bridge</title>
+        <script src="trusted.js"></script>
+    </head>
+    <body>
+        <p>Connected to the EVE-IGB Bridge. Please do not close this window. You may minimize it, however. Note that this window will refresh repeatedly; this does not indicate a malfunction and will not count towards your internet data usage if you have one.</p>
+    </body>
+</html>
diff --git a/src/chrome/content/igb/trusted.js b/src/chrome/content/igb/trusted.js
new file mode 100644
index 0000000..3c6a6c7
--- /dev/null
+++ b/src/chrome/content/igb/trusted.js
@@ -0,0 +1,15 @@
+window.onerror = function (e) { alert(e); };
+var xhr = new XMLHttpRequest();
+xhr.onreadystatechange = function () {
+    if (xhr.readyState === 4) {
+        var obj = JSON.parse(xhr.responseText);
+        CCPEVE[obj[0]].apply(CCPEVE, obj[1]);
+        // IGB imposes a 1s delay between JS calls
+        setTimeout(function () {
+            xhr.open("GET", "trusted");
+            xhr.send();
+        }, 1000);
+    }
+};
+xhr.open("GET", "trusted");
+xhr.send();
diff --git a/src/chrome/content/igb/unknown.html b/src/chrome/content/igb/unknown.html
new file mode 100644
index 0000000..51170b3
--- /dev/null
+++ b/src/chrome/content/igb/unknown.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>EVE-IGB Bridge</title>
+    </head>
+    <body>
+        <p>Please open this site in the EVE in-game browser.</p>
+    </body>
+</html>
diff --git a/src/chrome/content/igb/untrusted.html b/src/chrome/content/igb/untrusted.html
new file mode 100644
index 0000000..1cb7499
--- /dev/null
+++ b/src/chrome/content/igb/untrusted.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>EVE-IGB Bridge</title>
+        <script>
+            CCPEVE.requestTrust("http://127.0.0.1:26001");
+        </script>
+    </head>
+    <body>
+        <p>Please grant trust to this site, then refresh the page.</p>
+    </body>
+</html>
-- 
cgit v1.2.3-70-g09d2