package org.digidoc4j.testutils;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.bouncycastle.util.encoders.Hex;
/**
* This utility simplifies debugging by decoding HTTP traffic logs in the
* DigiDoc4J log file. For example, this is useful for decoding OCSP requests
* and responses as logged by DigiDoc4J (internally, DigiDoc4J uses the "Wire"
* class from Apache HTTP Client for encoding).
*/
public class ApacheWireDecoder {
public static void main(String[] args) {
String input;
if(args.length == 0) {
input =
"0[0x82][0x2]U[\\n]\r\n" +
"[0x1][0x0][0xa0][0x82][0x2]N0[0x82][0x2]J[0x6][0x9]+[0x6][0x1][0x5][0x5][0x7]0[0x1][0x1][0x4][0x82][0x2];0[0x82][0x2]70[0x82][0x1][0x1f][0xa1][0x81][0x86]0[0x81][0x83]1[0xb]0[0x9][0x6][0x3]U[0x4][0x6][0x13][0x2]EE1\"0 [0x6][0x3]U[0x4][\\n]\r\n" +
"[0xc][0x19]AS Sertifitseerimiskeskus1[\\r]0[0xb][0x6][0x3]U[0x4][0xb][0xc][0x4]OCSP1'0%[0x6][0x3]U[0x4][0x3][0xc][0x1e]TEST of SK OCSP RESPONDER 20111[0x18]0[0x16][0x6][0x9]*[0x86]H[0x86][0xf7][\\r][0x1][0x9][0x1][0x16][0x9]pki@sk.ee[0x18][0xf]20150216144450Z0`0^0I0[0x9][0x6][0x5]+[0xe][0x3][0x2][0x1a][0x5][0x0][0x4][0x14]S=;[0xc8][0xf5][0xb1][\\n]\r\n" +
"[0xec][0xc3]|[0xb6]gW[0xbf][0xd9][0x98][0xae][0x93][0x3][0x89][0x4][0x14][0x12][0xf2]Z>[0xea]V[0x1c][0xbf][0xcd][0x6][0xac][0xf1][0xf1]%[0xc9][0xa9]K[0xd4][0x14][0x99][0x2][0x10]$[0xaf][0xec][0xeb][0x12]h[0xd0][0x2]T[0x17][0xf7][0x86][0xed]o[0x1]Y[0x80][0x0][0x18][0xf]20150216144450Z[0xa1]!0[0x1f]0[0x1d][0x6][0x9]+[0x6][0x1][0x5][0x5][0x7]0[0x1][0x2][0x1][0x1][0xff][0x4][\\r]14240978906440[\\r][0x6][0x9]*[0x86]H[0x86][0xf7][\\r][0x1][0x1][0x5][0x5][0x0][0x3][0x82][0x1][0x1][0x0][0xa0][0xff][0xb4][0x8e][0x82][0x12][0xc]!by[0xf9][0x8a][0xbb]@B*[0x0]cU[0xe9]1[0x18][0xb7]h\\n[0xb2][0x1e]4[0x80]'QK[0xc0][0xf2][0x98][0x8f][0xf8][0xf0]H[0x9f]_P[0xac][0x92]{[0x1d][0xc6]([0xcd][0xfe][0x1][0xc1]d[0xbc]l[0xdb][0xc0]9[0xe6]F[0xf3][0x92]8[0xeb][0xd5][0xdc]Y+[0xc2][0xe0][0x8d]p[0x15]Yo[0xe][0xed]be[0xa9]6nd[0xed]JP[0xe8]f[0x1c][0xe1][0x99]:[0xed][0xa5]<xU[0xd7]P[0x9a][0x17][0xb3][0xe6];-[0x85][0xf3]p[0xf6][0xd7][0xee]k[0xb5][0x5]e:K$[0xaf]G[0xe5]H[0xb1][0x1f]%[0xa0]Z[0x11]1y'[\\r]%[0x95][0xaf][0x9c]j>[0x18]Pw[0xc1][0xbe][0xc4][0xe8]BT7X[0xce]fo[0xfd][0x15][0x7][0x18]Ta$A[0x97]L[0x16][0x5]y[0xf][0xc4][0xf3]pJ|~[0xa2][0xc3][0x90][0x8b][0xe5][0xe]f[0xa0][0x92]d*[0xb2][0x8][0xb8][0xc3][0xad][0xa5][0xb6][0x84]>[0x82][0xa3][0x81][0x13]#[0xd7][0x98][0xc7]{N a7[0xc3]R'gt[0x9][0x97][0xd1][0xb2]'\\[0x9b][0x12][0xb6]I[0xea]Uq[0xd0]Q>![0xfc][0x8a][0x99][0x2]:[0xed][0xdf]Eo[0x14]dd[0x9a] b[0xbc][0xb4]BbX[0x84]bS[0xc2][0xca]\r\n" +
"";
System.out.println("No input provided on the command line, using the following sample input:");
System.out.println();
System.out.println(input);
System.out.println();
System.out.println("Corresponding output is:");
System.out.println();
} else if(args.length == 1) {
input = args[0];
} else {
throw new IllegalArgumentException("Please pack the entire input into one command line argument, as whitspace is important in the input");
}
ApacheWireDecoder decoder = new ApacheWireDecoder();
System.out.println(Hex.toHexString(decoder.decode(input)));
}
public byte[] decode(String input) {
input = removeUnescapedNewlines(input);
List<Byte> result = new ArrayList<>();
for (int pos = 0; pos < input.length(); ) {
int charactersConsumed = readNextByte(input, pos, result);
pos += charactersConsumed;
}
return ArrayUtils.toPrimitive(result.toArray(new Byte[0]));
}
protected int readNextByte(String input, int pos, List<Byte> result) {
if (input.charAt(pos) == '[') {
return readEscapedByte(input, pos, result);
}
return readPlainByte(input, pos, result);
}
protected int readPlainByte(String input, int pos, List<Byte> result) {
result.add((byte) input.charAt(pos));
return 1;
}
protected int readEscapedByte(String input, int pos, List<Byte> result) {
String bracketContents = extractBracketContents(input, pos);
return readEscapedByteFromBracketContents(bracketContents, result);
}
protected String extractBracketContents(String input, int openingBracket) {
int closingBracket = input.indexOf(']', openingBracket);
if(closingBracket == -1) {
// This is not an escape sequence, but a regular opening bracket
return "";
}
return input.substring(openingBracket + 1, closingBracket);
}
protected int readEscapedByteFromBracketContents(String bracketContents, List<Byte> result) {
if (bracketContents.equals("\\r")) {
result.add((byte) '\r');
return bracketContents.length() + 2;
} else if (bracketContents.equals("\\n")) {
result.add((byte) '\n');
return bracketContents.length() + 2;
} else if (bracketContents.startsWith("0x")) {
result.add(decodeHexByte(bracketContents));
return bracketContents.length() + 2;
} else {
// Unfortunately this encoding is nondeterministic: the opening bracket "["
// can either mean the start of an escape sequence, or it can simply represent
// the bracket itself. Once we've reached here, we know it is not an escape
// sequence, so it must be a plain byte.
return readPlainByte("[", 0, result);
}
}
protected byte decodeHexByte(String byteInHex) {
String characterHexCode = byteInHex.substring("0x".length());
if(characterHexCode.length() == 1) {
characterHexCode = "0" + characterHexCode;
}
return Hex.decode(characterHexCode)[0];
}
protected String removeUnescapedNewlines(String input) {
return input.replaceAll("[\\r\\n]", "");
}
}