gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: case-insensitive URIs


From: gnunet
Subject: [taler-wallet-core] branch master updated: case-insensitive URIs
Date: Fri, 06 Dec 2019 12:47:34 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new ee1fc03a case-insensitive URIs
ee1fc03a is described below

commit ee1fc03ae82f2f4662041af3d4243113e92ffeaf
Author: Florian Dold <address@hidden>
AuthorDate: Fri Dec 6 12:47:28 2019 +0100

    case-insensitive URIs
---
 src/dbTypes.ts                   |  16 +++---
 src/headless/taler-wallet-cli.ts |  53 +++++++++++-------
 src/util/taleruri-test.ts        |  55 ++++++-------------
 src/util/taleruri.ts             |  81 ++++++++++++++++++++++------
 src/wallet-impl/pay.ts           |  23 ++++----
 src/wallet-impl/pending.ts       |   3 +-
 src/walletTypes.ts               |   1 +
 src/webex/wxBackend.ts           | 114 ++++++++++++++++++++-------------------
 8 files changed, 200 insertions(+), 146 deletions(-)

diff --git a/src/dbTypes.ts b/src/dbTypes.ts
index 3625740e..55304061 100644
--- a/src/dbTypes.ts
+++ b/src/dbTypes.ts
@@ -699,11 +699,11 @@ export class ProposalDownload {
  */
 @Checkable.Class()
 export class ProposalRecord {
-  /**
-   * URL where the proposal was downloaded.
-   */
   @Checkable.String()
-  url: string;
+  orderId: string;
+
+  @Checkable.String()
+  merchantBaseUrl: string;
 
   /**
    * Downloaded data from the merchant.
@@ -970,7 +970,6 @@ export interface WireFee {
   sig: string;
 }
 
-
 /**
  * Record that stores status information about one purchase, starting from when
  * the customer accepts a proposal.  Includes refund status if applicable.
@@ -1058,7 +1057,7 @@ export interface PurchaseRecord {
    */
   lastRefundStatusError: OperationError | undefined;
 
-    /**
+  /**
    * Retry information for querying the refund status with the merchant.
    */
   refundApplyRetryInfo: RetryInfo;
@@ -1242,7 +1241,10 @@ export namespace Stores {
     constructor() {
       super("proposals", { keyPath: "proposalId" });
     }
-    urlIndex = new Index<string, ProposalRecord>(this, "urlIndex", "url");
+    urlAndOrderIdIndex = new Index<string, ProposalRecord>(this, "urlIndex", [
+      "merchantBaseUrl",
+      "orderId",
+    ]);
   }
 
   class PurchasesStore extends Store<PurchaseRecord> {
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index 2073c257..4cec984d 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -28,6 +28,7 @@ import * as Amounts from "../util/amounts";
 import { decodeCrock } from "../crypto/talerCrypto";
 import { OperationFailedAndReportedError } from "../wallet-impl/errors";
 import { Bank } from "./bank";
+import { classifyTalerUri, TalerUriType } from "../util/taleruri";
 
 const logger = new Logger("taler-wallet-cli.ts");
 
@@ -212,25 +213,39 @@ walletCli
   .action(async args => {
     await withWallet(args, async wallet => {
       const uri: string = args.handleUri.uri;
-      if (uri.startsWith("taler://pay/")) {
-        await doPay(wallet, uri, { alwaysYes: args.handleUri.autoYes });
-      } else if (uri.startsWith("taler://tip/")) {
-        const res = await wallet.getTipStatus(uri);
-        console.log("tip status", res);
-        await wallet.acceptTip(res.tipId);
-      } else if (uri.startsWith("taler://refund/")) {
-        await wallet.applyRefund(uri);
-      } else if (uri.startsWith("taler://withdraw/")) {
-        const withdrawInfo = await wallet.getWithdrawalInfo(uri);
-        const selectedExchange = withdrawInfo.suggestedExchange;
-        if (!selectedExchange) {
-          console.error("no suggested exchange!");
-          process.exit(1);
-          return;
-        }
-        const res = await wallet.acceptWithdrawal(uri, selectedExchange);
-        await wallet.processReserve(res.reservePub);
+      const uriType = classifyTalerUri(uri);
+      switch (uriType) {
+        case TalerUriType.TalerPay:
+          await doPay(wallet, uri, { alwaysYes: args.handleUri.autoYes });
+          break;
+        case TalerUriType.TalerTip:
+          {
+            const res = await wallet.getTipStatus(uri);
+            console.log("tip status", res);
+            await wallet.acceptTip(res.tipId);
+          }
+          break;
+        case TalerUriType.TalerRefund:
+          await wallet.applyRefund(uri);
+          break;
+        case TalerUriType.TalerWithdraw:
+          {
+            const withdrawInfo = await wallet.getWithdrawalInfo(uri);
+            const selectedExchange = withdrawInfo.suggestedExchange;
+            if (!selectedExchange) {
+              console.error("no suggested exchange!");
+              process.exit(1);
+              return;
+            }
+            const res = await wallet.acceptWithdrawal(uri, selectedExchange);
+            await wallet.processReserve(res.reservePub);
+          }
+          break;
+        default:
+          console.log(`URI type (${uriType}) not handled`);
+          break;
       }
+      return;
     });
   });
 
@@ -445,7 +460,7 @@ testCli
   .requiredOption("bank", ["-b", "--bank"], clk.STRING, {
     default: "https://bank.test.taler.net/";,
   })
-  .action(async (args) => {
+  .action(async args => {
     const b = new Bank(args.genWithdrawUri.bank);
     const user = await b.registerRandomUser();
     const url = await b.generateWithdrawUri(user, args.genWithdrawUri.amount);
diff --git a/src/util/taleruri-test.ts b/src/util/taleruri-test.ts
index c687a671..de4a9069 100644
--- a/src/util/taleruri-test.ts
+++ b/src/util/taleruri-test.ts
@@ -22,23 +22,6 @@ import {
   parseTipUri,
 } from "./taleruri";
 
-test("taler pay url parsing: http(s)", t => {
-  const url1 = "https://example.com/bar?spam=eggs";;
-  const r1 = parsePayUri(url1);
-  if (!r1) {
-    t.fail();
-    return;
-  }
-  t.is(r1.downloadUrl, url1);
-  t.is(r1.sessionId, undefined);
-  const url2 = "http://example.com/bar?spam=eggs";;
-  const r2 = parsePayUri(url2);
-  if (!r2) {
-    t.fail();
-    return;
-  }
-});
-
 test("taler pay url parsing: wrong scheme", t => {
   const url1 = "talerfoo://";
   const r1 = parsePayUri(url1);
@@ -56,7 +39,7 @@ test("taler pay url parsing: defaults", t => {
     t.fail();
     return;
   }
-  t.is(r1.downloadUrl, "https://example.com/public/proposal?order_id=myorder";);
+  t.is(r1.merchantBaseUrl, "https://example.com/public/";);
   t.is(r1.sessionId, undefined);
 
   const url2 = "taler://pay/example.com/-/-/myorder/mysession";
@@ -65,7 +48,7 @@ test("taler pay url parsing: defaults", t => {
     t.fail();
     return;
   }
-  t.is(r2.downloadUrl, "https://example.com/public/proposal?order_id=myorder";);
+  t.is(r2.merchantBaseUrl, "https://example.com/public/";);
   t.is(r2.sessionId, "mysession");
 });
 
@@ -76,7 +59,7 @@ test("taler pay url parsing: trailing parts", t => {
     t.fail();
     return;
   }
-  t.is(r1.downloadUrl, "https://example.com/public/proposal?order_id=myorder";);
+  t.is(r1.merchantBaseUrl, "https://example.com/public/";);
   t.is(r1.sessionId, "mysession");
 });
 
@@ -87,10 +70,8 @@ test("taler pay url parsing: instance", t => {
     t.fail();
     return;
   }
-  t.is(
-    r1.downloadUrl,
-    "https://example.com/public/instances/myinst/proposal?order_id=myorder";,
-  );
+  t.is(r1.merchantBaseUrl, "https://example.com/public/instances/myinst/";);
+  t.is(r1.orderId, "myorder");
 });
 
 test("taler pay url parsing: path prefix and instance", t => {
@@ -100,10 +81,7 @@ test("taler pay url parsing: path prefix and instance", t 
=> {
     t.fail();
     return;
   }
-  t.is(
-    r1.downloadUrl,
-    "https://example.com/mypfx/instances/myinst/proposal?order_id=myorder";,
-  );
+  t.is(r1.merchantBaseUrl, "https://example.com/mypfx/instances/myinst/";);
 });
 
 test("taler pay url parsing: complex path prefix", t => {
@@ -113,10 +91,9 @@ test("taler pay url parsing: complex path prefix", t => {
     t.fail();
     return;
   }
-  t.is(
-    r1.downloadUrl,
-    "https://example.com/mypfx/public/proposal?order_id=myorder";,
-  );
+  t.is(r1.merchantBaseUrl, "https://example.com/mypfx/public/";);
+  t.is(r1.orderId, "myorder");
+  t.is(r1.sessionId, undefined);
 });
 
 test("taler pay url parsing: complex path prefix and instance", t => {
@@ -126,10 +103,8 @@ test("taler pay url parsing: complex path prefix and 
instance", t => {
     t.fail();
     return;
   }
-  t.is(
-    r1.downloadUrl,
-    "https://example.com/mypfx/public/instances/foo/proposal?order_id=myorder";,
-  );
+  t.is(r1.merchantBaseUrl, "https://example.com/mypfx/public/instances/foo/";);
+  t.is(r1.orderId, "myorder");
 });
 
 test("taler pay url parsing: non-https #1", t => {
@@ -138,8 +113,9 @@ test("taler pay url parsing: non-https #1", t => {
   if (!r1) {
     t.fail();
     return;
-  }
-  t.is(r1.downloadUrl, "http://example.com/public/proposal?order_id=myorder";);
+  } 
+  t.is(r1.merchantBaseUrl, "http://example.com/public/";);
+  t.is(r1.orderId, "myorder")
 });
 
 test("taler pay url parsing: non-https #2", t => {
@@ -149,7 +125,8 @@ test("taler pay url parsing: non-https #2", t => {
     t.fail();
     return;
   }
-  t.is(r1.downloadUrl, "https://example.com/public/proposal?order_id=myorder";);
+  t.is(r1.merchantBaseUrl, "https://example.com/public/";);
+  t.is(r1.orderId, "myorder");
 });
 
 test("taler withdraw uri parsing", t => {
diff --git a/src/util/taleruri.ts b/src/util/taleruri.ts
index 50886a91..f34b82a5 100644
--- a/src/util/taleruri.ts
+++ b/src/util/taleruri.ts
@@ -15,7 +15,8 @@
  */
 
 export interface PayUriResult {
-  downloadUrl: string;
+  merchantBaseUrl: string;
+  orderId: string;
   sessionId?: string;
 }
 
@@ -36,7 +37,7 @@ export interface TipUriResult {
 
 export function parseWithdrawUri(s: string): WithdrawUriResult | undefined {
   const pfx = "taler://withdraw/";
-  if (!s.startsWith(pfx)) {
+  if (!s.toLowerCase().startsWith(pfx)) {
     return undefined;
   }
 
@@ -44,6 +45,20 @@ export function parseWithdrawUri(s: string): 
WithdrawUriResult | undefined {
 
   let [host, path, withdrawId] = rest.split("/");
 
+  if (!host) {
+    return undefined;
+  }
+
+  host = host.toLowerCase();
+
+  if (!path) {
+    return undefined;
+  }
+
+  if (!withdrawId) {
+    return undefined;
+  }
+
   if (path === "-") {
     path = "api/withdraw-operation";
   }
@@ -53,15 +68,45 @@ export function parseWithdrawUri(s: string): 
WithdrawUriResult | undefined {
   };
 }
 
-export function parsePayUri(s: string): PayUriResult | undefined {
-  if (s.startsWith("https://";) || s.startsWith("http://";)) {
-    return {
-      downloadUrl: s,
-      sessionId: undefined,
-    };
+export const enum TalerUriType {
+  TalerPay = "taler-pay",
+  TalerWithdraw = "taler-withdraw",
+  TalerTip = "taler-tip",
+  TalerRefund = "taler-refund",
+  TalerNotifyReserve = "taler-notify-reserve",
+  Unknown = "unknown",
+}
+
+export function classifyTalerUri(s: string): TalerUriType {
+  const sl = s.toLowerCase();
+  if (sl.startsWith("taler://pay/")) {
+    return TalerUriType.TalerPay;
   }
+  if (sl.startsWith("taler://tip/")) {
+    return TalerUriType.TalerTip;
+  }
+  if (sl.startsWith("taler://refund/")) {
+    return TalerUriType.TalerRefund;
+  }
+  if (sl.startsWith("taler://withdraw/")) {
+    return TalerUriType.TalerWithdraw;
+  }
+  if (sl.startsWith("taler://notify-reserve/")) {
+    return TalerUriType.TalerWithdraw;
+  }
+  return TalerUriType.Unknown;
+
+}
+
+export function getOrderDownloadUrl(merchantBaseUrl: string, orderId: string) {
+  const u = new URL("proposal", merchantBaseUrl);
+  u.searchParams.set("order_id", orderId);
+  return u.href
+}
+
+export function parsePayUri(s: string): PayUriResult | undefined {
   const pfx = "taler://pay/";
-  if (!s.startsWith(pfx)) {
+  if (!s.toLowerCase().startsWith(pfx)) {
     return undefined;
   }
 
@@ -75,6 +120,8 @@ export function parsePayUri(s: string): PayUriResult | 
undefined {
     return undefined;
   }
 
+  host = host.toLowerCase();
+
   if (!maybePath) {
     return undefined;
   }
@@ -99,21 +146,21 @@ export function parsePayUri(s: string): PayUriResult | 
undefined {
     protocol = "http";
   }
 
-  const downloadUrl =
+  const merchantBaseUrl =
     `${protocol}://${host}/` +
     decodeURIComponent(maybePath) +
-    maybeInstancePath +
-    `proposal?order_id=${orderId}`;
+    maybeInstancePath;
 
   return {
-    downloadUrl,
+    merchantBaseUrl,
+    orderId,
     sessionId: maybeSessionid,
   };
 }
 
 export function parseTipUri(s: string): TipUriResult | undefined {
   const pfx = "taler://tip/";
-  if (!s.startsWith(pfx)) {
+  if (!s.toLowerCase().startsWith(pfx)) {
     return undefined;
   }
 
@@ -125,6 +172,8 @@ export function parseTipUri(s: string): TipUriResult | 
undefined {
     return undefined;
   }
 
+  host = host.toLowerCase();
+
   if (!maybePath) {
     return undefined;
   }
@@ -155,7 +204,7 @@ export function parseTipUri(s: string): TipUriResult | 
undefined {
 export function parseRefundUri(s: string): RefundUriResult | undefined {
   const pfx = "taler://refund/";
 
-  if (!s.startsWith(pfx)) {
+  if (!s.toLowerCase().startsWith(pfx)) {
     return undefined;
   }
 
@@ -167,6 +216,8 @@ export function parseRefundUri(s: string): RefundUriResult 
| undefined {
     return undefined;
   }
 
+  host = host.toLowerCase();
+
   if (!maybePath) {
     return undefined;
   }
diff --git a/src/wallet-impl/pay.ts b/src/wallet-impl/pay.ts
index 4933098c..7076f905 100644
--- a/src/wallet-impl/pay.ts
+++ b/src/wallet-impl/pay.ts
@@ -65,7 +65,7 @@ import {
 } from "../util/helpers";
 import { Logger } from "../util/logging";
 import { InternalWalletState } from "./state";
-import { parsePayUri, parseRefundUri } from "../util/taleruri";
+import { parsePayUri, parseRefundUri, getOrderDownloadUrl } from 
"../util/taleruri";
 import { getTotalRefreshCost, refresh } from "./refresh";
 import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto";
 import { guardOperationException } from "./errors";
@@ -557,9 +557,10 @@ async function processDownloadProposalImpl(
   if (proposal.proposalStatus != ProposalStatus.DOWNLOADING) {
     return;
   }
-  const parsed_url = new URL(proposal.url);
-  parsed_url.searchParams.set("nonce", proposal.noncePub);
-  const urlWithNonce = parsed_url.href;
+  
+  const parsedUrl = new URL(getOrderDownloadUrl(proposal.merchantBaseUrl, 
proposal.orderId));
+  parsedUrl.searchParams.set("nonce", proposal.noncePub);
+  const urlWithNonce = parsedUrl.href;
   console.log("downloading contract from '" + urlWithNonce + "'");
   let resp;
   try {
@@ -629,13 +630,14 @@ async function processDownloadProposalImpl(
  */
 async function startDownloadProposal(
   ws: InternalWalletState,
-  url: string,
+  merchantBaseUrl: string,
+  orderId: string,
   sessionId?: string,
 ): Promise<string> {
   const oldProposal = await oneShotGetIndexed(
     ws.db,
-    Stores.proposals.urlIndex,
-    url,
+    Stores.proposals.urlAndOrderIdIndex,
+    [merchantBaseUrl, orderId],
   );
   if (oldProposal) {
     await processDownloadProposal(ws, oldProposal.proposalId);
@@ -650,8 +652,8 @@ async function startDownloadProposal(
     noncePriv: priv,
     noncePub: pub,
     timestamp: getTimestampNow(),
-    url,
-    downloadSessionId: sessionId,
+    merchantBaseUrl,
+    orderId,
     proposalId: proposalId,
     proposalStatus: ProposalStatus.DOWNLOADING,
     repurchaseProposalId: undefined,
@@ -763,7 +765,8 @@ export async function preparePay(
 
   let proposalId = await startDownloadProposal(
     ws,
-    uriResult.downloadUrl,
+    uriResult.merchantBaseUrl,
+    uriResult.orderId,
     uriResult.sessionId,
   );
 
diff --git a/src/wallet-impl/pending.ts b/src/wallet-impl/pending.ts
index 02f8d9ef..5fb9ef5c 100644
--- a/src/wallet-impl/pending.ts
+++ b/src/wallet-impl/pending.ts
@@ -312,7 +312,8 @@ async function gatherProposalPending(
       resp.pendingOperations.push({
         type: "proposal-download",
         givesLifeness: true,
-        merchantBaseUrl: proposal.download?.contractTerms.merchant_base_url || 
"",
+        merchantBaseUrl: proposal.merchantBaseUrl,
+        orderId: proposal.orderId,
         proposalId: proposal.proposalId,
         proposalTimestamp: proposal.timestamp,
         lastError: proposal.lastError,
diff --git a/src/walletTypes.ts b/src/walletTypes.ts
index 40787166..e2be26b0 100644
--- a/src/walletTypes.ts
+++ b/src/walletTypes.ts
@@ -741,6 +741,7 @@ export interface PendingProposalDownloadOperation {
   merchantBaseUrl: string;
   proposalTimestamp: Timestamp;
   proposalId: string;
+  orderId: string;
   lastError?: OperationError;
   retryInfo: RetryInfo;
 }
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index 4363890e..27141247 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -42,6 +42,7 @@ import Port = chrome.runtime.Port;
 import MessageSender = chrome.runtime.MessageSender;
 import { BrowserCryptoWorkerFactory } from "../crypto/workers/cryptoApi";
 import { OpenedPromise, openPromise } from "../util/promiseUtils";
+import { classifyTalerUri, TalerUriType } from "../util/taleruri";
 
 const NeedsWallet = Symbol("NeedsWallet");
 
@@ -257,7 +258,11 @@ async function handleMessage(
         await walletInit.promise;
       } catch (e) {
         errors.push("Error during wallet initialization: " + e);
-        if (currentDatabase === undefined && outdatedDbVersion === undefined 
&& isFirefox()) {
+        if (
+          currentDatabase === undefined &&
+          outdatedDbVersion === undefined &&
+          isFirefox()
+        ) {
           firefoxIdbProblem = true;
         }
       }
@@ -435,7 +440,7 @@ async function reinitWallet() {
     http,
     new BrowserCryptoWorkerFactory(),
   );
-  wallet.runRetryLoop().catch((e) => {
+  wallet.runRetryLoop().catch(e => {
     console.log("error during wallet retry loop", e);
   });
   // Useful for debugging in the background page.
@@ -601,61 +606,60 @@ export async function wxMain() {
         for (let header of details.responseHeaders || []) {
           if (header.name.toLowerCase() === "taler") {
             const talerUri = header.value || "";
-            if (!talerUri.startsWith("taler://")) {
-              console.warn(
-                "Response with HTTP 402 has Taler header, but header value is 
not a taler:// URI.",
-              );
-              break;
-            }
-            if (talerUri.startsWith("taler://withdraw/")) {
-              return makeSyncWalletRedirect(
-                "withdraw.html",
-                details.tabId,
-                details.url,
-                {
-                  talerWithdrawUri: talerUri,
-                },
-              );
-            } else if (talerUri.startsWith("taler://pay/")) {
-              return makeSyncWalletRedirect(
-                "pay.html",
-                details.tabId,
-                details.url,
-                {
-                  talerPayUri: talerUri,
-                },
-              );
-            } else if (talerUri.startsWith("taler://tip/")) {
-              return makeSyncWalletRedirect(
-                "tip.html",
-                details.tabId,
-                details.url,
-                {
-                  talerTipUri: talerUri,
-                },
-              );
-            } else if (talerUri.startsWith("taler://refund/")) {
-              return makeSyncWalletRedirect(
-                "refund.html",
-                details.tabId,
-                details.url,
-                {
-                  talerRefundUri: talerUri,
-                },
-              );
-            } else if (talerUri.startsWith("taler://notify-reserve/")) {
-              Promise.resolve().then(() => {
-                const w = currentWallet;
-                if (!w) {
-                  return;
-                }
-                w.handleNotifyReserve();
-              });
+            const uriType = classifyTalerUri(talerUri);
+            switch (uriType) {
+              case TalerUriType.TalerWithdraw:
+                return makeSyncWalletRedirect(
+                  "withdraw.html",
+                  details.tabId,
+                  details.url,
+                  {
+                    talerWithdrawUri: talerUri,
+                  },
+                );
+              case TalerUriType.TalerPay:
+                return makeSyncWalletRedirect(
+                  "pay.html",
+                  details.tabId,
+                  details.url,
+                  {
+                    talerPayUri: talerUri,
+                  },
+                );
+              case TalerUriType.TalerTip:
+                return makeSyncWalletRedirect(
+                  "tip.html",
+                  details.tabId,
+                  details.url,
+                  {
+                    talerTipUri: talerUri,
+                  },
+                );
+              case TalerUriType.TalerRefund:
+                return makeSyncWalletRedirect(
+                  "refund.html",
+                  details.tabId,
+                  details.url,
+                  {
+                    talerRefundUri: talerUri,
+                  },
+                );
+              case TalerUriType.TalerNotifyReserve:
+                Promise.resolve().then(() => {
+                  const w = currentWallet;
+                  if (!w) {
+                    return;
+                  }
+                  w.handleNotifyReserve();
+                });
+                break;
 
-            } else {
-              console.warn("Unknown action in taler:// URI, ignoring.");
+              default:
+                console.warn(
+                  "Response with HTTP 402 has Taler header, but header value 
is not a taler:// URI.",
+                );
+                break;
             }
-            break;
           }
         }
       }

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]