gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-wallet-webex] branch master updated: fix module load


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated: fix module loading for node under fake web workers
Date: Sat, 27 May 2017 22:56:00 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 478a089e fix module loading for node under fake web workers
478a089e is described below

commit 478a089e521de81ca47fc54518bc6a241823d9ae
Author: Florian Dold <address@hidden>
AuthorDate: Sat May 27 22:55:52 2017 +0200

    fix module loading for node under fake web workers
---
 node_modules/tiny-worker/.npmignore    |   9 ---
 node_modules/tiny-worker/LICENSE       |  28 -------
 node_modules/tiny-worker/README.md     | 115 ---------------------------
 node_modules/tiny-worker/lib/index.js  | 137 ---------------------------------
 node_modules/tiny-worker/lib/noop.js   |   5 --
 node_modules/tiny-worker/lib/worker.js |  83 --------------------
 node_modules/tiny-worker/package.json  |  35 ---------
 package.json                           |   1 -
 src/crypto/cryptoApi.ts                |  27 +++++--
 src/crypto/emscLoader.js               |  24 ++++--
 src/types-test.ts                      |   3 +
 src/types.ts                           |   4 +-
 tsconfig.json                          |   4 +
 13 files changed, 45 insertions(+), 430 deletions(-)

diff --git a/node_modules/tiny-worker/.npmignore 
b/node_modules/tiny-worker/.npmignore
deleted file mode 100644
index b422e464..00000000
--- a/node_modules/tiny-worker/.npmignore
+++ /dev/null
@@ -1,9 +0,0 @@
-/.idea/
-/node_modules/
-/src/
-/test/
-.eslintrc
-.git
-.gitignore
-.travis.yml
-Gruntfile.js
diff --git a/node_modules/tiny-worker/LICENSE b/node_modules/tiny-worker/LICENSE
deleted file mode 100644
index f45e2837..00000000
--- a/node_modules/tiny-worker/LICENSE
+++ /dev/null
@@ -1,28 +0,0 @@
-Copyright (c) 2017, Jason Mulligan
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this
-  list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
-  this list of conditions and the following disclaimer in the documentation
-  and/or other materials provided with the distribution.
-
-* Neither the name of tiny-worker nor the names of its
-  contributors may be used to endorse or promote products derived from
-  this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
diff --git a/node_modules/tiny-worker/README.md 
b/node_modules/tiny-worker/README.md
deleted file mode 100644
index b7378733..00000000
--- a/node_modules/tiny-worker/README.md
+++ /dev/null
@@ -1,115 +0,0 @@
-# tiny-worker
-Tiny WebWorker for Server
-
-`require()` is available for flexible inline Worker scripts. Optional 
parameters `args` Array & `options` Object; see `child_process.fork()` 
documentation.
-
-[![build 
status](https://secure.travis-ci.org/avoidwork/tiny-worker.svg)](http://travis-ci.org/avoidwork/tiny-worker)
-
-## Example
-#### Creating a Worker from a file
-The worker script:
-```javascript
-onmessage = function (ev) {
-       postMessage(ev.data);
-};
-```
-
-The core script:
-```javascript
-var Worker = require("tiny-worker");
-var worker = new Worker("repeat.js");
-
-worker.onmessage = function (ev) {
-       console.log(ev.data);
-       worker.terminate();
-};
-
-worker.postMessage("Hello World!");
-```
-
-#### Creating a Worker from a Function
-```javascript
-var Worker = require("tiny-worker");
-var worker = new Worker(function () {
-       self.onmessage = function (ev) {
-               postMessage(ev.data);
-       };
-});
-
-worker.onmessage = function (ev) {
-       console.log(ev.data);
-       worker.terminate();
-};
-
-worker.postMessage("Hello World!");
-```
-
-# Debugging
-To be able to debug a child process, it must have a differnt debug port than 
the parent. 
-Tiny worker does this by adding a random port within a range to the parents 
debug port.
-The default Range is `[1, 300]`, it can be changed with the `setRange(min, 
max)` method.
-To disable any automatic port redirection set `options.noDebugRedirection = 
true`.
-
-### automatic redirection
-```javascript
-//parent is started with '--debug=1234'
-var Worker = require("tiny-worker");
-Worker.setRange(2, 20);
-
-var worker = new Worker(function () {
-       postMessage(process.debugPort); 
-});
-
-worker.onmessage = function (ev) {
-       console.log(ev.data); //prints any number between 1236 and 1254
-       worker.terminate();
-}
-```
-
-### manual redirection
-```javascript
-//parent is started with '--debug=1234'
-var Worker = require("tiny-worker");
-
-var worker = new Worker(function () {
-       postMessage(process.debugPort); 
-}, [], {noDebugRedirection: true, execArgv: ["--debug=1235"]});
-
-worker.onmessage = function (ev) {
-       console.log(ev.data); //prints 1235
-       worker.terminate();
-}
-```
-
-## Properties
-#### onmessage
-Message handler, accepts an `Event`
-
-#### onerror
-Error handler, accepts an `Event`
-
-## API
-#### addEventListener(event, fn)
-Adds an event listener
-
-#### postMessage()
-Broadcasts a message to the `Worker`
-
-#### terminate()
-Terminates the `Worker`
-
-#### static setRange(min, max)
-Sets range for debug ports, only affects current process.
-Returns true if successful.
-
-## FAQ
-1. I have an orphaned child process that lives on past the parent process' 
lifespan
-  * Most likely a `SIGTERM` or `SIGINT` is not reaching the child process
-2. How do I insure all process are terminated?
-  * In your core script register a listener for `SIGTERM` or `SIGINT` via 
`process.on()` which terminates (all) worker process(es) and then gracefully 
shutdowns via `process.exit(0);`
-3. Why `SIGTERM` or `SIGINT`?
-  * Unix/BSD will work with `SIGTERM`, but if you also need to support Windows 
use `SIGINT`
-
-## License
-Copyright (c) 2017 Jason Mulligan
-Licensed under the BSD-3 license
diff --git a/node_modules/tiny-worker/lib/index.js 
b/node_modules/tiny-worker/lib/index.js
deleted file mode 100644
index 1cce88cf..00000000
--- a/node_modules/tiny-worker/lib/index.js
+++ /dev/null
@@ -1,137 +0,0 @@
-"use strict";
-
-var _createClass = function () { function defineProperties(target, props) { 
for (var i = 0; i < props.length; i++) { var descriptor = props[i]; 
descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable 
= true; if ("value" in descriptor) descriptor.writable = true; 
Object.defineProperty(target, descriptor.key, descriptor); } } return function 
(Constructor, protoProps, staticProps) { if (protoProps) 
defineProperties(Constructor.prototype, protoProps); if (staticProps) [...]
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof 
Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-var path = require("path"),
-    fork = require("child_process").fork,
-    worker = path.join(__dirname, "worker.js"),
-    events = /^(error|message)$/,
-    defaultPorts = { inspect: 9229, debug: 5858 };
-var range = { min: 1, max: 300 };
-
-var Worker = function () {
-       function Worker(arg) {
-               var _this = this;
-
-               var args = arguments.length > 1 && arguments[1] !== undefined ? 
arguments[1] : [];
-               var options = arguments.length > 2 && arguments[2] !== 
undefined ? arguments[2] : { cwd: process.cwd() };
-
-               _classCallCheck(this, Worker);
-
-               var isfn = typeof arg === "function",
-                   input = isfn ? arg.toString() : arg;
-
-               if (!options.cwd) {
-                       options.cwd = process.cwd();
-               }
-
-               //get all debug related parameters
-               var debugVars = process.execArgv.filter(function (execArg) {
-                       return (/(debug|inspect)/.test(execArg)
-                       );
-               });
-               if (debugVars.length > 0 && !options.noDebugRedirection) {
-                       if (!options.execArgv) {
-                               //if no execArgs are given copy all arguments
-                               debugVars = Array.from(process.execArgv);
-                               options.execArgv = [];
-                       }
-
-                       var inspectIndex = debugVars.findIndex(function 
(debugArg) {
-                               //get index of inspect parameter
-                               return 
(/^--inspect(-brk)?(=\d+)?$/.test(debugArg)
-                               );
-                       });
-
-                       var debugIndex = debugVars.findIndex(function 
(debugArg) {
-                               //get index of debug parameter
-                               return (/^--debug(-brk)?(=\d+)?$/.test(debugArg)
-                               );
-                       });
-
-                       var portIndex = inspectIndex >= 0 ? inspectIndex : 
debugIndex; //get index of port, inspect has higher priority
-
-                       if (portIndex >= 0) {
-                               var match = 
/^--(debug|inspect)(?:-brk)?(?:=(\d+))?$/.exec(debugVars[portIndex]); //get port
-                               var port = defaultPorts[match[1]];
-                               if (match[2]) {
-                                       port = parseInt(match[2]);
-                               }
-                               debugVars[portIndex] = "--" + match[1] + "=" + 
(port + range.min + Math.floor(Math.random() * (range.max - range.min))); //new 
parameter
-
-                               if (debugIndex >= 0 && debugIndex !== 
portIndex) {
-                                       //remove "-brk" from debug if there
-                                       match = 
/^(--debug)(?:-brk)?(.*)/.exec(debugVars[debugIndex]);
-                                       debugVars[debugIndex] = match[1] + 
(match[2] ? match[2] : "");
-                               }
-                       }
-                       options.execArgv = options.execArgv.concat(debugVars);
-               }
-
-               delete options.noDebugRedirection;
-
-               this.child = fork(worker, args, options);
-               this.onerror = undefined;
-               this.onmessage = undefined;
-
-               this.child.on("error", function (e) {
-                       if (_this.onerror) {
-                               _this.onerror.call(_this, e);
-                       }
-               });
-
-               this.child.on("message", function (msg) {
-                       var message = JSON.parse(msg);
-                       var error = void 0;
-
-                       if (!message.error && _this.onmessage) {
-                               _this.onmessage.call(_this, message);
-                       }
-
-                       if (message.error && _this.onerror) {
-                               error = new Error(message.error);
-                               error.stack = message.stack;
-
-                               _this.onerror.call(_this, error);
-                       }
-               });
-
-               this.child.send({ input: input, isfn: isfn, cwd: options.cwd });
-       }
-
-       _createClass(Worker, [{
-               key: "addEventListener",
-               value: function addEventListener(event, fn) {
-                       if (events.test(event)) {
-                               this["on" + event] = fn;
-                       }
-               }
-       }, {
-               key: "postMessage",
-               value: function postMessage(msg) {
-                       this.child.send(JSON.stringify({ data: msg }, null, 0));
-               }
-       }, {
-               key: "terminate",
-               value: function terminate() {
-                       this.child.kill("SIGINT");
-               }
-       }], [{
-               key: "setRange",
-               value: function setRange(min, max) {
-                       if (min >= max) {
-                               return false;
-                       }
-                       range.min = min;
-                       range.max = max;
-
-                       return true;
-               }
-       }]);
-
-       return Worker;
-}();
-
-module.exports = Worker;
diff --git a/node_modules/tiny-worker/lib/noop.js 
b/node_modules/tiny-worker/lib/noop.js
deleted file mode 100644
index 312fce83..00000000
--- a/node_modules/tiny-worker/lib/noop.js
+++ /dev/null
@@ -1,5 +0,0 @@
-"use strict";
-
-module.exports = function () {
-  return void 0;
-};
diff --git a/node_modules/tiny-worker/lib/worker.js 
b/node_modules/tiny-worker/lib/worker.js
deleted file mode 100644
index c8e5d688..00000000
--- a/node_modules/tiny-worker/lib/worker.js
+++ /dev/null
@@ -1,83 +0,0 @@
-"use strict";
-
-var fs = require("fs"),
-    path = require("path"),
-    vm = require("vm"),
-    noop = require(path.join(__dirname, "noop.js")),
-    events = /^(error|message)$/;
-
-function trim(arg) {
-       return arg.replace(/^(\s+|\t+|\n+)|(\s+|\t+|\n+)$/g, "");
-}
-
-function explode(arg) {
-       return trim(arg).split(new RegExp("\\s*,\\s*"));
-}
-
-function toFunction(arg) {
-       var args = trim(arg.replace(/^.*\(/, "").replace(/[\t|\r|\n|\"|\']+/g, 
"").replace(/\).*/, "")),
-           body = trim(arg.replace(/^.*\{/, "").replace(/\}$/, ""));
-
-       return Function.apply(Function, explode(args).concat([body]));
-}
-
-// Bootstraps the Worker
-process.once("message", function (obj) {
-       var exp = obj.isfn ? toFunction(obj.input) : fs.readFileSync(obj.input, 
"utf8");
-
-       global.self = {
-               close: function close() {
-                       process.exit(0);
-               },
-               postMessage: function postMessage(msg) {
-                       process.send(JSON.stringify({ data: msg }, null, 0));
-               },
-               onmessage: void 0,
-               onerror: function onerror(err) {
-                       process.send(JSON.stringify({ error: err.message, 
stack: err.stack }, null, 0));
-               },
-               addEventListener: function addEventListener(event, fn) {
-                       if (events.test(event)) {
-                               global["on" + event] = global.self["on" + 
event] = fn;
-                       }
-               }
-       };
-
-       global.__dirname = obj.cwd;
-       global.__filename = __filename;
-       global.require = require;
-
-       global.importScripts = function () {
-               for (var _len = arguments.length, files = Array(_len), _key = 
0; _key < _len; _key++) {
-                       files[_key] = arguments[_key];
-               }
-
-               if (files.length > 0) {
-                       vm.createScript(files.map(function (file) {
-                               return fs.readFileSync(file, "utf8");
-                       }).join("\n")).runInThisContext();
-               }
-       };
-
-       Object.keys(global.self).forEach(function (key) {
-               global[key] = global.self[key];
-       });
-
-       process.on("message", function (msg) {
-               try {
-                       (global.onmessage || global.self.onmessage || 
noop)(JSON.parse(msg));
-               } catch (err) {
-                       (global.onerror || global.self.onerror || noop)(err);
-               }
-       });
-
-       process.on("error", function (err) {
-               (global.onerror || global.self.onerror || noop)(err);
-       });
-
-       if (typeof exp === "function") {
-               exp();
-       } else {
-               vm.createScript(exp).runInThisContext();
-       }
-});
diff --git a/node_modules/tiny-worker/package.json 
b/node_modules/tiny-worker/package.json
deleted file mode 100644
index 1176ef94..00000000
--- a/node_modules/tiny-worker/package.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "name": "tiny-worker",
-  "version": "2.1.1",
-  "description": "Tiny WebWorker for Server",
-  "main": "lib/index.js",
-  "scripts": {
-    "test": "grunt test"
-  },
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/avoidwork/tiny-worker.git";
-  },
-  "keywords": [
-    "web",
-    "worker",
-    "ps",
-    "webworker"
-  ],
-  "author": "Jason Mulligan <address@hidden>",
-  "license": "BSD-3-Clause",
-  "bugs": {
-    "url": "https://github.com/avoidwork/tiny-worker/issues";
-  },
-  "homepage": "https://github.com/avoidwork/tiny-worker";,
-  "devDependencies": {
-    "babel-preset-es2015": "~6.22.0",
-    "grunt": "~1.0.1",
-    "grunt-babel": "~6.0.0",
-    "grunt-cli": "~1.2.0",
-    "grunt-contrib-nodeunit": "~1.0.0",
-    "grunt-contrib-watch": "~1.0.0",
-    "grunt-eslint": "~19.0.0"
-  },
-  "dependencies": {}
-}
diff --git a/package.json b/package.json
index cd6033da..fc7dfb13 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,6 @@
     "react": "^15.5.4",
     "react-dom": "^15.5.4",
     "through2": "^2.0.1",
-    "tiny-worker": "^2.1.1",
     "ts-loader": "^2.0.3",
     "typedoc": "^0.7.1",
     "typescript": "next",
diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts
index a386eab4..b291cd17 100644
--- a/src/crypto/cryptoApi.ts
+++ b/src/crypto/cryptoApi.ts
@@ -38,6 +38,9 @@ import {
   OfferRecord,
   CoinWithDenom,
 } from "../wallet";
+import * as timer from "../timer";
+
+import { startWorker } from "./startWorker";
 
 
 /**
@@ -57,7 +60,7 @@ interface WorkerState {
   /**
    * Timer to terminate the worker if it's not busy enough.
    */
-  terminationTimerHandle: number|null;
+  terminationTimerHandle: timer.TimerHandle|null;
 }
 
 interface WorkItem {
@@ -73,6 +76,8 @@ interface WorkItem {
 }
 
 
+
+
 /**
  * Number of different priorities. Each priority p
  * must be 0 <= p < NUM_PRIO.
@@ -98,7 +103,7 @@ export class CryptoApi {
     ws.currentWorkItem = work;
     this.numBusy++;
     if (!ws.w) {
-      let w = new Worker("/dist/cryptoWorker-bundle.js");
+      let w = startWorker();
       w.onmessage = (m: MessageEvent) => this.handleWorkerMessage(ws, m);
       w.onerror = (e: ErrorEvent) => this.handleWorkerError(ws, e);
       ws.w = w;
@@ -114,7 +119,8 @@ export class CryptoApi {
 
   resetWorkerTimeout(ws: WorkerState) {
     if (ws.terminationTimerHandle != null) {
-      clearTimeout(ws.terminationTimerHandle);
+      ws.terminationTimerHandle.clear();
+      ws.terminationTimerHandle = null;
     }
     let destroy = () => {
       // terminate worker if it's idle
@@ -123,7 +129,7 @@ export class CryptoApi {
         ws.w = null;
       }
     };
-    ws.terminationTimerHandle = window.setTimeout(destroy, 20 * 1000);
+    ws.terminationTimerHandle = timer.after(20 * 1000, destroy);
   }
 
   handleWorkerError(ws: WorkerState, e: ErrorEvent) {
@@ -182,7 +188,14 @@ export class CryptoApi {
   }
 
   constructor() {
-    this.workers = new Array<WorkerState>((navigator as 
any)["hardwareConcurrency"] || 2);
+    let concurrency = 2;
+    try {
+      // only works in the browser
+      concurrency = (navigator as any)["hardwareConcurrency"];
+    } catch (e) {
+      // ignore
+    }
+    this.workers = new Array<WorkerState>(concurrency);
 
     for (let i = 0; i < this.workers.length; i++) {
       this.workers[i] = {
@@ -199,7 +212,7 @@ export class CryptoApi {
 
   private doRpc<T>(operation: string, priority: number,
                    ...args: any[]): Promise<T> {
-    let start = performance.now();
+    let start = timer.performanceNow();
 
     let p = new Promise((resolve, reject) => {
       let rpcId = this.nextRpcId++;
@@ -228,7 +241,7 @@ export class CryptoApi {
     });
 
     return p.then((r: T) => {
-      console.log(`rpc ${operation} took ${performance.now() - start}ms`);
+      console.log(`rpc ${operation} took ${timer.performanceNow() - start}ms`);
       return r;
     });
   }
diff --git a/src/crypto/emscLoader.js b/src/crypto/emscLoader.js
index eebe20c0..190f007f 100644
--- a/src/crypto/emscLoader.js
+++ b/src/crypto/emscLoader.js
@@ -31,6 +31,22 @@
  * be globally available.  Inside node, require is used.
  */
 export function getLib() {
+  if (typeof require !== "undefined") {
+    // Make sure that TypeScript doesn't try
+    // to check the taler-emscripten-lib.
+    const indirectRequire = require;
+    const g = global as any;
+    // unavoidable hack, so that emscripten detects
+    // the environment as node even though importScripts
+    // is present.
+    const savedImportScripts = g.importScripts;
+    delete g.importScripts;
+    // Assume that the code is run from the build/ directory.
+    const lib = indirectRequire("../../../emscripten/taler-emscripten-lib.js");
+    g.importScripts = savedImportScripts;
+    return lib;
+  }
+
   if (typeof importScripts !== "undefined") {
     importScripts('/src/emscripten/taler-emscripten-lib.js')
     if (TalerEmscriptenLib) {
@@ -39,14 +55,6 @@ export function getLib() {
     return TalerEmscriptenLib
   }
 
-  if (typeof require !== "undefined") {
-    // Make sure that TypeScript doesn't try
-    // to check the taler-emscripten-lib.
-    const fn = require;
-    // Assume that the code is run from the build/ directory.
-    return fn("../../../emscripten/taler-emscripten-lib.js");
-  }
-
   if (typeof window !== "undefined") {
     if (window.TalerEmscriptenLib) {
       return TalerEmscriptenLib;
diff --git a/src/types-test.ts b/src/types-test.ts
index bc053b40..780d49e4 100644
--- a/src/types-test.ts
+++ b/src/types-test.ts
@@ -53,6 +53,9 @@ test("contract validation", t => {
     timestamp: "Date(12345)",
     transaction_id: 1234,
     fulfillment_url: "foo",
+    wire_method: "test",
+    order_id: "test_order",
+    pay_url: "https://example.com/pay";,
   };
 
   types.Contract.checked(c);
diff --git a/src/types.ts b/src/types.ts
index 26280874..24027597 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -587,10 +587,10 @@ export class Contract {
   @Checkable.String
   fulfillment_url: string;
 
-  @Checkable.Number
+  @Checkable.Optional(Checkable.Number)
   wire_fee_amortization?: number;
 
-  @Checkable.Value(AmountJson)
+  @Checkable.Optional(Checkable.Value(AmountJson))
   max_wire_fee?: AmountJson;
 
   @Checkable.Any
diff --git a/tsconfig.json b/tsconfig.json
index 0c684214..412616d6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -34,6 +34,9 @@
     "src/crypto/emscInterface.ts",
     "src/crypto/emscLoader.d.ts",
     "src/crypto/emscLoader.js",
+    "src/crypto/nodeWorker.ts",
+    "src/crypto/nodeWorkerEntry.ts",
+    "src/crypto/startWorker.js",
     "src/helpers-test.ts",
     "src/helpers.ts",
     "src/http.ts",
@@ -52,6 +55,7 @@
     "src/pages/tree.tsx",
     "src/query.ts",
     "src/renderHtml.tsx",
+    "src/timer.ts",
     "src/types-test.ts",
     "src/types.ts",
     "src/wallet-test.ts",

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



reply via email to

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