mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-05 20:57:40 +00:00
GUACAMOLE-615: Correct parser calculation of element lengths.
This commit is contained in:
@@ -93,6 +93,22 @@ public class GuacamoleInstruction {
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given value to the provided StringBuilder as a Guacamole
|
||||
* instruction element, including length prefix.
|
||||
*
|
||||
* @param buff
|
||||
* The StringBuilder to append the element to.
|
||||
*
|
||||
* @param element
|
||||
* The string value of the element to append.
|
||||
*/
|
||||
private static void appendElement(StringBuilder buff, String element) {
|
||||
buff.append(element.codePointCount(0, element.length()));
|
||||
buff.append('.');
|
||||
buff.append(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this GuacamoleInstruction in the form it would be sent over the
|
||||
* Guacamole protocol.
|
||||
@@ -111,16 +127,12 @@ public class GuacamoleInstruction {
|
||||
StringBuilder buff = new StringBuilder();
|
||||
|
||||
// Write opcode
|
||||
buff.append(opcode.length());
|
||||
buff.append('.');
|
||||
buff.append(opcode);
|
||||
appendElement(buff, opcode);
|
||||
|
||||
// Write argument values
|
||||
for (String value : args) {
|
||||
buff.append(',');
|
||||
buff.append(value.length());
|
||||
buff.append('.');
|
||||
buff.append(value);
|
||||
appendElement(buff, value);
|
||||
}
|
||||
|
||||
// Write terminator
|
||||
|
@@ -21,7 +21,6 @@ package org.apache.guacamole.protocol;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
|
||||
@@ -87,10 +86,18 @@ public class GuacamoleParser implements Iterator<GuacamoleInstruction> {
|
||||
private State state = State.PARSING_LENGTH;
|
||||
|
||||
/**
|
||||
* The length of the current element, if known.
|
||||
* The length of the current element, if known, in Java characters. This
|
||||
* value may be adjusted as an element is parsed to take surrogates into
|
||||
* account.
|
||||
*/
|
||||
private int elementLength = 0;
|
||||
|
||||
/**
|
||||
* The length of the current element, if known, in Unicode codepoints. This
|
||||
* value will NOT change as an element is parsed.
|
||||
*/
|
||||
private int elementCodepoints;
|
||||
|
||||
/**
|
||||
* The number of elements currently parsed.
|
||||
*/
|
||||
@@ -104,13 +111,22 @@ public class GuacamoleParser implements Iterator<GuacamoleInstruction> {
|
||||
/**
|
||||
* Appends data from the given buffer to the current instruction.
|
||||
*
|
||||
* @param chunk The buffer containing the data to append.
|
||||
* @param offset The offset within the buffer where the data begins.
|
||||
* @param length The length of the data to append.
|
||||
* @return The number of characters appended, or 0 if complete instructions
|
||||
* have already been parsed and must be read via next() before
|
||||
* more data can be appended.
|
||||
* @throws GuacamoleException If an error occurs while parsing the new data.
|
||||
* @param chunk
|
||||
* The buffer containing the data to append.
|
||||
*
|
||||
* @param offset
|
||||
* The offset within the buffer where the data begins.
|
||||
*
|
||||
* @param length
|
||||
* The length of the data to append.
|
||||
*
|
||||
* @return
|
||||
* The number of characters appended, or 0 if complete instructions
|
||||
* have already been parsed and must be read via next() before more
|
||||
* data can be appended.
|
||||
*
|
||||
* @throws GuacamoleException
|
||||
* If an error occurs while parsing the new data.
|
||||
*/
|
||||
public int append(char chunk[], int offset, int length) throws GuacamoleException {
|
||||
|
||||
@@ -156,39 +172,63 @@ public class GuacamoleParser implements Iterator<GuacamoleInstruction> {
|
||||
}
|
||||
|
||||
// Save length
|
||||
elementLength = parsedLength;
|
||||
elementCodepoints = elementLength = parsedLength;
|
||||
|
||||
} // end parse length
|
||||
|
||||
// Parse element content, if available
|
||||
if (state == State.PARSING_CONTENT && charsParsed + elementLength + 1 <= length) {
|
||||
while (state == State.PARSING_CONTENT && charsParsed + elementLength + 1 <= length) {
|
||||
|
||||
// Read element
|
||||
// Read element (which may not match element length if surrogate
|
||||
// characters are present)
|
||||
String element = new String(chunk, offset + charsParsed, elementLength);
|
||||
|
||||
// Verify element contains the number of whole Unicode characters
|
||||
// expected, scheduling a future read if we don't yet have enough
|
||||
// characters
|
||||
int codepoints = element.codePointCount(0, element.length());
|
||||
if (codepoints < elementCodepoints) {
|
||||
elementLength += elementCodepoints - codepoints;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the current element ends with a character involving both
|
||||
// a high and low surrogate, elementLength points to the low
|
||||
// surrogate and NOT the element terminator. We must correct the
|
||||
// length and reevaluate.
|
||||
else if (Character.isSurrogatePair(chunk[offset + charsParsed + elementLength - 1],
|
||||
chunk[offset + charsParsed + elementLength])) {
|
||||
elementLength++;
|
||||
continue;
|
||||
}
|
||||
|
||||
charsParsed += elementLength;
|
||||
elementLength = 0;
|
||||
|
||||
// Read terminator char following element
|
||||
char terminator = chunk[offset + charsParsed++];
|
||||
|
||||
// Add element to currently parsed elements
|
||||
elements[elementCount++] = element;
|
||||
|
||||
// If semicolon, store end-of-instruction
|
||||
if (terminator == ';') {
|
||||
state = State.COMPLETE;
|
||||
parsedInstruction = new GuacamoleInstruction(elements[0],
|
||||
Arrays.asList(elements).subList(1, elementCount));
|
||||
}
|
||||
|
||||
// If comma, move on to next element
|
||||
else if (terminator == ',')
|
||||
state = State.PARSING_LENGTH;
|
||||
// Read terminator char following element
|
||||
char terminator = chunk[offset + charsParsed++];
|
||||
switch (terminator) {
|
||||
|
||||
// If semicolon, store end-of-instruction
|
||||
case ';':
|
||||
state = State.COMPLETE;
|
||||
parsedInstruction = new GuacamoleInstruction(elements[0],
|
||||
Arrays.asList(elements).subList(1, elementCount));
|
||||
break;
|
||||
|
||||
// If comma, move on to next element
|
||||
case ',':
|
||||
state = State.PARSING_LENGTH;
|
||||
break;
|
||||
|
||||
// Otherwise, parse error
|
||||
default:
|
||||
state = State.ERROR;
|
||||
throw new GuacamoleServerException("Element terminator of instruction was not ';' nor ','");
|
||||
|
||||
// Otherwise, parse error
|
||||
else {
|
||||
state = State.ERROR;
|
||||
throw new GuacamoleServerException("Element terminator of instruction was not ';' nor ','");
|
||||
}
|
||||
|
||||
} // end parse content
|
||||
|
Reference in New Issue
Block a user