-# EstousAPI
+# RosadoAPI
Easily transform as many CLI programs as you wish into web APIs.
structure for this table.
```sql
-
-
+MariaDB [RosadoAPI]> describe ServiceInfo;
++------------+---------+------+-----+---------+----------------+
+| Field | Type | Null | Key | Default | Extra |
++------------+---------+------+-----+---------+----------------+
+| id | int(11) | NO | PRI | NULL | auto_increment |
+| service | text | YES | | NULL | |
+| path | text | YES | | NULL | |
+| parameters | text | YES | | NULL | |
++------------+---------+------+-----+---------+----------------+
```
The `service` field is the name of the service and also corresponds
structure for this table.
```sql
-
+MariaDB [RosadoAPI]> describe AuthInfo;
++----------+---------+------+-----+---------+----------------+
+| Field | Type | Null | Key | Default | Extra |
++----------+---------+------+-----+---------+----------------+
+| id | int(11) | NO | PRI | NULL | auto_increment |
+| service | text | YES | | NULL | |
+| username | text | YES | | NULL | |
+| authkey | text | YES | | NULL | |
+| timeout | int(11) | YES | | NULL | |
++----------+---------+------+-----+---------+----------------+
```
The `service` field must correspond to a `service` in the `ServiceInfo`
openssl rand -hex 32
```
-Communication with the API service is encrypted with ChaCha20-HMAC
+Communication with the API service is encrypted with ChaCha20-HMAC-SHA256
This means it cannot be pinged using plaintext. To ping it and get a
-response, the request must be encrypted using ChaCha20-HMAC, and
+response, the request must be encrypted in this way, and
the response also must be decrypted in the same way.
Below are variables that will need to be populated in order to
ping the API service.
+- `$addr`: The address of the server to send the request to.
- `$srvc`: The service to send a request to.
- `$user`: The user requesting the service.
- `$auth`: The authentication key.
which is randomly generated.
```sh
- srvc=$1
- user=$2
- auth=$3
+ addr=$1
+ srvc=$2
+ user=$3
+ auth=$4
read rqst
sess=$(openssl rand -hex 12)
```
ChaCha20 using the authentication key. Then, a digest will also
need to be computed for the message which allows the server to
verify the message has not been tampered with. This must be
-calculated from the plaintext message using HMAC-SHA256.
-
-Both of these values also need to be encoded as base64. Below
-is a Bash script that generates the body and digest for the
+calculated from the encrypted data using HMAC-SHA256. Both of
+these values also need to be encoded as base64. Below is a Bash
+script that generates the encrypted data and digest for the
request.
```sh
and store the response in variable in Bash.
```sh
- resp=$(curl -s -X POST -d "user=$user&sess=$sess&dgst=$dgst&data=$data" "$srvr/$srvc")
+ resp=$(curl -s -X POST -d "user=$user&sess=$sess&dgst=$dgst&data=$data" "$addr/$srvc")
```
The response from the server is in a similar format, so it
then
while [ 1 ]
do
- ./bin/APIServer
+ ./bin/rosado
done
+elif [ "$1" = "install" ]
+then
+ sudo cp bin/rosado /usr/local/bin/rosado
+elif [ "$1" = "uninstall" ]
+then
+ sudo rm /usr/local/bin/rosado
else
xxd -i ./rcs/frontend.html ./src/frontend.h
- gcc ./src/main.c -o ./bin/APIServer $(mysql_config --cflags --libs) -lCryptoFoleo
+ gcc ./src/main.c -o ./bin/rosado $(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>
-
uint8_t HOSTNAME[1024];
uint8_t HOSTNAME_I[1024];
uint8_t SQLPASS[1024];
+uint8_t WEBPORT[1024];
//grabs auth info from the database
uint8_t* getDatabaseInfo(uint8_t type, uint8_t* a, uint8_t* b, uint8_t* c)
SQLPASS[plen] = 0;
}
+void get_webport()
+{
+ FILE *f = fopen("/usr/share/rosadoapi/port", "r");
+ if (!f)
+ {
+ WEBPORT[0] = '8';
+ WEBPORT[1] = '6';
+ WEBPORT[2] = '6';
+ WEBPORT[3] = '6';
+ WEBPORT[4] = 0;
+ fprintf(stderr, "RosadoAPI: Could not access /usr/share/rosadoapi/port\n");
+ fprintf(stderr, "RosadoAPI: Using default port %s.\n", WEBPORT);
+ printf("RosadoAPI: Listening on port %s...\n", WEBPORT);
+ return;
+ }
+ int c;
+ int plen = 0;
+ while ( (c = fgetc(f)) != EOF)
+ {
+ if (c == '\n' || c == '\r' || c == ' ') break;
+ WEBPORT[plen++] = c;
+ if (plen == sizeof(WEBPORT) - 2) break;
+ }
+ WEBPORT[plen] = 0;
+ printf("RosadoAPI: Listening on port %s...\n", WEBPORT);
+}
+
void get_hostname()
{
for (uint8_t i = 0; i <= 1; i++)
{
get_hostname();
get_sqlpass();
+ get_webport();
MYSQL *conn;
MYSQL_RES *res;
mysql_close(conn);
//Open the socket
- int socket_fd = httpOpenSocket(HOSTNAME_I, "8080");
+ int socket_fd = httpOpenSocket(HOSTNAME_I, WEBPORT);
//Begin processing packets
for (;;)
--- /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>
+