diff options
Diffstat (limited to 'packages/taler-wallet-android')
-rw-r--r-- | packages/taler-wallet-android/package.json | 44 | ||||
-rw-r--r-- | packages/taler-wallet-android/rollup.config.js | 30 | ||||
-rw-r--r-- | packages/taler-wallet-android/src/index.d.ts | 26 | ||||
-rw-r--r-- | packages/taler-wallet-android/src/index.js | 284 | ||||
-rw-r--r-- | packages/taler-wallet-android/src/index.js.map | 1 | ||||
-rw-r--r-- | packages/taler-wallet-android/src/index.ts | 285 | ||||
-rw-r--r-- | packages/taler-wallet-android/tsconfig.json | 32 |
7 files changed, 702 insertions, 0 deletions
diff --git a/packages/taler-wallet-android/package.json b/packages/taler-wallet-android/package.json new file mode 100644 index 000000000..a24dbdbeb --- /dev/null +++ b/packages/taler-wallet-android/package.json @@ -0,0 +1,44 @@ +{ + "name": "taler-wallet-android", + "version": "0.6.12", + "description": "", + "engines": { + "node": ">=0.12.0" + }, + "repository": { + "type": "git", + "url": "git://git.taler.net/wallet-core.git" + }, + "main": "dist/taler-wallet-android.js", + "author": "Florian Dold", + "license": "GPL-3.0", + "scripts": { + "compile": "rollup -c && tsc", + "pretty": "prettier --config ../../.prettierrc --write src", + "test": "tsc && ava", + "coverage": "tsc && nyc ava", + "clean": "rimraf lib dist" + }, + "files": [ + "AUTHORS", + "README", + "COPYING", + "bin/", + "dist/node", + "src/" + ], + "devDependencies": { + "rimraf": "^3.0.2", + "@rollup/plugin-commonjs": "^14.0.0", + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^8.4.0", + "@rollup/plugin-replace": "^2.3.3", + "rollup": "^2.23.0", + "rollup-plugin-sourcemaps": "^0.6.2", + "rollup-plugin-terser": "^6.1.0" + }, + "dependencies": { + "taler-wallet-core": "workspace:*", + "tslib": "^2.0.0" + } +} diff --git a/packages/taler-wallet-android/rollup.config.js b/packages/taler-wallet-android/rollup.config.js new file mode 100644 index 000000000..7cdca3b98 --- /dev/null +++ b/packages/taler-wallet-android/rollup.config.js @@ -0,0 +1,30 @@ +// rollup.config.js +import commonjs from "@rollup/plugin-commonjs"; +import nodeResolve from "@rollup/plugin-node-resolve"; +import json from "@rollup/plugin-json"; +import builtins from "builtin-modules"; +import pkg from "./package.json"; + +export default { + input: "lib/index.js", + output: { + file: pkg.main, + format: "cjs", + }, + external: builtins, + plugins: [ + nodeResolve({ + preferBuiltins: true, + }), + + commonjs({ + include: [/node_modules/, /dist/], + extensions: [".js", ".ts"], + ignoreGlobal: false, + sourceMap: false, + }), + + json(), + ], +} + diff --git a/packages/taler-wallet-android/src/index.d.ts b/packages/taler-wallet-android/src/index.d.ts new file mode 100644 index 000000000..18e240f35 --- /dev/null +++ b/packages/taler-wallet-android/src/index.d.ts @@ -0,0 +1,26 @@ +import { + HttpRequestLibrary, + HttpResponse, + HttpRequestOptions, +} from "../../taler-wallet-core/src/util/http"; +export { + handleWorkerError, + handleWorkerMessage, +} from "../../taler-wallet-core/src/crypto/workers/nodeThreadWorker"; +export declare class AndroidHttpLib implements HttpRequestLibrary { + private sendMessage; + useNfcTunnel: boolean; + private nodeHttpLib; + private requestId; + private requestMap; + constructor(sendMessage: (m: string) => void); + get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse>; + postJson( + url: string, + body: any, + opt?: HttpRequestOptions, + ): Promise<import("../../taler-wallet-core/src/util/http").HttpResponse>; + handleTunnelResponse(msg: any): void; +} +export declare function installAndroidWalletListener(): void; +//# sourceMappingURL=index.d.ts.map diff --git a/packages/taler-wallet-android/src/index.js b/packages/taler-wallet-android/src/index.js new file mode 100644 index 000000000..ca4b7f971 --- /dev/null +++ b/packages/taler-wallet-android/src/index.js @@ -0,0 +1,284 @@ +"use strict"; +/* + This file is part of GNU Taler + (C) 2019 GNUnet e.V. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.installAndroidWalletListener = exports.AndroidHttpLib = void 0; +const tslib_1 = require("tslib"); +/** + * Imports. + */ +const taler_wallet_core_1 = require("taler-wallet-core"); +const promiseUtils_1 = require("../../taler-wallet-core/src/util/promiseUtils"); +const fs_1 = tslib_1.__importDefault(require("fs")); +const http_1 = require("../../taler-wallet-core/src/util/http"); +const NodeHttpLib_1 = require("../../taler-wallet-core/src/headless/NodeHttpLib"); +const walletCoreApiHandler_1 = require("../../taler-wallet-core/src/walletCoreApiHandler"); +const errors_1 = require("../../taler-wallet-core/src/operations/errors"); +const TalerErrorCode_1 = require("../../taler-wallet-core/src/TalerErrorCode"); +// @ts-ignore: special built-in module +//import akono = require("akono"); +var nodeThreadWorker_1 = require("../../taler-wallet-core/src/crypto/workers/nodeThreadWorker"); +Object.defineProperty(exports, "handleWorkerError", { + enumerable: true, + get: function () { + return nodeThreadWorker_1.handleWorkerError; + }, +}); +Object.defineProperty(exports, "handleWorkerMessage", { + enumerable: true, + get: function () { + return nodeThreadWorker_1.handleWorkerMessage; + }, +}); +class AndroidHttpLib { + constructor(sendMessage) { + this.sendMessage = sendMessage; + this.useNfcTunnel = false; + this.nodeHttpLib = new NodeHttpLib_1.NodeHttpLib(); + this.requestId = 1; + this.requestMap = {}; + } + get(url, opt) { + if (this.useNfcTunnel) { + const myId = this.requestId++; + const p = promiseUtils_1.openPromise(); + this.requestMap[myId] = p; + const request = { + method: "get", + url, + }; + this.sendMessage( + JSON.stringify({ + type: "tunnelHttp", + request, + id: myId, + }), + ); + return p.promise; + } else { + return this.nodeHttpLib.get(url, opt); + } + } + postJson(url, body, opt) { + if (this.useNfcTunnel) { + const myId = this.requestId++; + const p = promiseUtils_1.openPromise(); + this.requestMap[myId] = p; + const request = { + method: "postJson", + url, + body, + }; + this.sendMessage( + JSON.stringify({ type: "tunnelHttp", request, id: myId }), + ); + return p.promise; + } else { + return this.nodeHttpLib.postJson(url, body, opt); + } + } + handleTunnelResponse(msg) { + const myId = msg.id; + const p = this.requestMap[myId]; + if (!p) { + console.error( + `no matching request for tunneled HTTP response, id=${myId}`, + ); + } + const headers = new http_1.Headers(); + if (msg.status != 0) { + const resp = { + // FIXME: pass through this URL + requestUrl: "", + headers, + status: msg.status, + json: () => + tslib_1.__awaiter(this, void 0, void 0, function* () { + return JSON.parse(msg.responseText); + }), + text: () => + tslib_1.__awaiter(this, void 0, void 0, function* () { + return msg.responseText; + }), + }; + p.resolve(resp); + } else { + p.reject(new Error(`unexpected HTTP status code ${msg.status}`)); + } + delete this.requestMap[myId]; + } +} +exports.AndroidHttpLib = AndroidHttpLib; +function sendAkonoMessage(ev) { + // @ts-ignore + const sendMessage = globalThis.__akono_sendMessage; + if (typeof sendMessage !== "function") { + const errMsg = + "FATAL: cannot install android wallet listener: akono functions missing"; + console.error(errMsg); + throw new Error(errMsg); + } + const m = JSON.stringify(ev); + // @ts-ignore + sendMessage(m); +} +class AndroidWalletMessageHandler { + constructor() { + this.wp = promiseUtils_1.openPromise(); + this.httpLib = new NodeHttpLib_1.NodeHttpLib(); + } + /** + * Handle a request from the Android wallet. + */ + handleMessage(operation, id, args) { + return tslib_1.__awaiter(this, void 0, void 0, function* () { + const wrapResponse = (result) => { + return { + type: "response", + id, + operation, + result, + }; + }; + switch (operation) { + case "init": { + this.walletArgs = { + notifyHandler: (notification) => + tslib_1.__awaiter(this, void 0, void 0, function* () { + sendAkonoMessage({ + type: "notification", + payload: notification, + }); + }), + persistentStoragePath: args.persistentStoragePath, + httpLib: this.httpLib, + }; + const w = yield taler_wallet_core_1.getDefaultNodeWallet( + this.walletArgs, + ); + this.maybeWallet = w; + w.runRetryLoop().catch((e) => { + console.error("Error during wallet retry loop", e); + }); + this.wp.resolve(w); + return wrapResponse({ + supported_protocol_versions: { + exchange: + taler_wallet_core_1.versions.WALLET_EXCHANGE_PROTOCOL_VERSION, + merchant: + taler_wallet_core_1.versions.WALLET_MERCHANT_PROTOCOL_VERSION, + }, + }); + } + case "getHistory": { + return wrapResponse({ history: [] }); + } + case "startTunnel": { + // this.httpLib.useNfcTunnel = true; + throw Error("not implemented"); + } + case "stopTunnel": { + // this.httpLib.useNfcTunnel = false; + throw Error("not implemented"); + } + case "tunnelResponse": { + // httpLib.handleTunnelResponse(msg.args); + throw Error("not implemented"); + } + case "reset": { + const oldArgs = this.walletArgs; + this.walletArgs = Object.assign({}, oldArgs); + if (oldArgs && oldArgs.persistentStoragePath) { + try { + fs_1.default.unlinkSync(oldArgs.persistentStoragePath); + } catch (e) { + console.error("Error while deleting the wallet db:", e); + } + // Prevent further storage! + this.walletArgs.persistentStoragePath = undefined; + } + const wallet = yield this.wp.promise; + wallet.stop(); + this.wp = promiseUtils_1.openPromise(); + this.maybeWallet = undefined; + const w = yield taler_wallet_core_1.getDefaultNodeWallet( + this.walletArgs, + ); + this.maybeWallet = w; + w.runRetryLoop().catch((e) => { + console.error("Error during wallet retry loop", e); + }); + this.wp.resolve(w); + return wrapResponse({}); + } + default: { + const wallet = yield this.wp.promise; + return yield walletCoreApiHandler_1.handleCoreApiRequest( + wallet, + operation, + id, + args, + ); + } + } + }); + } +} +function installAndroidWalletListener() { + const handler = new AndroidWalletMessageHandler(); + const onMessage = (msgStr) => + tslib_1.__awaiter(this, void 0, void 0, function* () { + if (typeof msgStr !== "string") { + console.error("expected string as message"); + return; + } + const msg = JSON.parse(msgStr); + const operation = msg.operation; + if (typeof operation !== "string") { + console.error( + "message to android wallet helper must contain operation of type string", + ); + return; + } + const id = msg.id; + console.log(`android listener: got request for ${operation} (${id})`); + try { + const respMsg = yield handler.handleMessage(operation, id, msg.args); + console.log( + `android listener: sending success response for ${operation} (${id})`, + ); + sendAkonoMessage(respMsg); + } catch (e) { + const respMsg = { + type: "error", + id, + operation, + error: errors_1.makeErrorDetails( + TalerErrorCode_1.TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION, + "unexpected exception", + {}, + ), + }; + sendAkonoMessage(respMsg); + return; + } + }); + // @ts-ignore + globalThis.__akono_onMessage = onMessage; + console.log("android wallet listener installed"); +} +exports.installAndroidWalletListener = installAndroidWalletListener; +//# sourceMappingURL=index.js.map diff --git a/packages/taler-wallet-android/src/index.js.map b/packages/taler-wallet-android/src/index.js.map new file mode 100644 index 000000000..2c6d50b9f --- /dev/null +++ b/packages/taler-wallet-android/src/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
\ No newline at end of file diff --git a/packages/taler-wallet-android/src/index.ts b/packages/taler-wallet-android/src/index.ts new file mode 100644 index 000000000..d0001e991 --- /dev/null +++ b/packages/taler-wallet-android/src/index.ts @@ -0,0 +1,285 @@ +/* + This file is part of GNU Taler + (C) 2019 GNUnet e.V. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Imports. + */ +import { + Wallet, + getDefaultNodeWallet, + DefaultNodeWalletArgs, + versions, + httpLib, + nodeThreadWorker, + promiseUtil, + NodeHttpLib, + walletCoreApi, + walletNotifications, + TalerErrorCode, + makeErrorDetails, +} from "taler-wallet-core"; + +import fs from "fs"; + +export const handleWorkerError = nodeThreadWorker.handleWorkerError; +export const handleWorkerMessage = nodeThreadWorker.handleWorkerMessage; + +export class AndroidHttpLib implements httpLib.HttpRequestLibrary { + useNfcTunnel = false; + + private nodeHttpLib: httpLib.HttpRequestLibrary = new NodeHttpLib(); + + private requestId = 1; + + private requestMap: { + [id: number]: promiseUtil.OpenedPromise<httpLib.HttpResponse>; + } = {}; + + constructor(private sendMessage: (m: string) => void) {} + + get( + url: string, + opt?: httpLib.HttpRequestOptions, + ): Promise<httpLib.HttpResponse> { + if (this.useNfcTunnel) { + const myId = this.requestId++; + const p = promiseUtil.openPromise<httpLib.HttpResponse>(); + this.requestMap[myId] = p; + const request = { + method: "get", + url, + }; + this.sendMessage( + JSON.stringify({ + type: "tunnelHttp", + request, + id: myId, + }), + ); + return p.promise; + } else { + return this.nodeHttpLib.get(url, opt); + } + } + + postJson( + url: string, + body: any, + opt?: httpLib.HttpRequestOptions, + ): Promise<httpLib.HttpResponse> { + if (this.useNfcTunnel) { + const myId = this.requestId++; + const p = promiseUtil.openPromise<httpLib.HttpResponse>(); + this.requestMap[myId] = p; + const request = { + method: "postJson", + url, + body, + }; + this.sendMessage( + JSON.stringify({ type: "tunnelHttp", request, id: myId }), + ); + return p.promise; + } else { + return this.nodeHttpLib.postJson(url, body, opt); + } + } + + handleTunnelResponse(msg: any): void { + const myId = msg.id; + const p = this.requestMap[myId]; + if (!p) { + console.error( + `no matching request for tunneled HTTP response, id=${myId}`, + ); + } + const headers = new httpLib.Headers(); + if (msg.status != 0) { + const resp: httpLib.HttpResponse = { + // FIXME: pass through this URL + requestUrl: "", + headers, + status: msg.status, + json: async () => JSON.parse(msg.responseText), + text: async () => msg.responseText, + }; + p.resolve(resp); + } else { + p.reject(new Error(`unexpected HTTP status code ${msg.status}`)); + } + delete this.requestMap[myId]; + } +} + +function sendAkonoMessage(ev: walletCoreApi.CoreApiEnvelope): void { + // @ts-ignore + const sendMessage = globalThis.__akono_sendMessage; + if (typeof sendMessage !== "function") { + const errMsg = + "FATAL: cannot install android wallet listener: akono functions missing"; + console.error(errMsg); + throw new Error(errMsg); + } + const m = JSON.stringify(ev); + // @ts-ignore + sendMessage(m); +} + +class AndroidWalletMessageHandler { + walletArgs: DefaultNodeWalletArgs | undefined; + maybeWallet: Wallet | undefined; + wp = promiseUtil.openPromise<Wallet>(); + httpLib = new NodeHttpLib(); + + /** + * Handle a request from the Android wallet. + */ + async handleMessage( + operation: string, + id: string, + args: any, + ): Promise<walletCoreApi.CoreApiResponse> { + const wrapResponse = ( + result: unknown, + ): walletCoreApi.CoreApiResponseSuccess => { + return { + type: "response", + id, + operation, + result, + }; + }; + switch (operation) { + case "init": { + this.walletArgs = { + notifyHandler: async ( + notification: walletNotifications.WalletNotification, + ) => { + sendAkonoMessage({ type: "notification", payload: notification }); + }, + persistentStoragePath: args.persistentStoragePath, + httpLib: this.httpLib, + }; + const w = await getDefaultNodeWallet(this.walletArgs); + this.maybeWallet = w; + w.runRetryLoop().catch((e) => { + console.error("Error during wallet retry loop", e); + }); + this.wp.resolve(w); + return wrapResponse({ + supported_protocol_versions: { + exchange: versions.WALLET_EXCHANGE_PROTOCOL_VERSION, + merchant: versions.WALLET_MERCHANT_PROTOCOL_VERSION, + }, + }); + } + case "getHistory": { + return wrapResponse({ history: [] }); + } + case "startTunnel": { + // this.httpLib.useNfcTunnel = true; + throw Error("not implemented"); + } + case "stopTunnel": { + // this.httpLib.useNfcTunnel = false; + throw Error("not implemented"); + } + case "tunnelResponse": { + // httpLib.handleTunnelResponse(msg.args); + throw Error("not implemented"); + } + case "reset": { + const oldArgs = this.walletArgs; + this.walletArgs = { ...oldArgs }; + if (oldArgs && oldArgs.persistentStoragePath) { + try { + fs.unlinkSync(oldArgs.persistentStoragePath); + } catch (e) { + console.error("Error while deleting the wallet db:", e); + } + // Prevent further storage! + this.walletArgs.persistentStoragePath = undefined; + } + const wallet = await this.wp.promise; + wallet.stop(); + this.wp = promiseUtil.openPromise<Wallet>(); + this.maybeWallet = undefined; + const w = await getDefaultNodeWallet(this.walletArgs); + this.maybeWallet = w; + w.runRetryLoop().catch((e) => { + console.error("Error during wallet retry loop", e); + }); + this.wp.resolve(w); + return wrapResponse({}); + } + default: { + const wallet = await this.wp.promise; + return await walletCoreApi.handleCoreApiRequest( + wallet, + operation, + id, + args, + ); + } + } + } +} + +export function installAndroidWalletListener(): void { + const handler = new AndroidWalletMessageHandler(); + const onMessage = async (msgStr: any): Promise<void> => { + if (typeof msgStr !== "string") { + console.error("expected string as message"); + return; + } + const msg = JSON.parse(msgStr); + const operation = msg.operation; + if (typeof operation !== "string") { + console.error( + "message to android wallet helper must contain operation of type string", + ); + return; + } + const id = msg.id; + console.log(`android listener: got request for ${operation} (${id})`); + + try { + const respMsg = await handler.handleMessage(operation, id, msg.args); + console.log( + `android listener: sending success response for ${operation} (${id})`, + ); + sendAkonoMessage(respMsg); + } catch (e) { + const respMsg: walletCoreApi.CoreApiResponse = { + type: "error", + id, + operation, + error: makeErrorDetails( + TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION, + "unexpected exception", + {}, + ), + }; + sendAkonoMessage(respMsg); + return; + } + }; + + // @ts-ignore + globalThis.__akono_onMessage = onMessage; + + console.log("android wallet listener installed"); +} diff --git a/packages/taler-wallet-android/tsconfig.json b/packages/taler-wallet-android/tsconfig.json new file mode 100644 index 000000000..abb21b4db --- /dev/null +++ b/packages/taler-wallet-android/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "composite": true, + "declaration": true, + "declarationMap": true, + "target": "ES6", + "jsx": "react", + "reactNamespace": "React", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "lib": [ + "es6", + "dom" + ], + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "strict": true, + "strictPropertyInitialization": false, + "outDir": "lib", + "noImplicitAny": true, + "noImplicitThis": true, + "allowJs": true, + "checkJs": true, + "incremental": true, + "esModuleInterop": true, + "importHelpers": true, + "rootDir": "./src" + }, + "include": ["src/**/*"] +} |