From ffd2a62c3f7df94365980302fef3bc3376b48182 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 3 Aug 2020 13:00:48 +0530 Subject: modularize repo, use pnpm, improve typechecking --- packages/taler-wallet-android/src/index.d.ts | 26 +++ packages/taler-wallet-android/src/index.js | 284 ++++++++++++++++++++++++ packages/taler-wallet-android/src/index.js.map | 1 + packages/taler-wallet-android/src/index.ts | 285 +++++++++++++++++++++++++ 4 files changed, 596 insertions(+) create mode 100644 packages/taler-wallet-android/src/index.d.ts create mode 100644 packages/taler-wallet-android/src/index.js create mode 100644 packages/taler-wallet-android/src/index.js.map create mode 100644 packages/taler-wallet-android/src/index.ts (limited to 'packages/taler-wallet-android/src') 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; + postJson( + url: string, + body: any, + opt?: HttpRequestOptions, + ): Promise; + 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 + */ +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 + */ + +/** + * 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; + } = {}; + + constructor(private sendMessage: (m: string) => void) {} + + get( + url: string, + opt?: httpLib.HttpRequestOptions, + ): Promise { + if (this.useNfcTunnel) { + const myId = this.requestId++; + const p = promiseUtil.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: string, + body: any, + opt?: httpLib.HttpRequestOptions, + ): Promise { + if (this.useNfcTunnel) { + const myId = this.requestId++; + const p = promiseUtil.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: 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(); + httpLib = new NodeHttpLib(); + + /** + * Handle a request from the Android wallet. + */ + async handleMessage( + operation: string, + id: string, + args: any, + ): Promise { + 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(); + 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 => { + 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"); +} -- cgit v1.2.3