import java.io.*;
import java.util.regex.*;
import nice.getopt;
let Pattern shortMessageFieldRegex = Pattern.compile(
"\\{short_message \"(.+)\" \\}" );
let Pattern messagePayloadTlvRegex = Pattern.compile(
"\\{tlv \\{t 0x424\\} \\{l 0x([0-9A-Fa-f]+)\\} \\{v ([^}]+)\\}" );
let Pattern additionalDataRegex = Pattern.compile(
"^LOG \\[([^]]+)\\].*\\{source_addr \"([^\"]+)\" \\}.*\\{dest_addr \"([^\"]+)\" }" );
int getByte( byte v ) = int(v) & 0xff;
byte[]
stringImageToBinary( String line )
{
String unescapeString( String what )
{
(char, int) specialCharHandler( char ch, int index, String where )
{
char charFromHex( String hex ) = char(Integer.parseInt( hex, 16 ));
if( 'n' == ch )
return ('\n', index);
else if( 'r' == ch )
return ('\r', index);
else if( 't' == ch )
return ('\t', index);
else if( 'x' == ch )
return ( charFromHex(
where.substring( index + 1, index + 3 ) ), index + 2 );
else
return (ch, index);
}
let result = new StringBuilder( what.length() );
for( var i = 0; i != what.length(); ++i )
{
var c = what.charAt( i );
if( '\\' == c )
(c, i) = specialCharHandler( what.charAt( i + 1 ), i + 1, what );
result.append( c );
}
return result.toString();
}
return unescapeString( unescapeString( line ) ).getBytes();
}
byte[]
hexImageToBinary( String line )
{
let hexSubstrings = line.split( " " );
let result = new byte[ hexSubstrings.length ];
for( i : 0..(hexSubstrings.length - 1) )
result[ i ] = byte(Integer.parseInt( hexSubstrings[ i ], 16 ));
return result;
}
?(byte[])
tryExtractShortMessageBody( String line )
{
let shortMessageFieldMatch = shortMessageFieldRegex.matcher( line );
if( shortMessageFieldMatch.find() )
return stringImageToBinary( shortMessageFieldMatch.group( 1 ) );
let messagePayloadMatch = messagePayloadTlvRegex.matcher( line );
if( messagePayloadMatch.find() )
return hexImageToBinary( messagePayloadMatch.group( 2 ) );
return null;
}
String
formatHelper( PrintStream -> void formatter )
{
let buffer = new ByteArrayOutputStream();
let printer = new PrintStream( buffer );
formatter( printer );
return buffer.toString();
}
String
printf( String formatString, Object[] args ) =
formatHelper( PrintStream printer => printer.printf( formatString, args ) );
String
makeHexTextRepresentation( byte[] what )
{
return formatHelper( PrintStream printer =>
{
for( i : 0..(what.length - 1) )
{
let v = getByte( what[ i ] );
printer.printf( ( 0 == i ? "%02X" : " %02X" ), [ v ] );
}
}
);
}
interface AnalyzingResult
{
boolean isGoodMessage();
}
class HeaderElement
{
byte id;
byte[] data;
}
class SuccessfulResult implements AnalyzingResult
{
HeaderElement[] headerElements = [];
byte[] message = [];
isGoodMessage() = true;
toString()
{
return formatHelper( PrintStream printer =>
{
for( element : headerElements )
printer.printf( "Element: Id: %X, Data: %s; ",
[ element.id,
makeHexTextRepresentation( element.data ) ] );
printer.printf( "Message: %s",
[ makeHexTextRepresentation( message ) ] );
}
);
}
}
class BadResult implements AnalyzingResult
{
String reason;
byte[] fullBody;
isGoodMessage() = false;
toString() =
"What: " reason "; Full body: " makeHexTextRepresentation( fullBody );
}
BadResult
makeBadResult( String reason, byte[] fullBody ) = new BadResult(
reason: reason, fullBody: fullBody );
AnalyzingResult
tryHandleHeadersAndBody( byte[] binImage )
{
if( 0 == binImage.length )
return new SuccessfulResult();
int headerLength = getByte( binImage[ 0 ] );
if( 0 == headerLength )
return makeBadResult( "Zero header length", binImage );
if( headerLength >= binImage.length )
return makeBadResult(
printf(
"Header length too big! Length: %d, Body length: %d",
[ headerLength, binImage.length ] ),
binImage );
List< HeaderElement > elements = new ArrayList();
for( int i = 1; i + 2 < headerLength; )
{
byte elementId = binImage[ i++ ];
int elementSize = getByte( binImage[ i++ ] );
if( elementSize <= headerLength + 1 - i )
elements.add(
new HeaderElement(
id: elementId,
data: binImage.slice( i, i + elementSize - 1 ) ) );
else
return makeBadResult(
printf(
"Invalid element size! Position=%d, " +
"HeaderLength=%d, ElementSize:%d",
[ i, headerLength, elementSize ] ),
binImage );
i += elementSize;
}
byte[] message = binImage.slice( 1 + headerLength );
for( b : message )
if( 0 != ( b & 0x80 ) )
return makeBadResult(
printf(
"Byte with non-zero highest bit in message! Byte: {0:X}",
[ b ] ),
binImage );
return new SuccessfulResult(
headerElements: elements.toArray(),
message: message );
}
void
showBadMessage( String line, AnalyzingResult analyzingResult )
{
let additionalDataMatch = additionalDataRegex.matcher( line );
additionalDataMatch.find;
println(
printf( "ERROR -- at: [%s]; from '%s'; to '%s'; ",
[ additionalDataMatch.group( 1 ),
additionalDataMatch.group( 2 ),
additionalDataMatch.group( 3 ) ] ) +
analyzingResult );
}
void
parseLine( String line, boolean showGoodMessages )
{
if( line.length() <= 2 )
return;
let binImage = tryExtractShortMessageBody( line );
if( binImage != null )
{
let analyzingResult = tryHandleHeadersAndBody( binImage );
if( analyzingResult.isGoodMessage() )
{
if( showGoodMessages )
println( "OK: " analyzingResult );
}
else
showBadMessage( line, analyzingResult );
}
else
println( stderr, "nothing found in " line );
}
void
readLines( boolean showGoodMessages )
{
let stdin = new BufferedReader( new InputStreamReader( System.in ) );
var line = readLine( stdin );
while( line != null )
{
parseLine( line, showGoodMessages );
line = readLine( stdin );
}
}
class Options
{
boolean showGoodMessages = false;
}
Options
processArgs( String[] args )
{
let result = new Options();
let cmdLineOptions = [
option(
"show-good",
"show good messages",
() => result.showGoodMessages = true )
];
parse( "decode_7bit_bodies", args, cmdLineOptions );
return result;
}
void
main( String[] args )
{
try
{
let options = processArgs( args );
readLines( options.showGoodMessages );
}
catch( Exception x )
{
println( stderr, "*** Exception: " x );
x.printStackTrace();
}
}
// vim:ft=nice:ts=2:sw=2:sts=2:expandtab