Skip to content

Commit

Permalink
Merge pull request jpos#252 from demsey/tlv-undefined
Browse files Browse the repository at this point in the history
Handle undefined tags and prevention against an infinite loop
  • Loading branch information
ar authored Oct 4, 2019
2 parents c24c90a + 0a2256b commit f65dbd9
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.IntStream;

/**
*
Expand All @@ -54,10 +56,37 @@ public int getFieldNumber() {
return fieldId;
}

private Set<Integer> selectFields() {
Map<Integer, ISOFieldPackager> fldMap = new ConcurrentHashMap<>();
IntStream.range(getFirstField(), fld.length)
.filter(i -> fld[i] != null)
.forEach(i -> fldMap.put(i, fld[i]));

return fldMap.keySet();
}

private int unpackTag(Set<Integer> flds, ISOComponent m, byte[] b, int consumed)
throws ISOException {

for (Integer i : flds) {
ISOFieldPackager fpack = fld[i];
ISOComponent c = fpack.createComponent(i);
int unpacked = fpack.unpack(c, b, consumed);
if (unpacked > 0) {
m.set(c);
// for optimize subsequent iterations
flds.remove(i);
return unpacked;
}
}

// undefined tag - all defined fields was tested
return 0;
}

@Override
public int unpack(ISOComponent m, byte[] b) throws ISOException {
LogEvent evt = new LogEvent(this, "unpack");
ISOFieldPackager[] fields = Arrays.copyOf(fld, fld.length);
try {
if (m.getComposite() != m)
throw new ISOException("Can't call packager on non Composite");
Expand All @@ -67,20 +96,12 @@ public int unpack(ISOComponent m, byte[] b) throws ISOException {
evt.addMessage(ISOUtil.hexString(b));

int consumed = 0;
int maxField = fld.length;
while (consumed < b.length) {
for (int i = getFirstField(); i < maxField && consumed < b.length; i++) {
if (fields[i] != null) {
ISOComponent c = fields[i].createComponent(i);
int unpacked = fields[i].unpack(c, b, consumed);
consumed = consumed + unpacked;
if (unpacked > 0) {
if (!(fields[i] instanceof TaggedFieldPackagerBase))
fields[i] = null;
m.set(c);
}
}
}
Set<Integer> flds = selectFields();
for (int i : flds) {
if (consumed >= b.length)
break;

consumed += unpackTag(flds, m, b, consumed);
}
if (b.length != consumed) {
evt.addMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,111 +18,199 @@

package org.jpos.iso.packager;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;

import java.io.BufferedInputStream;

import org.jpos.iso.ISOField;
import org.jpos.iso.ISOMsg;
import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.jpos.iso.ISOPackager;
import org.jpos.iso.ISOUtil;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;

/**
*
*/
public class TaggedFieldPackagerBaseTest {

@Test
public void testPack() throws Exception {
String path = "build/resources/test/org/jpos/iso/packagers/";
GenericPackager genericPackager = new GenericPackager(new FileInputStream(path + "ISO93TLVPackager.xml"));
private static final Path PACKAGERS_LOCATION = FileSystems
.getDefault()
.getPath("build/resources/test/org/jpos/iso/packagers");

ISOMsg msg = new ISOMsg();
msg.setMTI("1100");
msg.set(new ISOField(2, "123456"));
private static final Path ISO83TLVPACKAGER = PACKAGERS_LOCATION.resolve("ISO93TLVPackager.xml");

private static final String MESSAGE_MTI1 = "1100";

/**
* The first bitmap with DE2, DE48 and DE60 set.
*/
private static final byte[] MESSAGE_BITMAP1 = ISOUtil.hex2byte("4000000000010010");

private static final String MESSAGE_VALUE1 = "06123456022A10748TagA1A30748TagA3012A100760TagA1";

/**
* Unsorted DE48 tags.
* <p>
* The A3 <i>(id 3)</i> before A1 <i>(id 1)</i>.
*/
private static final String MESSAGE_VALUE2 = "06123456022A30748TagA3A10748TagA1012A100760TagA1";

/**
* Undefined DE48 tag.
* <p>
* After A1 <i>(id 1)</i> followed by undefined in packager A2 <i>(id 2)</i>.
*/
private static final String MESSAGE_VALUE3 = "06123456022A10748TagA1A20748TagA2012A100760TagA1";

/**
* Undefined DE48 tag with followed tag.
* <p>
* Undefined in packager A2 <i>(id 2)</i> and following A3 <i>(id 3)</i>.
*/
private static final String MESSAGE_VALUE4 = "06123456022A20748TagA2A30748TagA3012A100760TagA1";

ISOMsg subFieldsContainer = new ISOMsg(48);
ISOField tlvField = new ISOField(1);
tlvField.setValue("48TagA1");
subFieldsContainer.set(tlvField);
private static final String REPR_MESSAGE1 = MESSAGE_MTI1 + toString(MESSAGE_BITMAP1) + MESSAGE_VALUE1;

/**
* Unsorted DE48 tags message representation.
*/
private static final String REPR_MESSAGE2 = MESSAGE_MTI1 + toString(MESSAGE_BITMAP1) + MESSAGE_VALUE2;

/**
* Undefined DE48 tag message representation.
*/
private static final String REPR_MESSAGE3 = MESSAGE_MTI1 + toString(MESSAGE_BITMAP1) + MESSAGE_VALUE3;

private static final String REPR_MESSAGE4 = MESSAGE_MTI1 + toString(MESSAGE_BITMAP1) + MESSAGE_VALUE4;

private static ISOPackager packager;

@BeforeAll
static void beforeClass() throws Throwable {
packager = new GenericPackager(new FileInputStream(ISO83TLVPACKAGER.toFile()));
}

private static String toString(byte[] b) {
return new String(b, ISOUtil.CHARSET);
}

private static byte[] toBytes(String str) {
return str.getBytes(ISOUtil.CHARSET);
}

ISOField tlvField2 = new ISOField(3);
tlvField2.setValue("48TagA3");
subFieldsContainer.set(tlvField2);
@Test
public void testPack() throws Exception {
ISOMsg msg = new ISOMsg(MESSAGE_MTI1);
msg.set(2, "123456");

msg.set(subFieldsContainer);
msg.set("48.1", "48TagA1");
msg.set("48.3", "48TagA3");

ISOMsg subFieldsContainer2 = new ISOMsg(60);
ISOField tlvField3 = new ISOField(1);
tlvField3.setValue("60TagA1");
subFieldsContainer2.set(tlvField3);
msg.set("60.1", "60TagA1");

msg.set(subFieldsContainer2);
msg.setPackager(packager);

msg.setHeader("HEADER ".getBytes());
msg.setPackager(genericPackager);
byte[] packed = msg.pack();
assertNotNull(packed);

FileOutputStream fos = new FileOutputStream(path + "ISO93TLVPackager.bin");
try {
fos.write(packed);
} finally {
fos.close();
}
String packedAscii = toString(packed);
assertAll(
() -> assertEquals(REPR_MESSAGE1.length(), packed.length),
() -> assertEquals(MESSAGE_MTI1, StringUtils.left(packedAscii, 4)),
() -> assertEquals(MESSAGE_VALUE1, StringUtils.right(packedAscii, 48))
);
}

@Test
public void testUnpack() throws Exception {
String path = "build/resources/test/org/jpos/iso/packagers/";
GenericPackager genericPackager = new GenericPackager(new FileInputStream(path + "ISO93TLVPackager.xml"));
ISOMsg msg = new ISOMsg();
packager.unpack(msg, toBytes(REPR_MESSAGE1));

assertAll(
() -> assertEquals("1100", msg.getString(0)),
() -> assertEquals("123456", msg.getString(2)),
() -> assertEquals("48TagA1", msg.getString("48.1")),
() -> assertEquals("48TagA3", msg.getString("48.3")),
() -> assertEquals("60TagA1", msg.getString("60.1"))
);
}

@Test
public void testUnpackUnsortedTags() throws Exception {
ISOMsg msg = new ISOMsg();
InputStream input = new BufferedInputStream(new FileInputStream(path + "ISO93TLVPackager.bin"));
try {
genericPackager.unpack(msg, input);
} finally {
input.close();
}
packager.unpack(msg, toBytes(REPR_MESSAGE2));

assertAll(
() -> assertEquals("1100", msg.getString(0)),
() -> assertEquals("123456", msg.getString(2)),
() -> assertEquals("48TagA1", msg.getString("48.1")),
() -> assertEquals("48TagA3", msg.getString("48.3")),
() -> assertEquals("60TagA1", msg.getString("60.1"))
);
}

@Test
public void testUnpackUndefinedTag() throws Exception {
ISOMsg msg = new ISOMsg();
packager.unpack(msg, toBytes(REPR_MESSAGE3));

assertAll(
() -> assertEquals("1100", msg.getString(0)),
() -> assertEquals("123456", msg.getString(2)),
() -> assertEquals("48TagA1", msg.getString("48.1")),
() -> assertNull(msg.getString("48.2")),
() -> assertEquals("60TagA1", msg.getString("60.1"))
);
}

assertEquals("1100", msg.getMTI());
assertEquals("48TagA1", ((ISOField) ((ISOMsg) msg.getComponent(48)).getComponent(1)).getValue());
@Disabled("Skipping undefined A2 is impossible in current solution so follwing A3 will be lost")
@Test
public void testUnpackUndefinedTagWithFollowing() throws Exception {
ISOMsg msg = new ISOMsg();
packager.unpack(msg, toBytes(REPR_MESSAGE4));

assertAll("Should unpack DE48.3 (A3) and any other standard elements",
() -> assertEquals("1100", msg.getString(0)),
() -> assertEquals("123456", msg.getString(2)),
() -> assertNull(msg.getString("48.2")),
() -> assertEquals("48TagA3", msg.getString("48.3")),
() -> assertEquals("60TagA1", msg.getString("60.1"))
);
}

public static class TagMapperImpl implements TagMapper {

private static Map<String, Integer> tagToNumberMap = new HashMap<String, Integer>();
private static Map<String, String> numberToTagMap = new HashMap<String, String>();
private static final Map<String, Integer> MAP_TAG_MUMBER = new HashMap<>();
private static final Map<String, String> MAP_NUMBER_TAG = new HashMap<>();

static {
tagToNumberMap.put("48.A1", 1);
numberToTagMap.put("48.1", "A1");

tagToNumberMap.put("48.A2", 2);
numberToTagMap.put("48.2", "A2");

tagToNumberMap.put("48.A3", 3);
numberToTagMap.put("48.3", "A3");

tagToNumberMap.put("60.A1", 1);
numberToTagMap.put("60.1", "A1");
}

public TagMapperImpl() {
MAP_TAG_MUMBER.put("48.A1", 1);
MAP_TAG_MUMBER.put("48.A2", 2);
MAP_TAG_MUMBER.put("48.A3", 3);
MAP_TAG_MUMBER.put("60.A1", 1);

MAP_NUMBER_TAG.put("48.1", "A1");
MAP_NUMBER_TAG.put("48.2", "A2");
MAP_NUMBER_TAG.put("48.3", "A3");
MAP_NUMBER_TAG.put("60.1", "A1");
}

@Override
public String getTagForField(int fieldNumber, int subFieldNumber) {
return numberToTagMap.get(fieldNumber + "." + subFieldNumber);
return MAP_NUMBER_TAG.get(fieldNumber + "." + subFieldNumber);
}

@Override
public Integer getFieldNumberForTag(int fieldNumber, String tag) {
return tagToNumberMap.get(fieldNumber + "." + tag);
return MAP_TAG_MUMBER.get(fieldNumber + "." + tag);
}
}

}
Binary file not shown.
Loading

0 comments on commit f65dbd9

Please sign in to comment.