001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.iso.packager; 020 021import org.jpos.iso.*; 022import org.jpos.util.LogEvent; 023import org.jpos.util.Logger; 024import org.jpos.util.SimpleLogSource; 025 026import java.io.ByteArrayOutputStream; 027import java.io.IOException; 028import java.io.InputStream; 029 030/** 031 * ISO-8583 packager for the VISA-1 message format. 032 * @author apr@cs.com.uy 033 * @version $Id$ 034 * @see ISOPackager 035 * @see ISOBasePackager 036 * @see ISOComponent 037 */ 038@SuppressWarnings("unchecked") 039public class VISA1Packager 040 extends SimpleLogSource implements ISOPackager, VISA1ResponseFilter 041{ 042 /** ASCII Field Separator (0x1C) used between VISA1 sub-fields. */ 043 public static final byte[] FS = { (byte)'\034' }; 044 int[] sequence; 045 int respField; 046 String badResultCode; 047 String okPattern; 048 VISA1ResponseFilter filter; 049 050 /** 051 * Constructs a VISA1Packager with the given field sequence and response-handling rules. 052 * 053 * @param sequence array of fields that go to VISA1 request 054 * @param respField where to put response 055 * @param badResultCode (i.e. "05") 056 * @param okPattern (i.e. "AUT. ") 057 */ 058 public VISA1Packager 059 (int[] sequence, int respField, String badResultCode, String okPattern) 060 { 061 super(); 062 this.sequence = sequence; 063 this.respField = respField; 064 this.badResultCode = badResultCode; 065 this.okPattern = okPattern; 066 setVISA1ResponseFilter (this); 067 } 068 /** 069 * Replaces the VISA1 response filter used to inspect inbound responses. 070 * 071 * @param filter the new response filter 072 */ 073 public void setVISA1ResponseFilter (VISA1ResponseFilter filter) { 074 this.filter = filter; 075 } 076 077 /** 078 * Handles the VISA1-specific encoding of field 35 (track-2 data with entry mode). 079 * 080 * @param m message being packed 081 * @param bout destination byte stream 082 * @return number of bytes written 083 * @throws ISOException on field-access failure 084 * @throws IOException if writing to {@code bout} fails 085 */ 086 protected int handleSpecialField35 (ISOMsg m, ByteArrayOutputStream bout) 087 throws ISOException, IOException 088 { 089 int len = 0; 090 byte[] entryMode = new byte[1]; 091 if (m.hasField (35)) { 092 entryMode[0] = (byte) '\001'; 093 byte[] value = m.getString(35).getBytes(); 094 bout.write(entryMode); 095 bout.write(value); 096 bout.write(FS); 097 len += value.length+2; 098 } else if (m.hasField (2) && m.hasField (14)) { 099 entryMode[0] = (byte) '\000'; 100 String simulatedTrack2 = m.getString(2) + "=" + m.getString(14); 101 bout.write(entryMode); 102 bout.write(simulatedTrack2.getBytes()); 103 bout.write(FS); 104 len += simulatedTrack2.length()+2; 105 } 106 return len; 107 } 108 109 @Override 110 public byte[] pack (ISOComponent c) throws ISOException 111 { 112 LogEvent evt = new LogEvent (this, "pack"); 113 try (ByteArrayOutputStream bout = new ByteArrayOutputStream(100)) 114 { 115 if (!(c instanceof ISOMsg)) 116 throw new ISOException 117 ("Can't call VISA1 packager on non ISOMsg"); 118 119 ISOMsg m = (ISOMsg) c; 120 121 for (int i=0; i<sequence.length; i++) { 122 int fld = sequence[i]; 123 if (fld == 35) 124 handleSpecialField35(m, bout); 125 else if (m.hasField(fld)) { 126 byte[] value; 127 if (fld == 4) { 128 long amt = Long.valueOf(m.getString(4)); 129 value = ISOUtil.formatAmount (amt,12).trim().getBytes(); 130 } 131 else 132 value = m.getString(fld).getBytes(); 133 bout.write(value); 134 if (i < sequence.length-1) { 135 bout.write(FS); 136 } 137 } 138 } 139 140 byte[] d = bout.toByteArray(); 141 if (logger != null) // save a few CPU cycle if no logger available 142 evt.addMessage (ISOUtil.dumpString (d)); 143 return d; 144 } catch (ISOException ex) { 145 evt.addMessage(ex); 146 throw ex; 147 } catch (IOException ex) { 148 evt.addMessage(ex); 149 throw new ISOException(ex); 150 } finally { 151 Logger.log(evt); 152 } 153 } 154 155 public String guessAutNumber (String s) { 156 StringBuilder buf = new StringBuilder(); 157 for (int i=0; i<s.length(); i++) 158 if (Character.isDigit(s.charAt(i))) 159 buf.append (s.charAt(i)); 160 if (buf.length() == 0) 161 return null; 162 163 while (buf.length() > 6) 164 buf.deleteCharAt(0); 165 while (buf.length() < 6) 166 buf.insert(0, "0"); 167 168 return buf.toString(); 169 } 170 171 @Override 172 public int unpack (ISOComponent m, byte[] b) throws ISOException 173 { 174 String response = new String (b); 175 m.set (new ISOField (respField, response)); 176 m.set (new ISOField (39, badResultCode)); 177 if (response.startsWith (okPattern)) { 178 String autNumber = filter.guessAutNumber (response); 179 if (autNumber != null) { 180 m.set (new ISOField (39, "00")); 181 m.set (new ISOField (38, autNumber)); 182 } 183 } 184 return b.length; 185 } 186 public void unpack (ISOComponent m, InputStream in) throws ISOException { 187 throw new ISOException ("not implemented"); 188 } 189 public String getFieldDescription(ISOComponent m, int fldNumber) 190 { 191 return "VISA 1 fld "+fldNumber; 192 } 193 public String getDescription () { 194 return getClass().getName(); 195 } 196 public ISOMsg createISOMsg() { 197 return new ISOMsg(); 198 } 199}