All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.binaryfoo.decoders.TLVDecoder.kt Maven / Gradle / Ivy

There is a newer version: 0.1.8
Show newest version
package io.github.binaryfoo.decoders

import io.github.binaryfoo.DecodedData
import io.github.binaryfoo.Decoder
import io.github.binaryfoo.TagInfo
import io.github.binaryfoo.tlv.*
import java.util.*
import kotlin.collections.*

class TLVDecoder : Decoder {

    override fun decode(input: String, startIndexInBytes: Int, session: DecodeSession): List {
        try {
            return decode(input, session, startIndexInBytes).second
        } catch(e: TlvParseException) {
            val errorMessage = e.message ?: e.javaClass.simpleName
            if (session.tagRecognitionMode == CommonVendorErrorMode) {
                if (!e.resultsSoFar.filter(::hasCommonVendorErrorTag).isEmpty()) {
                    try {
                        val (tlvs, decoded) = decode(input, session, startIndexInBytes, CommonVendorErrorMode)
                        val tagErrors = HashSet(tlvs.filter(::hasCommonVendorErrorTag).map { it.tag.hexString }).toList().sorted()
                        val warning = DecodedData(null, "Warning", "This result is a second attempt ignoring the spec for these (often abused) tags: $tagErrors. The first attempt (following the the spec) produced an error: $errorMessage", 0, 0, category = "parse-warning")
                        return decoded + warning
                    } catch(e: TlvParseException) {
                    }
                }
            }
            val decoded = decodeTlvs(e.resultsSoFar, startIndexInBytes, session)
            val error = DecodedData(null, "Error", errorMessage, decoded.lastOrNull()?.endIndex ?: 0, input.length / 2, category = "parse-error")
            return decoded + error
        }
    }

    private fun decode(input: String, session: DecodeSession, startIndexInBytes: Int, mode: TagRecognitionMode = CompliantTagMode): Pair, List> {
        val tlvs = BerTlv.parseList(input.decodeAsHex(), true, mode)
        val decoded = decodeTlvs(tlvs, startIndexInBytes, session)
        return Pair(tlvs, decoded)
    }

    private fun decodeTlvs(list: List, startIndex: Int, session: DecodeSession): List {
        var currentStartIndex = startIndex
        val decodedItems = ArrayList()
        for (tlv in list) {
            val valueAsHexString = tlv.valueAsHexString
            val tag = tlv.tag
            val length = tlv.toBinary().size
            val contentEndIndex = currentStartIndex + length
            val compositeStartElementIndex = currentStartIndex + tlv.startIndexOfValue
            val tagMetaData = session.tagMetaData!!
            val decoded = if (tag.constructed) {
                DecodedData.fromTlv(tlv, tagMetaData, valueAsHexString, currentStartIndex, contentEndIndex, decodeTlvs(tlv.getChildren(), compositeStartElementIndex, session))
            } else {
                val tagInfo = tagMetaData.get(tag)
                DecodedData.fromTlv(tlv, tagMetaData, tagInfo.decodePrimitiveTlvValue(valueAsHexString), currentStartIndex, contentEndIndex, decodeOrBackDown(compositeStartElementIndex, tagInfo, valueAsHexString, session))
            }
            decodedItems.add(decoded)
            currentStartIndex += length
        }
        return decodedItems
    }

    private fun decodeOrBackDown(compositeStartElementIndex: Int, tagInfo: TagInfo, valueAsHexString: String, session: DecodeSession): List {
        try {
            return tagInfo.decoder.decode(valueAsHexString, compositeStartElementIndex, session)
        } catch(e: Exception) {
            return listOf(DecodedData(null, "Error: Failed parsing " + valueAsHexString, e.message ?:e.javaClass.simpleName, compositeStartElementIndex, compositeStartElementIndex + valueAsHexString.length /2, category = "parse-error"))
        }
    }

    override fun getMaxLength(): Int {
        return 10000
    }

    override fun validate(input: String?): String? {
        if (input == null || input.length < 2) {
            return "Value must be at least 2 characters"
        }
        if (input.length % 2 != 0) {
            return "Length must be a multiple of 2"
        }
        if (!ISOUtil.isValidHexString(input)) {
            return "Value must contain only the characters 0-9 and A-F"
        }
        return null
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy