UNPKG

@bakesaled/muster

Version:

A tool that collects your financial information from the web.

530 lines (516 loc) 21.6 kB
import { v1 } from 'uuid'; import { parse } from 'url'; import { connect } from 'tls'; import { addDays } from 'date-fns'; import { DOMParser } from 'xmldom'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } function __awaiter(thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } var OfxService = (function () { function OfxService() { } OfxService.buildAccountListRequest = function (options) { return (OfxService.getOfxHeaders(options) + "<OFX>" + OfxService.getSignOnMsg(options) + "<SIGNUPMSGSRQV1>" + "<ACCTINFOTRNRQ>" + ("<TRNUID>" + v1()) + "<ACCTINFORQ>" + "<DTACCTUP>19900101" + "</ACCTINFORQ>" + "</ACCTINFOTRNRQ>" + "</SIGNUPMSGSRQV1>" + "</OFX>"); }; OfxService.buildStatementRequest = function (options, debug) { if (debug === void 0) { debug = false; } var type = (options.accType || '').toUpperCase(); var reqStr = OfxService.getOfxHeaders(options) + '<OFX>' + OfxService.getSignOnMsg(options); switch (type) { case 'INVESTMENT': reqStr += '<INVSTMTMSGSRQV1>' + '<INVSTMTTRNRQ>' + '<TRNUID>' + v1() + '<CLTCOOKIE>' + v1().substr(0, 5) + '<INVSTMTRQ>' + '<INVACCTFROM>' + '<BROKERID>' + options.brokerId + '<ACCTID>' + options.accId + '</INVACCTFROM>' + '<INCTRAN>' + '<DTSTART>' + options.start + (typeof options.end !== 'undefined' ? '<DTEND>' + options.end : '') + '<INCLUDE>Y</INCTRAN>' + '<INCOO>Y' + '<INCPOS>' + '<INCLUDE>Y' + '</INCPOS>' + '<INCBAL>Y' + '</INVSTMTRQ>' + '</INVSTMTTRNRQ>' + '</INVSTMTMSGSRQV1>'; break; case 'CREDITCARD': reqStr += '<CREDITCARDMSGSRQV1>' + '<CCSTMTTRNRQ>' + '<TRNUID>' + v1() + '<CLTCOOKIE>' + v1().substr(0, 5) + '<CCSTMTRQ>' + '<CCACCTFROM>' + '<ACCTID>' + options.accId + '</CCACCTFROM>' + '<INCTRAN>' + '<DTSTART>' + options.start + (typeof options.end !== 'undefined' ? '<DTEND>' + options.end : '') + '<INCLUDE>Y</INCTRAN>' + '</CCSTMTRQ>' + '</CCSTMTTRNRQ>' + '</CREDITCARDMSGSRQV1>'; break; default: reqStr += '<BANKMSGSRQV1>' + '<STMTTRNRQ>' + '<TRNUID>' + v1() + '<CLTCOOKIE>' + v1().substr(0, 5) + '<STMTRQ>' + '<BANKACCTFROM>' + '<BANKID>' + options.bankId + '<ACCTID>' + options.accId + '<ACCTTYPE>' + type + '</BANKACCTFROM>' + '<INCTRAN>' + '<DTSTART>' + options.start + (typeof options.end !== 'undefined' ? '<DTEND>' + options.end : '') + '<INCLUDE>Y</INCTRAN>' + '</STMTRQ>' + '</STMTTRNRQ>' + '</BANKMSGSRQV1>'; } reqStr += '</OFX>'; if (debug) { console.debug('OFX-RequestString:', reqStr); } return reqStr; }; OfxService.getOfxHeaders = function (options) { return ("OFXHEADER:" + options.ofxHeaderVer + "\r\n" + "DATA:OFXSGML\r\n" + ("VERSION:" + options.ofxVer + "\r\n") + "SECURITY:NONE\r\n" + "ENCODING:USASCII\r\n" + "CHARSET:1252\r\n" + "COMPRESSION:NONE\r\n" + "OLDFILEUID:NONE\r\n" + ("NEWFILEUID:" + v1() + "\r\n") + "\r\n"); }; OfxService.getSignOnMsg = function (options) { var dtClient = new Date() .toISOString() .substring(0, 20) .replace(/[^0-9]/g, ''); return ("<SIGNONMSGSRQV1>" + "<SONRQ>" + "<DTCLIENT>" + dtClient + "<USERID>" + options.user + "<USERPASS>" + options.password + "<LANGUAGE>ENG" + "<FI>" + "<ORG>" + options.fidOrg + "<FID>" + options.fid + "</FI>" + "<APPID>" + options.app + "<APPVER>" + options.appVer + (typeof options.clientId !== "undefined" && options.clientId.length > 0 ? "<CLIENTUID>" + options.clientId : "") + "</SONRQ>" + "</SIGNONMSGSRQV1>"); }; return OfxService; }()); var OfxConnectionError = (function (_super) { __extends(OfxConnectionError, _super); function OfxConnectionError(errorCode) { var params = []; for (var _i = 1; _i < arguments.length; _i++) { params[_i - 1] = arguments[_i]; } var _this = _super.apply(this, params) || this; _this.errorCode = errorCode; if (Error.captureStackTrace) { Error.captureStackTrace(_this, OfxConnectionError); } _this.ofxConnectionStatus = { code: errorCode, message: '?' }; switch (errorCode) { case 301: _this.ofxConnectionStatus.message = 'Moved Permanently. Check url.'; break; case 302: _this.ofxConnectionStatus.message = 'Temporarily unable to connect.'; break; case 400: _this.ofxConnectionStatus.message = 'Bad Request'; break; case 401: _this.ofxConnectionStatus.message = 'Unauthorized. Check login info.'; break; case 403: _this.ofxConnectionStatus.message = 'Forbidden'; break; case 408: _this.ofxConnectionStatus.message = 'Request Timeout'; break; default: _this.ofxConnectionStatus.message = 'Undefined connection failure:' + params[0]; } Object.setPrototypeOf(_this, OfxConnectionError.prototype); return _this; } return OfxConnectionError; }(Error)); var OfxRequestService = (function () { function OfxRequestService(options) { this.options = options; } OfxRequestService.prototype.getAccounts = function () { return __awaiter(this, void 0, void 0, function () { var ofxPayload; return __generator(this, function (_a) { switch (_a.label) { case 0: ofxPayload = OfxService.buildAccountListRequest(this.options); return [4, this.sendRequest(this.options, ofxPayload)]; case 1: return [2, _a.sent()]; } }); }); }; OfxRequestService.prototype.getStatement = function (dateRange, debug) { if (debug === void 0) { debug = false; } return __awaiter(this, void 0, void 0, function () { var ofxPayload; return __generator(this, function (_a) { switch (_a.label) { case 0: this.debug = debug; this.options.start = dateRange.start; this.options.end = dateRange.end; ofxPayload = OfxService.buildStatementRequest(this.options, debug); return [4, this.sendRequest(this.options, ofxPayload)]; case 1: return [2, _a.sent()]; } }); }); }; OfxRequestService.prototype.convertBankingOptionsToHeaderString = function (options, parsedUrl, ofxPayload) { var result = ''; options.headers.forEach(function (header) { var value; if (options[header]) { value = options[header]; } else if (header === 'Content-Length') { value = ofxPayload.length; } else if (header === 'Host') { value = parsedUrl.host; } result += header + ': ' + value + '\r\n'; }); return result; }; OfxRequestService.prototype.sendRequest = function (options, ofxPayload) { var _this = this; return new Promise(function (resolve, reject) { return _this.buildSocket(options, ofxPayload, function (err, res) { if (err) { reject(err); } else { resolve(res); } }); }); }; OfxRequestService.prototype.buildSocket = function (options, ofxPayload, cb) { var _this = this; var parsedUrl = parse(options.url); var tlsOptions = { port: parseInt(parsedUrl.port, 10) || (parsedUrl.protocol === 'https:' ? 443 : 80), host: parsedUrl.hostname, rejectUnauthorized: true }; if (parsedUrl.hostname === 'localhost') { tlsOptions.rejectUnauthorized = false; } var socket = connect(tlsOptions, function () { var buffer = "POST " + parsedUrl.path + " HTTP/1.1\r\n"; buffer += _this.convertBankingOptionsToHeaderString(options, parsedUrl, ofxPayload); buffer += '\r\n'; buffer += ofxPayload; if (_this.debug) { console.debug('sending request', tlsOptions.port, tlsOptions.host, buffer); } socket.write(buffer); }); var data = ''; socket.on('data', function (chunk) { data += chunk; }); socket.on('error', function (err) { var ofxErr = new OfxConnectionError(500, err); cb(ofxErr); }); socket.on('end', function () { var error = true; var httpHeaderMatcher = new RegExp(/HTTP\/\d\.\d (\d{3}) (.*)/); if (_this.debug) { console.debug('response', data); } var matches = httpHeaderMatcher.exec(data); if (matches && matches.length > 2) { if (parseInt(matches[1], 10) === 200) { error = false; } else { error = new OfxConnectionError(parseInt(matches[1], 10), matches[1] + ": " + matches[0]); } } cb(error, data); }); }; return OfxRequestService; }()); var OfxDateUtil = (function () { function OfxDateUtil() { } OfxDateUtil.DateToOfxDate = function (date, adjustment) { if (adjustment) { date = addDays(date, adjustment); } var year = date.getFullYear(); var month = this.addLeadingZero(date.getMonth() + 1); var day = this.addLeadingZero(date.getDate()); return "" + year + month + day; }; OfxDateUtil.addLeadingZero = function (number, targetLength) { if (targetLength === void 0) { targetLength = 2; } if (typeof number === 'number') { number = number.toString(); } while (number.length < targetLength) { number = "0" + number; } return number; }; return OfxDateUtil; }()); var OfxResultValidator = (function () { function OfxResultValidator() { } OfxResultValidator.validate = function (res, connStatus) { if (connStatus === void 0) { connStatus = { code: 200, message: 'OK' }; } if (connStatus.code !== 200) { return { connectionStatus: connStatus, signOnStatus: undefined, signUpStatus: undefined, specificStatus: undefined }; } var cleanRes = OfxResultValidator.cleanXML(res); var parser = new DOMParser(); var xmlDocument = parser.parseFromString(cleanRes, 'text/xml'); var root = xmlDocument.documentElement; var signOnStatusNode = OfxResultValidator.getSignOnStatus(root); var signOnStatus = OfxResultValidator.convertStatusToJSON(signOnStatusNode); var signUpStatusNode = OfxResultValidator.getSignUpStatus(root); var signUpStatus = OfxResultValidator.convertStatusToJSON(signUpStatusNode); var bankStatusNode = OfxResultValidator.getBankStatus(root); var creditCardStatusNode = OfxResultValidator.getCreditCardStatus(root); var investmentStatusNode = OfxResultValidator.getInvestmentStatus(root); var specificStatus; if (bankStatusNode) { specificStatus = OfxResultValidator.convertStatusToJSON(bankStatusNode); } else if (creditCardStatusNode) { specificStatus = OfxResultValidator.convertStatusToJSON(creditCardStatusNode); } else if (investmentStatusNode) { specificStatus = OfxResultValidator.convertStatusToJSON(investmentStatusNode); } return { connectionStatus: connStatus, signOnStatus: signOnStatus, signUpStatus: signUpStatus, specificStatus: specificStatus }; }; OfxResultValidator.getSignOnStatus = function (root) { var signOnNode = root.getElementsByTagName('SIGNONMSGSRSV1'); if (!signOnNode || !signOnNode.item(0)) { return null; } var sonRsNode = signOnNode.item(0).getElementsByTagName('SONRS'); return sonRsNode.item(0); }; OfxResultValidator.getSignUpStatus = function (root) { var signUpNode = root.getElementsByTagName('SIGNUPMSGSRSV1'); if (!signUpNode || !signUpNode.item(0)) { return null; } var accountInfoRsNode = signUpNode .item(0) .getElementsByTagName('ACCTINFOTRNRS'); return accountInfoRsNode.item(0); }; OfxResultValidator.getBankStatus = function (root) { var bankNode = root.getElementsByTagName('BANKMSGSRSV1'); if (!bankNode || !bankNode.item(0)) { return null; } var statementRsNode = bankNode.item(0).getElementsByTagName('STMTTRNRS'); return statementRsNode.item(0); }; OfxResultValidator.getInvestmentStatus = function (root) { var investmentNode = root.getElementsByTagName('INVSTMTMSGSRSV1'); if (!investmentNode || !investmentNode.item(0)) { return null; } var statementRsNode = investmentNode .item(0) .getElementsByTagName('INVSTMTTRNRS'); return statementRsNode.item(0); }; OfxResultValidator.getCreditCardStatus = function (root) { var creditCardNode = root.getElementsByTagName('CREDITCARDMSGSRSV1'); if (!creditCardNode || !creditCardNode.item(0)) { return null; } var statementRsNode = creditCardNode .item(0) .getElementsByTagName('CCSTMTTRNRS'); return statementRsNode.item(0); }; OfxResultValidator.convertStatusToJSON = function (statusNode) { if (!statusNode) { return null; } var statusXML = statusNode.getElementsByTagName('STATUS').item(0); if (!statusXML) { return null; } return { CODE: statusXML.getElementsByTagName('CODE').item(0).childNodes[0] .nodeValue, MESSAGE: statusXML.getElementsByTagName('MESSAGE').item(0) ? statusXML.getElementsByTagName('MESSAGE').item(0).childNodes[0] .nodeValue : '', SEVERITY: statusXML.getElementsByTagName('SEVERITY').item(0).childNodes[0] .nodeValue }; }; OfxResultValidator.cleanXML = function (ofxPart) { return (ofxPart .replace(/&/g, "&#038;") .replace(/&amp;/g, "&#038;") .replace(/>\s+</g, '><') .replace(/\s+</g, '<') .replace(/>\s+/g, '>') .replace(/<([A-Z0-9_]*)+\.+([A-Z0-9_]*)>([^<]+)(<\/\1\.\2>)?/g, '<$1$2>$3') .replace(/<(\w+?)>([^<]+)/g, '<$1>$2</<added>$1>') .replace(/<\/<added>(\w+?)>(<\/\1>)?/g, '</$1>')); }; return OfxResultValidator; }()); export { OfxConnectionError, OfxDateUtil, OfxRequestService, OfxResultValidator }; //# sourceMappingURL=main.es.js.map