int guac_parser_append(guac_parser* parser, void* buffer, int length) { char* char_buffer = (char*) buffer; int bytes_parsed = 0; /* Do not exceed maximum number of elements */ if (parser->__elementc == GUAC_INSTRUCTION_MAX_ELEMENTS && parser->state != GUAC_PARSE_COMPLETE) { parser->state = GUAC_PARSE_ERROR; return 0; } /* Parse element length */ if (parser->state == GUAC_PARSE_LENGTH) { int parsed_length = parser->__element_length; while (bytes_parsed < length) { /* Pull next character */ char c = *(char_buffer++); bytes_parsed++; /* If digit, add to length */ if (c >= '0' && c <= '9') parsed_length = parsed_length*10 + c - '0'; /* If period, switch to parsing content */ else if (c == '.') { parser->__elementv[parser->__elementc++] = char_buffer; parser->state = GUAC_PARSE_CONTENT; break; } /* If not digit, parse error */ else { parser->state = GUAC_PARSE_ERROR; return 0; } } /* If too long, parse error */ if (parsed_length > GUAC_INSTRUCTION_MAX_LENGTH) { parser->state = GUAC_PARSE_ERROR; return 0; } /* Save length */ parser->__element_length = parsed_length; } /* end parse length */ /* Parse element content */ if (parser->state == GUAC_PARSE_CONTENT) { while (bytes_parsed < length && parser->__element_length >= 0) { /* Get length of current character */ char c = *char_buffer; int char_length = guac_utf8_charsize((unsigned char) c); /* If full character not present in buffer, stop now */ if (char_length + bytes_parsed > length) break; /* Record character as parsed */ bytes_parsed += char_length; /* If end of element, handle terminator */ if (parser->__element_length == 0) { *char_buffer = '\0'; /* If semicolon, store end-of-instruction */ if (c == ';') { parser->state = GUAC_PARSE_COMPLETE; parser->opcode = parser->__elementv[0]; parser->argv = &(parser->__elementv[1]); parser->argc = parser->__elementc - 1; break; } /* If comma, move on to next element */ else if (c == ',') { parser->state = GUAC_PARSE_LENGTH; break; } /* Otherwise, parse error */ else { parser->state = GUAC_PARSE_ERROR; return 0; } } /* end if end of element */ /* Advance to next character */ parser->__element_length--; char_buffer += char_length; } } /* end parse content */ return bytes_parsed; }
ssize_t __guac_socket_nest_write_handler(guac_socket* socket, const void* buf, size_t count) { __guac_socket_nest_data* data = (__guac_socket_nest_data*) socket->data; unsigned char* source = (unsigned char*) buf; /* Current location in destination buffer during copy */ char* current = data->buffer; /* Number of bytes remaining in source buffer */ int remaining = count; /* If we can't actually store that many bytes, reduce number of bytes * expected to be written */ if (remaining > GUAC_SOCKET_NEST_BUFFER_SIZE) remaining = GUAC_SOCKET_NEST_BUFFER_SIZE; /* Current offset within destination buffer */ int offset; /* Number of characters before start of next character */ int skip = 0; /* Copy UTF-8 characters into buffer */ for (offset = 0; offset < GUAC_SOCKET_NEST_BUFFER_SIZE; offset++) { /* Get next byte */ unsigned char c = *source; remaining--; /* If skipping, then skip */ if (skip > 0) skip--; /* Otherwise, determine next skip value, and increment length */ else { /* Determine skip value (size in bytes of rest of character) */ skip = guac_utf8_charsize(c) - 1; /* If not enough bytes to complete character, break */ if (skip > remaining) break; } /* Store byte */ *current = c; /* Advance to next character */ source++; current++; } /* Append null-terminator */ *current = 0; /* Send nest instruction containing read UTF-8 segment */ guac_protocol_send_nest(data->parent, data->index, data->buffer); /* Return number of bytes actually written */ return offset; }
/* Returns new instruction if one exists, or NULL if no more instructions. */ guac_instruction* guac_instruction_read(guac_socket* socket, int usec_timeout) { int retval; /* Loop until a instruction is read */ for (;;) { /* Length of element, in Unicode characters */ int element_length = 0; /* Length of element, in bytes */ int element_byte_length = 0; /* Current position within the element, in Unicode characters */ int current_unicode_length = 0; /* Position within buffer */ int i = socket->__instructionbuf_parse_start; /* Parse instruction in buffer */ while (i < socket->__instructionbuf_used_length) { /* Read character from buffer */ char c = socket->__instructionbuf[i++]; /* If digit, calculate element length */ if (c >= '0' && c <= '9') element_length = element_length * 10 + c - '0'; /* Otherwise, if end of length */ else if (c == '.') { /* Calculate element byte length by walking buffer */ while (i + element_byte_length < socket->__instructionbuf_used_length && current_unicode_length < element_length) { /* Get next byte */ c = socket->__instructionbuf[i + element_byte_length]; /* Update byte and character lengths */ element_byte_length += guac_utf8_charsize((unsigned) c); current_unicode_length++; } /* Verify element is fully read */ if (current_unicode_length == element_length) { /* Get element value */ char* elementv = &(socket->__instructionbuf[i]); /* Get terminator, set null terminator of elementv */ char terminator = elementv[element_byte_length]; elementv[element_byte_length] = '\0'; /* Move to char after terminator of element */ i += element_byte_length+1; /* Reset element length */ element_length = element_byte_length = current_unicode_length = 0; /* As element has been read successfully, update * parse start */ socket->__instructionbuf_parse_start = i; /* Save element */ socket->__instructionbuf_elementv[socket->__instructionbuf_elementc++] = elementv; /* Finish parse if terminator is a semicolon */ if (terminator == ';') { guac_instruction* parsed_instruction; int j; /* Allocate instruction */ parsed_instruction = malloc(sizeof(guac_instruction)); if (parsed_instruction == NULL) { guac_error = GUAC_STATUS_NO_MEMORY; guac_error_message = "Could not allocate memory for parsed instruction"; return NULL; } /* Init parsed instruction */ parsed_instruction->argc = socket->__instructionbuf_elementc - 1; parsed_instruction->argv = malloc(sizeof(char*) * parsed_instruction->argc); /* Fail if memory could not be alloc'd for argv */ if (parsed_instruction->argv == NULL) { guac_error = GUAC_STATUS_NO_MEMORY; guac_error_message = "Could not allocate memory for arguments of parsed instruction"; free(parsed_instruction); return NULL; } /* Set opcode */ parsed_instruction->opcode = strdup(socket->__instructionbuf_elementv[0]); /* Fail if memory could not be alloc'd for opcode */ if (parsed_instruction->opcode == NULL) { guac_error = GUAC_STATUS_NO_MEMORY; guac_error_message = "Could not allocate memory for opcode of parsed instruction"; free(parsed_instruction->argv); free(parsed_instruction); return NULL; } /* Copy element values to parsed instruction */ for (j=0; j<parsed_instruction->argc; j++) { parsed_instruction->argv[j] = strdup(socket->__instructionbuf_elementv[j+1]); /* Free memory and fail if out of mem */ if (parsed_instruction->argv[j] == NULL) { guac_error = GUAC_STATUS_NO_MEMORY; guac_error_message = "Could not allocate memory for single argument of parsed instruction"; /* Free all alloc'd argv values */ while (--j >= 0) free(parsed_instruction->argv[j]); free(parsed_instruction->opcode); free(parsed_instruction->argv); free(parsed_instruction); return NULL; } } /* Reset buffer */ memmove(socket->__instructionbuf, socket->__instructionbuf + i, socket->__instructionbuf_used_length - i); socket->__instructionbuf_used_length -= i; socket->__instructionbuf_parse_start = 0; socket->__instructionbuf_elementc = 0; /* Done */ return parsed_instruction; } /* end if terminator */ /* Error if expected comma is not present */ else if (terminator != ',') { guac_error = GUAC_STATUS_BAD_ARGUMENT; guac_error_message = "Element terminator of instruction was not ';' nor ','"; return NULL; } } /* end if element fully read */ /* Otherwise, read more data */ else break; } /* Error if length is non-numeric or does not end in a period */ else { guac_error = GUAC_STATUS_BAD_ARGUMENT; guac_error_message = "Non-numeric character in element length"; return NULL; } } /* No instruction yet? Get more data ... */ retval = guac_socket_select(socket, usec_timeout); if (retval <= 0) return NULL; /* If more data is available, fill into buffer */ retval = __guac_fill_instructionbuf(socket); /* Error, guac_error already set */ if (retval < 0) return NULL; /* EOF */ if (retval == 0) { guac_error = GUAC_STATUS_NO_INPUT; guac_error_message = "End of stream reached while reading instruction"; return NULL; } } }