mirror of
https://git.eta.st/eta/rsp6-decoder.git
synced 2024-11-21 15:05:41 +00:00
webshite
This commit is contained in:
parent
7a8648da9c
commit
4924b13893
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
||||
/target
|
||||
rsp6-webshite/dist
|
||||
*.swp
|
||||
|
@ -2,5 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/rsp6-webshite" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
174
Cargo.lock
generated
174
Cargo.lock
generated
@ -26,6 +26,34 @@ dependencies = [
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
@ -47,6 +75,42 @@ version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memory_units"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.3"
|
||||
@ -77,6 +141,12 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.49"
|
||||
@ -107,11 +177,16 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitvec",
|
||||
"console_error_panic_hook",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"num-bigint",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"wee_alloc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -129,6 +204,17 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.152"
|
||||
@ -201,6 +287,94 @@ version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "wee_alloc"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"memory_units",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
|
11
Cargo.toml
11
Cargo.toml
@ -5,6 +5,12 @@ edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
wasm = ["wasm-bindgen", "lazy_static", "serde-wasm-bindgen", "wee_alloc", "console_error_panic_hook"]
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
anyhow = "1.0"
|
||||
@ -13,3 +19,8 @@ num-bigint = "0.4"
|
||||
bitvec = "1.0"
|
||||
hex = { version = "0.4", features = ["serde"] }
|
||||
time = { version = "0.3", features = ["macros", "serde", "serde-human-readable"] }
|
||||
wasm-bindgen = { version = "0.2", optional = true }
|
||||
lazy_static = { version = "1.4", optional = true }
|
||||
serde-wasm-bindgen = { version = "0.4", optional = true }
|
||||
wee_alloc = { version = "0.4", optional = true }
|
||||
console_error_panic_hook = { version = "0.1", optional = true }
|
25
LICENSE
Normal file
25
LICENSE
Normal file
@ -0,0 +1,25 @@
|
||||
Copyright (c) 2023 eta
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
2
rsp6-webshite/.gitignore
vendored
Normal file
2
rsp6-webshite/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dist
|
5
rsp6-webshite/bootstrap.js
vendored
Normal file
5
rsp6-webshite/bootstrap.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
// A dependency graph that contains any wasm must all be imported
|
||||
// asynchronously. This `bootstrap.js` file does the single async import, so
|
||||
// that no one else needs to worry about it again.
|
||||
import("./index.js")
|
||||
.catch(e => console.error("Error importing `index.js`:", e));
|
3447
rsp6-webshite/fares.json
Normal file
3447
rsp6-webshite/fares.json
Normal file
File diff suppressed because it is too large
Load Diff
587
rsp6-webshite/govuk.css
Normal file
587
rsp6-webshite/govuk.css
Normal file
File diff suppressed because one or more lines are too long
160
rsp6-webshite/index.html
Normal file
160
rsp6-webshite/index.html
Normal file
@ -0,0 +1,160 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>RSP6 decoder</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
|
||||
<link rel="stylesheet" href="./govuk.css"/>
|
||||
</head>
|
||||
<body class="govuk-template__body">
|
||||
<a href="#main-content" class="govuk-skip-link">Skip to main content</a>
|
||||
<header class="govuk-header" role="banner">
|
||||
<div class="govuk-header__container govuk-width-container" style="border-bottom: 10px solid #1d70b8;">
|
||||
<div class="govuk-header__logo">
|
||||
<a href="/" class="govuk-header__link govuk-header__link--homepage">
|
||||
<span class="govuk-header__logotype-text">tickets™<small> by eta</small></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="govuk-width-container">
|
||||
<main class="govuk-main-wrapper" id="main-content" role="main">
|
||||
<h1 class="govuk-heading-l">Decode a National Rail mobile ticket</h1>
|
||||
<noscript>
|
||||
<div class="govuk-error-summary" data-module="govuk-error-summary" id="error-banner" style="display: none;">
|
||||
<div role="alert">
|
||||
<h2 class="govuk-error-summary__title" id="error-header">
|
||||
JavaScript is required
|
||||
</h2>
|
||||
<div class="govuk-error-summary__body">
|
||||
<p class="govuk-body" id="error-text">Sorry! You need JavaScript to run this page at all, since it does all the decoding inside your browser.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
<div class="govuk-error-summary" data-module="govuk-error-summary" id="error-banner" style="display: none;">
|
||||
<div role="alert">
|
||||
<h2 class="govuk-error-summary__title" id="error-header">
|
||||
There is a problem
|
||||
</h2>
|
||||
<div class="govuk-error-summary__body">
|
||||
<p class="govuk-body" id="error-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="govuk-body">Scan a National Rail mTicket with your device's camera, and we'll tell you what data is inside! (If it works, that is. This was thrown together in an evening and is probably quite unreliable.)</p>
|
||||
<div class="govuk-inset-text">
|
||||
Ticket decoding happens entirely inside your browser; no data is transmitted back to us.
|
||||
</div>
|
||||
<button class="govuk-button govuk-button--start" data-module="govuk-button" disabled id="scan-button">
|
||||
Scan now
|
||||
<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" aria-hidden="true" focusable="false">
|
||||
<path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z" />
|
||||
</svg>
|
||||
</button>
|
||||
<details class="govuk-details" id="screenshot-details">
|
||||
<summary class="govuk-details__summary">
|
||||
<span class="govuk-details__summary-text">
|
||||
Use a screenshot instead
|
||||
</span>
|
||||
</summary>
|
||||
<div class="govuk-details__text">
|
||||
<p class="govuk-body"><strong>Please note!</strong> You'll need to crop your screenshot so only the barcode bit is showing, otherwise it might fail to scan. I apologise for how finicky it is; you might need to play around with it.</p>
|
||||
<div class="govuk-form-group">
|
||||
<label class="govuk-label" for="file-upload-1">
|
||||
Choose a file
|
||||
</label>
|
||||
<input class="govuk-file-upload" id="file-upload-1" name="file-upload-1" type="file">
|
||||
<div id="image-sanctuary" style="display: none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<details class="govuk-details" id="text-details">
|
||||
<summary class="govuk-details__summary">
|
||||
<span class="govuk-details__summary-text">
|
||||
Use raw text instead
|
||||
</span>
|
||||
</summary>
|
||||
<div class="govuk-details__text">
|
||||
<p class="govuk-body">Does the barcode scanner just not want to work? Use another Aztec barcode scanner (there are plenty on the mobile app stores), and then copy the result into the bottom form.</p>
|
||||
<div class="govuk-form-group" style="margin-bottom: 10px;">
|
||||
<input class="govuk-input" id="raw-text-in" name="raw-text-in" type="text">
|
||||
</div>
|
||||
<button class="govuk-button" data-module="govuk-button" id="raw-text-btn">
|
||||
Hope for the best
|
||||
</button>
|
||||
</div>
|
||||
</details>
|
||||
<div id="video-container" style="display: none;">
|
||||
<video id="video" width="100%" height="300" style="border: 1px solid gray"></video>
|
||||
</div>
|
||||
<div id="ticket-content" style="display: none;">
|
||||
<p class="govuk-body" id="ticket-fromto"></p>
|
||||
<span class="govuk-heading-xl" style="font-weight: normal; text-align: center; margin-bottom: 0.5em !important;"><span id="origin">from</span> → <span id="dest">to</span></span>
|
||||
<table class="govuk-table">
|
||||
<tbody class="govuk-table__body" id="ticket-data">
|
||||
</tbody>
|
||||
</table>
|
||||
<details class="govuk-details">
|
||||
<summary class="govuk-details__summary">
|
||||
<span class="govuk-details__summary-text">
|
||||
View ticket as JSON
|
||||
</span>
|
||||
</summary>
|
||||
<div class="govuk-details__text">
|
||||
<pre id="raw-json">
|
||||
{
|
||||
"lol": "lmao"
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<details class="govuk-details" id="raw-barcode-wrapper" style="display: none;">
|
||||
<summary class="govuk-details__summary">
|
||||
<span class="govuk-details__summary-text">
|
||||
View scanned barcode
|
||||
</span>
|
||||
</summary>
|
||||
<div class="govuk-details__text">
|
||||
<pre id="raw-barcode">
|
||||
{
|
||||
"lol": "lmao"
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
</details>
|
||||
<div class="govuk-notification-banner" role="region" aria-labelledby="govuk-notification-banner-title" data-module="govuk-notification-banner" id="decode-banner" style="display: none;">
|
||||
<div class="govuk-notification-banner__header">
|
||||
<h2 class="govuk-notification-banner__title" id="govuk-notification-banner-title">
|
||||
Decode failed
|
||||
</h2>
|
||||
</div>
|
||||
<div class="govuk-notification-banner__content">
|
||||
<p class="govuk-notification-banner__heading">
|
||||
We couldn't understand that ticket
|
||||
</p>
|
||||
<p class="govuk-body">Try another one instead.</br><pre id="decode-reason"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<footer class="govuk-footer" role="contentinfo">
|
||||
<div class="govuk-width-container">
|
||||
<div class="govuk-footer__meta">
|
||||
<div class="govuk-footer__meta-item govuk-footer__meta-item--grow">
|
||||
<span class="govuk-footer__licence-description">
|
||||
Powered by <a class="govuk-footer__link" href="https://git.eta.st/eta/rsp6-decoder">rsp6-decoder (Rust)</a>,
|
||||
<a class="govuk-footer__link" href="https://github.com/zxing-js/library">zxing-js</a>, and
|
||||
<a class="govuk-footer__link" href="https://github.com/rustwasm/wasm-bindgen">wasm-bindgen</a>.
|
||||
Site design using the <a class="govuk-footer__link" href="https://github.com/alphagov/govuk-frontend">GOV.UK Design System</a>, under the terms of the <a class="govuk-footer__link" href="https://github.com/alphagov/govuk-frontend/blob/master/LICENSE.txt">MIT License</a>.
|
||||
If you liked this, you might also enjoy <a class="govuk-footer__link" href="https://intertube.eta.st/">intertube</a>.
|
||||
<span style="float: right;"><a class="govuk-footer__link" href="https://eta.st/">an eta project</a></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="./bootstrap.js"></script>
|
||||
</body>
|
||||
</html>
|
246
rsp6-webshite/index.js
Normal file
246
rsp6-webshite/index.js
Normal file
@ -0,0 +1,246 @@
|
||||
console.log("[+] Wow, JavaScript!");
|
||||
import * as wasm from "rsp6-decoder";
|
||||
import { BrowserAztecCodeReader } from '@zxing/library';
|
||||
|
||||
let stations = require("./stations.json");
|
||||
let fares = require("./fares.json");
|
||||
window.stations = stations;
|
||||
window.fares = fares;
|
||||
|
||||
window.addEventListener('load', function () {
|
||||
console.log("[+] initialising wasm");
|
||||
window.wasm = wasm;
|
||||
window.wasm.init();
|
||||
let video_div = document.getElementById("video-container");
|
||||
let scan_button = document.getElementById("scan-button");
|
||||
let error_banner = document.getElementById("error-banner");
|
||||
let error_header = document.getElementById("error-header");
|
||||
let error_text = document.getElementById("error-text");
|
||||
let decode_banner = document.getElementById("decode-banner");
|
||||
let decode_reason = document.getElementById("decode-reason");
|
||||
let raw_barcode = document.getElementById("raw-barcode");
|
||||
let raw_barcode_wrapper = document.getElementById("raw-barcode-wrapper");
|
||||
let raw_json = document.getElementById("raw-json");
|
||||
let ticket_data = document.getElementById("ticket-data");
|
||||
let ticket_content = document.getElementById("ticket-content");
|
||||
let file_upload = document.getElementById("file-upload-1");
|
||||
const codeReader = new BrowserAztecCodeReader();
|
||||
console.log("[+] Looks like everything initialised fine!");
|
||||
|
||||
let selectedDeviceId;
|
||||
|
||||
function error(header, text) {
|
||||
video_div.style.display = "none";
|
||||
error_header.innerHTML = header;
|
||||
error_text.innerHTML = text;
|
||||
error_banner.style.display = "block";
|
||||
error_banner.focus();
|
||||
error_banner.scrollIntoView();
|
||||
}
|
||||
|
||||
function decodeError(text) {
|
||||
decode_banner.style.display = "block";
|
||||
decode_reason.innerHTML = text;
|
||||
decode_banner.scrollIntoView();
|
||||
}
|
||||
|
||||
function row(k, v) {
|
||||
let tr = document.createElement("tr");
|
||||
tr.classList.add("govuk-table__row");
|
||||
let th = document.createElement("th");
|
||||
th.classList.add("govuk-table__header");
|
||||
th.scope = "row";
|
||||
let td = document.createElement("td");
|
||||
td.classList.add("govuk-table__cell");
|
||||
th.innerHTML = k;
|
||||
td.innerHTML = v;
|
||||
tr.appendChild(th);
|
||||
tr.appendChild(td);
|
||||
ticket_data.appendChild(tr);
|
||||
}
|
||||
|
||||
function nlcify(nlc) {
|
||||
let data = stations[nlc];
|
||||
if (!data || !data.desc) {
|
||||
return nlc;
|
||||
}
|
||||
else {
|
||||
if (data.crs) {
|
||||
return data.crs;
|
||||
}
|
||||
else {
|
||||
return nlc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleTicket(ticket) {
|
||||
window.ticket = ticket;
|
||||
video_div.style.display = "none";
|
||||
codeReader.reset();
|
||||
console.log("Got ticket: " + ticket);
|
||||
raw_barcode.innerHTML = ticket;
|
||||
raw_barcode_wrapper.style.display = "block";
|
||||
try {
|
||||
let result = window.wasm.decode_ticket(window.ticket);
|
||||
if (result["Err"]) {
|
||||
decodeError(result["Err"]);
|
||||
}
|
||||
else {
|
||||
let data = result["Ok"];
|
||||
raw_json.innerHTML = JSON.stringify(data, null, 2);
|
||||
ticket_data.innerHTML = "";
|
||||
document.getElementById("origin").innerHTML = nlcify(data.origin_nlc);
|
||||
document.getElementById("dest").innerHTML = nlcify(data.destination_nlc);
|
||||
let fare = fares[data.fare] || ("Unknown fare (" + data.fare + ")");
|
||||
let origin = stations[data.origin_nlc] ? stations[data.origin_nlc].desc : "NLC " + data.origin_nlc;
|
||||
let destination = stations[data.destination_nlc] ? stations[data.destination_nlc].desc : "NLC " + data.destination_nlc;
|
||||
let type = "";
|
||||
if (data.coupon_type == "Single") {
|
||||
type = "Single";
|
||||
}
|
||||
else if (data.coupon_type == "ReturnOutbound") {
|
||||
type = "Return (outbound)";
|
||||
}
|
||||
else if (data.coupon_type == "ReturnInbound") {
|
||||
type = "Return (inbound)";
|
||||
}
|
||||
else {
|
||||
type = "<b>Season</b>";
|
||||
}
|
||||
document.getElementById("ticket-fromto").innerHTML = origin + " to " + destination;
|
||||
row("Fare", fare);
|
||||
row("Ticket type", type);
|
||||
row("Start date", data.start_date);
|
||||
if (data.depart_time_flag || data.depart_time != "00:00:00.0") {
|
||||
let extra = "";
|
||||
if (data.depart_time_flag == "Suggested") {
|
||||
extra = " (suggested)";
|
||||
}
|
||||
else if (data.depart_time_flag == "Specific") {
|
||||
extra = " (mandatory)";
|
||||
}
|
||||
row("Depart at", data.depart_time.substring(0, 5) + extra);
|
||||
}
|
||||
row("Ticket reference", data.issuer_id + data.ticket_reference);
|
||||
if (data.passenger_name) {
|
||||
row("Passenger name", data.passenger_name);
|
||||
}
|
||||
if (data.purchase_details) {
|
||||
let dov = data.purchase_details.days_of_validity;
|
||||
let maybe_s = "";
|
||||
if (dov > 1) {
|
||||
maybe_s = "s";
|
||||
}
|
||||
row("Valid for", dov + " day" + maybe_s);
|
||||
}
|
||||
row("Fare code", data.fare);
|
||||
if (data.purchase_details) {
|
||||
let price = data.purchase_details.price_pence;
|
||||
row("Price", "£" + Math.floor(price / 100) + "." + (price % 100).toString().padStart(2, "0"));
|
||||
row("Purchased", data.purchase_details.purchase_time.substring(0, 16));
|
||||
if (data.purchase_details.purchase_reference) {
|
||||
row("Purchase reference", data.purchase_details.purchase_reference);
|
||||
}
|
||||
}
|
||||
let retailer = stations[data.retailer_id] ? stations[data.retailer_id].desc : "???";
|
||||
row("Sold by", retailer + " (" + data.retailer_id + ")");
|
||||
if (data.route_code != 0) {
|
||||
row("Route code", data.route_code);
|
||||
}
|
||||
if (data.restriction_code) {
|
||||
row("Restriction", data.restriction_code);
|
||||
}
|
||||
if (data.discount_code != 0) {
|
||||
row("Discount code", data.route_code);
|
||||
}
|
||||
if (data.passenger_id) {
|
||||
row("Passenger ID", data.passenger_id);
|
||||
}
|
||||
row("Lennon code", data.lennon_ticket_type);
|
||||
row("Sequence number", data.sub_utn);
|
||||
ticket_content.style.display = "block";
|
||||
ticket_content.scrollIntoView();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
decodeError("exception: " + e);
|
||||
}
|
||||
}
|
||||
window.handleTicket = handleTicket;
|
||||
|
||||
codeReader.getVideoInputDevices()
|
||||
.then((videoInputDevices) => {
|
||||
console.log("[+] Detected " + videoInputDevices.length + " video sources");
|
||||
if (videoInputDevices.length >= 1) {
|
||||
selectedDeviceId = videoInputDevices[0].deviceId;
|
||||
scan_button.removeAttribute("disabled");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
error("Scanner failed", "Couldn't figure out what cameras you have on your device.<br/><pre>" + err + "</pre>");
|
||||
});
|
||||
|
||||
document.getElementById('scan-button').addEventListener('click', () => {
|
||||
decode_banner.style.display = "none";
|
||||
ticket_content.style.display = "none";
|
||||
codeReader.decodeFromInputVideoDevice(undefined, 'video').then((result) => {
|
||||
error_banner.style.display = "none";
|
||||
handleTicket(result);
|
||||
}).catch((err) => {
|
||||
error("Scanner failed", "Couldn't start the camera.<br/><pre>" + err + "</pre>");
|
||||
console.error(err);
|
||||
});
|
||||
video_div.style.display = "block";
|
||||
console.log(`[+] Barcode scanner started on device ${selectedDeviceId}`);
|
||||
});
|
||||
document.getElementById('video').addEventListener('play', () => {
|
||||
console.log('[+] Video started playing');
|
||||
video_div.scrollIntoView();
|
||||
});
|
||||
document.getElementById('raw-text-btn').addEventListener('click', () => {
|
||||
console.log("[+] Raw text input pressed");
|
||||
decode_banner.style.display = "none";
|
||||
ticket_content.style.display = "none";
|
||||
let value = document.getElementById('raw-text-in').value;
|
||||
document.getElementById('text-details').removeAttribute('open');
|
||||
handleTicket(value);
|
||||
});
|
||||
file_upload.addEventListener('change', () => {
|
||||
if (file_upload.files.length > 0) {
|
||||
console.log('[+] File selected');
|
||||
const file = file_upload.files[0];
|
||||
const uri = window.URL.createObjectURL(file);
|
||||
console.log('[+] Scanning URL ' + uri);
|
||||
const img = document.createElement("img");
|
||||
img.src = uri;
|
||||
img.videoWidth = 0;
|
||||
let done = false;
|
||||
document.getElementById('image-sanctuary').innerHTML = '';
|
||||
document.getElementById('image-sanctuary').appendChild(img);
|
||||
decode_banner.style.display = "none";
|
||||
ticket_content.style.display = "none";
|
||||
codeReader.reset();
|
||||
setTimeout(() => {
|
||||
if (!done) {
|
||||
console.log("[+] Timed out!");
|
||||
codeReader.reset();
|
||||
}
|
||||
}, 750);
|
||||
codeReader.decodeFromImage(img).then((result) => {
|
||||
console.log("[+] Screenshot decode done!");
|
||||
done = true;
|
||||
document.getElementById('screenshot-details').removeAttribute('open');
|
||||
error_banner.style.display = "none";
|
||||
window.URL.revokeObjectURL(uri);
|
||||
handleTicket(result);
|
||||
}).catch((err) => {
|
||||
done = true;
|
||||
console.error(err);
|
||||
error("Barcode reader failed", "Couldn't detect a barcode in that screenshot. Try another image.<br/><pre>" + err + "</pre>");
|
||||
window.URL.revokeObjectURL(uri);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
})
|
12998
rsp6-webshite/package-lock.json
generated
Normal file
12998
rsp6-webshite/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
rsp6-webshite/package.json
Normal file
22
rsp6-webshite/package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "rsp6-webshite",
|
||||
"version": "0.1.0",
|
||||
"description": "massive excess of web stuff to make tickets work",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"start": "webpack-dev-server"
|
||||
},
|
||||
"author": "eta",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^5.0.0",
|
||||
"rsp6-decoder": "file:../pkg",
|
||||
"webpack": "^4.29.3",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-dev-server": "^3.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@zxing/library": "^0.19.1"
|
||||
}
|
||||
}
|
23
rsp6-webshite/stationify.lisp
Normal file
23
rsp6-webshite/stationify.lisp
Normal file
@ -0,0 +1,23 @@
|
||||
(defun f (x)
|
||||
(let ((tr (string-right-trim '(#\Space #\Tab) x)))
|
||||
(when (> (length tr) 0)
|
||||
tr)))
|
||||
|
||||
(with-open-file (f "~/Downloads/CORPUSExtract.json")
|
||||
(let* ((cl-json:*json-identifier-name-to-lisp* #'identity)
|
||||
(data (cdr (assoc "TIPLOCDATA" (cl-json:decode-json f)
|
||||
:test #'string=)))
|
||||
(ret (make-hash-table :test 'equal)))
|
||||
(loop
|
||||
for entry in data
|
||||
do (let ((nlc (cdr (assoc "NLC" entry :test #'string=)))
|
||||
(crs (f (cdr (assoc "3ALPHA" entry :test #'string=))))
|
||||
(desc (f (cdr (assoc "NLCDESC" entry :test #'string=)))))
|
||||
(when (and nlc desc
|
||||
(eql (rem nlc 100) 0))
|
||||
(let ((nlc (format nil "~4,'0D" (/ nlc 100))))
|
||||
(format t "~A → ~A / ~A~%" nlc crs desc)
|
||||
(setf (gethash nlc ret)
|
||||
`(("crs" . ,crs)
|
||||
("desc" . ,desc)))))))
|
||||
ret))
|
1
rsp6-webshite/stations.json
Normal file
1
rsp6-webshite/stations.json
Normal file
File diff suppressed because one or more lines are too long
14
rsp6-webshite/webpack.config.js
Normal file
14
rsp6-webshite/webpack.config.js
Normal file
@ -0,0 +1,14 @@
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: "./bootstrap.js",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
filename: "bootstrap.js",
|
||||
},
|
||||
mode: "development",
|
||||
plugins: [
|
||||
new CopyWebpackPlugin(['index.html', 'govuk.css'])
|
||||
],
|
||||
};
|
96
src/lib.rs
Normal file
96
src/lib.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use crate::keys::IssuerKeyStore;
|
||||
use crate::payload::Rsp6Ticket;
|
||||
use anyhow::anyhow;
|
||||
use num_bigint::BigUint;
|
||||
|
||||
pub mod keys;
|
||||
pub mod payload;
|
||||
#[cfg(feature = "wasm")]
|
||||
pub mod wasm;
|
||||
|
||||
fn base26_decode(input: &str) -> BigUint {
|
||||
let mut out = BigUint::new(Vec::new());
|
||||
for val in input.as_bytes().iter().rev() {
|
||||
out *= 26u32;
|
||||
out += *val - b'A';
|
||||
}
|
||||
BigUint::from_bytes_le(&out.to_bytes_be())
|
||||
}
|
||||
|
||||
fn strip_padding(tkt: &[u8]) -> Option<&[u8]> {
|
||||
if tkt.is_empty() {
|
||||
return None;
|
||||
}
|
||||
match tkt[0] {
|
||||
1 => {
|
||||
// PKCS#1 v1
|
||||
let tkt = &tkt[1..];
|
||||
let mut iter = tkt.iter();
|
||||
loop {
|
||||
match iter.next()? {
|
||||
0 => {
|
||||
return Some(iter.as_slice());
|
||||
}
|
||||
255 => {}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
// PKCS#1 v2
|
||||
let tkt = &tkt[1..];
|
||||
let mut iter = tkt.iter();
|
||||
loop {
|
||||
match iter.next()? {
|
||||
0 => {
|
||||
return Some(iter.as_slice());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_ticket(iks: &IssuerKeyStore, ticket_str: &str) -> anyhow::Result<Rsp6Ticket> {
|
||||
if ticket_str.len() < 16 {
|
||||
return Err(anyhow!("ticket too short"));
|
||||
}
|
||||
if &ticket_str[0..2] != "06" {
|
||||
return Err(anyhow!(
|
||||
"ticket isn't a RSP6 ticket: magic was {}",
|
||||
&ticket_str[0..2]
|
||||
));
|
||||
}
|
||||
let issuer_id = &ticket_str[13..15];
|
||||
let ticket = base26_decode(&ticket_str[15..]);
|
||||
let keys = iks
|
||||
.keys
|
||||
.get(issuer_id)
|
||||
.ok_or_else(|| anyhow!("unknown issuer ID {}", issuer_id))?;
|
||||
for key in keys {
|
||||
let message = ticket.modpow(&key.public_exponent, &key.modulus);
|
||||
let message = message.to_bytes_be();
|
||||
if let Some(unpadded) = strip_padding(&message) {
|
||||
let ticket = Rsp6Ticket::decode(unpadded, issuer_id.into(), ticket_str[11..13].into())?;
|
||||
return Ok(ticket);
|
||||
}
|
||||
}
|
||||
Err(anyhow!(
|
||||
"all signature unwrap attempts failed (tried {} keys for issuer {})",
|
||||
keys.len(),
|
||||
issuer_id
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn test_base26() {
|
||||
assert_eq!(
|
||||
super::base26_decode("RANEBZCYPNQVMMYJBOJBONYSIYXTREYFSHTZFZEXWTVBNXJBFVOFBMXVQPZTFWVYSWYKINRXRVDCCUWUERKQZKYBPVIIAPJOOFJJXUBFGNVXGXTCFPBHXYVPEKWIURBEOYTYNZUXWVIXHAODACOQLZEQKRUNGWSJHIIWOYSNXJKVYWIGLWCIZKAHFKKAKRDUQSQBGEJMOFCSHSKXSFDDKYCFQI").to_bytes_be(),
|
||||
[53, 242, 184, 141, 14, 99, 169, 215, 200, 223, 85, 250, 45, 253, 184, 100, 225, 124, 82, 70, 138, 222, 246, 185, 192, 129, 247, 218, 24, 26, 249, 112, 74, 225, 71, 139, 27, 50, 218, 11, 93, 238, 232, 163, 151, 68, 159, 146, 80, 133, 11, 45, 57, 245, 163, 117, 218, 11, 187, 246, 18, 147, 88, 171, 133, 216, 166, 47, 232, 246, 198, 170, 99, 36, 120, 114, 73, 207, 19, 218, 202, 146, 158, 223, 107, 234, 171, 172, 20, 189, 133, 246, 192, 248, 57, 111, 65, 65, 135, 64, 241, 99, 87, 107, 75, 40, 224, 223, 100, 53, 180, 212, 53, 200, 172, 117, 127, 248, 193, 0, 147, 167, 222, 81, 135, 158, 135, 137]
|
||||
)
|
||||
}
|
||||
}
|
106
src/main.rs
106
src/main.rs
@ -1,103 +1,21 @@
|
||||
use crate::keys::IssuerKeyStore;
|
||||
use crate::payload::Rsp6Ticket;
|
||||
use anyhow::anyhow;
|
||||
use num_bigint::BigUint;
|
||||
use rsp6_decoder::decode_ticket;
|
||||
use rsp6_decoder::keys::IssuerKeyStore;
|
||||
|
||||
mod keys;
|
||||
mod payload;
|
||||
|
||||
fn base26_decode(input: &str) -> BigUint {
|
||||
let mut out = BigUint::new(Vec::new());
|
||||
for val in input.as_bytes().iter().rev() {
|
||||
out *= 26u32;
|
||||
out += *val - b'A';
|
||||
}
|
||||
BigUint::from_bytes_le(&out.to_bytes_be())
|
||||
}
|
||||
|
||||
fn strip_padding(tkt: &[u8]) -> Option<&[u8]> {
|
||||
if tkt.is_empty() {
|
||||
return None;
|
||||
}
|
||||
match tkt[0] {
|
||||
1 => {
|
||||
// PKCS#1 v1
|
||||
let tkt = &tkt[1..];
|
||||
let mut iter = tkt.iter();
|
||||
loop {
|
||||
match iter.next()? {
|
||||
0 => {
|
||||
return Some(iter.as_slice());
|
||||
}
|
||||
255 => {}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
// PKCS#1 v2
|
||||
let tkt = &tkt[1..];
|
||||
let mut iter = tkt.iter();
|
||||
loop {
|
||||
match iter.next()? {
|
||||
0 => {
|
||||
return Some(iter.as_slice());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
fn main() {
|
||||
let iks = IssuerKeyStore::new();
|
||||
eprintln!("[+] Loaded {} public keys!", iks.keys.len());
|
||||
eprintln!("[+] mmm, give me a tasty ticket on stdin please");
|
||||
eprintln!("Feed in a ticket on stdin (raw scan result starting 06...)");
|
||||
let ticket_str = std::io::stdin()
|
||||
.lines()
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("that was a bit rude, give me an actual ticket"))??;
|
||||
if ticket_str.len() < 16 {
|
||||
return Err(anyhow!("ticket too short"));
|
||||
.expect("no ticket provided")
|
||||
.expect("error reading ticket");
|
||||
match decode_ticket(&iks, &ticket_str) {
|
||||
Ok(t) => {
|
||||
eprintln!("Decode successful.");
|
||||
serde_json::to_writer_pretty(std::io::stdout(), &t).expect("error serializing ticket");
|
||||
}
|
||||
if &ticket_str[0..2] != "06" {
|
||||
return Err(anyhow!(
|
||||
"ticket isn't a RSP6 ticket: magic was {}",
|
||||
&ticket_str[0..2]
|
||||
));
|
||||
}
|
||||
let issuer_id = &ticket_str[13..15];
|
||||
let ticket_reference = format!("{}{}", issuer_id, &ticket_str[2..11]);
|
||||
eprintln!("[+] RSP6 ticket, reference {}", ticket_reference);
|
||||
let ticket = base26_decode(&ticket_str[15..]);
|
||||
let keys = iks
|
||||
.keys
|
||||
.get(issuer_id)
|
||||
.ok_or_else(|| anyhow!("unknown issuer ID {}", issuer_id))?;
|
||||
for key in keys {
|
||||
let message = ticket.modpow(&key.public_exponent, &key.modulus);
|
||||
let message = message.to_bytes_be();
|
||||
if let Some(unpadded) = strip_padding(&message) {
|
||||
eprintln!("[+] decrypt done!");
|
||||
let ticket = Rsp6Ticket::decode(unpadded, issuer_id.into(), ticket_str[11..13].into())?;
|
||||
serde_json::to_writer_pretty(std::io::stdout(), &ticket)?;
|
||||
return Ok(());
|
||||
} else {
|
||||
eprintln!("[-] failed decrypt: {:?}", message);
|
||||
Err(e) => {
|
||||
eprintln!("Failed to decode ticket: {}", e);
|
||||
}
|
||||
}
|
||||
Err(anyhow!("no valid decryptions"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn test_base26() {
|
||||
assert_eq!(
|
||||
super::base26_decode("RANEBZCYPNQVMMYJBOJBONYSIYXTREYFSHTZFZEXWTVBNXJBFVOFBMXVQPZTFWVYSWYKINRXRVDCCUWUERKQZKYBPVIIAPJOOFJJXUBFGNVXGXTCFPBHXYVPEKWIURBEOYTYNZUXWVIXHAODACOQLZEQKRUNGWSJHIIWOYSNXJKVYWIGLWCIZKAHFKKAKRDUQSQBGEJMOFCSHSKXSFDDKYCFQI").to_bytes_be(),
|
||||
[53, 242, 184, 141, 14, 99, 169, 215, 200, 223, 85, 250, 45, 253, 184, 100, 225, 124, 82, 70, 138, 222, 246, 185, 192, 129, 247, 218, 24, 26, 249, 112, 74, 225, 71, 139, 27, 50, 218, 11, 93, 238, 232, 163, 151, 68, 159, 146, 80, 133, 11, 45, 57, 245, 163, 117, 218, 11, 187, 246, 18, 147, 88, 171, 133, 216, 166, 47, 232, 246, 198, 170, 99, 36, 120, 114, 73, 207, 19, 218, 202, 146, 158, 223, 107, 234, 171, 172, 20, 189, 133, 246, 192, 248, 57, 111, 65, 65, 135, 64, 241, 99, 87, 107, 75, 40, 224, 223, 100, 53, 180, 212, 53, 200, 172, 117, 127, 248, 193, 0, 147, 167, 222, 81, 135, 158, 135, 137]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
40
src/wasm.rs
Normal file
40
src/wasm.rs
Normal file
@ -0,0 +1,40 @@
|
||||
//! wasm bindings to run in a browser
|
||||
|
||||
use crate::keys::IssuerKeyStore;
|
||||
use lazy_static::lazy_static;
|
||||
use std::ops::Deref;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
lazy_static! {
|
||||
static ref KEY_STORE: IssuerKeyStore = IssuerKeyStore::new();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn init() {
|
||||
log("[rust] init()");
|
||||
log("[rust] setting panic hook");
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
log("[rust] loading key store");
|
||||
let _store = KEY_STORE.deref();
|
||||
log("[rust] init done");
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn decode_ticket(ticket: String) -> Result<JsValue, String> {
|
||||
log(&format!("[rust] decode_ticket called; ticket = {}", ticket));
|
||||
let iks = KEY_STORE.deref();
|
||||
let ticket = crate::decode_ticket(&iks, &ticket).map_err(|e| e.to_string());
|
||||
let ret = serde_wasm_bindgen::to_value(&ticket)
|
||||
.map_err(|e| format!("failed to serialise ticket: {}", e))?;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
Loading…
Reference in New Issue
Block a user