diff options
Diffstat (limited to 'packages/taler-util/src/taleruri.ts')
-rw-r--r-- | packages/taler-util/src/taleruri.ts | 146 |
1 files changed, 127 insertions, 19 deletions
diff --git a/packages/taler-util/src/taleruri.ts b/packages/taler-util/src/taleruri.ts index 97b82c061..54b7525e3 100644 --- a/packages/taler-util/src/taleruri.ts +++ b/packages/taler-util/src/taleruri.ts @@ -25,9 +25,10 @@ */ import { Codec, Context, DecodingError, renderContext } from "./codec.js"; import { canonicalizeBaseUrl } from "./helpers.js"; +import { opFixedSuccess, opKnownTalerFailure } from "./operation.js"; +import { TalerErrorCode } from "./taler-error-codes.js"; import { AmountString } from "./taler-types.js"; import { URL, URLSearchParams } from "./url.js"; - /** * A parsed taler URI. */ @@ -40,7 +41,8 @@ export type TalerUri = | BackupRestoreUri | RefundUriResult | WithdrawUriResult - | WithdrawExchangeUri; + | WithdrawExchangeUri + | AddExchangeUri; declare const __action_str: unique symbol; export type TalerUriString = string & { [__action_str]: true }; @@ -81,7 +83,6 @@ export interface PayTemplateUriResult { type: TalerUriAction.PayTemplate; merchantBaseUrl: string; templateId: string; - templateParams: TemplateParams; } export interface WithdrawUriResult { @@ -122,23 +123,29 @@ export interface BackupRestoreUri { export interface WithdrawExchangeUri { type: TalerUriAction.WithdrawExchange; exchangeBaseUrl: string; - exchangePub?: string; amount?: AmountString; } +export interface AddExchangeUri { + type: TalerUriAction.AddExchange; + exchangeBaseUrl: string; +} + /** * Parse a taler[+http]://withdraw URI. * Return undefined if not passed a valid URI. */ -export function parseWithdrawUri(s: string): WithdrawUriResult | undefined { - const pi = parseProtoInfo(s, "withdraw"); - if (!pi) { - return undefined; +export function parseWithdrawUriWithError(s: string) { + const pi = parseProtoInfoWithError(s, "withdraw"); + if (pi.type === "fail") { + return pi; } - const parts = pi.rest.split("/"); + const parts = pi.body.rest.split("/"); if (parts.length < 2) { - return undefined; + return opKnownTalerFailure(TalerErrorCode.WALLET_TALER_URI_MALFORMED, { + code: TalerErrorCode.WALLET_TALER_URI_MALFORMED, + }); } const host = parts[0].toLowerCase(); @@ -153,11 +160,69 @@ export function parseWithdrawUri(s: string): WithdrawUriResult | undefined { const withdrawId = parts[parts.length - 1]; const p = [host, ...pathSegments].join("/"); - return { + const result: WithdrawUriResult = { type: TalerUriAction.Withdraw, - bankIntegrationApiBaseUrl: canonicalizeBaseUrl(`${pi.innerProto}://${p}/`), + bankIntegrationApiBaseUrl: canonicalizeBaseUrl( + `${pi.body.innerProto}://${p}/`, + ), withdrawalOperationId: withdrawId, }; + return opFixedSuccess(result); +} + +/** + * + * @deprecated use parseWithdrawUriWithError + */ +export function parseWithdrawUri(s: string): WithdrawUriResult | undefined { + const r = parseWithdrawUriWithError(s); + if (r.type === "fail") return undefined; + return r.body; +} + +/** + * Parse a taler[+http]://withdraw URI. + * Return undefined if not passed a valid URI. + */ +export function parseAddExchangeUriWithError(s: string) { + const pi = parseProtoInfoWithError(s, "add-exchange"); + if (pi.type === "fail") { + return pi; + } + const parts = pi.body.rest.split("/"); + + if (parts.length < 2) { + return opKnownTalerFailure(TalerErrorCode.WALLET_TALER_URI_MALFORMED, { + code: TalerErrorCode.WALLET_TALER_URI_MALFORMED, + }); + } + + const host = parts[0].toLowerCase(); + const pathSegments = parts.slice(1, parts.length - 1); + /** + * The statement below does not tolerate a slash-ended URI. + * This results in (1) the withdrawalId being passed as the + * empty string, and (2) the bankIntegrationApi ending with the + * actual withdrawal operation ID. That can be fixed by + * trimming the parts-list. FIXME + */ + const p = [host, ...pathSegments].join("/"); + + const result: AddExchangeUri = { + type: TalerUriAction.AddExchange, + exchangeBaseUrl: canonicalizeBaseUrl(`${pi.body.innerProto}://${p}/`), + }; + return opFixedSuccess(result); +} + +/** + * + * @deprecated use parseWithdrawUriWithError + */ +export function parseAddExchangeUri(s: string): AddExchangeUri | undefined { + const r = parseAddExchangeUriWithError(s); + if (r.type === "fail") return undefined; + return r.body; } /** @@ -187,6 +252,7 @@ export enum TalerUriAction { Restore = "restore", DevExperiment = "dev-experiment", WithdrawExchange = "withdraw-exchange", + AddExchange = "add-exchange", } interface TalerUriProtoInfo { @@ -215,6 +281,34 @@ function parseProtoInfo( } } +function parseProtoInfoWithError(s: string, action: string) { + if ( + !s.toLowerCase().startsWith("taler://") && + !s.toLowerCase().startsWith("taler+http://") + ) { + return opKnownTalerFailure(TalerErrorCode.WALLET_TALER_URI_MALFORMED, { + code: TalerErrorCode.WALLET_TALER_URI_MALFORMED, + }); + } + const pfxPlain = `taler://${action}/`; + const pfxHttp = `taler+http://${action}/`; + if (s.toLowerCase().startsWith(pfxPlain)) { + return opFixedSuccess({ + innerProto: "https", + rest: s.substring(pfxPlain.length), + }); + } else if (s.toLowerCase().startsWith(pfxHttp)) { + return opFixedSuccess({ + innerProto: "http", + rest: s.substring(pfxHttp.length), + }); + } else { + return opKnownTalerFailure(TalerErrorCode.WALLET_TALER_URI_MALFORMED, { + code: TalerErrorCode.WALLET_TALER_URI_MALFORMED, + }); + } +} + type Parser = (s: string) => TalerUri | undefined; const parsers: { [A in TalerUriAction]: Parser } = { [TalerUriAction.Pay]: parsePayUri, @@ -226,6 +320,7 @@ const parsers: { [A in TalerUriAction]: Parser } = { [TalerUriAction.Withdraw]: parseWithdrawUri, [TalerUriAction.DevExperiment]: parseDevExperimentUri, [TalerUriAction.WithdrawExchange]: parseWithdrawExchangeUri, + [TalerUriAction.AddExchange]: parseAddExchangeUri, }; export function parseTalerUri(string: string): TalerUri | undefined { @@ -269,6 +364,9 @@ export function stringifyTalerUri(uri: TalerUri): string { case TalerUriAction.WithdrawExchange: { return stringifyWithdrawExchange(uri); } + case TalerUriAction.AddExchange: { + return stringifyAddExchange(uri); + } } } @@ -338,7 +436,6 @@ export function parsePayTemplateUri( type: TalerUriAction.PayTemplate, merchantBaseUrl, templateId, - templateParams: params, }; } @@ -405,7 +502,14 @@ export function parseWithdrawExchangeUri( return undefined; } const host = parts[0].toLowerCase(); - const exchangePub = parts.length > 1 ? parts[parts.length - 1] : undefined; + // Used to be the reserve public key, now it's empty! + const lastPathComponent = + parts.length > 1 ? parts[parts.length - 1] : undefined; + + if (lastPathComponent) { + // invalid taler://withdraw-exchange URI, must end with a slash + return undefined; + } const pathSegments = parts.slice(1, parts.length - 1); const hostAndSegments = [host, ...pathSegments].join("/"); const exchangeBaseUrl = canonicalizeBaseUrl( @@ -417,7 +521,6 @@ export function parseWithdrawExchangeUri( return { type: TalerUriAction.WithdrawExchange, exchangeBaseUrl, - exchangePub: exchangePub != "" ? exchangePub : undefined, amount, }; } @@ -539,13 +642,19 @@ export function stringifyRestoreUri({ export function stringifyWithdrawExchange({ exchangeBaseUrl, - exchangePub, amount, }: Omit<WithdrawExchangeUri, "type">): string { const { proto, path, query } = getUrlInfo(exchangeBaseUrl, { a: amount, }); - return `${proto}://withdraw-exchange/${path}${exchangePub ?? ""}${query}`; + return `${proto}://withdraw-exchange/${path}${query}`; +} + +export function stringifyAddExchange({ + exchangeBaseUrl, +}: Omit<AddExchangeUri, "type">): string { + const { proto, path } = getUrlInfo(exchangeBaseUrl); + return `${proto}://add-exchange/${path}`; } export function stringifyDevExperimentUri({ @@ -557,9 +666,8 @@ export function stringifyDevExperimentUri({ export function stringifyPayTemplateUri({ merchantBaseUrl, templateId, - templateParams, }: Omit<PayTemplateUriResult, "type">): string { - const { proto, path, query } = getUrlInfo(merchantBaseUrl, templateParams); + const { proto, path, query } = getUrlInfo(merchantBaseUrl); return `${proto}://pay-template/${path}${templateId}${query}`; } |