--- /dev/null
+# EstousAPI
+
+Easily transform as many CLI programs as you wish into web APIs.
+
+## Compilation
+
+Please click the `tree` link at the top and navigate to the
+`bin` folder and download the following file to get the source
+code for the simulator.
+
+```
+ qansel-source-base.zip
+```
+
+To compile it, navigate to the folder it installed within and
+run the following commands within that folder.
+
+```sh
+ mkdir qansel
+ mv *qansel-source-base.zip qansel
+ cd qansel
+ unzip *qansel-source-base.zip
+ make
+```
+
+If you are compiling for a device which cannot support
+hardware acceleration, then you can use `make simple` which
+will build the program with those features stripped out.
+
+## Usage
+
+
+
+
+
+Many example programs that can be executed inside of QAnsel can
+be found by clicking the `tree` link at the top fo the page and
+then navigating to the `examples` folder. Simply click on one of
+the examples in order to see the source code.
+
+## Usage
+
+The QAnsel simulator expects programs to be written in a language
+similar to OpenQASM 2.0. These programs must be piped into QAnsel
+as standard input and the simulation results will be displayed as
+standard output. Below is an example using a here document.
+
+```sh
+ $ ./QAnsel << EOF
+ > qreg q[2];
+ > creg c[2];
+ > h q[0];
+ > cx q[0], q[1];
+ > measure q[0] -> c[0];
+ > measure q[1] -> c[1];
+ > sample c;
+ > EOF
+ 00: 50.7%
+ 01: 0.0%
+ 10: 0.0%
+ 11: 49.3%
+```
+
+Please use the `-?` flag to see a help document.
+
+## Special Hardware
+
+To enable a hardware random number generator, the `-r`
+flag must be used. This flag will select the hardware
+random number generator based on an order of precedence.
+The order is as follows.
+
+1. Quantis-PCIe-40M
+2. TrueRNG V3
+3. Secure Key Technology
+
+To enable GPU acceleration, the `-oX` flag has to be
+set replacing `X` with an optimization level equal to
+4 or greater.
+
+It is recommended that you run a simple program with the
+`-v` flag which will produce output stating which hardware
+devices were actually found and enabled.
+
+QAnsel can handle up to 16 qubits, however, qubit counts
+greater than 14 will not fit into most consumer-end GPUs.
+The amount of qubits that can fit is limited by the
+amount of VRAM. The GPU must have at least 8 GB for 14
+qubits, 16 GB for 15 qubits, and 48 GB for 16 qubits.
+
+## API and Web Interface
+
+There is a drag-and-drop interface that runs in the browser
+that can be found by clicking the `tree` button and navigating
+to the `bin` folder and downloading the following file.
+
+```
+ qansel-source-web.zip
+```
+
+This web front end expects to connect to an API. The API
+service is handled through FoleoAPI. Please see the FoleoAPI
+project in order to set up this service.
+
+Once FoleoAPI is running, a ServiceInfo record will need to be
+added to the APIService schema. Below is an example of how this
+entry may look.
+
+```sql
+ MariaDB [APIService]> select * from ServiceInfo;
+ +----+---------+---------------------------------+-------------+
+ | id | service | path | parameters |
+ +----+---------+---------------------------------+-------------+
+ | 1 | qansel | /fakepath/QAnsel | -q14 -o5 -r |
+ +----+---------+---------------------------------+-------------+
+```
+
+The `-qX` parameter is useful here as it allows one to cap
+the maximum qubit count allowed for the process in case the
+hardware running the simulator is not sufficient to handle large
+qubit counts. In this case, it is capped at 14 to make sure that
+programs that exceed the limitations of the GPU are rejected.
+
+The web interface expects an API key. The API key is formed in three
+parts `A:B:C` where `A` is the `APIService.AuthInfo.username`, `B` is
+the `APIService.AuthInfo.authkey`, and `C` is the URL which it needs
+to ping. It is important that the address does not contain `http://`
+or `https://`. For example, if the address is `http://example.com/api`,
+then `C` should simply be written as `example.com/api`.
+
+Once `A:B:C` is filled out, the entire thing needs to be converted to
+a base64 string. This can be done in JavaScript in the developer console
+in the web browser using the `btoa()` function. This final encoded string
+functions as the API key for the web interface. Any equal signs at the
+end of the API key should be removed.
+
+The web interface allows for appending the URL with `?apikey=` followed by
+an API key to auto fill the API key. This can be useful if the service is
+linked from another source (such as another website or an email) and will
+prevent the user from having to input the APIkey themselves.
+
+## Android
+
+The Android app version of this project merely is enclose a web view
+containing the web interface. This means it requires pinging a server
+running the API for it to work. It must be compiled using Android Studio.
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+ <title>EstoulsAPI</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<script>
+
+var EstoulsAPI =
+{
+ username: undefined,
+ apikey: undefined,
+ endpoint: undefined,
+
+ Math32:
+ {
+ clamp: a =>
+ {
+ while (a < 0 || a > 0xFFFFFFFF)
+ a += a < 0 ? 0x100000000 : -0x100000000;
+ return a;
+ },
+ shl: (a, b) => EstoulsAPI.Math32.clamp(a << b),
+ shr: (a, b) => EstoulsAPI.Math32.clamp(a >>> b),
+ rtl: (a, b) => EstoulsAPI.Math32.clamp((a << b) | (a >>> (32 - b))),
+ rtr: (a, b) => EstoulsAPI.Math32.clamp((a >>> b) | (a << (32 - b))),
+ add: (a, b) => EstoulsAPI.Math32.clamp(a + b),
+ sub: (a, b) => EstoulsAPI.Math32.clamp(a + b),
+ or: (a, b) => EstoulsAPI.Math32.clamp(a | b),
+ xor: (a, b) => EstoulsAPI.Math32.clamp(a ^ b)
+ },
+ HMACSHA256:
+ {
+ sign: async (key, msg) =>
+ {
+ key = new Uint8Array(key);
+ msg = new Uint8Array(msg);
+ var ikey = await crypto.subtle.importKey
+ (
+ "raw",
+ key,
+ { name: "HMAC", hash: { name: "SHA-256" }},
+ false,
+ ["sign", "verify"]
+ );
+ return new Uint8Array(await window.crypto.subtle.sign({ name: "HMAC", }, ikey, msg));
+ }
+ },
+ ChaCha20:
+ {
+ QR: (cc, a, b, c, d) =>
+ {
+ cc[a] = EstoulsAPI.Math32.add(cc[a], cc[b]);
+ cc[d] = EstoulsAPI.Math32.xor(cc[d], cc[a]);
+ cc[d] = EstoulsAPI.Math32.rtl(cc[d], 16);
+
+ cc[c] = EstoulsAPI.Math32.add(cc[c], cc[d]);
+ cc[b] = EstoulsAPI.Math32.xor(cc[b], cc[c]);
+ cc[b] = EstoulsAPI.Math32.rtl(cc[b], 12);
+
+ cc[a] = EstoulsAPI.Math32.add(cc[a], cc[b]);
+ cc[d] = EstoulsAPI.Math32.xor(cc[d], cc[a]);
+ cc[d] = EstoulsAPI.Math32.rtl(cc[d], 8);
+
+ cc[c] = EstoulsAPI.Math32.add(cc[c], cc[d]);
+ cc[b] = EstoulsAPI.Math32.xor(cc[b], cc[c]);
+ cc[b] = EstoulsAPI.Math32.rtl(cc[b], 7);
+ },
+ DR: (cc) =>
+ {
+ EstoulsAPI.ChaCha20.QR(cc, 0, 4, 8, 12);
+ EstoulsAPI.ChaCha20.QR(cc, 1, 5, 9, 13);
+ EstoulsAPI.ChaCha20.QR(cc, 2, 6, 10, 14);
+ EstoulsAPI.ChaCha20.QR(cc, 3, 7, 11, 15);
+ EstoulsAPI.ChaCha20.QR(cc, 0, 5, 10, 15);
+ EstoulsAPI.ChaCha20.QR(cc, 1, 6, 11, 12);
+ EstoulsAPI.ChaCha20.QR(cc, 2, 7, 8, 13);
+ EstoulsAPI.ChaCha20.QR(cc, 3, 4, 9, 14);
+ },
+ CB: (cc) =>
+ {
+ var i;
+ var x = new Array(16);
+ for (i = 0; i < 16; i++)
+ {
+ x[i] = cc[i];
+ }
+ for (i = 0; i < 10; i++)
+ {
+ EstoulsAPI.ChaCha20.DR(cc);
+ }
+ for (i = 0; i < 16; i++)
+ {
+ cc[i] = EstoulsAPI.Math32.add(cc[i], x[i]);
+ }
+ },
+ S: (cc, cs) =>
+ {
+ for (var i = 0; i < 16; i++)
+ {
+ cs[4 * i] = (cc[i] & 0xFF);
+ cs[4 * i + 1] = ((cc[i] >> 8) & 0xFF);
+ cs[4 * i + 2] = ((cc[i] >> 16) & 0xFF);
+ cs[4 * i + 3] = ((cc[i] >> 24) & 0xFF);
+ }
+ },
+ B: (key, nonce, block, out) =>
+ {
+ var cc =
+ [
+ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
+
+ key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24),
+ key[4] | (key[5] << 8) | (key[6] << 16) | (key[7] << 24),
+ key[8] | (key[9] << 8) | (key[10] << 16) | (key[11] << 24),
+ key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24),
+
+ key[16] | (key[17] << 8) | (key[18] << 16) | (key[19] << 24),
+ key[20] | (key[21] << 8) | (key[22] << 16) | (key[23] << 24),
+ key[24] | (key[25] << 8) | (key[26] << 16) | (key[27] << 24),
+ key[28] | (key[29] << 8) | (key[30] << 16) | (key[31] << 24),
+
+ block,
+
+ nonce[0] | (nonce[1] << 8) | (nonce[2] << 16) | (nonce[3] << 24),
+ nonce[4] | (nonce[5] << 8) | (nonce[6] << 16) | (nonce[7] << 24),
+ nonce[8] | (nonce[9] << 8) | (nonce[10] << 16) | (nonce[11] << 24)
+ ];
+
+ EstoulsAPI.ChaCha20.CB(cc);
+ EstoulsAPI.ChaCha20.S(cc, out);
+ },
+ encrypt: async (key, nonce, block, data) =>
+ {
+ var count = data.length;
+ if (count > (274877906944 - block * 64)) return null;
+ var ret = new Array(0);
+ var ccblock = new Array(64);
+ var size = 0;
+ while (count > 64)
+ {
+ ret.length = size + 64;
+ EstoulsAPI.ChaCha20.B(key, nonce, block++, ccblock);
+ for (var i = 0; i < 64; i++) ret[size + i] = ccblock[i];
+ size += 64;
+ count -= 64;
+ }
+ if (count > 0)
+ {
+ ret.length = size + count;
+ EstoulsAPI.ChaCha20.B(key, nonce, block, ccblock);
+ for (var i = 0; i < count; i++) ret[size + i] = ccblock[i];
+ }
+ for (var i = 0; i < data.length; i++) ret[i] ^= data[i];
+ return new Uint8Array(ret);
+ }
+ },
+
+ Base64:
+ {
+ encode: async (x) =>
+ {
+ return await new Promise(r =>
+ {
+ const reader = new FileReader();
+ reader.addEventListener("load", () => r(reader.result.split(",")[1]));
+ reader.readAsDataURL(new Blob([new Uint8Array(x)]));
+ });
+ },
+
+ decode: (b64) =>
+ {
+ var dec1 = (v) =>
+ {
+ switch (v)
+ {
+ case 'A': return 0;
+ case 'B': return 1;
+ case 'C': return 2;
+ case 'D': return 3;
+ case 'E': return 4;
+ case 'F': return 5;
+ case 'G': return 6;
+ case 'H': return 7;
+ case 'I': return 8;
+ case 'J': return 9;
+ case 'K': return 10;
+ case 'L': return 11;
+ case 'M': return 12;
+ case 'N': return 13;
+ case 'O': return 14;
+ case 'P': return 15;
+ case 'Q': return 16;
+ case 'R': return 17;
+ case 'S': return 18;
+ case 'T': return 19;
+ case 'U': return 20;
+ case 'V': return 21;
+ case 'W': return 22;
+ case 'X': return 23;
+ case 'Y': return 24;
+ case 'Z': return 25;
+ case 'a': return 26;
+ case 'b': return 27;
+ case 'c': return 28;
+ case 'd': return 29;
+ case 'e': return 30;
+ case 'f': return 31;
+ case 'g': return 32;
+ case 'h': return 33;
+ case 'i': return 34;
+ case 'j': return 35;
+ case 'k': return 36;
+ case 'l': return 37;
+ case 'm': return 38;
+ case 'n': return 39;
+ case 'o': return 40;
+ case 'p': return 41;
+ case 'q': return 42;
+ case 'r': return 43;
+ case 's': return 44;
+ case 't': return 45;
+ case 'u': return 46;
+ case 'v': return 47;
+ case 'w': return 48;
+ case 'x': return 49;
+ case 'y': return 50;
+ case 'z': return 51;
+ case '0': return 52;
+ case '1': return 53;
+ case '2': return 54;
+ case '3': return 55;
+ case '4': return 56;
+ case '5': return 57;
+ case '6': return 58;
+ case '7': return 59;
+ case '8': return 60;
+ case '9': return 61;
+ case '+': return 62;
+ case '/': return 63;
+ }
+ return 255;
+ };
+
+ var ret = new Array(0);
+ var buffer = 0;
+ var bufferS = 0;
+ for (var i = 0; i < b64.length; i++)
+ {
+ if (b64[i] == '=') { bufferS = 0; continue; };
+ var val = dec1(b64[i]);
+ if (val == 255) return null;
+ buffer = ((buffer << 6) | val) & 0xFFFF;
+ bufferS += 6;
+ if (bufferS >= 8)
+ {
+ var shift = (16 - bufferS) & 0xFF;
+ buffer = (buffer << shift) & 0xFFFF;
+ ret[ret.length] = (buffer >>> 8) & 0xFF;
+ buffer = buffer & 0x00FF;
+ buffer = (buffer >>> shift) & 0xFFFF;
+ bufferS -= 8;
+ }
+ }
+ if (bufferS > 0)
+ {
+ buffer = (buffer << (16 - bufferS)) & 0xFFFF;
+ ret[ret.length] = (buffer >>> 8) & 0xFF;
+ }
+ return new Uint8Array(ret);
+ }
+ },
+
+ Hex:
+ {
+ encode: (x) =>
+ {
+ var ret = "";
+ x.forEach(y => { ret += y.toString(16).padStart(2, "0"); });
+ return ret;
+ },
+ decode: (x) =>
+ {
+ var arr = new Array(0);
+ for (var i = 0; i < x.length; i += 2)
+ {
+ arr[arr.length] = parseInt(x.charAt(i) + x.charAt(i + 1), 16);
+ }
+ return new Uint8Array(arr);
+ }
+ },
+
+ generateRequest: async (msg) =>
+ {
+ msg = new TextEncoder().encode(msg);
+ var key = EstoulsAPI.Hex.decode(EstoulsAPI.apikey);
+ //var sess = crypto.getRandomValues(new Uint8Array(12));
+ var sess = new Uint8Array([0xba, 0x1f, 0x34, 0xdf, 0xf1, 0xb1, 0xd1, 0xce, 0xfb, 0x72, 0x28, 0x2b]);
+ var rawdata = await EstoulsAPI.ChaCha20.encrypt(key, sess, 0, msg);
+ var dgst = await EstoulsAPI.Hex.encode(await EstoulsAPI.HMACSHA256.sign(key, rawdata));
+ var data = await EstoulsAPI.Base64.encode(rawdata);
+ var resp = "user=" + EstoulsAPI.username;
+ resp += "&sess=" + EstoulsAPI.Hex.encode(sess);
+ resp += "&dgst=" + dgst;
+ resp += "&data=" + data;
+ return resp;
+ },
+
+ parseResponse: async (msg) =>
+ {
+ if (!msg.includes("user=") || !msg.includes("&sess=") || !msg.includes("&dgst=") || !msg.includes("&data="))
+ {
+ return { success: false, response: "invalid response" };
+ }
+ var user = msg.split("user=")[1].split("&")[0].trim();
+ var sess = msg.split("&sess=")[1].split("&")[0].trim();
+ var dgst = msg.split("&dgst=")[1].split("&")[0].trim();
+ var data = msg.split("&data=")[1].split("&")[0].trim();
+
+ if (user != EstoulsAPI.username)
+ {
+ return { success: false, response: "invalid user in response" };
+ }
+ data = EstoulsAPI.Base64.decode(data);
+ sess = EstoulsAPI.Hex.decode(sess);
+ var key = EstoulsAPI.Hex.decode(EstoulsAPI.apikey);
+ var sig = await EstoulsAPI.Hex.encode(await EstoulsAPI.HMACSHA256.sign(key, data));
+ if (sig != dgst)
+ {
+ return { success: false, response: "invalid signature" };
+ }
+ var resp = await EstoulsAPI.ChaCha20.encrypt(key, sess, 0, data);
+ return { success: true, response: new TextDecoder().decode(resp) };
+ },
+
+ request: async (msg) =>
+ {
+ if (msg.trim().length == 0) return { success: true, response: "" };
+ var req = await EstoulsAPI.generateRequest(msg);
+ return await new Promise(r =>
+ {
+ const xhr = new XMLHttpRequest();
+ xhr.open("POST", EstoulsAPI.endpoint, true);
+ xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+ xhr.onreadystatechange = (x) =>
+ {
+ if (xhr.readyState === XMLHttpRequest.DONE)
+ {
+ if (xhr.status === 200)
+ {
+ EstoulsAPI.parseResponse(xhr.responseText).then(pr => { r(pr); }).catch(x => alert("ERR(3)"));
+ }
+ else
+ {
+ r({success: false, response: xhr.status});
+ }
+ }
+ }
+ xhr.send(req);
+ }).catch(x => alert("ERR(1): " + x));
+ }
+
+};
+</script>
+
+
+
+<style>
+body { background-color: #1f1f1f; }
+table { background-color: #1f1f1f; }
+.basis
+{
+ font-size: 14px;
+ position: absolute;
+ font-family: monospace;
+ color: rgba(255, 255, 255, 0.5);
+ width: 100%;
+ background-color: rgba(0, 0, 0, 0);
+ overflow-y: auto;
+ outline: none;
+ white-space: nowrap;
+}
+.overlay
+{
+ font-size: 14px;
+ pointer-events: none;
+ font-family: monospace;
+ width: 100%;
+ background-color: #1f1f1f;
+ color: #9cdcfe;
+ font-weight: bold;
+ overflow-y: auto;
+ outline: none;
+ white-space: nowrap;
+}
+.linecounter
+{
+ font-size: 14px;
+ width: 100%;
+ text-align: center;
+ font-weight: bold;
+ font-family: monospace;
+ background-color: #1f1f1f;
+ color: #6e7681;
+ overflow-y: auto;
+ &::-webkit-scrollbar { display: none; }
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+}
+
+.bracket { color: #da70d6; }
+.number { color: #b5cea8; }
+.comment { color: #6a9955; }
+.keyword { color: #dcdcaa; }
+.semicolon { color: #ffffff; }
+
+table { width: 100%; }
+
+.response
+{
+ color: red;
+ font-weight: bold;
+ font-family: monospace;
+}
+
+.optionspanel
+{
+ text-align: right;
+ width: 100%;
+}
+.option
+{
+ font-size: 24px;
+ font-weight: bold;
+ font-family: monospace;
+ cursor: pointer;
+ background-color: rgba(0,0,0,0);
+ padding: 0;
+ margin: 0;
+ border: 0;
+ display: inline-block;
+}
+#run { color: green; }
+#setup { font-size: 28px; color: grey; }
+#save,#help { font-size: 20px; display: none; }
+#load,#copy,#clear,#apk { font-size: 20px; }
+#apkwrapper { display: none; }
+
+</style>
+
+<div id="horizontal">
+
+<div class="optionspanel">
+ <button class="option" id="load">📂</button>
+ <button class="option" id="copy">📋</button>
+ <button class="option" id="clear">🧹</button>
+ <button class="option" id="save">💾</button><!-- -->
+ <button class="option" id="run">▶</button><!-- -->
+ <button class="option" id="help">❓</button>
+ <div id="apkwrapper"><button class="option" id="apk">📲</button> </div>
+ <button class="option" id="setup">⚙</button>
+</div>
+<table border="0">
+ <tr>
+ <td style="width: 3%;"><div class="linecounter">1</div></td>
+ <td style="width: 96%; text-align: top;">
+ <div class="basis" contenteditable spellcheck="false" autocorrect="off" autocapitalization="off"></div>
+ <div class="overlay"></div>
+ </td>
+ </tr>
+ <!-- <div>qreg q[2];</div><div>h q[0];</div><div>cx q[0], q[1];</div><div>sample;</div> -->
+</table>
+<hr style='border-color: #6e7681;' />
+<div class="response"></div>
+</div>
+
+<script>
+window.$ = x => document.querySelectorAll(x);
+var escapables =
+[
+ [ "&", "&" ],
+ [ " ", " " ],
+ [ ">", ">" ],
+ [ "<", "<" ],
+];
+
+function htmlToText(x, y)
+{
+ var str;
+ if (y == undefined)
+ {
+ var div = document.createElement("div");
+ div.innerHTML = x;
+ str = htmlToText(x, div);
+ if (str.length > 0)
+ {
+ str = str.substring(0, str.length - 1);
+ }
+ return str;
+ }
+
+ if (y.nodeType == Node.TEXT_NODE)
+ {
+ return y.textContent + "\n";
+ }
+
+ str = "";
+ if (y.tagName == "BR")
+ {
+ var prev = y.previousSibling;
+ var next = y.nextSibling;
+ window.brb = y;
+
+ str = "\n";
+ /*if (prev == null && next == null)
+ {
+ str = "\n";
+ }
+ else if (prev != null && prev.nodeType != Node.TEXT_NODE)
+ {
+ str = "\n";
+ }
+ else if (next != null && next.nodeType != Node.TEXT_NODE)
+ {
+ str = "\n";
+ }*/
+ if (next != null && next.tagName == "DIV")
+ {
+ str = "";
+ if (prev == null)
+ {
+ str = "\n";
+ }
+ }
+ }
+
+ for (var i = 0; i < y.childNodes.length; i++)
+ {
+ str += htmlToText(x, y.childNodes[i]);
+ }
+ return str;
+}
+
+function removeEmptyNodes(v)
+{
+ if (window.pauseCleaning) return;
+ if (v == undefined)
+ {
+ removeEmptyNodes($(".basis")[0]);
+ return;
+ }
+ /*
+ if (v.tagName == "SPAN" && v.childNodes.length > 0)
+ {
+ v.parentNode.insertBefore(v.childNodes[0], v);
+ v.remove();
+ return;
+ }
+ */;
+ if (v.tagName != "DIV")
+ {
+ return;
+ }
+ if (v.className != "basis")
+ {
+ if (v.innerHTML.length == 0)
+ {
+ v.remove();
+ return;
+ }
+ }
+ for (var i = 0; i < v.childNodes.length; i++)
+ {
+ removeEmptyNodes(v.childNodes[i]);
+ }
+}
+
+function getText(x, y, z)
+{
+ if (x == undefined || y == undefined)
+ {
+ z = [];
+ getText([], $(".basis")[0], z);
+ if (z.length == 0) return "";
+ z.sort((a, b) => a[0] - b[0]);
+ while (z.length > 0 && z[0][0] == 0)
+ {
+ z.splice(0, 1);
+ }
+ if (z.length == 0) return "";
+ var str = "";
+ var prevline = z[0][0];
+ for (var i = 0; i < z.length; i++)
+ {
+ if (Math.abs(prevline - z[i][0]) > 5)
+ {
+ str += "\n";
+ }
+ str += z[i][1];
+ prevline = z[i][0];
+ }
+ return str.replaceAll(String.fromCharCode(160), " ");
+ }
+
+ if (y.nodeType == Node.TEXT_NODE)
+ {
+ var r = document.createRange();
+ r.selectNodeContents(y);
+ var n = parseFloat(r.getBoundingClientRect().y.toString());
+ n += parseFloat($(".basis")[0].scrollTop.toString());
+ z[z.length] = [ r.getBoundingClientRect().y + $(".basis")[0].scrollTop, y.textContent ];
+ return;
+ }
+ else if (y.className != "basis")
+ {
+ var n = parseFloat(y.getBoundingClientRect().y.toString());
+ n += parseFloat($(".basis")[0].scrollTop.toString());
+ z[z.length] = [ y.getBoundingClientRect().y + $(".basis")[0].scrollTop, "" ];
+ }
+
+ for (var i = 0; i < y.childNodes.length; i++)
+ {
+ getText(x, y.childNodes[i], z);
+ }
+}
+
+function textToNodes(txt)
+{
+ escapables.forEach(x => { txt = txt.replaceAll(x[1], x[0]); });
+ var out = [];
+ txt = txt.replaceAll("\r", "").replaceAll(" ", " ").split("\n");
+ for (var i = 0; i < txt.length; i++)
+ {
+ var tmp = txt[i].trim();
+ if (i == 0)
+ {
+ if (tmp.length > 0)
+ {
+ escapables.forEach(x => tmp = tmp.replaceAll(x[0], x[1]));
+ out[out.length] = document.createTextNode(tmp);
+ }
+ }
+ else
+ {
+ out[out.length] = document.createElement("div");
+ if (tmp.length == 0)
+ {
+ out[out.length - 1].innerHTML = "<br>";
+ }
+ else
+ {
+ out[out.length - 1].innerHTML = tmp;
+ }
+ }
+ }
+ return out;
+}
+
+function setText(txt)
+{
+ escapables.forEach(x => { txt = txt.replaceAll(x[1], x[0]); });
+ var out = "";
+ txt = txt.trim().replaceAll("\r", "").split("\n");
+ for (var i = 0; i < txt.length; i++)
+ {
+ var tmp = txt[i].trim();
+ if (tmp.length == 0)
+ {
+ out += "<div><br></div>";
+ }
+ else
+ {
+ out += "<div>" + tmp + "</div>";
+ }
+ }
+ out = out.replaceAll(" ", " ");
+ $(".basis")[0].innerHTML = out;
+}
+
+function highlightForQAnsel(txt)
+{
+ var lhs = "!!!" + Math.random().toString().replace(".", "0") + "!!!";
+ var rhs = "!!!" + Math.random().toString().replace(".", "1") + "!!!";
+ txt = txt.replaceAll(/[&].*?[;]/g, x => lhs + x.replace("&", "").replace(";", "") + rhs);
+ txt = txt.replaceAll(/[;]/g, "<span class='semicolon'>;</span>");
+ txt = txt.replaceAll(new RegExp(lhs + ".*?" + rhs, "g"), x => "&" + x.replace(lhs, "").replace(rhs, "") + ";");
+
+ txt = txt.replaceAll
+ (
+ /[\[][0-9][0-9]*[\]]/g,
+ x =>
+ {
+ x = x.replace("[", "<span class='bracket'>[</span><span class='number'>");
+ x = x.replace("]", "</span><span class='bracket'>]</span>");
+ return x;
+ }
+ );
+ //u(#,#,#); rx(#); ry(#); rz(#); if(c==#); if(c[#]==%i)
+ var keywords =
+ [
+ "qreg",
+ "creg",
+ "density",
+ "h",
+ "x",
+ "y",
+ "z",
+ "t",
+ "s",
+ "cx",
+ "swap",
+ "cswap",
+ "fredkin",
+ "ccx",
+ "toffoli",
+ "measure",
+ "print",
+ "sample",
+ "reset",
+ "barrier",
+ "hvar",
+ "rand",
+ "born"
+ ];
+
+ for (var i = 0; i < keywords.length; i++)
+ {
+ var tmp = new RegExp("(^|[ ]|[;]|[>])" + keywords[i] + "($|[ ]|[<]|[&])", "g");
+ txt = txt.replaceAll(tmp, x =>
+ {
+ var c = x.charAt(0);
+ var tagonL = "";
+ if (c == " " || c == ";" || c == ">")
+ {
+ tagonL = c;
+ x = x.substring(1, x.length);
+ }
+ var tagonR = "";
+ c = x.charAt(x.length - 1);
+ if (c == " " || c == "<" || c == "&")
+ {
+ tagonR = c;
+ x = x.substring(0, x.length - 1);
+ }
+ return tagonL + "<span class='keyword'>" + x + "</span>" + tagonR;
+ });
+ }
+ txt = txt.replaceAll
+ (
+ /[/][/].*?([<]div|[<]br|$)/g,
+ x =>
+ {
+ var tagon = "";
+ var tagons = [ "<div", "<br" ];
+ for (var i = 0; i < tagons.length; i++)
+ {
+ if (x.substring(x.length - tagons[i].length, x.length) == tagons[i])
+ {
+ x = x.substring(0, x.length - tagons[i].length);
+ tagon = tagons[i];
+ break;
+ }
+ }
+ x = x.replaceAll(/[<]span[ ]class[=]['].*?['][>]/g, "<span>");
+ x = "<span class='comment'>" + x + "</span>" + tagon;
+ return x;
+ }
+ );
+ return txt;
+}
+
+if (window.location.hash == "#mobile")
+{
+ $("#horizontal")[0].remove();
+ var iframe = document.createElement("iframe");
+ iframe.src = window.location.href.split("#")[0];
+ iframe.style.width = window.innerHeight + "px";
+ iframe.style.height = window.innerWidth + "px";
+ iframe.style.transformOrigin = "top left";
+ iframe.style.transform = "rotate(90deg)";
+ iframe.style.position = "fixed";
+ iframe.style.top = "0px";
+ iframe.style.left = window.innerWidth + "px";
+ iframe.frameBorder = "0";
+ document.body.appendChild(iframe);
+
+}
+else
+{
+ window.addEventListener("DOMContentLoaded", () =>
+ {
+ if (window.location.href.includes(".com") || window.location.href.includes("192."))
+ {
+ $("#apkwrapper")[0].style.display = "inline-block";
+ }
+ setTimeout(function()
+ {
+ try
+ {
+ var tmp = Math.round($(".overlay")[0].getBoundingClientRect().width) + "px";
+ $(".overlay")[0].style.width = tmp;
+ $(".basis")[0].style.width = tmp;
+ $(".basis,.overlay,.linecounter").forEach(x =>
+ {
+ x.style.height = (window.innerHeight * 0.40) + "px";
+ });
+ $(".response")[0].style.height = (window.innerHeight * 0.40) + "px";
+
+ var prevText = localStorage.getItem("previous_text");
+ if (prevText)
+ {
+ if (typeof(prevText) == "string")
+ {
+ setText(prevText);
+ }
+ }
+ }
+ catch (x)
+ {
+ alert("ERR(4): " + x);
+ }
+ }, 100);
+
+ //$(".basis")[0].style.top = Math.round($(".overlay")[0].getBoundingClientRect().top) + "px";
+ //$(".basis")[0].style.left = Math.round($(".overlay")[0].getBoundingClientRect().left) + "px";
+ });
+
+ $("#clear")[0].addEventListener("click", () =>
+ {
+ setText("");
+ });
+
+ $("#apk")[0].addEventListener("click", () =>
+ {
+ var a = document.createElement("a");
+ a.href = "/apk";
+ a.download = "EstoulsAPI.apk";
+ a.style.display = "none";
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ });
+
+ $("#save")[0].addEventListener("click", () =>
+ {
+ var txt = btoa($(".basis")[0].innerText);
+ txt = "data:text/plain;base64," + txt;
+ var a = document.createElement("a");
+ a.href = txt;
+ a.download = "program.txt";
+ a.style.display = "none";
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ });
+
+ $("#copy")[0].addEventListener("click", () =>
+ {
+ var tmp = document.createElement('textarea');
+ tmp.value = getText();
+ document.body.appendChild(tmp);
+ tmp.focus();
+ tmp.select();
+ document.execCommand('copy');
+ tmp.remove();
+ });
+
+
+ $("#help")[0].addEventListener("click", () =>
+ {
+ window.location.href = "https://doi.org/10.48550/arXiv.1707.03429";
+ });
+
+ $("#load")[0].addEventListener("click", () =>
+ {
+ var load = document.createElement("input");
+ load.type = "file";
+ load.style.display = "none";
+ /*while ($("input[type=file]").length > 0)
+ {
+ $("input[type=file]")[0].remove();
+ }*/
+ window.pr = new Promise(r =>
+ {
+ load.addEventListener("change", () =>
+ {
+ var reader = new FileReader();
+ reader.addEventListener("load", (e) =>
+ {
+ r(atob(e.target.result.split(",")[1]));
+ load.remove();
+ });
+ reader.readAsDataURL(load.files[0]);
+ });
+ document.body.appendChild(load);
+ load.click();
+ }).then(text =>
+ {
+ var tmp = "!!!" + Math.random().toString().replace(".", "0") + "!!!";
+ text = text.trim().replaceAll("\n", tmp).replaceAll("\r", "");
+ tmp = text.replaceAll(tmp, "</div><div>");
+ if (tmp.substring(0, 5) != "<div>")
+ {
+ tmp = "<div>" + tmp;
+ }
+ if (tmp.substring(tmp.length - 5, tmp.length) == "<div>")
+ {
+ tmp = tmp.substring(0, tmp.length - 5);
+ }
+ tmp = tmp.replaceAll("<div></div>", "<div><br></div>");
+ $(".basis")[0].innerHTML = tmp;
+ });
+ });
+
+ $("#setup")[0].addEventListener("click", () =>
+ {
+ var msg = "username:apikey:endpoint";
+ var dat = localStorage.getItem("api_info");
+ dat = dat == null ? prompt(msg) : prompt(msg, dat);
+ if (dat == null) return;
+ dat = dat.replace("http://", "").replace("https://", "");
+ localStorage.setItem("api_info", dat);
+ });
+
+ $("#run")[0].addEventListener("click", () =>
+ {
+ var dat = localStorage.getItem("api_info");
+ if (dat == null || dat == undefined)
+ {
+ $(".response")[0].style.color = "red";
+ $(".response")[0].innerText = "API info not set.\nPlease press the gear on the top-right.";
+ return;
+ }
+ dat = dat.split(":");
+ if (dat.length != 3)
+ {
+ $(".response")[0].style.color = "red";
+ $(".response")[0].innerText = "API info not set correctly.\nPlease press the gear on the top-right.";
+ return;
+ }
+
+ EstoulsAPI.username = dat[0];
+ EstoulsAPI.apikey = dat[1];
+ EstoulsAPI.endpoint = "https://" + dat[2];
+ EstoulsAPI.request(getText()).then(x =>
+ {
+ if (x.success && x.response.trim().length > 0)
+ {
+ $(".response")[0].style.color = "green";
+ $(".response")[0].innerText = x.response;
+ }
+ else if (x.success && x.response.trim().length == 0)
+ {
+ $(".response")[0].style.color = "yellow";
+ $(".response")[0].innerText = "Empty response.";
+ }
+ else
+ {
+ $(".response")[0].style.color = "red";
+ $(".response")[0].innerText = x.response;
+ }
+ }).catch(x => alert("ERR(2)" + x));
+ });
+
+ $(".basis")[0].addEventListener("paste", (e) =>
+ {
+ window.pauseCleaning = true;
+ e.preventDefault();
+ var text = (event.clipboardData || window.clipboardData).getData('text');
+ //text = htmlToText(text);
+ var sel = window.getSelection();
+ if (sel.rangeCount > 0)
+ {
+ var range = sel.getRangeAt(0);
+ range.deleteContents();
+ if (!text.includes("\n"))
+ {
+ var textNode = document.createTextNode(text);
+ range.insertNode(textNode);
+ range.setStartAfter(textNode);
+ range.setEndAfter(textNode);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+ else
+ {
+ var last;
+ textToNodes(text).forEach(x =>
+ {
+ range.insertNode(x);
+ range.setStartAfter(x);
+ range.setEndAfter(x);
+ });
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+ }
+ window.pauseCleaning = undefined;
+ });
+
+ setInterval(() =>
+ {
+ if (window.stopMainLoop) return;
+ try
+ {
+ removeEmptyNodes();
+ $(".overlay")[0].scrollTop = $(".basis")[0].scrollTop;
+ $(".overlay")[0].scrollLeft = $(".basis")[0].scrollLeft;
+ $(".linecounter")[0].scrollTop = $(".basis")[0].scrollTop;
+ $(".linecounter")[0].scrollTop = $(".basis")[0].scrollTop;
+ var txt = $(".basis")[0].innerHTML;
+ if (txt == window.oldHTML) return;
+ localStorage.setItem("previous_text", getText());
+
+ //<fix for a mobile bug>
+ /*
+ var pos = 0;
+ var found = false;
+ for (; pos < txt.length; pos++)
+ {
+ if (txt.substring(pos, pos + "<div>".length) == "<div>")
+ {
+ found = true;
+ break;
+ }
+ }
+ if (pos != 0 && found)
+ {
+ var lhs = txt.substring(0, pos);
+ var rhs = txt.substring(pos, txt.length);
+ txt = "<div>" + lhs + "</div>" + rhs;
+ $(".basis")[0].innerHTML = txt;
+ }
+ if (txt.includes("</div><br><div>"))
+ {
+ txt = txt.replaceAll("</div><br><div>", "");
+ $(".basis")[0].innerHTML = txt;
+
+ }*/
+ //</fix for a mobile bug>
+
+ if (txt.includes("qreg") || txt.includes("creg"))
+ {
+ txt = highlightForQAnsel(txt);
+ }
+
+ $(".overlay")[0].innerHTML = txt;
+ window.oldHTML = $(".basis")[0].innerHTML;
+ txt = $(".basis")[0].innerHTML;
+ var linecount = getText().split("\n").length;
+ txt = "";
+ for (var i = 0; i < linecount; i++)
+ {
+ txt += (i + 1) + "<br />";
+ }
+ $(".linecounter")[0].innerHTML = txt;
+ }
+ catch (x)
+ {
+ alert("ERR(3): " + x);
+ window.stopMainLoop = true;
+ }
+ }, 10);
+}
+</script>
+</body>
+</html>
+
--- /dev/null
+#!/bin/bash
+if [ "$1" == "run" ]
+then
+ while [ 1 ]
+ do
+ ./bin/APIServer
+ done
+else
+ xxd -i ./rcs/frontend.html ./src/frontend.h
+ gcc ./src/main.c -o ./bin/APIServer $(mysql_config --cflags --libs) -lCryptoFoleo
+ rm ./src/frontend.h
+ cp ./rcs/* ./bin/
+fi
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+ <title>EstoulsAPI</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<script>
+
+var EstoulsAPI =
+{
+ username: undefined,
+ apikey: undefined,
+ endpoint: undefined,
+
+ Math32:
+ {
+ clamp: a =>
+ {
+ while (a < 0 || a > 0xFFFFFFFF)
+ a += a < 0 ? 0x100000000 : -0x100000000;
+ return a;
+ },
+ shl: (a, b) => EstoulsAPI.Math32.clamp(a << b),
+ shr: (a, b) => EstoulsAPI.Math32.clamp(a >>> b),
+ rtl: (a, b) => EstoulsAPI.Math32.clamp((a << b) | (a >>> (32 - b))),
+ rtr: (a, b) => EstoulsAPI.Math32.clamp((a >>> b) | (a << (32 - b))),
+ add: (a, b) => EstoulsAPI.Math32.clamp(a + b),
+ sub: (a, b) => EstoulsAPI.Math32.clamp(a + b),
+ or: (a, b) => EstoulsAPI.Math32.clamp(a | b),
+ xor: (a, b) => EstoulsAPI.Math32.clamp(a ^ b)
+ },
+ HMACSHA256:
+ {
+ sign: async (key, msg) =>
+ {
+ key = new Uint8Array(key);
+ msg = new Uint8Array(msg);
+ var ikey = await crypto.subtle.importKey
+ (
+ "raw",
+ key,
+ { name: "HMAC", hash: { name: "SHA-256" }},
+ false,
+ ["sign", "verify"]
+ );
+ return new Uint8Array(await window.crypto.subtle.sign({ name: "HMAC", }, ikey, msg));
+ }
+ },
+ ChaCha20:
+ {
+ QR: (cc, a, b, c, d) =>
+ {
+ cc[a] = EstoulsAPI.Math32.add(cc[a], cc[b]);
+ cc[d] = EstoulsAPI.Math32.xor(cc[d], cc[a]);
+ cc[d] = EstoulsAPI.Math32.rtl(cc[d], 16);
+
+ cc[c] = EstoulsAPI.Math32.add(cc[c], cc[d]);
+ cc[b] = EstoulsAPI.Math32.xor(cc[b], cc[c]);
+ cc[b] = EstoulsAPI.Math32.rtl(cc[b], 12);
+
+ cc[a] = EstoulsAPI.Math32.add(cc[a], cc[b]);
+ cc[d] = EstoulsAPI.Math32.xor(cc[d], cc[a]);
+ cc[d] = EstoulsAPI.Math32.rtl(cc[d], 8);
+
+ cc[c] = EstoulsAPI.Math32.add(cc[c], cc[d]);
+ cc[b] = EstoulsAPI.Math32.xor(cc[b], cc[c]);
+ cc[b] = EstoulsAPI.Math32.rtl(cc[b], 7);
+ },
+ DR: (cc) =>
+ {
+ EstoulsAPI.ChaCha20.QR(cc, 0, 4, 8, 12);
+ EstoulsAPI.ChaCha20.QR(cc, 1, 5, 9, 13);
+ EstoulsAPI.ChaCha20.QR(cc, 2, 6, 10, 14);
+ EstoulsAPI.ChaCha20.QR(cc, 3, 7, 11, 15);
+ EstoulsAPI.ChaCha20.QR(cc, 0, 5, 10, 15);
+ EstoulsAPI.ChaCha20.QR(cc, 1, 6, 11, 12);
+ EstoulsAPI.ChaCha20.QR(cc, 2, 7, 8, 13);
+ EstoulsAPI.ChaCha20.QR(cc, 3, 4, 9, 14);
+ },
+ CB: (cc) =>
+ {
+ var i;
+ var x = new Array(16);
+ for (i = 0; i < 16; i++)
+ {
+ x[i] = cc[i];
+ }
+ for (i = 0; i < 10; i++)
+ {
+ EstoulsAPI.ChaCha20.DR(cc);
+ }
+ for (i = 0; i < 16; i++)
+ {
+ cc[i] = EstoulsAPI.Math32.add(cc[i], x[i]);
+ }
+ },
+ S: (cc, cs) =>
+ {
+ for (var i = 0; i < 16; i++)
+ {
+ cs[4 * i] = (cc[i] & 0xFF);
+ cs[4 * i + 1] = ((cc[i] >> 8) & 0xFF);
+ cs[4 * i + 2] = ((cc[i] >> 16) & 0xFF);
+ cs[4 * i + 3] = ((cc[i] >> 24) & 0xFF);
+ }
+ },
+ B: (key, nonce, block, out) =>
+ {
+ var cc =
+ [
+ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
+
+ key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24),
+ key[4] | (key[5] << 8) | (key[6] << 16) | (key[7] << 24),
+ key[8] | (key[9] << 8) | (key[10] << 16) | (key[11] << 24),
+ key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24),
+
+ key[16] | (key[17] << 8) | (key[18] << 16) | (key[19] << 24),
+ key[20] | (key[21] << 8) | (key[22] << 16) | (key[23] << 24),
+ key[24] | (key[25] << 8) | (key[26] << 16) | (key[27] << 24),
+ key[28] | (key[29] << 8) | (key[30] << 16) | (key[31] << 24),
+
+ block,
+
+ nonce[0] | (nonce[1] << 8) | (nonce[2] << 16) | (nonce[3] << 24),
+ nonce[4] | (nonce[5] << 8) | (nonce[6] << 16) | (nonce[7] << 24),
+ nonce[8] | (nonce[9] << 8) | (nonce[10] << 16) | (nonce[11] << 24)
+ ];
+
+ EstoulsAPI.ChaCha20.CB(cc);
+ EstoulsAPI.ChaCha20.S(cc, out);
+ },
+ encrypt: async (key, nonce, block, data) =>
+ {
+ var count = data.length;
+ if (count > (274877906944 - block * 64)) return null;
+ var ret = new Array(0);
+ var ccblock = new Array(64);
+ var size = 0;
+ while (count > 64)
+ {
+ ret.length = size + 64;
+ EstoulsAPI.ChaCha20.B(key, nonce, block++, ccblock);
+ for (var i = 0; i < 64; i++) ret[size + i] = ccblock[i];
+ size += 64;
+ count -= 64;
+ }
+ if (count > 0)
+ {
+ ret.length = size + count;
+ EstoulsAPI.ChaCha20.B(key, nonce, block, ccblock);
+ for (var i = 0; i < count; i++) ret[size + i] = ccblock[i];
+ }
+ for (var i = 0; i < data.length; i++) ret[i] ^= data[i];
+ return new Uint8Array(ret);
+ }
+ },
+
+ Base64:
+ {
+ encode: async (x) =>
+ {
+ return await new Promise(r =>
+ {
+ const reader = new FileReader();
+ reader.addEventListener("load", () => r(reader.result.split(",")[1]));
+ reader.readAsDataURL(new Blob([new Uint8Array(x)]));
+ });
+ },
+
+ decode: (b64) =>
+ {
+ var dec1 = (v) =>
+ {
+ switch (v)
+ {
+ case 'A': return 0;
+ case 'B': return 1;
+ case 'C': return 2;
+ case 'D': return 3;
+ case 'E': return 4;
+ case 'F': return 5;
+ case 'G': return 6;
+ case 'H': return 7;
+ case 'I': return 8;
+ case 'J': return 9;
+ case 'K': return 10;
+ case 'L': return 11;
+ case 'M': return 12;
+ case 'N': return 13;
+ case 'O': return 14;
+ case 'P': return 15;
+ case 'Q': return 16;
+ case 'R': return 17;
+ case 'S': return 18;
+ case 'T': return 19;
+ case 'U': return 20;
+ case 'V': return 21;
+ case 'W': return 22;
+ case 'X': return 23;
+ case 'Y': return 24;
+ case 'Z': return 25;
+ case 'a': return 26;
+ case 'b': return 27;
+ case 'c': return 28;
+ case 'd': return 29;
+ case 'e': return 30;
+ case 'f': return 31;
+ case 'g': return 32;
+ case 'h': return 33;
+ case 'i': return 34;
+ case 'j': return 35;
+ case 'k': return 36;
+ case 'l': return 37;
+ case 'm': return 38;
+ case 'n': return 39;
+ case 'o': return 40;
+ case 'p': return 41;
+ case 'q': return 42;
+ case 'r': return 43;
+ case 's': return 44;
+ case 't': return 45;
+ case 'u': return 46;
+ case 'v': return 47;
+ case 'w': return 48;
+ case 'x': return 49;
+ case 'y': return 50;
+ case 'z': return 51;
+ case '0': return 52;
+ case '1': return 53;
+ case '2': return 54;
+ case '3': return 55;
+ case '4': return 56;
+ case '5': return 57;
+ case '6': return 58;
+ case '7': return 59;
+ case '8': return 60;
+ case '9': return 61;
+ case '+': return 62;
+ case '/': return 63;
+ }
+ return 255;
+ };
+
+ var ret = new Array(0);
+ var buffer = 0;
+ var bufferS = 0;
+ for (var i = 0; i < b64.length; i++)
+ {
+ if (b64[i] == '=') { bufferS = 0; continue; };
+ var val = dec1(b64[i]);
+ if (val == 255) return null;
+ buffer = ((buffer << 6) | val) & 0xFFFF;
+ bufferS += 6;
+ if (bufferS >= 8)
+ {
+ var shift = (16 - bufferS) & 0xFF;
+ buffer = (buffer << shift) & 0xFFFF;
+ ret[ret.length] = (buffer >>> 8) & 0xFF;
+ buffer = buffer & 0x00FF;
+ buffer = (buffer >>> shift) & 0xFFFF;
+ bufferS -= 8;
+ }
+ }
+ if (bufferS > 0)
+ {
+ buffer = (buffer << (16 - bufferS)) & 0xFFFF;
+ ret[ret.length] = (buffer >>> 8) & 0xFF;
+ }
+ return new Uint8Array(ret);
+ }
+ },
+
+ Hex:
+ {
+ encode: (x) =>
+ {
+ var ret = "";
+ x.forEach(y => { ret += y.toString(16).padStart(2, "0"); });
+ return ret;
+ },
+ decode: (x) =>
+ {
+ var arr = new Array(0);
+ for (var i = 0; i < x.length; i += 2)
+ {
+ arr[arr.length] = parseInt(x.charAt(i) + x.charAt(i + 1), 16);
+ }
+ return new Uint8Array(arr);
+ }
+ },
+
+ generateRequest: async (msg) =>
+ {
+ msg = new TextEncoder().encode(msg);
+ var key = EstoulsAPI.Hex.decode(EstoulsAPI.apikey);
+ //var sess = crypto.getRandomValues(new Uint8Array(12));
+ var sess = new Uint8Array([0xba, 0x1f, 0x34, 0xdf, 0xf1, 0xb1, 0xd1, 0xce, 0xfb, 0x72, 0x28, 0x2b]);
+ var rawdata = await EstoulsAPI.ChaCha20.encrypt(key, sess, 0, msg);
+ var dgst = await EstoulsAPI.Hex.encode(await EstoulsAPI.HMACSHA256.sign(key, rawdata));
+ var data = await EstoulsAPI.Base64.encode(rawdata);
+ var resp = "user=" + EstoulsAPI.username;
+ resp += "&sess=" + EstoulsAPI.Hex.encode(sess);
+ resp += "&dgst=" + dgst;
+ resp += "&data=" + data;
+ return resp;
+ },
+
+ parseResponse: async (msg) =>
+ {
+ if (!msg.includes("user=") || !msg.includes("&sess=") || !msg.includes("&dgst=") || !msg.includes("&data="))
+ {
+ return { success: false, response: "invalid response" };
+ }
+ var user = msg.split("user=")[1].split("&")[0].trim();
+ var sess = msg.split("&sess=")[1].split("&")[0].trim();
+ var dgst = msg.split("&dgst=")[1].split("&")[0].trim();
+ var data = msg.split("&data=")[1].split("&")[0].trim();
+
+ if (user != EstoulsAPI.username)
+ {
+ return { success: false, response: "invalid user in response" };
+ }
+ data = EstoulsAPI.Base64.decode(data);
+ sess = EstoulsAPI.Hex.decode(sess);
+ var key = EstoulsAPI.Hex.decode(EstoulsAPI.apikey);
+ var sig = await EstoulsAPI.Hex.encode(await EstoulsAPI.HMACSHA256.sign(key, data));
+ if (sig != dgst)
+ {
+ return { success: false, response: "invalid signature" };
+ }
+ var resp = await EstoulsAPI.ChaCha20.encrypt(key, sess, 0, data);
+ return { success: true, response: new TextDecoder().decode(resp) };
+ },
+
+ request: async (msg) =>
+ {
+ if (msg.trim().length == 0) return { success: true, response: "" };
+ var req = await EstoulsAPI.generateRequest(msg);
+ return await new Promise(r =>
+ {
+ const xhr = new XMLHttpRequest();
+ xhr.open("POST", EstoulsAPI.endpoint, true);
+ xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+ xhr.onreadystatechange = (x) =>
+ {
+ if (xhr.readyState === XMLHttpRequest.DONE)
+ {
+ if (xhr.status === 200)
+ {
+ EstoulsAPI.parseResponse(xhr.responseText).then(pr => { r(pr); }).catch(x => alert("ERR(3)"));
+ }
+ else
+ {
+ r({success: false, response: xhr.status});
+ }
+ }
+ }
+ xhr.send(req);
+ }).catch(x => alert("ERR(1): " + x));
+ }
+
+};
+</script>
+
+
+
+<style>
+body { background-color: #1f1f1f; }
+table { background-color: #1f1f1f; }
+.basis
+{
+ font-size: 14px;
+ position: absolute;
+ font-family: monospace;
+ color: rgba(255, 255, 255, 0.5);
+ width: 100%;
+ background-color: rgba(0, 0, 0, 0);
+ overflow-y: auto;
+ outline: none;
+ white-space: nowrap;
+}
+.overlay
+{
+ font-size: 14px;
+ pointer-events: none;
+ font-family: monospace;
+ width: 100%;
+ background-color: #1f1f1f;
+ color: #9cdcfe;
+ font-weight: bold;
+ overflow-y: auto;
+ outline: none;
+ white-space: nowrap;
+}
+.linecounter
+{
+ font-size: 14px;
+ width: 100%;
+ text-align: center;
+ font-weight: bold;
+ font-family: monospace;
+ background-color: #1f1f1f;
+ color: #6e7681;
+ overflow-y: auto;
+ &::-webkit-scrollbar { display: none; }
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+}
+
+.bracket { color: #da70d6; }
+.number { color: #b5cea8; }
+.comment { color: #6a9955; }
+.keyword { color: #dcdcaa; }
+.semicolon { color: #ffffff; }
+
+table { width: 100%; }
+
+.response
+{
+ color: red;
+ font-weight: bold;
+ font-family: monospace;
+}
+
+.optionspanel
+{
+ text-align: right;
+ width: 100%;
+}
+.option
+{
+ font-size: 24px;
+ font-weight: bold;
+ font-family: monospace;
+ cursor: pointer;
+ background-color: rgba(0,0,0,0);
+ padding: 0;
+ margin: 0;
+ border: 0;
+ display: inline-block;
+}
+#run { color: green; }
+#setup { font-size: 28px; color: grey; }
+#save,#help { font-size: 20px; display: none; }
+#load,#copy,#clear,#apk { font-size: 20px; }
+#apkwrapper { display: none; }
+
+</style>
+
+<div id="horizontal">
+
+<div class="optionspanel">
+ <button class="option" id="load">📂</button>
+ <button class="option" id="copy">📋</button>
+ <button class="option" id="clear">🧹</button>
+ <button class="option" id="save">💾</button><!-- -->
+ <button class="option" id="run">▶</button><!-- -->
+ <button class="option" id="help">❓</button>
+ <div id="apkwrapper"><button class="option" id="apk">📲</button> </div>
+ <button class="option" id="setup">⚙</button>
+</div>
+<table border="0">
+ <tr>
+ <td style="width: 3%;"><div class="linecounter">1</div></td>
+ <td style="width: 96%; text-align: top;">
+ <div class="basis" contenteditable spellcheck="false" autocorrect="off" autocapitalization="off"></div>
+ <div class="overlay"></div>
+ </td>
+ </tr>
+ <!-- <div>qreg q[2];</div><div>h q[0];</div><div>cx q[0], q[1];</div><div>sample;</div> -->
+</table>
+<hr style='border-color: #6e7681;' />
+<div class="response"></div>
+</div>
+
+<script>
+window.$ = x => document.querySelectorAll(x);
+var escapables =
+[
+ [ "&", "&" ],
+ [ " ", " " ],
+ [ ">", ">" ],
+ [ "<", "<" ],
+];
+
+function htmlToText(x, y)
+{
+ var str;
+ if (y == undefined)
+ {
+ var div = document.createElement("div");
+ div.innerHTML = x;
+ str = htmlToText(x, div);
+ if (str.length > 0)
+ {
+ str = str.substring(0, str.length - 1);
+ }
+ return str;
+ }
+
+ if (y.nodeType == Node.TEXT_NODE)
+ {
+ return y.textContent + "\n";
+ }
+
+ str = "";
+ if (y.tagName == "BR")
+ {
+ var prev = y.previousSibling;
+ var next = y.nextSibling;
+ window.brb = y;
+
+ str = "\n";
+ /*if (prev == null && next == null)
+ {
+ str = "\n";
+ }
+ else if (prev != null && prev.nodeType != Node.TEXT_NODE)
+ {
+ str = "\n";
+ }
+ else if (next != null && next.nodeType != Node.TEXT_NODE)
+ {
+ str = "\n";
+ }*/
+ if (next != null && next.tagName == "DIV")
+ {
+ str = "";
+ if (prev == null)
+ {
+ str = "\n";
+ }
+ }
+ }
+
+ for (var i = 0; i < y.childNodes.length; i++)
+ {
+ str += htmlToText(x, y.childNodes[i]);
+ }
+ return str;
+}
+
+function removeEmptyNodes(v)
+{
+ if (window.pauseCleaning) return;
+ if (v == undefined)
+ {
+ removeEmptyNodes($(".basis")[0]);
+ return;
+ }
+ /*
+ if (v.tagName == "SPAN" && v.childNodes.length > 0)
+ {
+ v.parentNode.insertBefore(v.childNodes[0], v);
+ v.remove();
+ return;
+ }
+ */;
+ if (v.tagName != "DIV")
+ {
+ return;
+ }
+ if (v.className != "basis")
+ {
+ if (v.innerHTML.length == 0)
+ {
+ v.remove();
+ return;
+ }
+ }
+ for (var i = 0; i < v.childNodes.length; i++)
+ {
+ removeEmptyNodes(v.childNodes[i]);
+ }
+}
+
+function getText(x, y, z)
+{
+ if (x == undefined || y == undefined)
+ {
+ z = [];
+ getText([], $(".basis")[0], z);
+ if (z.length == 0) return "";
+ z.sort((a, b) => a[0] - b[0]);
+ while (z.length > 0 && z[0][0] == 0)
+ {
+ z.splice(0, 1);
+ }
+ if (z.length == 0) return "";
+ var str = "";
+ var prevline = z[0][0];
+ for (var i = 0; i < z.length; i++)
+ {
+ if (Math.abs(prevline - z[i][0]) > 5)
+ {
+ str += "\n";
+ }
+ str += z[i][1];
+ prevline = z[i][0];
+ }
+ return str.replaceAll(String.fromCharCode(160), " ");
+ }
+
+ if (y.nodeType == Node.TEXT_NODE)
+ {
+ var r = document.createRange();
+ r.selectNodeContents(y);
+ var n = parseFloat(r.getBoundingClientRect().y.toString());
+ n += parseFloat($(".basis")[0].scrollTop.toString());
+ z[z.length] = [ r.getBoundingClientRect().y + $(".basis")[0].scrollTop, y.textContent ];
+ return;
+ }
+ else if (y.className != "basis")
+ {
+ var n = parseFloat(y.getBoundingClientRect().y.toString());
+ n += parseFloat($(".basis")[0].scrollTop.toString());
+ z[z.length] = [ y.getBoundingClientRect().y + $(".basis")[0].scrollTop, "" ];
+ }
+
+ for (var i = 0; i < y.childNodes.length; i++)
+ {
+ getText(x, y.childNodes[i], z);
+ }
+}
+
+function textToNodes(txt)
+{
+ escapables.forEach(x => { txt = txt.replaceAll(x[1], x[0]); });
+ var out = [];
+ txt = txt.replaceAll("\r", "").replaceAll(" ", " ").split("\n");
+ for (var i = 0; i < txt.length; i++)
+ {
+ var tmp = txt[i].trim();
+ if (i == 0)
+ {
+ if (tmp.length > 0)
+ {
+ escapables.forEach(x => tmp = tmp.replaceAll(x[0], x[1]));
+ out[out.length] = document.createTextNode(tmp);
+ }
+ }
+ else
+ {
+ out[out.length] = document.createElement("div");
+ if (tmp.length == 0)
+ {
+ out[out.length - 1].innerHTML = "<br>";
+ }
+ else
+ {
+ out[out.length - 1].innerHTML = tmp;
+ }
+ }
+ }
+ return out;
+}
+
+function setText(txt)
+{
+ escapables.forEach(x => { txt = txt.replaceAll(x[1], x[0]); });
+ var out = "";
+ txt = txt.trim().replaceAll("\r", "").split("\n");
+ for (var i = 0; i < txt.length; i++)
+ {
+ var tmp = txt[i].trim();
+ if (tmp.length == 0)
+ {
+ out += "<div><br></div>";
+ }
+ else
+ {
+ out += "<div>" + tmp + "</div>";
+ }
+ }
+ out = out.replaceAll(" ", " ");
+ $(".basis")[0].innerHTML = out;
+}
+
+function highlightForQAnsel(txt)
+{
+ var lhs = "!!!" + Math.random().toString().replace(".", "0") + "!!!";
+ var rhs = "!!!" + Math.random().toString().replace(".", "1") + "!!!";
+ txt = txt.replaceAll(/[&].*?[;]/g, x => lhs + x.replace("&", "").replace(";", "") + rhs);
+ txt = txt.replaceAll(/[;]/g, "<span class='semicolon'>;</span>");
+ txt = txt.replaceAll(new RegExp(lhs + ".*?" + rhs, "g"), x => "&" + x.replace(lhs, "").replace(rhs, "") + ";");
+
+ txt = txt.replaceAll
+ (
+ /[\[][0-9][0-9]*[\]]/g,
+ x =>
+ {
+ x = x.replace("[", "<span class='bracket'>[</span><span class='number'>");
+ x = x.replace("]", "</span><span class='bracket'>]</span>");
+ return x;
+ }
+ );
+ //u(#,#,#); rx(#); ry(#); rz(#); if(c==#); if(c[#]==%i)
+ var keywords =
+ [
+ "qreg",
+ "creg",
+ "density",
+ "h",
+ "x",
+ "y",
+ "z",
+ "t",
+ "s",
+ "cx",
+ "swap",
+ "cswap",
+ "fredkin",
+ "ccx",
+ "toffoli",
+ "measure",
+ "print",
+ "sample",
+ "reset",
+ "barrier",
+ "hvar",
+ "rand",
+ "born"
+ ];
+
+ for (var i = 0; i < keywords.length; i++)
+ {
+ var tmp = new RegExp("(^|[ ]|[;]|[>])" + keywords[i] + "($|[ ]|[<]|[&])", "g");
+ txt = txt.replaceAll(tmp, x =>
+ {
+ var c = x.charAt(0);
+ var tagonL = "";
+ if (c == " " || c == ";" || c == ">")
+ {
+ tagonL = c;
+ x = x.substring(1, x.length);
+ }
+ var tagonR = "";
+ c = x.charAt(x.length - 1);
+ if (c == " " || c == "<" || c == "&")
+ {
+ tagonR = c;
+ x = x.substring(0, x.length - 1);
+ }
+ return tagonL + "<span class='keyword'>" + x + "</span>" + tagonR;
+ });
+ }
+ txt = txt.replaceAll
+ (
+ /[/][/].*?([<]div|[<]br|$)/g,
+ x =>
+ {
+ var tagon = "";
+ var tagons = [ "<div", "<br" ];
+ for (var i = 0; i < tagons.length; i++)
+ {
+ if (x.substring(x.length - tagons[i].length, x.length) == tagons[i])
+ {
+ x = x.substring(0, x.length - tagons[i].length);
+ tagon = tagons[i];
+ break;
+ }
+ }
+ x = x.replaceAll(/[<]span[ ]class[=]['].*?['][>]/g, "<span>");
+ x = "<span class='comment'>" + x + "</span>" + tagon;
+ return x;
+ }
+ );
+ return txt;
+}
+
+if (window.location.hash == "#mobile")
+{
+ $("#horizontal")[0].remove();
+ var iframe = document.createElement("iframe");
+ iframe.src = window.location.href.split("#")[0];
+ iframe.style.width = window.innerHeight + "px";
+ iframe.style.height = window.innerWidth + "px";
+ iframe.style.transformOrigin = "top left";
+ iframe.style.transform = "rotate(90deg)";
+ iframe.style.position = "fixed";
+ iframe.style.top = "0px";
+ iframe.style.left = window.innerWidth + "px";
+ iframe.frameBorder = "0";
+ document.body.appendChild(iframe);
+
+}
+else
+{
+ window.addEventListener("DOMContentLoaded", () =>
+ {
+ if (window.location.href.includes(".com") || window.location.href.includes("192."))
+ {
+ $("#apkwrapper")[0].style.display = "inline-block";
+ }
+ setTimeout(function()
+ {
+ try
+ {
+ var tmp = Math.round($(".overlay")[0].getBoundingClientRect().width) + "px";
+ $(".overlay")[0].style.width = tmp;
+ $(".basis")[0].style.width = tmp;
+ $(".basis,.overlay,.linecounter").forEach(x =>
+ {
+ x.style.height = (window.innerHeight * 0.40) + "px";
+ });
+ $(".response")[0].style.height = (window.innerHeight * 0.40) + "px";
+
+ var prevText = localStorage.getItem("previous_text");
+ if (prevText)
+ {
+ if (typeof(prevText) == "string")
+ {
+ setText(prevText);
+ }
+ }
+ }
+ catch (x)
+ {
+ alert("ERR(4): " + x);
+ }
+ }, 100);
+
+ //$(".basis")[0].style.top = Math.round($(".overlay")[0].getBoundingClientRect().top) + "px";
+ //$(".basis")[0].style.left = Math.round($(".overlay")[0].getBoundingClientRect().left) + "px";
+ });
+
+ $("#clear")[0].addEventListener("click", () =>
+ {
+ setText("");
+ });
+
+ $("#apk")[0].addEventListener("click", () =>
+ {
+ var a = document.createElement("a");
+ a.href = "/apk";
+ a.download = "EstoulsAPI.apk";
+ a.style.display = "none";
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ });
+
+ $("#save")[0].addEventListener("click", () =>
+ {
+ var txt = btoa($(".basis")[0].innerText);
+ txt = "data:text/plain;base64," + txt;
+ var a = document.createElement("a");
+ a.href = txt;
+ a.download = "program.txt";
+ a.style.display = "none";
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ });
+
+ $("#copy")[0].addEventListener("click", () =>
+ {
+ var tmp = document.createElement('textarea');
+ tmp.value = getText();
+ document.body.appendChild(tmp);
+ tmp.focus();
+ tmp.select();
+ document.execCommand('copy');
+ tmp.remove();
+ });
+
+
+ $("#help")[0].addEventListener("click", () =>
+ {
+ window.location.href = "https://doi.org/10.48550/arXiv.1707.03429";
+ });
+
+ $("#load")[0].addEventListener("click", () =>
+ {
+ var load = document.createElement("input");
+ load.type = "file";
+ load.style.display = "none";
+ /*while ($("input[type=file]").length > 0)
+ {
+ $("input[type=file]")[0].remove();
+ }*/
+ window.pr = new Promise(r =>
+ {
+ load.addEventListener("change", () =>
+ {
+ var reader = new FileReader();
+ reader.addEventListener("load", (e) =>
+ {
+ r(atob(e.target.result.split(",")[1]));
+ load.remove();
+ });
+ reader.readAsDataURL(load.files[0]);
+ });
+ document.body.appendChild(load);
+ load.click();
+ }).then(text =>
+ {
+ var tmp = "!!!" + Math.random().toString().replace(".", "0") + "!!!";
+ text = text.trim().replaceAll("\n", tmp).replaceAll("\r", "");
+ tmp = text.replaceAll(tmp, "</div><div>");
+ if (tmp.substring(0, 5) != "<div>")
+ {
+ tmp = "<div>" + tmp;
+ }
+ if (tmp.substring(tmp.length - 5, tmp.length) == "<div>")
+ {
+ tmp = tmp.substring(0, tmp.length - 5);
+ }
+ tmp = tmp.replaceAll("<div></div>", "<div><br></div>");
+ $(".basis")[0].innerHTML = tmp;
+ });
+ });
+
+ $("#setup")[0].addEventListener("click", () =>
+ {
+ var msg = "username:apikey:endpoint";
+ var dat = localStorage.getItem("api_info");
+ dat = dat == null ? prompt(msg) : prompt(msg, dat);
+ if (dat == null) return;
+ dat = dat.replace("http://", "").replace("https://", "");
+ localStorage.setItem("api_info", dat);
+ });
+
+ $("#run")[0].addEventListener("click", () =>
+ {
+ var dat = localStorage.getItem("api_info");
+ if (dat == null || dat == undefined)
+ {
+ $(".response")[0].style.color = "red";
+ $(".response")[0].innerText = "API info not set.\nPlease press the gear on the top-right.";
+ return;
+ }
+ dat = dat.split(":");
+ if (dat.length != 3)
+ {
+ $(".response")[0].style.color = "red";
+ $(".response")[0].innerText = "API info not set correctly.\nPlease press the gear on the top-right.";
+ return;
+ }
+
+ EstoulsAPI.username = dat[0];
+ EstoulsAPI.apikey = dat[1];
+ EstoulsAPI.endpoint = "https://" + dat[2];
+ EstoulsAPI.request(getText()).then(x =>
+ {
+ if (x.success && x.response.trim().length > 0)
+ {
+ $(".response")[0].style.color = "green";
+ $(".response")[0].innerText = x.response;
+ }
+ else if (x.success && x.response.trim().length == 0)
+ {
+ $(".response")[0].style.color = "yellow";
+ $(".response")[0].innerText = "Empty response.";
+ }
+ else
+ {
+ $(".response")[0].style.color = "red";
+ $(".response")[0].innerText = x.response;
+ }
+ }).catch(x => alert("ERR(2)" + x));
+ });
+
+ $(".basis")[0].addEventListener("paste", (e) =>
+ {
+ window.pauseCleaning = true;
+ e.preventDefault();
+ var text = (event.clipboardData || window.clipboardData).getData('text');
+ //text = htmlToText(text);
+ var sel = window.getSelection();
+ if (sel.rangeCount > 0)
+ {
+ var range = sel.getRangeAt(0);
+ range.deleteContents();
+ if (!text.includes("\n"))
+ {
+ var textNode = document.createTextNode(text);
+ range.insertNode(textNode);
+ range.setStartAfter(textNode);
+ range.setEndAfter(textNode);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+ else
+ {
+ var last;
+ textToNodes(text).forEach(x =>
+ {
+ range.insertNode(x);
+ range.setStartAfter(x);
+ range.setEndAfter(x);
+ });
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+ }
+ window.pauseCleaning = undefined;
+ });
+
+ setInterval(() =>
+ {
+ if (window.stopMainLoop) return;
+ try
+ {
+ removeEmptyNodes();
+ $(".overlay")[0].scrollTop = $(".basis")[0].scrollTop;
+ $(".overlay")[0].scrollLeft = $(".basis")[0].scrollLeft;
+ $(".linecounter")[0].scrollTop = $(".basis")[0].scrollTop;
+ $(".linecounter")[0].scrollTop = $(".basis")[0].scrollTop;
+ var txt = $(".basis")[0].innerHTML;
+ if (txt == window.oldHTML) return;
+ localStorage.setItem("previous_text", getText());
+
+ //<fix for a mobile bug>
+ /*
+ var pos = 0;
+ var found = false;
+ for (; pos < txt.length; pos++)
+ {
+ if (txt.substring(pos, pos + "<div>".length) == "<div>")
+ {
+ found = true;
+ break;
+ }
+ }
+ if (pos != 0 && found)
+ {
+ var lhs = txt.substring(0, pos);
+ var rhs = txt.substring(pos, txt.length);
+ txt = "<div>" + lhs + "</div>" + rhs;
+ $(".basis")[0].innerHTML = txt;
+ }
+ if (txt.includes("</div><br><div>"))
+ {
+ txt = txt.replaceAll("</div><br><div>", "");
+ $(".basis")[0].innerHTML = txt;
+
+ }*/
+ //</fix for a mobile bug>
+
+ if (txt.includes("qreg") || txt.includes("creg"))
+ {
+ txt = highlightForQAnsel(txt);
+ }
+
+ $(".overlay")[0].innerHTML = txt;
+ window.oldHTML = $(".basis")[0].innerHTML;
+ txt = $(".basis")[0].innerHTML;
+ var linecount = getText().split("\n").length;
+ txt = "";
+ for (var i = 0; i < linecount; i++)
+ {
+ txt += (i + 1) + "<br />";
+ }
+ $(".linecounter")[0].innerHTML = txt;
+ }
+ catch (x)
+ {
+ alert("ERR(3): " + x);
+ window.stopMainLoop = true;
+ }
+ }, 10);
+}
+</script>
+</body>
+</html>
+
--- /dev/null
+#ifndef __HEADERS__
+#define __HEADERS__
+#include <stdint.h>
+#include <gmp.h>
+
+uint8_t* foleo_chacha20(uint8_t[32], uint8_t[12], uint32_t, size_t);
+uint8_t* foleo_chacha20_poly1305(uint8_t[32], uint8_t[12], uint8_t*, size_t);
+uint8_t* foleo_dhke(uint8_t*, uint8_t*);
+uint16_t foleo_dhke_modsize();
+
+uint8_t* foleo_poly1305(uint8_t[32], uint8_t*, size_t);
+#define FOLEO_RSA_PADDING_NONE 99
+#define FOLEO_RSA_PADDING_ENCRYPTION 1
+#define FOLEO_RSA_PADDING_SIGNATURE 2
+#define FOLEO_RSA_PADDING_OAEP 3
+#define FOLEO_RSA_PADDING_PSS 4
+#define FOLEO_RAND_MODE_DEVR 1
+#define FOLEO_RAND_MODE_DEV 2
+#ifdef ENABLE_X86
+#define FOLEO_RAND_MODE_X86 3
+#endif
+typedef struct
+{
+ mpz_t n, k;
+ uint8_t* label;
+ uint16_t bitWidth;
+} rsakey_t;
+void foleo_rsa_import(rsakey_t*, uint8_t*);
+uint8_t* foleo_rsa_export(rsakey_t*);
+void foleo_rsa_free(rsakey_t*);
+void foleo_rsa_keygen(uint16_t, rsakey_t*, rsakey_t*);
+
+//The maximum message block size that can be used
+// for a particular padding scheme.
+uint16_t foleo_rsa_msgsize(rsakey_t*, uint8_t);
+
+//Size of the rsakey struct
+uint16_t foleo_rsa_keysize();
+
+//Size in bytes of RSA modulus, same thing as the number
+// of bytes the encrypt() function will return
+uint16_t foleo_rsa_modsize(rsakey_t*);
+
+uint8_t* foleo_rsa_encrypt(rsakey_t*, uint8_t, uint8_t*, uint16_t);
+uint8_t* foleo_rsa_decrypt(rsakey_t*, uint8_t, uint8_t*, uint16_t*);
+uint8_t* foleo_sha256(uint8_t*, uint32_t);
+
+#define FOLEO_SHA256 1
+uint8_t* foleo_hmac(uint8_t, uint8_t*, uint32_t, uint8_t*, uint32_t);
+uint8_t* foleo_hmac_hkdf(uint8_t, uint32_t, uint8_t*, uint32_t, uint8_t*, uint32_t, uint8_t*, uint32_t);
+uint8_t* foleo_hmac_prf(uint8_t, uint32_t, uint8_t*, uint32_t, uint8_t*, uint32_t, uint8_t*, uint32_t);
+
+uint8_t foleo_hash_size(uint8_t);
+
+void foleo_rand_mode(uint8_t, uint8_t*);
+
+uint8_t* foleo_decode_base64(uint8_t*, size_t*);
+uint8_t* foleo_decode_hex(uint8_t*);
+uint8_t* foleo_encode_hex(uint8_t*, size_t);
+uint8_t* foleo_encode_base64(uint8_t*, size_t);
+
+#endif
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+
+char *HTTP_SERVER = "Server: FoleoSoft\n";
+
+int httpOpenSocket(char* ip, char* port)
+{
+ //get info about the server socket
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; //IPv4 or IPv6
+ hints.ai_protocol = 0; //Only useful if family is specific
+ hints.ai_socktype = SOCK_STREAM; //TCP only (excludes UDP)
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ struct addrinfo *sockinfo = 0;
+ int err = getaddrinfo(ip, port, &hints, &sockinfo);
+ if (err != 0)
+ {
+ printf("getaddrinfo(): %i\n", err);
+ return -1;
+ }
+
+ //create the server socket from that info
+ int server_fd = socket(sockinfo->ai_family, sockinfo->ai_socktype, sockinfo->ai_protocol);
+ if (server_fd == -1)
+ {
+ printf("socket(): %s", strerror(errno));
+ return -1;
+ }
+
+ //Allow network service to be restarted when need-be
+ int reuseaddr = 1;
+ if ( setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1 )
+ {
+ printf("setsockopt(): %i\n", errno);
+ return -1;
+ }
+
+ //Bind IP to socket
+ if ( bind(server_fd, sockinfo->ai_addr, sockinfo->ai_addrlen) == -1 )
+ {
+ printf("bind(): %i\n", errno);
+ return -1;
+ }
+
+ //Free no longer needed sockinfo
+ freeaddrinfo(sockinfo);
+
+ //Start listening on the socket
+ if ( listen(server_fd, SOMAXCONN) )
+ {
+ printf("listen(): %i\n", errno);
+ return -1;
+ }
+
+ return server_fd;
+}
+
+char* httpParseNext(char end, char* buff, int* buff_pos, int buff_len)
+{
+ char* ret = malloc(0);
+ int ret_len = 0;
+ for (int i = *buff_pos; i < buff_len && buff[i] != end; i++)
+ {
+ ret = realloc(ret, ret_len + 1);
+ ret[ret_len] = buff[i];
+ *buff_pos += 1;
+ ret_len += 1;
+ }
+ ret = realloc(ret, ret_len + 1);
+ ret[ret_len] = 0;
+ *buff_pos += 1;
+ return ret;
+}
+
+void httpNext(char end, char* buff, int* buff_pos, int buff_len)
+{
+ int pos = 0;
+ for (int i = *buff_pos; i < buff_len && buff[i] != end; i++)
+ {
+ *buff_pos += 1;
+ }
+ *buff_pos += 1;
+}
+
+char* httpParse(char* search, char* buff, long buff_len)
+{
+ int buff_pos = 0;
+
+ if ( strcmp(search, "Method") == 0 )
+ {
+ return httpParseNext(' ', buff, &buff_pos, buff_len);
+ }
+ else if ( strcmp(search, "Path") == 0 )
+ {
+ httpNext(' ', buff, &buff_pos, buff_len);
+ return httpParseNext(' ', buff, &buff_pos, buff_len);
+ }
+ else if ( strcmp(search, "Version") == 0 )
+ {
+ if (buff[0] == 'H' && buff[1] == 'T' && buff[2] == 'T')
+ {
+ return httpParseNext(' ', buff, &buff_pos, buff_len);
+ }
+ else
+ {
+ httpNext(' ', buff, &buff_pos, buff_len);
+ httpNext(' ', buff, &buff_pos, buff_len);
+ return httpParseNext('\n', buff, &buff_pos, buff_len);
+ }
+ }
+ else if ( strcmp(search, "Content") == 0 )
+ {
+ for (int i = 3; i < buff_len; i++)
+ {
+ if (buff[i - 3] == '\r' && buff[i - 2] == '\n' && buff[i - 1] == '\r' && buff[i - 0] == '\n')
+ {
+ i++;
+ char* ret = malloc(buff_len - i);
+ memcpy(ret, buff + i, buff_len - i);
+ return ret;
+ }
+ }
+ printf("hit a null\n");
+ return NULL;
+ }
+ else
+ {
+ httpNext('\n', buff, &buff_pos, buff_len);
+ while (buff_pos < buff_len)
+ {
+ char* tmp = httpParseNext(':', buff, &buff_pos, buff_len);
+ if ( strcmp(tmp, search) == 0 )
+ {
+ free(tmp);
+ buff_pos++;
+ return httpParseNext('\n', buff, &buff_pos, buff_len);
+ }
+ free(tmp);
+ httpNext('\n', buff, &buff_pos, buff_len);
+ }
+ return NULL;
+ }
+}
+
+char* httpParsePost(char* search, char* post, long post_len)
+{
+ int rsearchL = strlen(search) + 1;
+ char* rsearch = malloc(rsearchL);
+ memcpy(rsearch, search, rsearchL);
+ rsearch[rsearchL - 1] = '=';
+ for (int i = 0; i < post_len - rsearchL; i++)
+ {
+ if (memcmp(rsearch, post + i, rsearchL) == 0)
+ {
+ i += rsearchL;
+ char* ret = malloc(1);
+ int retL = 1;
+ ret[0] = 0;
+ for (; i < post_len; i++)
+ {
+ if (post[i] == '&') break;
+ ret = realloc(ret, ++retL);
+ ret[retL - 2] = post[i];
+ ret[retL - 1] = 0;
+ }
+ free(rsearch);
+ return ret;
+ }
+ }
+ free(rsearch);
+ return NULL;
+}
+
+void httpBegin(int socket_fd, int* session_fd)
+{
+ *session_fd = accept(socket_fd, 0, 0);
+}
+
+void httpEnd(int session_fd)
+{
+ close(session_fd);
+}
+
+void httpRespond(int session_fd, char* str)
+{
+ size_t strL = strlen(str);
+ for (size_t i = 0; i < strL; i++)
+ {
+ if (write(session_fd, str + i, 1) <= 0)
+ {
+ i -= 1;
+ }
+ }
+}
+
+void httpRespondByte(int session_fd, uint8_t byte)
+{
+ while (write(session_fd, &byte, 1) <= 0) ;
+}
+
+void httpError(int session_fd, int error, char *msg)
+{
+ char headerTemplate[] = "HTTP/1.1 %i %s\n";
+ int headerLen = snprintf(NULL, 0, headerTemplate, error, msg);
+ char *header = malloc(headerLen);
+ sprintf(header, headerTemplate, error, msg);
+
+ char pageTemplate[] = "Error %i: %s\n";
+ int pageLen = snprintf(NULL, 0, pageTemplate, error, msg);
+ char *page = malloc(pageLen);
+ sprintf(page, pageTemplate, error, msg);
+
+ char contentLengthTemplate[] = "Content-Length: %i\n\n";
+ int contentLengthLen = snprintf(NULL, 0, contentLengthTemplate, pageLen);
+ char *contentLength = malloc(contentLengthLen);
+ sprintf(contentLength, contentLengthTemplate, pageLen);
+
+ httpRespond(session_fd, header);
+ httpRespond(session_fd, HTTP_SERVER);
+ httpRespond(session_fd, contentLength);
+ httpRespond(session_fd, page);
+
+ free(header);
+ free(contentLength);
+ free(page);
+}
+
+int httpFileOut(int session_fd, char* path, uint8_t fileType)
+{
+ char* safePath;
+ int safePathLen;
+ int pathLen = strlen(path);
+ if (pathLen == 0) return 0;
+ if (path[0] == '/')
+ {
+ safePath = malloc(1);
+ safePath[0] = '.';
+ safePathLen = 1;
+ }
+ else
+ {
+ safePath = malloc(0);
+ safePathLen = 0;
+ }
+ for (int i = 0; i < pathLen; i++)
+ {
+ if (i > 0 && path[i] == '.')
+ {
+ if (path[i - 1] == '/' || path[i - 1] == '.')
+ {
+ continue;
+ }
+ }
+ if (path[i] == '/' && safePathLen > 0)
+ {
+ if (safePath[safePathLen - 1] == '/')
+ {
+ continue;
+ }
+ }
+ safePath = realloc(safePath, ++safePathLen);
+ safePath[safePathLen - 1] = path[i];
+ }
+ safePath = realloc(safePath, ++safePathLen);
+ safePath[safePathLen - 1] = 0;
+
+ FILE *fd = fopen(safePath, "r");
+ free(safePath);
+ if (fd == NULL)
+ {
+ httpError(session_fd, 404, "Page Not Found");
+ return 1;
+ }
+ fseek(fd, 0, SEEK_END);
+ size_t fileSize = ftell(fd);
+ rewind(fd);
+ char* fileData = malloc(fileSize);
+ fread(fileData, 1, fileSize, fd);
+ fclose(fd);
+
+
+ if (fileSize < 0)
+ {
+ httpError(session_fd, 404, "Page Not Found");
+ return 1;
+ }
+
+ //Good header
+ httpRespond(session_fd, "HTTP/1.1 200 OK\n");
+ httpRespond(session_fd, HTTP_SERVER);
+
+ //Spit out file content length part of the HTTP message
+ char* contentLengthTemplate;
+ const char contentLengthTemplate0[] = "Access-Control-Allow-Origin: *\nContent-Type: application/octet-stream\nContent-Length: %i\n\n";
+ const char contentLengthTemplate1[] = "Access-Control-Allow-Origin: *\nContent-Type: image/x-icon\nContent-Length: %i\n\n";
+ switch (fileType)
+ {
+ case 0:
+ contentLengthTemplate = malloc(strlen(contentLengthTemplate0) + 1);
+ strcpy(contentLengthTemplate, contentLengthTemplate0);
+ break;
+ case 1:
+ contentLengthTemplate = malloc(strlen(contentLengthTemplate1) + 1);
+ strcpy(contentLengthTemplate, contentLengthTemplate1);
+ break;
+ }
+ size_t contentLengthLen = snprintf(NULL, 0, contentLengthTemplate, fileSize);
+ char* contentLength = malloc(contentLengthLen + 1);
+ sprintf(contentLength, contentLengthTemplate, fileSize);
+ httpRespond(session_fd, contentLength);
+ free(contentLength);
+ for (size_t i = 0; i < fileSize; i++)
+ {
+ if (i == 0)
+ {
+ printf(">%02X<\n", fileData[i]);
+ }
+ httpRespondByte(session_fd, fileData[i]);
+ }
+ free(fileData);
+ free(contentLengthTemplate);
+}
+
+int httpStringOut(int session_fd, char* str)
+{
+ //Good header
+ httpRespond(session_fd, "HTTP/1.1 200 OK\n");
+ httpRespond(session_fd, HTTP_SERVER);
+
+ int strSize = strlen(str);
+ //Spit out file content length part of the HTTP message
+ const char *contentLengthTemplate = "Access-Control-Allow-Origin: *\nContent-Type: text/html\nContent-Length: %i\n\n";
+ int contentLengthLen = snprintf(NULL, 0, contentLengthTemplate, strSize);
+ char* contentLength = malloc(contentLengthLen + 1);
+ sprintf(contentLength, contentLengthTemplate, strSize);
+ httpRespond(session_fd, contentLength);
+ free(contentLength);
+
+ httpRespond(session_fd, str);
+ httpRespond(session_fd, "\n");
+}
+
+void httpRequest(int session_fd, char** str, int* strlen)
+{
+ int flags = fcntl(session_fd, F_GETFL, 0);
+ fcntl(session_fd, F_SETFL, flags | O_NONBLOCK);
+
+ *strlen = 0;
+ *str = malloc(0);
+ uint8_t buffer[1024];
+ int32_t length;
+ int8_t tries = 3;
+
+ while (tries > 0)
+ {
+ while ( (length = read(session_fd, buffer, sizeof(buffer))) > 0 )
+ {
+ *str = realloc(*str, (*strlen) + length);
+ memcpy((*str) + (*strlen), buffer, length);
+ *strlen += length;
+ }
+ usleep(1000);
+ tries--;
+ }
+
+ *str = realloc(*str, (*strlen) + 1);
+ (*str)[*strlen] = 0;
+ *strlen += 1;
+
+ /*
+ ioctl(session_fd, FIONREAD, strlen);
+ if (*strlen > 0)
+ {
+ *str = malloc(*strlen * sizeof(char));
+ read(session_fd, *str, *strlen);
+ }
+ else
+ {
+ *str = NULL;
+ *strlen = 0;
+ }
+ */
+
+ printf("-------------------------------\n");
+ printf("Length: %i\n", *strlen);
+ printf("-------------------------------\n");
+ for (int i = 0; i < *strlen; i++)
+ {
+ putchar((*str)[i]);
+ }
+ putchar('\n');
+ printf("-------------------------------\n");
+}
+
--- /dev/null
+#include <mysql.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include "http.c"
+#include "frontend.h"
+#include "CryptoFoleo.h"
+
+uint8_t HOSTNAME[1024];
+uint8_t HOSTNAME_I[1024];
+
+//grabs auth info from the database
+uint8_t* getDatabaseInfo(uint8_t type, uint8_t* a, uint8_t* b, uint8_t* c)
+{
+ uint8_t* ret = NULL;
+ MYSQL *conn;
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ conn = mysql_init(NULL);
+ FILE *f = fopen("/usr/share/estous/password", "r");
+ if (!f)
+ {
+ fprintf(stderr, "Could not open /usr/share/estousapi/password\n");
+ exit(1);
+ }
+ char* p = malloc(0);
+ int c;
+ while ( (c = fgetc(f)) != EOF)
+
+ if (!mysql_real_connect(conn, "localhost", HOSTNAME, "", "APIService", 0, NULL, 0)) {
+ fprintf(stderr, "%s\n", mysql_error(conn));
+ return NULL;
+ }
+
+ if (type == 1)
+ {
+ uint8_t* prequery = "select %s from AuthInfo where service='%s' and username='%s' limit 1;";
+ uint16_t queryLength = snprintf(NULL, 0, prequery, b, c, a);
+ uint8_t query[queryLength + 1];
+ sprintf(query, prequery, b, c, a);
+ if (mysql_query(conn, query))
+ {
+ fprintf(stderr, "%s\n", mysql_error(conn));
+ return NULL;
+ }
+ }
+ else
+ {
+ uint8_t* prequery = "select %s from ServiceInfo where service='%s' limit 1;";
+ uint16_t queryLength = snprintf(NULL, 0, prequery, b, a);
+ uint8_t query[queryLength + 1];
+ sprintf(query, prequery, b, a);
+ if (mysql_query(conn, query))
+ {
+ fprintf(stderr, "%s\n", mysql_error(conn));
+ return NULL;
+ }
+ }
+
+ res = mysql_use_result(conn);
+ if ((row = mysql_fetch_row(res)) != NULL)
+ {
+ ret = malloc(strlen(row[0]) + 1);
+ strcpy(ret, row[0]);
+ }
+ mysql_free_result(res);
+ mysql_close(conn);
+
+ return ret;
+}
+
+uint8_t* runService(uint8_t* service_name, uint8_t* service_data, uint32_t* service_dataL, uint32_t timeout)
+{
+ uint8_t* data = malloc(strlen(service_data) + 1);
+ strcpy(data, service_data);
+ uint8_t* service_path = getDatabaseInfo(2, service_name, "path", "");
+ uint8_t* service_parms = getDatabaseInfo(2, service_name, "parameters", "");
+ if (service_path == NULL || service_parms == NULL)
+ {
+ free(service_path);
+ free(service_parms);
+ return NULL;
+ }
+
+ uint8_t heredoc[] = "HERE";
+ uint16_t heredocL = strlen(heredoc);
+ uint32_t dataL = strlen(data);
+ for (uint32_t i = heredocL; i < dataL; i++)
+ {
+ if (memcmp(data + (i - heredocL), heredoc, heredocL) == 0)
+ {
+ memset(data + (i - heredocL), ' ', heredocL);
+ }
+ }
+
+ uint8_t template[] = "/tmp/apiserviceXXXXXX";
+ int8_t fd = mkstemp(template);
+ if (fd == -1)
+ {
+ perror("mkstemp");
+ exit(EXIT_FAILURE);
+ }
+ write(fd, data, dataL);
+ close(fd);
+
+ uint8_t precommand[] = "(cat %s | (timeout %is %s %s) || echo Timed out.)2>&1";
+ uint16_t commandL = snprintf(NULL, 0, precommand, template, timeout, service_path, service_parms);
+ uint8_t command[commandL + 1];
+ sprintf(command, precommand, template, timeout, service_path, service_parms);
+ free(service_path);
+ free(service_parms);
+ free(data);
+
+ printf("-------------Command--------------\n");
+ printf("%s\n", command);
+ printf("----------------------------------\n");
+ FILE *service;
+ service = popen(command, "r");
+ if (service == NULL)
+ {
+ perror("popen");
+ return NULL;
+ }
+
+ uint8_t* ret = malloc(0);
+ uint32_t retL = 0;
+ int32_t c;
+ while ((c = fgetc(service)) != EOF)
+ {
+ retL++;
+ ret = realloc(ret, retL);
+ ret[retL - 1] = (uint8_t)c;
+ }
+
+ int status = pclose(service);
+ if (status == -1)
+ {
+ free(ret);
+ perror("pclose");
+ return NULL;
+ }
+ *service_dataL = retL;
+ unlink(template);
+ return ret;
+}
+
+//process a connection that was provided necessary info
+void processUserAndData(int session_fd, char* user, char* data, char* session, char* digest, char* service)
+{
+ for (int i = 0; i < strlen(user); i++)
+ {
+ if (user[i] == '\'')
+ {
+ httpError(session_fd, 400, "Bad Request (6)");
+ return;
+ }
+ }
+
+ if (strlen(digest) != (32 * 2) || strlen(session) != (12 * 2))
+ {
+ httpError(session_fd, 400, "Bad Request (7)");
+ return;
+ }
+
+ char* authkey = getDatabaseInfo(1, user, "authkey", service);
+ if (authkey == NULL)
+ {
+ free(authkey);
+ httpError(session_fd, 403, "Forbidden (1)");
+ return;
+ }
+
+ int keyid = 0;
+ uint8_t* sessionAsBinary = foleo_decode_hex(session);
+ uint8_t* authkeyAsBinary = foleo_decode_hex(authkey);
+ uint8_t* digestAsBinary = foleo_decode_hex(digest);
+ size_t dataSize;
+ uint8_t* dataAsBinary = foleo_decode_base64(data, &dataSize);
+ free(authkey);
+
+ if (sessionAsBinary == NULL || authkeyAsBinary == NULL || dataAsBinary == NULL || digestAsBinary == NULL || dataSize == 0)
+ {
+ free(sessionAsBinary);
+ free(authkeyAsBinary);
+ free(dataAsBinary);
+ free(digestAsBinary);
+ httpError(session_fd, 400, "Bad Request (8)");
+ return;
+ }
+
+ uint8_t* mac = foleo_hmac(FOLEO_SHA256, authkeyAsBinary, 256 / 8, dataAsBinary, dataSize);
+ if (memcmp(mac, digestAsBinary, 256 / 8) != 0)
+ {
+ free(sessionAsBinary);
+ free(authkeyAsBinary);
+ free(dataAsBinary);
+ free(digestAsBinary);
+ free(mac);
+ httpError(session_fd, 403, "Forbidden (2)");
+ return;
+ }
+ free(digestAsBinary);
+ free(mac);
+
+ uint8_t* ret = foleo_chacha20(authkeyAsBinary, sessionAsBinary, 0, dataSize);
+ for (int i = 0; i < dataSize; i++) dataAsBinary[i] ^= ret[i];
+ free(ret);
+
+ uint32_t receivedL = dataSize + 1;
+ uint8_t* received = malloc(receivedL);
+ memcpy(received, dataAsBinary, receivedL - 1);
+ received[receivedL - 1] = 0;
+ free(dataAsBinary);
+
+ uint32_t timeout = 10;
+ char* stimeout = getDatabaseInfo(1, user, "timeout", service);
+ timeout = atoi(stimeout);
+ free(stimeout);
+
+ uint32_t resultL;
+ uint8_t* result = runService(service, received, &resultL, timeout);
+ free(received);
+ if (result != NULL)
+ {
+ uint8_t carry = 1;
+ for (int8_t i = 11; i >= 0; i--)
+ {
+ if (sessionAsBinary[i] == 0xFF)
+ {
+ if (carry == 1) sessionAsBinary[i] = 0;
+ else carry = 0;
+ }
+ else
+ {
+ if (carry == 1) sessionAsBinary[i]++;
+ carry = 0;
+ }
+ }
+ uint8_t* ret_sess = foleo_encode_hex(sessionAsBinary, 12);
+ uint8_t* ret_data = foleo_chacha20(authkeyAsBinary, sessionAsBinary, 0, resultL);
+ for (uint32_t i = 0; i < resultL; i++) ret_data[i] ^= result[i];
+ uint8_t* ret_data_b64 = foleo_encode_base64(ret_data, resultL);
+ uint8_t* ret_mac = foleo_hmac(FOLEO_SHA256, authkeyAsBinary, 256 / 8, ret_data, resultL);
+ uint8_t* ret_mac_hex = foleo_encode_hex(ret_mac, 256 / 8);
+
+ uint32_t finalL = snprintf(NULL, 0, "user=%s&sess=%s&dgst=%s&data=%s\n", user, ret_sess, ret_mac_hex, ret_data_b64);
+ uint8_t final[finalL + 1];
+ sprintf(final, "user=%s&sess=%s&dgst=%s&data=%s\n", user, ret_sess, ret_mac_hex, ret_data_b64);
+ httpStringOut(session_fd, final);
+
+ free(ret_sess);
+ free(ret_data);
+ free(ret_data_b64);
+ free(ret_mac);
+ free(ret_mac_hex);
+ }
+ free(result);
+ free(authkeyAsBinary);
+ free(sessionAsBinary);
+
+ return;
+}
+
+void *handleRequest(void *vargp)
+{
+ int session_fd;
+ memcpy(&session_fd, vargp, sizeof(int));
+ free(vargp);
+
+ char *buff;
+ char *info;
+ char *user;
+ char *data;
+ char *sess;
+ char *dgst;
+ int buff_len;
+ httpRequest(session_fd, &buff, &buff_len);
+ printf("-----Request-----\n");
+ for (int32_t i = 0; i < buff_len; i++)
+ {
+ putchar(buff[i]);
+ }
+ printf("\n-----------------\n");
+ if (buff != NULL)
+ {
+ info = httpParse("Method", buff, buff_len);
+ if (info == NULL)
+ {
+ httpError(session_fd, 400, "Bad Request (1)");
+ }
+ else if ( strcmp(info, "POST") == 0 )
+ {
+ free(info);
+ info = httpParse("Path", buff, buff_len);
+ uint8_t* isValidService = getDatabaseInfo(2, info + 1, "1", "");
+ if (isValidService == NULL)
+ {
+ httpError(session_fd, 400, "Bad Request (2)");
+ }
+ else
+ {
+ uint8_t service[strlen(info + 1) + 1];
+ strcpy(service, info + 1);
+ free(isValidService);
+ free(info);
+ info = httpParse("Content-Length", buff, buff_len);
+ if (info == NULL)
+ {
+ httpError(session_fd, 400, "Bad Request (3)");
+ }
+ else
+ {
+ uint32_t contentLength = atoi(info);
+ if (contentLength == 0)
+ {
+ httpError(session_fd, 400, "Bad Request (4)");
+ }
+ else
+ {
+ free(info);
+ info = httpParse("Content", buff, buff_len);
+ if (info == NULL)
+ {
+ httpError(session_fd, 400, "Bad Request (5)");
+ }
+ else
+ {
+ user = httpParsePost("user", info, contentLength);
+ data = httpParsePost("data", info, contentLength);
+ sess = httpParsePost("sess", info, contentLength);
+ dgst = httpParsePost("dgst", info, contentLength);
+ if (user != NULL && data != NULL && dgst != NULL)
+ {
+ processUserAndData(session_fd, user, data, sess, dgst, service);
+ }
+ else
+ {
+ httpError(session_fd, 400, "Bad Request (6)");
+ }
+ if (user != NULL) free(user);
+ if (data != NULL) free(data);
+ if (sess != NULL) free(sess);
+ if (dgst != NULL) free(dgst);
+ }
+ }
+ }
+ }
+ }
+ else if ( strcmp(info, "GET") == 0 )
+ {
+ free(info);
+ info = httpParse("Path", buff, buff_len);
+ if ( strcmp(info, "/") == 0 )
+ {
+ uint8_t* frontend = malloc(__rcs_frontend_html_len + 1);
+ memcpy(frontend, __rcs_frontend_html, __rcs_frontend_html_len);
+ frontend[__rcs_frontend_html_len] = 0;
+ httpStringOut(session_fd, frontend);
+ free(frontend);
+ }
+ else if ( strcmp(info, "/apk") == 0 )
+ {
+ httpFileOut(session_fd, "./EstoulsAPI.apk", 0);
+ }
+ else if ( strcmp(info, "/favicon.ico") == 0 )
+ {
+ httpFileOut(session_fd, "./favicon.ico", 1);
+ }
+ else
+ {
+ httpError(session_fd, 400, "Bad Request (7)");
+ }
+ }
+ else
+ {
+ httpError(session_fd, 400, "Bad Request (8)");
+ }
+ free(info);
+ free(buff);
+ }
+
+ if (session_fd == -1 && errno != EINTR)
+ {
+ printf("Failed to accept connection %i.\n", errno);
+ }
+
+ //End session
+ httpEnd(session_fd);
+}
+
+void get_hostname()
+{
+ for (uint8_t i = 0; i <= 1; i++)
+ {
+ FILE* proc;
+ if (i) proc = popen("hostname -I | awk '{print $1}'", "r");
+ else proc = popen("hostname", "r");
+ char* hostname = malloc(1);
+ hostname[0] = 0;
+ size_t hostname_len = 1;
+ if (proc == NULL)
+ {
+ fprintf(stderr, "Unknown error in get_hostname().\n");
+ exit(1);
+ }
+ int c;
+ while ( (c = fgetc(proc)) != EOF )
+ {
+ if (c == '\n' || c == '\r') continue;
+ hostname = realloc(hostname, ++hostname_len);
+ hostname[hostname_len - 2] = c;
+ hostname[hostname_len - 1] = 0;
+ }
+ if (strlen(hostname) >= 1023)
+ {
+ hostname[1023] = 0;
+ }
+ if (i) strcpy(HOSTNAME_I, hostname);
+ else strcpy(HOSTNAME, hostname);
+ free(hostname);
+ }
+}
+
+void main()
+{
+ get_hostname();
+
+ MYSQL *conn;
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+
+ // Initialize the connection handler
+ conn = mysql_init(NULL);
+
+ // Connect to the database
+ if (!mysql_real_connect(conn, "localhost", HOSTNAME, "PAAAAAAAAAAAA", "APIService", 0, NULL, 0)) {
+ fprintf(stderr, "%s\n", mysql_error(conn));
+ return;
+ }
+
+ if (mysql_query(conn, "select 1 from information_schema.tables where table_schema='APIService' and table_name='AuthInfo' limit 1;"))
+ {
+ fprintf(stderr, "%s\n", mysql_error(conn));
+ return;
+ }
+ res = mysql_use_result(conn);
+ uint8_t createTable = ((row = mysql_fetch_row(res)) == NULL);
+ if (createTable)
+ {
+ if (mysql_query(conn, "create table AuthInfo ( id int auto_increment primary key, service text, username text, authkey text, timeout int );"))
+ {
+ fprintf(stderr, "%s\n", mysql_error(conn));
+ return;
+ }
+ }
+ mysql_free_result(res);
+
+ if (mysql_query(conn, "select 1 from information_schema.tables where table_schema='APIService' and table_name='ServiceInfo' limit 1;"))
+ {
+ fprintf(stderr, "%s\n", mysql_error(conn));
+ return;
+ }
+ res = mysql_use_result(conn);
+ createTable = ((row = mysql_fetch_row(res)) == NULL);
+ if (createTable)
+ {
+ if (mysql_query(conn, "create table ServiceInfo ( id int auto_increment primary key, service text, path text, parameters text );"))
+ {
+ fprintf(stderr, "%s\n", mysql_error(conn));
+ return;
+ }
+ }
+
+ mysql_free_result(res);
+ mysql_close(conn);
+
+ //Open the socket
+ int socket_fd = httpOpenSocket(HOSTNAME_I, "8080");
+
+ //Begin processing packets
+ for (;;)
+ //for (int i = 0; i < 3; i++)
+ {
+ int session_fd;
+ httpBegin(socket_fd, &session_fd);
+ char* arg = malloc(sizeof(int));
+ memcpy(arg, &session_fd, sizeof(int));
+ pthread_t thread_id;
+ pthread_create(&thread_id, NULL, handleRequest, arg);
+ //pthread_join(thread_id, NULL);
+ }
+
+ mysql_library_end();
+}