Skip to content

Commit 0d39f56

Browse files
committed
Changed to allow TSA feature in web browser
1 parent e5372e2 commit 0d39f56

7 files changed

+168
-87
lines changed

README.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ And I use this name to hope the merits from this application will be dedicated t
1818
* A visible signature can be placed on multiple pages. (In the same position)
1919
* Sign a pdf and set [DocMDP](https://github.com/zboris12/zgapdfsigner/wiki/API#note).
2020
* Add a new signature to a pdf if it has been signed already. (An incremental update)
21-
* Add a document timestamp from [TSA](https://github.com/zboris12/zgapdfsigner/wiki/API#note). ( :no_entry_sign:__Not__ available in web browser)
22-
* Sign a pdf with a timestamp from [TSA](https://github.com/zboris12/zgapdfsigner/wiki/API#note). ( :no_entry_sign:__Not__ available in web browser)
23-
* Enable signature's [LTV](https://github.com/zboris12/zgapdfsigner/wiki/API#note). ( :no_entry_sign:__Not__ available in web browser)
21+
* Add a document timestamp from [TSA](https://github.com/zboris12/zgapdfsigner/wiki/API#note). ( :no_entry_sign:__Not__ available in web browser :sunflower:)
22+
* Sign a pdf with a timestamp from [TSA](https://github.com/zboris12/zgapdfsigner/wiki/API#note). ( :no_entry_sign:__Not__ available in web browser :sunflower:)
23+
* Enable signature's [LTV](https://github.com/zboris12/zgapdfsigner/wiki/API#note). ( :no_entry_sign:__Not__ available in web browser :sunflower:)
2424
* Set password protection to a pdf. Supported algorithms:
2525
* 40bit RC4 Encryption
2626
* 128bit RC4 Encryption
@@ -32,7 +32,9 @@ And I use this name to hope the merits from this application will be dedicated t
3232
## About signing with [TSA](https://github.com/zboris12/zgapdfsigner/wiki/API#note) and [LTV](https://github.com/zboris12/zgapdfsigner/wiki/API#note)
3333

3434
Because of the [CORS](https://github.com/zboris12/zgapdfsigner/wiki/API#note) security restrictions in web browser,
35-
signing with a timestamp from [TSA](https://github.com/zboris12/zgapdfsigner/wiki/API#note) or enabling [LTV](https://github.com/zboris12/zgapdfsigner/wiki/API#note) can only be used in [Google Apps Script](https://developers.google.com/apps-script) or [nodejs](https://nodejs.org/).
35+
signing with a timestamp from [TSA](https://github.com/zboris12/zgapdfsigner/wiki/API#note) or enabling [LTV](https://github.com/zboris12/zgapdfsigner/wiki/API#note) can only be used in [Google Apps Script](https://developers.google.com/apps-script) or [nodejs](https://nodejs.org/).
36+
:sunflower: However, if you can avoid the [CORS](https://github.com/zboris12/zgapdfsigner/wiki/API#note) security restrictions
37+
by creating your own service or providing a reverse proxy server, these features are also available in web browser.
3638

3739
## The Dependencies
3840

build.sh

+9-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ else
99
mkdir ${OUTFLDR}
1010
fi
1111

12+
VER=$(sed -n -r "s/^.*\"version\": ?\"([0-9.]+)\".*$/\1/p" package.json)
13+
1214
GCCOPT="--charset UTF-8 --compilation_level SIMPLE_OPTIMIZATIONS --warning_level VERBOSE"
1315
GCCEXT="--externs closure/google-ext.js --externs closure/forge-ext.js --externs closure/pdflib-ext.js --externs closure/zb-externs.js"
1416
jss=""
@@ -20,7 +22,12 @@ do
2022
if [ "$c" != "#" ]
2123
then
2224
outf="${OUTFLDR}/_${js}"
23-
sed -e "s/\/\/Only for nodejs Start\/\//\/*/g" -e "s/\/\/Only for nodejs End\/\//*\//g" "lib/${js}" > "${outf}"
25+
if [ "${js}" = "zgaindex.js" ]
26+
then
27+
sed -e "s/\/\/Only for nodejs Start\/\//\/*/g" -e "s/\/\/Only for nodejs End\/\//*\//g" -e "s/ver: \"\"/ver: \"${VER}\"/" "lib/${js}" > "${outf}"
28+
else
29+
sed -e "s/\/\/Only for nodejs Start\/\//\/*/g" -e "s/\/\/Only for nodejs End\/\//*\//g" "lib/${js}" > "${outf}"
30+
fi
2431
if [ $? -eq 0 ]
2532
then
2633
echo "Created js file: ${outf}"
@@ -32,6 +39,7 @@ do
3239
fi
3340
fi
3441
done <<EOF
42+
zgafetch.js
3543
zgacertsutil.js
3644
zgapdfcryptor.js
3745
zgapdfsigner.js

lib/zgafetch.js

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/**
2+
* @param {Object<string, *>} z
3+
*/
4+
function supplyZgaUrlFetch(z){
5+
6+
//Only for nodejs Start//
7+
const m_urlparser = require("url");
8+
const m_h = {
9+
"http:": require('follow-redirects').http,
10+
"https:": require('follow-redirects').https,
11+
};
12+
// @type {boolean}
13+
z.isNode = function(){return this === globalThis.global;}();
14+
//Only for nodejs End//
15+
16+
/** @type {boolean} */
17+
z.isBrowser = function(){return this === globalThis.self;}();
18+
19+
/**
20+
* @param {string} url
21+
* @param {UrlFetchParams} params
22+
* @return {Promise<Uint8Array>}
23+
*/
24+
z.urlFetch = function(url, params){
25+
26+
//Only for nodejs Start//
27+
if(z.isNode){
28+
return new Promise(function(resolve, reject){
29+
// @type {URL}
30+
var opts = m_urlparser.parse(url);
31+
var http = m_h[opts.protocol];
32+
// @type {string|Buffer}
33+
var dat = null;
34+
var encoding = undefined;
35+
opts.method = "GET";
36+
if(params){
37+
if(params.payload instanceof Buffer){
38+
dat = params.payload;
39+
}else if(params.payload instanceof Uint8Array){
40+
dat = Buffer.from(params.payload.buffer);
41+
}else if(params.payload instanceof ArrayBuffer){
42+
dat = Buffer.from(params.payload);
43+
}else{
44+
dat = params.payload;
45+
encoding = "binary";
46+
}
47+
if(params.headers){
48+
opts.headers = params.headers;
49+
}
50+
if(params.method){
51+
opts.method = params.method;
52+
}
53+
if(params.validateHttpsCertificates === false){
54+
opts.rejectUnauthorized = false;
55+
}
56+
}
57+
58+
// @type {http.ClientRequest}
59+
var hreq = http.request(opts, function(a_res){ // @type {http.IncomingMessage} a_res
60+
if(a_res.statusCode !== 200){
61+
var a_err = new Error("Failed to request url. " + url + "\n Status Code: " + a_res.statusCode);
62+
a_res.resume();
63+
throw a_err;
64+
}
65+
// @type {Array<Buffer>}
66+
var a_bufs = [];
67+
var a_bufs_len = 0;
68+
a_res.on("data", function(b_chunk){ // @type {Buffer} b_chunk
69+
a_bufs.push(b_chunk);
70+
a_bufs_len += b_chunk.length;
71+
});
72+
a_res.on("end", function(){
73+
// @type {Buffer}
74+
var b_bdat = Buffer.concat(a_bufs, a_bufs_len);
75+
resolve(b_bdat);
76+
});
77+
});
78+
hreq.on("error", function(a_err){
79+
throw a_err;
80+
});
81+
hreq.end(dat, encoding);
82+
});
83+
}
84+
//Only for nodejs End//
85+
86+
// Google Apps Script
87+
if(globalThis.UrlFetchApp){
88+
return new Promise(function(resolve){
89+
/** @type {GBlob} */
90+
var tblob = UrlFetchApp.fetch(url, params).getBlob();
91+
resolve(new Uint8Array(tblob.getBytes()));
92+
});
93+
}
94+
95+
// browser
96+
if(z.isBrowser && globalThis.self.fetch){
97+
/**
98+
* @return {Promise<Uint8Array>}
99+
*/
100+
var func = async function(){
101+
/** @type {!RequestInit} */
102+
var reqinf = {
103+
method: "GET",
104+
redirect: "follow",
105+
};
106+
if(params){
107+
if(params.payload){
108+
reqinf.body = params.payload;
109+
}
110+
if(params.headers){
111+
reqinf.headers = params.headers;
112+
}
113+
if(params.method){
114+
reqinf.method = params.method;
115+
}
116+
}
117+
/** @type {Response} */
118+
var resp = await fetch(url, reqinf);
119+
if(resp.ok){
120+
/** @type {ArrayBuffer} */
121+
var abdat = await resp.arrayBuffer();
122+
return new Uint8Array(abdat);
123+
}else{
124+
/** @type {string} */
125+
var msg = await resp.text();
126+
throw new Error("Fetch failed." + resp.status + ": " + msg);
127+
}
128+
};
129+
return func();
130+
}
131+
return null;
132+
};
133+
134+
}
135+
136+
//Only for nodejs Start//
137+
if(typeof exports === "object" && typeof module !== "undefined"){
138+
module.exports = supplyZgaUrlFetch;
139+
}
140+
//Only for nodejs End//

lib/zgaindex.js

+4-17
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
*/
66
function genZga(){
77
/** @const {Object<string, *>} */
8-
const z = {};
8+
const z = {
9+
ver: "",
10+
};
911

1012
/**
1113
* @param {...string} msg
@@ -42,22 +44,6 @@ function genZga(){
4244
return arr;
4345
};
4446

45-
// Google Apps Script
46-
if(globalThis.UrlFetchApp){
47-
/**
48-
* @param {string} url
49-
* @param {UrlFetchParams} params
50-
* @return {Promise<Uint8Array>}
51-
*/
52-
z.urlFetch = function(url, params){
53-
return new Promise(function(resolve){
54-
/** @type {GBlob} */
55-
var tblob = UrlFetchApp.fetch(url, params).getBlob();
56-
resolve(new Uint8Array(tblob.getBytes()));
57-
});
58-
};
59-
}
60-
6147
return z;
6248
}
6349

@@ -68,6 +54,7 @@ if(typeof exports === "object" && typeof module !== "undefined"){
6854
//Only for nodejs End//
6955
if(!globalThis.Zga){
7056
globalThis.Zga = genZga();
57+
supplyZgaUrlFetch(globalThis.Zga);
7158
supplyZgaCertsChain(globalThis.Zga);
7259
supplyZgaCryptor(globalThis.Zga);
7360
supplyZgaSigner(globalThis.Zga);

lib/zganode.js

+1-62
Original file line numberDiff line numberDiff line change
@@ -9,69 +9,8 @@ z.PDFLib = require("pdf-lib");
99
// z.fontkit = require("@pdf-lib/fontkit");
1010
z.fontkit = require("pdf-fontkit");
1111
z.pako = require("pako");
12-
/**
13-
* @param {string} url
14-
* @param {UrlFetchParams} params
15-
* @return {Promise<Uint8Array>}
16-
*/
17-
z.urlFetch = function(url, params){
18-
return new Promise(function(resolve, reject){
19-
/** @type {URL} */
20-
var opts = m_urlparser.parse(url);
21-
var http = m_h[opts.protocol];
22-
/** @type {string|Buffer} */
23-
var dat = null;
24-
var encoding = undefined;
25-
opts.method = "GET";
26-
if(params){
27-
if(params.payload instanceof Buffer){
28-
dat = params.payload;
29-
}else if(params.payload instanceof Uint8Array){
30-
dat = Buffer.from(params.payload.buffer);
31-
}else if(params.payload instanceof ArrayBuffer){
32-
dat = Buffer.from(params.payload);
33-
}else{
34-
dat = params.payload;
35-
encoding = "binary";
36-
}
37-
if(params.headers){
38-
opts.headers = params.headers;
39-
}
40-
if(params.method){
41-
opts.method = params.method;
42-
}
43-
if(params.validateHttpsCertificates === false){
44-
opts.rejectUnauthorized = false;
45-
}
46-
}
47-
48-
/** @type {http.ClientRequest} */
49-
var hreq = http.request(opts, function(/** @type {http.IncomingMessage} */a_res){
50-
if(a_res.statusCode !== 200){
51-
var a_err = new Error("Failed to request url. " + url + "\n Status Code: " + a_res.statusCode);
52-
a_res.resume();
53-
throw a_err;
54-
}
55-
/** @type {Array<Buffer>} */
56-
var a_bufs = [];
57-
var a_bufs_len = 0;
58-
a_res.on("data", function(/** @type {Buffer} */b_chunk){
59-
a_bufs.push(b_chunk);
60-
a_bufs_len += b_chunk.length;
61-
});
62-
a_res.on("end", function(){
63-
/** @type {Buffer} */
64-
var b_bdat = Buffer.concat(a_bufs, a_bufs_len);
65-
resolve(b_bdat);
66-
});
67-
});
68-
hreq.on("error", function(a_err){
69-
throw a_err;
70-
});
71-
hreq.end(dat, encoding);
72-
});
73-
};
7412

13+
require("./zgafetch.js")(z);
7514
require("./zgacertsutil.js")(z);
7615
require("./zgapdfcryptor.js")(z);
7716
require("./zgapdfsigner.js")(z);

lib/zgapdfsigner.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ z.PdfSigner = class{
260260
if(!(globalThis.forge || forge)){
261261
throw new Error("node-forge is not imported.");
262262
}
263+
if(z.ver){
264+
z.log("ZgaPdfSigner Version:", z.ver);
265+
}
263266
/** @type {?TsaServiceInfo} */
264267
var tsainf = null;
265268
if(signopt.signdate){
@@ -273,11 +276,13 @@ z.PdfSigner = class{
273276
}
274277
if(tsainf){
275278
if(!z.urlFetch){
276-
throw new Error("Because of the CORS security restrictions, signing with TSA is not supported in web browser.");
279+
// throw new Error("Because of the CORS security restrictions, signing with TSA is not supported in web browser.");
280+
throw new Error("No fetch method found in this environment.");
277281
}
278282
if(z.TSAURLS[tsainf.url]){
279283
Object.assign(tsainf, z.TSAURLS[tsainf.url]);
280-
}else if(!(new RegExp("^https?://")).test(tsainf.url)){
284+
}else if(!tsainf.url || (!z.isBrowser && !(new RegExp("^https?://")).test(tsainf.url))){
285+
// It may be a relative path in browser environment, so only check in non-browser environment
281286
throw new Error("Unknown tsa data. " + JSON.stringify(tsainf));
282287
}
283288
if(!tsainf.len){

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "zgapdfsigner",
3-
"version": "2.7.2",
3+
"version": "2.7.3",
44
"author": "zboris12",
55
"description": "A javascript tool to sign a pdf or set protection to a pdf in web browser, Google Apps Script and nodejs.",
66
"homepage": "https://github.com/zboris12/zgapdfsigner",

0 commit comments

Comments
 (0)