/* * otr4j, the open source java otr library. * * Distributable under LGPL license. See terms of license at gnu.org. */ package net.java.otr4j.session; import java.net.ProtocolException; /** * * @author Felix Eckhofer * */ public final class OtrAssembler { public OtrAssembler() { discard(); } /** * Accumulated fragment thus far. */ private StringBuffer fragment; /** * Number of last fragment received. This variable must be able to store an * unsigned short value. */ private int fragmentCur; /** * Total number of fragments in message. This variable must be able to store * an unsigned short value. */ private int fragmentMax; private static final String HEAD_FRAGMENT_V2 = "?OTR,"; private static final String HEAD_FRAGMENT_V3 = "?OTR|"; /** * Appends a message fragment to the internal buffer and returns the full * message if msgText was no fragmented message or all the fragments have * been combined. Returns null, if there are fragments pending or an invalid * fragment was received. <p> A fragmented OTR message looks like this: (V2) * ?OTR,k,n,piece-k, or (V3) * ?OTR|sender_instance|receiver_instance,k,n,piece-k, * * @param msgText Message to be processed. * * @return String with the accumulated message or null if the message was * incomplete or malformed */ public String accumulate(String msgText) throws ProtocolException { // if it's a fragment, remove everything before "k,n,piece-k" if (msgText.startsWith(HEAD_FRAGMENT_V2)) { // v2 msgText = msgText.substring(HEAD_FRAGMENT_V2.length()); } else if (msgText.startsWith(HEAD_FRAGMENT_V3)) { // v msgText = msgText.substring(HEAD_FRAGMENT_V3.length()); // break away the v2 part String[] instancePart = msgText.split(",", 2); // split the two instance ids String[] instances = instancePart[0].split("\\|", 2); if (instancePart.length != 2 || instances.length != 2) { discard(); throw new ProtocolException(); } int receiverInstance; try { receiverInstance = Integer.parseInt(instances[1], 16); } catch (NumberFormatException e) { discard(); throw new ProtocolException(); } // continue with v2 part of fragment msgText = instancePart[1]; } else { // not a fragmented message discard(); return msgText; } String[] params = msgText.split(",", 4); int k, n; try { k = Integer.parseInt(params[0]); n = Integer.parseInt(params[1]); } catch (NumberFormatException e) { discard(); throw new ProtocolException(); } catch (ArrayIndexOutOfBoundsException e) { discard(); throw new ProtocolException(); } if (k == 0 || n == 0 || k > n || params.length != 4 || params[3].length() != 0) { discard(); throw new ProtocolException(); } msgText = params[2]; if (k == 1) { // first fragment discard(); fragmentCur = k; fragmentMax = n; fragment.append(msgText); } else if (n == fragmentMax && k == fragmentCur + 1) { // consecutive fragment fragmentCur++; fragment.append(msgText); } else { // out-of-order fragment discard(); throw new ProtocolException(); } if (n == k && n > 0) { String result = fragment.toString(); discard(); return result; } else { return null; // incomplete fragment } } /** * Discard current fragment buffer and reset the counters. */ public void discard() { fragment = new StringBuffer(); fragmentCur = 0; fragmentMax = 0; } }