diff --git a/lib/Classes.js b/lib/Classes.js index ba91245..e3ffc3e 100644 --- a/lib/Classes.js +++ b/lib/Classes.js @@ -645,6 +645,7 @@ var Nachricht = function (proto_version) { seg.nr = me_msg.segments_ctr + 1 me_msg.segments[me_msg.segments_ctr] = seg me_msg.segments_ctr++ + return seg.nr } @@ -763,7 +764,7 @@ util.inherits(Exceptions.MalformedMessageFormat, Exceptions.OpenFinTSClientExcep Exceptions.OrderFailedException = function (msg) { Exceptions.OpenFinTSClientException.call(this) this.msg_detail = msg - this.message = 'Failed to perform Order, got error Message from Server.:' + msg.getEl(3) + this.message = 'Failed to perform Order, got error Message from Server.:' + msg.getEl ? msg.getEl(3) : msg[2] } util.inherits(Exceptions.OrderFailedException, Exceptions.OpenFinTSClientException) @@ -948,6 +949,25 @@ var Order = function (client) { } // 2. Add Segment msg.addSeg(int_send_msg[j].segment) + + // Kontoumsätze abrufen requires HKTAN + if (int_send_msg[j].segment.name === 'HKKAZ') { + let HKTAN_vers; + var tan_verfahren = me_order.client.bpd.tan.tan_verfahren[me_order.client.upd.availible_tan_verfahren[0]]; + try { + HKTAN_vers = tan_verfahren.HITAN_vers; + client.log.con.info({ + gv: 'HKKAZ' + }, `TAN-Verfahren: '${tan_verfahren.desc}', v${HKTAN_vers}`); + } catch (e) { + client.log.con.error(e, { + gv: 'HKKAZ' + }, `Error reading TAN-Verfahren data. Using HKTAN v6.`); + } + msg.addSeg( + Helper.newSegFromArray('HKTAN', HKTAN_vers ?? 6, ['4', 'HKIDN', '', '', '', '', 'N', '', '', '', '']) + ); + } } } // Send Segments to Destination diff --git a/lib/FinTSClient.js b/lib/FinTSClient.js index 0599d14..7491141 100644 --- a/lib/FinTSClient.js +++ b/lib/FinTSClient.js @@ -246,7 +246,7 @@ var encoding = require('encoding') closeSecure () - Stellt sicher, dass keine Sensiblen Informationen wie die PIN noch im RAM sind, sollte am Ende immer gerufen werden */ -var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) { +var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger, in_client_name, in_client_version, in_available_tan_verfahren) { var me = this me.Exceptions = Exceptions // Logger @@ -276,8 +276,8 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) // Technical me.dialog_id = 0 me.next_msg_nr = 1 - me.client_name = 'Open-FinTS-JS-Client' - me.client_version = 4 + me.client_name = in_client_name || 'Open-FinTS-JS-Client' + me.client_version = in_client_version || 4 me.proto_version = 300 // 300=FinTS;220=HBCI 2.2 me.in_connection = false @@ -348,7 +348,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) me.upd = { 'vers_upd': '0', 'geschaefts_vorg_gesp': true, - 'availible_tan_verfahren': ['999'], + 'availible_tan_verfahren': in_available_tan_verfahren || ['999'], 'clone': function () { return JSON.parse(JSON.stringify(this)) } @@ -397,6 +397,16 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) // BPD Vers = 0; UPD Vers = 0; Dialogspr. = 0 var HKVVB = Helper.newSegFromArray('HKVVB', 3, [me.bpd.vers_bpd, me.upd.vers_upd, 0, me.client_name, me.client_version]) msg.addSeg(HKVVB) + + // in step 1 me.upd.availible_tan_verfahren[0] is always '999' and we use HKTAN_vers 6 + // in step 2 we use the tan_verfahren requested by the bank server and send the corresponding HITANS version + const tan_verfahren = me.bpd.tan.tan_verfahren[me.upd.availible_tan_verfahren[0]]; + const HKTAN_vers = tan_verfahren.HITAN_vers || 6; + + msg.addSeg( + Helper.newSegFromArray('HKTAN', HKTAN_vers, ['4', 'HKIDN', '', '', '', '', 'N', '', '', '', '']) + ); + if (me.kunden_id != 9999999999 && me.sys_id == 0) var syn = msg.addSeg(Helper.newSegFromArray('HKSYN', me.proto_version == 220 ? 2 : 3, [0])) // Synchronisierung starten me.log.gv.debug({ gv: 'HKVVB' @@ -534,46 +544,121 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) } try { // 5.4 Tan - var HITANS = recvMsg.selectSegByName('HITANS')[0] - if (HITANS.vers == 5) { + var HITANSs = recvMsg.selectSegByName('HITANS'); + + me.bpd.tan.tan_verfahren = {}; + for (var HITANS of HITANSs) { + me.log.gv.info({ + gv: 'HITANS' + }, 'Analyse HITANS v' + HITANS.vers); + var tan_data = HITANS.getEl(4) me.bpd.tan.one_step_availible = tan_data.getEl(1).toUpperCase() == 'J' me.bpd.tan.multiple_tan = tan_data.getEl(2).toUpperCase() == 'J' me.bpd.tan.hash_type = tan_data.getEl(3) - me.bpd.tan.tan_verfahren = {} - for (var i = 3; i < tan_data.data.length; i++) { - var sicherheitsfunktion = {} - sicherheitsfunktion.code = tan_data.data[i] - sicherheitsfunktion.one_two_step_vers = tan_data.data[i + 1] // "1": Einschrittverfahren, "2": Zweischritt - sicherheitsfunktion.tech_id = tan_data.data[i + 2] - sicherheitsfunktion.zka_tan_verfahren = tan_data.data[i + 3] - sicherheitsfunktion.vers_zka_tan_verf = tan_data.data[i + 4] - sicherheitsfunktion.desc = tan_data.data[i + 5] - sicherheitsfunktion.max_len_tan = tan_data.data[i + 6] - sicherheitsfunktion.tan_alphanum = tan_data.data[i + 7] == '2' - sicherheitsfunktion.txt_rueckwert = tan_data.data[i + 8] - sicherheitsfunktion.max_len_rueckwert = tan_data.data[i + 9] - sicherheitsfunktion.anz_tanlist = tan_data.data[i + 10] - sicherheitsfunktion.multi_tan = tan_data.data[i + 11].toUpperCase() == 'J' - sicherheitsfunktion.tan_zeit_diabez = tan_data.data[i + 12] - sicherheitsfunktion.tan_list_nr_req = tan_data.data[i + 13] - sicherheitsfunktion.auftragsstorno = tan_data.data[i + 14].toUpperCase() == 'J' - sicherheitsfunktion.sms_abu_konto_req = tan_data.data[i + 15] - sicherheitsfunktion.auftrag_konto = tan_data.data[i + 16] - sicherheitsfunktion.challange_class_req = tan_data.data[i + 17].toUpperCase() == 'J' - sicherheitsfunktion.challange_structured = tan_data.data[i + 18].toUpperCase() == 'J' - sicherheitsfunktion.initialisierungs_mod = tan_data.data[i + 19] - sicherheitsfunktion.bez_tan_med_req = tan_data.data[i + 20] - sicherheitsfunktion.anz_supported_tan_vers = tan_data.data[i + 21] - // sicherheitsfunktion.challange_value_req = tan_data.data[i+14].toUpperCase()=="J"; - me.bpd.tan.tan_verfahren[sicherheitsfunktion.code] = sicherheitsfunktion - i += 21 + + if (HITANS.vers == 5) { + for (var i = 3; i < tan_data.data.length; i+= 22) { + var sicherheitsfunktion = {} + sicherheitsfunktion.code = tan_data.data[i] + sicherheitsfunktion.one_two_step_vers = tan_data.data[i + 1] // "1": Einschrittverfahren, "2": Zweischritt + sicherheitsfunktion.tech_id = tan_data.data[i + 2] + sicherheitsfunktion.zka_tan_verfahren = tan_data.data[i + 3] + sicherheitsfunktion.vers_zka_tan_verf = tan_data.data[i + 4] + sicherheitsfunktion.desc = tan_data.data[i + 5] + sicherheitsfunktion.max_len_tan = tan_data.data[i + 6] + sicherheitsfunktion.tan_alphanum = tan_data.data[i + 7] == '2' + sicherheitsfunktion.txt_rueckwert = tan_data.data[i + 8] + sicherheitsfunktion.max_len_rueckwert = tan_data.data[i + 9] + sicherheitsfunktion.anz_tanlist = tan_data.data[i + 10] + sicherheitsfunktion.multi_tan = tan_data.data[i + 11]?.toUpperCase() == 'J' + sicherheitsfunktion.tan_zeit_diabez = tan_data.data[i + 12] + sicherheitsfunktion.tan_list_nr_req = tan_data.data[i + 13] + sicherheitsfunktion.auftragsstorno = tan_data.data[i + 14]?.toUpperCase() == 'J' + sicherheitsfunktion.sms_abu_konto_req = tan_data.data[i + 15] + sicherheitsfunktion.auftrag_konto = tan_data.data[i + 16] + sicherheitsfunktion.challange_class_req = tan_data.data[i + 17]?.toUpperCase() == 'J' + sicherheitsfunktion.challange_structured = tan_data.data[i + 18]?.toUpperCase() == 'J' + sicherheitsfunktion.initialisierungs_mod = tan_data.data[i + 19] + sicherheitsfunktion.bez_tan_med_req = tan_data.data[i + 20] + sicherheitsfunktion.anz_supported_tan_vers = tan_data.data[i + 21] + // sicherheitsfunktion.challange_value_req = tan_data.data[i+14].toUpperCase()=="J"; + + sicherheitsfunktion.HITAN_vers = HITANS.vers; + me.bpd.tan.tan_verfahren[sicherheitsfunktion.code] = sicherheitsfunktion + } + } else if (HITANS.vers == 6) { + for (var i = 3; i < tan_data.data.length; i+=21) { + var sicherheitsfunktion = {}; + sicherheitsfunktion.code = tan_data.data[i]; + sicherheitsfunktion.one_two_step_vers = tan_data.data[i + 1]; // "1": Einschrittverfahren, "2": Zweischritt + sicherheitsfunktion.tech_id = tan_data.data[i + 2]; + sicherheitsfunktion.zka_tan_verfahren = tan_data.data[i + 3]; + sicherheitsfunktion.vers_zka_tan_verf = tan_data.data[i + 4]; + sicherheitsfunktion.desc = tan_data.data[i + 5]; + sicherheitsfunktion.max_len_tan = tan_data.data[i + 6]; + sicherheitsfunktion.tan_alphanum = tan_data.data[i + 7] === '2'; + sicherheitsfunktion.txt_rueckwert = tan_data.data[i + 8]; + sicherheitsfunktion.max_len_rueckwert = tan_data.data[i + 9]; + sicherheitsfunktion.multi_tan = tan_data.data[i + 10]?.toUpperCase() === 'J'; + sicherheitsfunktion.tan_zeit_diabez = tan_data.data[i + 11]; + sicherheitsfunktion.auftragsstorno = tan_data.data[i + 12]?.toUpperCase() === 'J'; + sicherheitsfunktion.sms_abu_konto_req = tan_data.data[i + 13]; + sicherheitsfunktion.auftrag_konto = tan_data.data[i + 14]; + sicherheitsfunktion.challange_class_req = tan_data.data[i + 15]?.toUpperCase() === 'J'; + sicherheitsfunktion.challange_structured = tan_data.data[i + 16]?.toUpperCase() === 'J'; + sicherheitsfunktion.initialisierungs_mod = tan_data.data[i + 17]; + sicherheitsfunktion.bez_tan_med_req = tan_data.data[i + 18]; + sicherheitsfunktion.antwort_HHD_UC_req = tan_data.data[i + 19]?.toUpperCase() === 'J'; + sicherheitsfunktion.anz_supported_tan_vers = tan_data.data[i + 20]; + + sicherheitsfunktion.HITAN_vers = HITANS.vers; + me.bpd.tan.tan_verfahren[sicherheitsfunktion.code] = sicherheitsfunktion + } + } else if (HITANS.vers == 7) { + for (var i = 3; i < tan_data.data.length; i+=26) { + var sicherheitsfunktion = {}; + sicherheitsfunktion.code = tan_data.data[i]; + sicherheitsfunktion.one_two_step_vers = tan_data.data[i + 1]; // "1": Einschrittverfahren, "2": Zweischritt + sicherheitsfunktion.tech_id = tan_data.data[i + 2]; + sicherheitsfunktion.zka_tan_verfahren = tan_data.data[i + 3]; + sicherheitsfunktion.vers_zka_tan_verf = tan_data.data[i + 4]; + sicherheitsfunktion.desc = tan_data.data[i + 5]; + sicherheitsfunktion.max_len_tan = tan_data.data[i + 6]; + sicherheitsfunktion.tan_alphanum = tan_data.data[i + 7] === '2'; + sicherheitsfunktion.txt_rueckwert = tan_data.data[i + 8]; + sicherheitsfunktion.max_len_rueckwert = tan_data.data[i + 9]; + sicherheitsfunktion.multi_tan = tan_data.data[i + 10]?.toUpperCase() === 'J'; + sicherheitsfunktion.tan_zeit_diabez = tan_data.data[i + 11]; + sicherheitsfunktion.auftragsstorno = tan_data.data[i + 12]?.toUpperCase() === 'J'; + sicherheitsfunktion.sms_abu_konto_req = tan_data.data[i + 13]; + sicherheitsfunktion.auftrag_konto = tan_data.data[i + 14]; + sicherheitsfunktion.challange_class_req = tan_data.data[i + 15]?.toUpperCase() === 'J'; + sicherheitsfunktion.challange_structured = tan_data.data[i + 16]?.toUpperCase() === 'J'; + sicherheitsfunktion.initialisierungs_mod = tan_data.data[i + 17]; + sicherheitsfunktion.bez_tan_med_req = tan_data.data[i + 18]; + sicherheitsfunktion.antwort_HHD_UC_req = tan_data.data[i + 19]?.toUpperCase() === 'J'; + sicherheitsfunktion.anz_supported_tan_vers = tan_data.data[i + 20]; + sicherheitsfunktion.max_anz_statusabfr = tan_data.data[i + 21]; + sicherheitsfunktion.wait_first_statusabfr = tan_data.data[i + 22]; + sicherheitsfunktion.wait_next_statusabfr = tan_data.data[i + 23]; + sicherheitsfunktion.manual_confirm = tan_data.data[i + 24]; + sicherheitsfunktion.auto_statusabfr_erlaubt = tan_data.data[i + 25]?.toUpperCase() === 'J'; + + sicherheitsfunktion.HITAN_vers = HITANS.vers; + me.bpd.tan.tan_verfahren[sicherheitsfunktion.code] = sicherheitsfunktion + } } } + for (const [code, sicherheitsfunktion] of Object.entries(me.bpd.tan.tan_verfahren)){ + me.log.gv.info({ + gv: 'HITANS' + }, `HITANS entry ${code}: '${sicherheitsfunktion.desc}', v${sicherheitsfunktion.HITAN_vers}`); + } } catch (ee) { me.log.gv.error(ee, { gv: 'HITANS' - }, 'Error while analyse HITANS') + }, `Error while analyse HITANS: ${ee.toString()}`); } // 6. Analysiere UPD try { @@ -583,7 +668,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) } catch (ee) { me.log.gv.error(ee, { gv: 'HIUPA' - }, 'Error while analyse UPD') + }, `Error while analyse UPD: ${ee.toString()}`) } // 7. Analysiere Verfügbare Tan Verfahren try { @@ -621,7 +706,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) } catch (ee) { me.log.gv.error(ee, { gv: 'HKVVB' - }, 'Error while analyse HKVVB result Tan Verfahren') + }, 'Error while analyse HKVVB gv params') } try { cb(error, recvMsg, has_neu_url) @@ -682,7 +767,29 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) hirmsg: HIRMG }, 'Error while analyse HKVVB Response Wrong HIRMG response code') try { - cb('Fehlerhafter Rückmeldungscode: ' + (HIRMG === null ? 'keiner' : HIRMG.getEl(1).getEl(3)), recvMsg, false) + const segMsgs = recvMsg.selectSegByName("HIRMS"); + const segMsgStr = segMsgs.map(rseg => { + const belongs_to = rseg.bez; + const segFor = msg.segments[belongs_to - 1]; + const segForStr = !!segFor ? segFor.name : "[Segment " + belongs_to + "]"; + const msgStr = rseg.store.data.map(data => { + const msgCode = data.data[0]; + const msgText = data.data[2]; + return msgCode + ": " + msgText; + }).join(", "); + return "Antworten auf "+segForStr+": ("+msgStr+")"; + }).join(", "); + const globMsgStr = !!HIRMG + ? HIRMG.store.data.map(data => { + const msgCode = data.data[0]; + const msgText = data.data[2]; + return msgCode + ": " + msgText; + }).join(", ") + : '(HIRMG nicht gefunden)'; + const errMsg = "Antwort des Bankservers: "+ globMsgStr + + (segMsgStr.length > 0 ? ", "+segMsgStr : ""); + + cb(errMsg, recvMsg, false); } catch (cb_error) { me.log.gv.error(cb_error, { gv: 'HKVVB' @@ -1147,6 +1254,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) // Im Step 1 und 2 bleiben keine Verbindungen erhalten // Diese Verbindung auf jeden Fall beenden var neu_url = me.bpd.url + var neu_tan_verfahren = me.bpd.tan.tan_verfahren var neu_sig_method = me.upd.availible_tan_verfahren[0] me.bpd = original_bpd.clone() me.upd = original_upd.clone() @@ -1166,6 +1274,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) }) me.clear() me.bpd.url = neu_url + me.bpd.tan.tan_verfahren = neu_tan_verfahren me.upd.availible_tan_verfahren[0] = neu_sig_method me.sys_id = orig_sys_id me.last_signatur_id = orig_last_sig @@ -1300,7 +1409,7 @@ var FinTSClient = function (in_blz, in_kunden_id, in_pin, in_bankenlist, logger) } var txt = msg.transformForSend() me.debugLogMsg(txt, true) - var post_data = new Buffer(txt).toString('base64') + var post_data = encoding.convert(txt, 'ISO-8859-1').toString('base64') var u = url.parse(me.bpd.url) var options = { hostname: u.hostname, diff --git a/lib/MTParser.js b/lib/MTParser.js index 57dbed0..0c4bf39 100644 --- a/lib/MTParser.js +++ b/lib/MTParser.js @@ -66,7 +66,7 @@ module.exports = function () { parser.nextPos() parser.nextPos() // Für Feld Mehrfach - while (parser.hasNext() && !(parser.getCurrentChar() == ':' || parser.getCurrentChar() == '-' || parser.getCurrentChar() == '@')) { + while (parser.hasNext() && !(parser.getCurrentChar() == ':' || (parser.getCurrentChar() == '-' && parser.data[parser.cur_pos + 1] == '\r' && parser.data[parser.cur_pos + 2] == '\n') || parser.getCurrentChar() == '@')) { parser.setMarkerWithCurrentPos('start') parser.gotoNextValidString(['\r\n', '@@']) val += parser.getTextFromMarkerToCurrentPos('start') @@ -203,6 +203,7 @@ module.exports = function () { if (satz.is_verwendungszweck_object) { satz.verwendungszweck = {} satz.verwendungszweck.text = '' + satz.verwendungszweck.name_kontrahent = '' var p = new Parser(raw_verwen_zweck) p.gotoNextValidChar('?') while (p.hasNext()) {