1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.serverOPDS_browse_v2 = exports.serverOPDS_auth_PATH = exports.serverOPDS_dataUrl_PATH = exports.serverOPDS_browse_v2_PATH = void 0;
|
4 | var tslib_1 = require("tslib");
|
5 | var crypto = require("crypto");
|
6 | var css2json = require("css2json");
|
7 | var debug_ = require("debug");
|
8 | var DotProp = require("dot-prop");
|
9 | var express = require("express");
|
10 | var jsonMarkup = require("json-markup");
|
11 | var morgan = require("morgan");
|
12 | var path = require("path");
|
13 | var request = require("request");
|
14 | var requestPromise = require("request-promise-native");
|
15 | var uuid_1 = require("uuid");
|
16 | var serializable_1 = require("r2-lcp-js/dist/es5/src/serializable");
|
17 | var opds2_1 = require("r2-opds-js/dist/es5/src/opds/opds2/opds2");
|
18 | var opds2_authentication_doc_1 = require("r2-opds-js/dist/es5/src/opds/opds2/opds2-authentication-doc");
|
19 | var opds2_publication_1 = require("r2-opds-js/dist/es5/src/opds/opds2/opds2-publication");
|
20 | var UrlUtils_1 = require("r2-utils-js/dist/es5/src/_utils/http/UrlUtils");
|
21 | var JsonUtils_1 = require("r2-utils-js/dist/es5/src/_utils/JsonUtils");
|
22 | var BufferUtils_1 = require("r2-utils-js/dist/es5/src/_utils/stream/BufferUtils");
|
23 | var json_schema_validate_1 = require("../utils/json-schema-validate");
|
24 | var request_ext_1 = require("./request-ext");
|
25 | var server_lcp_lsd_show_1 = require("./server-lcp-lsd-show");
|
26 | var server_opds_convert_v1_to_v2_1 = require("./server-opds-convert-v1-to-v2");
|
27 | var server_trailing_slash_redirect_1 = require("./server-trailing-slash-redirect");
|
28 | var debug = debug_("r2:streamer#http/server-opds-browse-v2");
|
29 | exports.serverOPDS_browse_v2_PATH = "/opds-v2-browse";
|
30 | exports.serverOPDS_dataUrl_PATH = "/data-url";
|
31 | exports.serverOPDS_auth_PATH = "/opds-auth";
|
32 | var salt = crypto.randomBytes(16).toString("hex");
|
33 | var OPDS_AUTH_ENCRYPTION_KEY_BUFFER = crypto.pbkdf2Sync(uuid_1.v4(), salt, 1000, 32, "sha256");
|
34 | var OPDS_AUTH_ENCRYPTION_KEY_HEX = OPDS_AUTH_ENCRYPTION_KEY_BUFFER.toString("hex");
|
35 | var AES_BLOCK_SIZE = 16;
|
36 | var OPDS_AUTH_ENCRYPTION_IV_BUFFER = Buffer.from(uuid_1.v4()).slice(0, AES_BLOCK_SIZE);
|
37 | var OPDS_AUTH_ENCRYPTION_IV_HEX = OPDS_AUTH_ENCRYPTION_IV_BUFFER.toString("hex");
|
38 | function serverOPDS_browse_v2(_server, topRouter) {
|
39 | var _this = this;
|
40 | var jsonStyle = "\n.json-markup {\n line-height: 17px;\n font-size: 13px;\n font-family: monospace;\n white-space: pre;\n}\n.json-markup-key {\n font-weight: bold;\n}\n.json-markup-bool {\n color: firebrick;\n}\n.json-markup-string {\n color: green;\n}\n.json-markup-null {\n color: gray;\n}\n.json-markup-number {\n color: blue;\n}\n";
|
41 | var routerOPDS_browse_v2 = express.Router({ strict: false });
|
42 | routerOPDS_browse_v2.use(morgan("combined", { stream: { write: function (msg) { return debug(msg); } } }));
|
43 | routerOPDS_browse_v2.use(server_trailing_slash_redirect_1.trailingSlashRedirect);
|
44 | routerOPDS_browse_v2.get("/", function (_req, res) {
|
45 | var html = "<html><head>";
|
46 | html += "<script type=\"text/javascript\">function encodeURIComponent_RFC3986(str) { " +
|
47 | "return encodeURIComponent(str).replace(/[!'()*]/g, (c) => { " +
|
48 | "return \"%\" + c.charCodeAt(0).toString(16); }); }" +
|
49 | "function go(evt) {" +
|
50 | "if (evt) { evt.preventDefault(); } var url = " +
|
51 | "location.origin +" +
|
52 | (" '" + exports.serverOPDS_browse_v2_PATH + "/' +") +
|
53 | " encodeURIComponent_RFC3986(document.getElementById(\"url\").value);" +
|
54 | "location.href = url;}</script>";
|
55 | html += "</head>";
|
56 | html += "<body><h1>OPDS feed browser</h1>";
|
57 | html += "<form onsubmit=\"go();return false;\">" +
|
58 | "<input type=\"text\" name=\"url\" id=\"url\" size=\"80\">" +
|
59 | "<input type=\"submit\" value=\"Go!\"></form>";
|
60 | html += "</body></html>";
|
61 | res.status(200).send(html);
|
62 | });
|
63 | routerOPDS_browse_v2.param("urlEncoded", function (req, _res, next, value, _name) {
|
64 | req.urlEncoded = value;
|
65 | next();
|
66 | });
|
67 | routerOPDS_browse_v2.get("/:" + request_ext_1._urlEncoded + "(*)", function (req, res) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
68 | var reqparams, authResponseJson, authResponseBase64, authResponseStr, authRequestBase64, urlDecoded, isSecureHttp, rootUrl, failure, success, headers, needsStreamingResponse, response, err_1;
|
69 | var _this = this;
|
70 | return tslib_1.__generator(this, function (_a) {
|
71 | switch (_a.label) {
|
72 | case 0:
|
73 | reqparams = req.params;
|
74 | if (!reqparams.urlEncoded) {
|
75 | reqparams.urlEncoded = req.urlEncoded;
|
76 | }
|
77 | authResponseBase64 = req.query.authResponse;
|
78 | if (authResponseBase64) {
|
79 | try {
|
80 | authResponseStr = Buffer.from(authResponseBase64, "base64").toString("utf8");
|
81 | authResponseJson = JSON.parse(authResponseStr);
|
82 | }
|
83 | catch (err) {
|
84 | debug(err);
|
85 | }
|
86 | }
|
87 | authRequestBase64 = req.query.authRequest;
|
88 | urlDecoded = reqparams.urlEncoded;
|
89 | debug(urlDecoded);
|
90 | isSecureHttp = req.secure ||
|
91 | req.protocol === "https" ||
|
92 | req.get("X-Forwarded-Proto") === "https";
|
93 | rootUrl = (isSecureHttp ? "https://" : "http://")
|
94 | + req.headers.host;
|
95 | failure = function (err) {
|
96 | debug(err);
|
97 | res.status(500).send("<html><body><p>Internal Server Error</p><p>"
|
98 | + err + "</p></body></html>");
|
99 | };
|
100 | success = function (response) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
101 | var isAuthStatusCode, redirectUrl, isBadStatusCode, responseData, err_2, responseStr, responseJson, isPublication, isAuth, opds2Feed, opds2FeedJson, validationStr, doValidate, jsonSchemasRootpath, jsonSchemasNames, validationErrors, _i, validationErrors_1, err, val, valueStr, title, val, valueStr, title, pubIndex, jsonPubTitlePath, funk, css, jsonPrettyOPDS2, authDoc, authObj, authLink, imageLink, imageUrl, authHtmlForm;
|
102 | return tslib_1.__generator(this, function (_a) {
|
103 | switch (_a.label) {
|
104 | case 0:
|
105 | isAuthStatusCode = response.statusCode === 401;
|
106 | if (isAuthStatusCode &&
|
107 | authRequestBase64 && authResponseJson && authResponseJson.refresh_token) {
|
108 | redirectUrl = rootUrl + req.originalUrl.substr(0, req.originalUrl.indexOf(exports.serverOPDS_browse_v2_PATH + "/")) +
|
109 | exports.serverOPDS_auth_PATH + "/" + UrlUtils_1.encodeURIComponent_RFC3986(authRequestBase64) +
|
110 | "?" + request_ext_1._authRefresh + "=" + authResponseJson.refresh_token;
|
111 | debug("REDIRECT: " + req.originalUrl + " ==> " + redirectUrl);
|
112 | res.redirect(301, redirectUrl);
|
113 | return [2];
|
114 | }
|
115 | isBadStatusCode = response.statusCode && (response.statusCode < 200 || response.statusCode >= 300);
|
116 | if (!isAuthStatusCode && isBadStatusCode) {
|
117 | failure("HTTP CODE " + response.statusCode);
|
118 | return [2];
|
119 | }
|
120 | _a.label = 1;
|
121 | case 1:
|
122 | _a.trys.push([1, 3, , 4]);
|
123 | return [4, BufferUtils_1.streamToBufferPromise(response)];
|
124 | case 2:
|
125 | responseData = _a.sent();
|
126 | return [3, 4];
|
127 | case 3:
|
128 | err_2 = _a.sent();
|
129 | debug(err_2);
|
130 | res.status(500).send("<html><body><p>Internal Server Error</p><p>"
|
131 | + err_2 + (isAuthStatusCode ? " (Auth 401)" : "") + "</p></body></html>");
|
132 | return [2];
|
133 | case 4:
|
134 | responseStr = responseData.toString("utf8");
|
135 | responseJson = JSON.parse(responseStr);
|
136 | isPublication = !responseJson.publications &&
|
137 | !responseJson.navigation &&
|
138 | !responseJson.groups &&
|
139 | !responseJson.catalogs &&
|
140 | responseJson.metadata;
|
141 | isAuth = !isPublication && responseJson.authentication;
|
142 | opds2Feed = isPublication ? serializable_1.TaJsonDeserialize(responseJson, opds2_publication_1.OPDSPublication) :
|
143 | (isAuth ? serializable_1.TaJsonDeserialize(responseJson, opds2_authentication_doc_1.OPDSAuthenticationDoc) :
|
144 | serializable_1.TaJsonDeserialize(responseJson, opds2_1.OPDSFeed));
|
145 | opds2FeedJson = serializable_1.TaJsonSerialize(opds2Feed);
|
146 | doValidate = !reqparams.jsonPath || reqparams.jsonPath === "all";
|
147 | if (doValidate) {
|
148 | jsonSchemasRootpath = path.join(process.cwd(), "misc", "json-schema");
|
149 | jsonSchemasNames = [
|
150 | "opds/publication",
|
151 | "opds/acquisition-object",
|
152 | "opds/catalog-entry",
|
153 | "opds/feed-metadata",
|
154 | "opds/properties",
|
155 | "webpub-manifest/publication",
|
156 | "webpub-manifest/contributor-object",
|
157 | "webpub-manifest/contributor",
|
158 | "webpub-manifest/link",
|
159 | "webpub-manifest/metadata",
|
160 | "webpub-manifest/subcollection",
|
161 | "webpub-manifest/properties",
|
162 | "webpub-manifest/subject",
|
163 | "webpub-manifest/subject-object",
|
164 | "webpub-manifest/extensions/epub/metadata",
|
165 | "webpub-manifest/extensions/epub/subcollections",
|
166 | "webpub-manifest/extensions/epub/properties",
|
167 | "webpub-manifest/extensions/presentation/metadata",
|
168 | "webpub-manifest/extensions/presentation/properties",
|
169 | "webpub-manifest/language-map",
|
170 | ];
|
171 | if (isAuth) {
|
172 | jsonSchemasNames.unshift("opds/authentication");
|
173 | }
|
174 | else if (!isPublication) {
|
175 | jsonSchemasNames.unshift("opds/feed");
|
176 | }
|
177 | validationErrors = json_schema_validate_1.jsonSchemaValidate(jsonSchemasRootpath, jsonSchemasNames, opds2FeedJson);
|
178 | if (validationErrors) {
|
179 | validationStr = "";
|
180 | for (_i = 0, validationErrors_1 = validationErrors; _i < validationErrors_1.length; _i++) {
|
181 | err = validationErrors_1[_i];
|
182 | debug("JSON Schema validation FAIL.");
|
183 | debug(err);
|
184 | if (isPublication) {
|
185 | val = DotProp.get(opds2FeedJson, err.jsonPath);
|
186 | valueStr = (typeof val === "string") ?
|
187 | "" + val :
|
188 | ((val instanceof Array || typeof val === "object") ?
|
189 | "" + JSON.stringify(val) :
|
190 | "");
|
191 | debug(valueStr);
|
192 | title = DotProp.get(opds2FeedJson, "metadata.title");
|
193 | debug(title);
|
194 | validationStr +=
|
195 | "\n\"" + title + "\"\n\n" + err.ajvMessage + ": " + valueStr + "\n\n'" + err.ajvDataPath.replace(/^\./, "") + "' (" + err.ajvSchemaPath + ")\n\n";
|
196 | }
|
197 | else {
|
198 | val = DotProp.get(opds2FeedJson, err.jsonPath);
|
199 | valueStr = (typeof val === "string") ?
|
200 | "" + val :
|
201 | ((val instanceof Array || typeof val === "object") ?
|
202 | "" + JSON.stringify(val) :
|
203 | "");
|
204 | debug(valueStr);
|
205 | title = "";
|
206 | pubIndex = "";
|
207 | if (/^publications\.[0-9]+/.test(err.jsonPath)) {
|
208 | jsonPubTitlePath = err.jsonPath.replace(/^(publications\.[0-9]+).*/, "$1.metadata.title");
|
209 | debug(jsonPubTitlePath);
|
210 | title = DotProp.get(opds2FeedJson, jsonPubTitlePath);
|
211 | debug(title);
|
212 | pubIndex = err.jsonPath.replace(/^publications\.([0-9]+).*/, "$1");
|
213 | debug(pubIndex);
|
214 | }
|
215 | validationStr +=
|
216 | "\n___________INDEX___________ #" + pubIndex + " \"" + title + "\"\n\n" + err.ajvMessage + ": " + valueStr + "\n\n'" + err.ajvDataPath.replace(/^\./, "") + "' (" + err.ajvSchemaPath + ")\n\n";
|
217 | }
|
218 | }
|
219 | }
|
220 | }
|
221 | funk = function (obj) {
|
222 | if ((obj.href && typeof obj.href === "string") ||
|
223 | (obj.Href && typeof obj.Href === "string")) {
|
224 | var fullHref = obj.href ? obj.href : obj.Href;
|
225 | var isDataUrl = /^data:/.test(fullHref);
|
226 | var isMailUrl = /^mailto:/.test(fullHref);
|
227 | var notFull = !isDataUrl && !isMailUrl && !UrlUtils_1.isHTTP(fullHref);
|
228 | if (notFull) {
|
229 | fullHref = UrlUtils_1.ensureAbsolute(urlDecoded, fullHref);
|
230 | }
|
231 | if ((obj.type && obj.type.indexOf("opds") >= 0 && obj.type.indexOf("json") >= 0) ||
|
232 | (obj.Type && obj.Type.indexOf("opds") >= 0 && obj.Type.indexOf("json") >= 0)) {
|
233 | obj.__href__ = rootUrl + req.originalUrl.substr(0, req.originalUrl.indexOf(exports.serverOPDS_browse_v2_PATH + "/")) +
|
234 | exports.serverOPDS_browse_v2_PATH + "/" + UrlUtils_1.encodeURIComponent_RFC3986(fullHref);
|
235 | if (authRequestBase64 && authResponseBase64) {
|
236 | obj.__href__AUTH = obj.__href__ +
|
237 | "?" +
|
238 | request_ext_1._authResponse + "=" + UrlUtils_1.encodeURIComponent_RFC3986(authResponseBase64) +
|
239 | "&" +
|
240 | request_ext_1._authRequest + "=" + UrlUtils_1.encodeURIComponent_RFC3986(authRequestBase64);
|
241 | }
|
242 | }
|
243 | else if (obj.type === "application/vnd.readium.lcp.license.v1.0+json") {
|
244 | obj.__href__ = rootUrl + req.originalUrl.substr(0, req.originalUrl.indexOf(exports.serverOPDS_browse_v2_PATH + "/")) +
|
245 | server_lcp_lsd_show_1.serverLCPLSD_show_PATH + "/" + UrlUtils_1.encodeURIComponent_RFC3986(fullHref);
|
246 | if (authRequestBase64 && authResponseBase64) {
|
247 | obj.__href__AUTH = obj.__href__ +
|
248 | "?" +
|
249 | request_ext_1._authResponse + "=" + UrlUtils_1.encodeURIComponent_RFC3986(authResponseBase64) +
|
250 | "&" +
|
251 | request_ext_1._authRequest + "=" + UrlUtils_1.encodeURIComponent_RFC3986(authRequestBase64);
|
252 | }
|
253 | }
|
254 | else if ((obj.type && obj.type.indexOf("application/atom+xml") >= 0) ||
|
255 | (obj.Type && obj.Type.indexOf("application/atom+xml") >= 0)) {
|
256 | obj.__href__ = rootUrl + req.originalUrl.substr(0, req.originalUrl.indexOf(exports.serverOPDS_browse_v2_PATH + "/")) +
|
257 | server_opds_convert_v1_to_v2_1.serverOPDS_convert_v1_to_v2_PATH + "/" + UrlUtils_1.encodeURIComponent_RFC3986(fullHref);
|
258 | }
|
259 | else if (isDataUrl) {
|
260 | }
|
261 | else if (notFull && !isMailUrl) {
|
262 | obj.__href__ = fullHref;
|
263 | }
|
264 | }
|
265 | };
|
266 | JsonUtils_1.traverseJsonObjects(opds2FeedJson, funk);
|
267 | css = css2json(jsonStyle);
|
268 | jsonPrettyOPDS2 = jsonMarkup(opds2FeedJson, css);
|
269 | jsonPrettyOPDS2 = jsonPrettyOPDS2.replace(/>"data:image\/(.*)"</g, "><a href=\"data:image/$1\" target=\"_BLANK\"><img style=\"max-width: 100px;\" src=\"data:image/$1\"></a><");
|
270 | authDoc = isAuth ? opds2Feed : undefined;
|
271 | authObj = (authDoc && authDoc.Authentication) ? authDoc.Authentication.find(function (auth) {
|
272 | return auth.Type === "http://opds-spec.org/auth/oauth/password";
|
273 | }) : undefined;
|
274 | authLink = authObj ? (authObj.Links && authObj.Links.find(function (link) {
|
275 | return link.Rel && link.Rel.includes("authenticate") && link.TypeLink === "application/json";
|
276 | })) : undefined;
|
277 | imageLink = authDoc ? (authDoc.Links && authDoc.Links.find(function (link) {
|
278 | return link.Rel && link.Rel.includes("logo") && link.TypeLink && link.TypeLink.startsWith("image/");
|
279 | })) : undefined;
|
280 | imageUrl = imageLink ? UrlUtils_1.ensureAbsolute(urlDecoded, imageLink.Href) : undefined;
|
281 | authHtmlForm = !authObj ? "" : "\n<hr>\n<form id=\"authForm\">\n <input type=\"text\" name=\"login\" id=\"login\" size=\"40\">\n <span>" + authObj.Labels.Login + "</span>\n<br><br>\n <input type=\"password\" name=\"password\" id=\"password\" size=\"40\">\n <span>" + authObj.Labels.Password + "</span>\n<br><br>\n <input type=\"submit\" value=\"Authenticate\">\n</form>\n" + (imageUrl ? "<img src=\"" + imageUrl + "\" />" : "") + "\n<script type=\"text/javascript\">\n// document.addEventListener(\"DOMContentLoaded\", (event) => {\n// });\nconst formElement = document.getElementById(\"authForm\");\nformElement.addEventListener(\"submit\", (event) => {\n event.preventDefault();\n doAuth();\n});\nfunction encodeURIComponent_RFC3986(str) {\n return encodeURIComponent(str).replace(/[!'()*]/g, (c) => {\n return \"%\" + c.charCodeAt(0).toString(16);\n });\n}\nfunction encodeFormData(json) {\n if (!json) {\n return \"\";\n }\n return Object.keys(json).map((key) => {\n return encodeURIComponent_RFC3986(key) + \"=\" + (json[key] ? encodeURIComponent_RFC3986(json[key]) : \"_\");\n }).join(\"&\");\n}\nfunction hexStrToArrayBuffer(hexStr) {\n return new Uint8Array(\n hexStr\n .match(/.{1,2}/g)\n .map((byte) => {\n return parseInt(byte, 16);\n })\n );\n}\nfunction doAuth() {\n " + (authLink ? "\n const bodyJson = {\n targetUrl: \"" + urlDecoded + "\",\n authUrl: \"" + authLink.Href + "\",\n grant_type: \"password\",\n username: document.getElementById(\"login\").value,\n password: document.getElementById(\"password\").value\n };\n const bodyStr = JSON.stringify(bodyJson);\n\n const textEncoder = new TextEncoder(\"utf-8\");\n const bodyStrEncoded = textEncoder.encode(bodyStr); // Uint8Array\n\n const keyPromise = window.crypto.subtle.importKey(\n \"raw\",\n hexStrToArrayBuffer(\"" + OPDS_AUTH_ENCRYPTION_KEY_HEX + "\"),\n { \"name\": \"AES-CBC\" },\n false,\n [\"encrypt\", \"decrypt\"]\n );\n keyPromise.then((key) => { // CryptoKey\n\n const iv = hexStrToArrayBuffer(\"" + OPDS_AUTH_ENCRYPTION_IV_HEX + "\");\n const encryptedBodyPromise = window.crypto.subtle.encrypt(\n {\n name: \"AES-CBC\",\n iv\n },\n key,\n bodyStrEncoded\n );\n encryptedBodyPromise.then((encryptedBody) => { // ArrayBuffer\n // const arg = String.fromCharCode.apply(null, new Uint8Array(encryptedBody));\n const arg = new Uint8Array(encryptedBody).reduce((data, byte) => {\n return data + String.fromCharCode(byte);\n }, '');\n const encryptedBodyB64 = window.btoa(arg);\n\n const url = location.origin + \"" + exports.serverOPDS_auth_PATH + "/\" + encodeURIComponent_RFC3986(encryptedBodyB64);\n location.href = url;\n }).catch((err) => {\n console.log(err);\n });\n }).catch((err) => {\n console.log(err);\n });\n\n/* does not work because of HTTP CORS, so we forward to NodeJS fetch/request via the serverOPDS_auth_PATH HTTP route\n window.fetch(\"" + authLink.Href + "\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-url-encoded\",\n \"Accept\": \"application/json\"\n },\n body: encodeFormData(bodyJson)\n })\n .then((response) => {\n const res = JSON.stringify(response, null, 4);\n console.log(res);\n })\n .catch((error) => {\n console.log(error);\n });\n*/\n " :
|
282 | "window.alert(\"no auth link!\");") + "\n}\n</script>";
|
283 | res.status(200).send("<html><body>" +
|
284 | "<h1>OPDS2 JSON " +
|
285 | (isPublication ? "entry" : (isAuth ? "authentication" : "feed")) +
|
286 | " (OPDS2) " + (isAuthStatusCode ? " [HTTP 401]" : "") + "</h1>" +
|
287 | "<h2><a href=\"" + urlDecoded + "\">" + urlDecoded + "</a></h2>" +
|
288 | "<hr>" +
|
289 | "<div style=\"overflow-x: auto;margin:0;padding:0;width:100%;height:auto;\">" +
|
290 | jsonPrettyOPDS2 + "</div>" +
|
291 | (doValidate ? (validationStr ? ("<hr><p><pre>" + validationStr + "</pre></p>") : ("<hr><p>JSON SCHEMA OK.</p>")) : "") +
|
292 | authHtmlForm +
|
293 | "</body></html>");
|
294 | return [2];
|
295 | }
|
296 | });
|
297 | }); };
|
298 | headers = {
|
299 | "Accept": "application/json,application/xml",
|
300 | "Accept-Language": "en-UK,en-US;q=0.7,en;q=0.5",
|
301 | "User-Agent": "READIUM2",
|
302 | };
|
303 | if (authResponseJson && authResponseJson.access_token) {
|
304 | headers.Authorization = "Bearer " + authResponseJson.access_token;
|
305 | }
|
306 | needsStreamingResponse = true;
|
307 | if (!needsStreamingResponse) return [3, 1];
|
308 | request.get({
|
309 | headers: headers,
|
310 | method: "GET",
|
311 | uri: urlDecoded,
|
312 | })
|
313 | .on("response", success)
|
314 | .on("error", failure);
|
315 | return [3, 7];
|
316 | case 1:
|
317 | response = void 0;
|
318 | _a.label = 2;
|
319 | case 2:
|
320 | _a.trys.push([2, 4, , 5]);
|
321 | return [4, requestPromise({
|
322 | headers: headers,
|
323 | method: "GET",
|
324 | resolveWithFullResponse: true,
|
325 | uri: urlDecoded,
|
326 | })];
|
327 | case 3:
|
328 | response = _a.sent();
|
329 | return [3, 5];
|
330 | case 4:
|
331 | err_1 = _a.sent();
|
332 | failure(err_1);
|
333 | return [2];
|
334 | case 5: return [4, success(response)];
|
335 | case 6:
|
336 | _a.sent();
|
337 | _a.label = 7;
|
338 | case 7: return [2];
|
339 | }
|
340 | });
|
341 | }); });
|
342 | topRouter.use(exports.serverOPDS_browse_v2_PATH, routerOPDS_browse_v2);
|
343 | var routerOPDS_auth = express.Router({ strict: false });
|
344 | routerOPDS_auth.use(morgan("combined", { stream: { write: function (msg) { return debug(msg); } } }));
|
345 | routerOPDS_auth.use(server_trailing_slash_redirect_1.trailingSlashRedirect);
|
346 | routerOPDS_auth.get("/", function (_req, res) {
|
347 | var html = "<html><body><h1>NOPE</h1></body></html>";
|
348 | res.status(200).send(html);
|
349 | });
|
350 | routerOPDS_auth.param("urlEncoded", function (req, _res, next, value, _name) {
|
351 | req.urlEncoded = value;
|
352 | next();
|
353 | });
|
354 | routerOPDS_auth.get("/:" + request_ext_1._urlEncoded + "(*)", function (req, res) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
355 | var reqparams, base64Payload, refreshToken, isSecureHttp, rootUrl, encrypted, decrypteds, decryptStream, buff1, buff2, decrypted, nPaddingBytes, size, decryptedStr, decryptedJson_1, authUrl, targetUrl_1, failure_1, success, headers, needsStreamingResponse, response, err_3, err_4;
|
356 | var _this = this;
|
357 | return tslib_1.__generator(this, function (_a) {
|
358 | switch (_a.label) {
|
359 | case 0:
|
360 | reqparams = req.params;
|
361 | if (!reqparams.urlEncoded) {
|
362 | reqparams.urlEncoded = req.urlEncoded;
|
363 | }
|
364 | base64Payload = reqparams.urlEncoded;
|
365 | refreshToken = req.query.authRefresh;
|
366 | isSecureHttp = req.secure ||
|
367 | req.protocol === "https" ||
|
368 | req.get("X-Forwarded-Proto") === "https";
|
369 | rootUrl = (isSecureHttp ? "https://" : "http://")
|
370 | + req.headers.host;
|
371 | _a.label = 1;
|
372 | case 1:
|
373 | _a.trys.push([1, 9, , 10]);
|
374 | encrypted = Buffer.from(base64Payload, "base64");
|
375 | decrypteds = [];
|
376 | decryptStream = crypto.createDecipheriv("aes-256-cbc", OPDS_AUTH_ENCRYPTION_KEY_BUFFER, OPDS_AUTH_ENCRYPTION_IV_BUFFER);
|
377 | decryptStream.setAutoPadding(false);
|
378 | buff1 = decryptStream.update(encrypted);
|
379 | if (buff1) {
|
380 | decrypteds.push(buff1);
|
381 | }
|
382 | buff2 = decryptStream.final();
|
383 | if (buff2) {
|
384 | decrypteds.push(buff2);
|
385 | }
|
386 | decrypted = Buffer.concat(decrypteds);
|
387 | nPaddingBytes = decrypted[decrypted.length - 1];
|
388 | size = encrypted.length - nPaddingBytes;
|
389 | decryptedStr = decrypted.slice(0, size).toString("utf8");
|
390 | decryptedJson_1 = JSON.parse(decryptedStr);
|
391 | authUrl = decryptedJson_1.authUrl;
|
392 | delete decryptedJson_1.authUrl;
|
393 | targetUrl_1 = decryptedJson_1.targetUrl;
|
394 | delete decryptedJson_1.targetUrl;
|
395 | if (refreshToken) {
|
396 | decryptedJson_1.grant_type = "refresh_token";
|
397 | decryptedJson_1.refresh_token = refreshToken;
|
398 | }
|
399 | failure_1 = function (err) {
|
400 | debug(err);
|
401 | res.status(500).send("<html><body><p>Internal Server Error</p><p>"
|
402 | + err + "</p></body></html>");
|
403 | };
|
404 | success = function (response) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
405 | var responseData, err_5, responseStr, responseJson, targetUrl_, refreshTokenUrl;
|
406 | return tslib_1.__generator(this, function (_a) {
|
407 | switch (_a.label) {
|
408 | case 0:
|
409 | if (response.statusCode && (response.statusCode < 200 || response.statusCode >= 300)) {
|
410 | failure_1("HTTP CODE " + response.statusCode);
|
411 | return [2];
|
412 | }
|
413 | _a.label = 1;
|
414 | case 1:
|
415 | _a.trys.push([1, 3, , 4]);
|
416 | return [4, BufferUtils_1.streamToBufferPromise(response)];
|
417 | case 2:
|
418 | responseData = _a.sent();
|
419 | return [3, 4];
|
420 | case 3:
|
421 | err_5 = _a.sent();
|
422 | debug(err_5);
|
423 | res.status(500).send("<html><body><p>Internal Server Error</p><p>"
|
424 | + err_5 + "</p></body></html>");
|
425 | return [2];
|
426 | case 4:
|
427 | try {
|
428 | responseStr = responseData.toString("utf8");
|
429 | responseJson = JSON.parse(responseStr);
|
430 | targetUrl_ = rootUrl + req.originalUrl.substr(0, req.originalUrl.indexOf(exports.serverOPDS_auth_PATH + "/")) +
|
431 | exports.serverOPDS_browse_v2_PATH + "/" + UrlUtils_1.encodeURIComponent_RFC3986(targetUrl_1) +
|
432 | "?" + request_ext_1._authResponse + "=" +
|
433 | UrlUtils_1.encodeURIComponent_RFC3986(Buffer.from(JSON.stringify(responseJson)).toString("base64")) +
|
434 | "&" + request_ext_1._authRequest + "=" + UrlUtils_1.encodeURIComponent_RFC3986(base64Payload);
|
435 | refreshTokenUrl = responseJson.refresh_token ? rootUrl + req.originalUrl.substr(0, req.originalUrl.indexOf(exports.serverOPDS_auth_PATH + "/")) +
|
436 | exports.serverOPDS_auth_PATH + "/" + UrlUtils_1.encodeURIComponent_RFC3986(base64Payload) +
|
437 | "?" + request_ext_1._authRefresh + "=" + UrlUtils_1.encodeURIComponent_RFC3986(responseJson.refresh_token) : undefined;
|
438 | decryptedJson_1.password = "***";
|
439 | res.status(200).send("\n <html><body>\n <hr>\n <a href=\"" + targetUrl_ + "\">" + targetUrl_1 + "</a>\n <hr>\n <pre>" + JSON.stringify(decryptedJson_1, null, 4) + "</pre>\n <hr>\n <pre>" + JSON.stringify(responseJson, null, 4) + "</pre>\n <hr>\n " + (refreshTokenUrl ? "<a href=\"" + refreshTokenUrl + "\">FORCE REFRESH TOKEN</a>" : "") + "\n <hr>\n </body></html>\n ");
|
440 | }
|
441 | catch (err) {
|
442 | debug(err);
|
443 | res.status(500).send("<html><body><p>Internal Server Error</p><p>"
|
444 | + err + "</p></body></html>");
|
445 | return [2];
|
446 | }
|
447 | return [2];
|
448 | }
|
449 | });
|
450 | }); };
|
451 | headers = {
|
452 | "Accept": "application/json,application/xml",
|
453 | "Accept-Language": "en-UK,en-US;q=0.7,en;q=0.5",
|
454 | "Content-Type": "application/x-www-form-url-encoded",
|
455 | "User-Agent": "READIUM2",
|
456 | };
|
457 | needsStreamingResponse = true;
|
458 | if (!needsStreamingResponse) return [3, 2];
|
459 | request.post({
|
460 | form: decryptedJson_1,
|
461 | headers: headers,
|
462 | method: "POST",
|
463 | uri: authUrl,
|
464 | })
|
465 | .on("response", success)
|
466 | .on("error", failure_1);
|
467 | return [3, 8];
|
468 | case 2:
|
469 | response = void 0;
|
470 | _a.label = 3;
|
471 | case 3:
|
472 | _a.trys.push([3, 5, , 6]);
|
473 | return [4, requestPromise({
|
474 | form: decryptedJson_1,
|
475 | headers: headers,
|
476 | method: "POST",
|
477 | resolveWithFullResponse: true,
|
478 | uri: authUrl,
|
479 | })];
|
480 | case 4:
|
481 | response = _a.sent();
|
482 | return [3, 6];
|
483 | case 5:
|
484 | err_3 = _a.sent();
|
485 | failure_1(err_3);
|
486 | return [2];
|
487 | case 6: return [4, success(response)];
|
488 | case 7:
|
489 | _a.sent();
|
490 | _a.label = 8;
|
491 | case 8: return [3, 10];
|
492 | case 9:
|
493 | err_4 = _a.sent();
|
494 | debug(err_4);
|
495 | res.status(500).send("<html><body><p>Internal Server Error</p><p>"
|
496 | + "--" + "</p></body></html>");
|
497 | return [3, 10];
|
498 | case 10: return [2];
|
499 | }
|
500 | });
|
501 | }); });
|
502 | topRouter.use(exports.serverOPDS_auth_PATH, routerOPDS_auth);
|
503 | }
|
504 | exports.serverOPDS_browse_v2 = serverOPDS_browse_v2;
|
505 | //# sourceMappingURL=server-opds-browse-v2.js.map |
\ | No newline at end of file |