int guac_instruction_waiting(guac_socket* socket, int usec_timeout) { if (socket->__instructionbuf_used_length > 0) return 1; return guac_socket_select(socket, usec_timeout); }
int guac_instruction_waiting(guac_socket* socket, int usec_timeout) { if (socket->__instructionbuf_unparsed_end > socket->__instructionbuf_unparsed_start) return 1; return guac_socket_select(socket, usec_timeout); }
int guac_parser_read(guac_parser* parser, guac_socket* socket, int usec_timeout) { char* unparsed_end = parser->__instructionbuf_unparsed_end; char* unparsed_start = parser->__instructionbuf_unparsed_start; char* instr_start = parser->__instructionbuf_unparsed_start; char* buffer_end = parser->__instructionbuf + sizeof(parser->__instructionbuf); /* Begin next instruction if previous was ended */ if (parser->state == GUAC_PARSE_COMPLETE) guac_parser_reset(parser); while (parser->state != GUAC_PARSE_COMPLETE && parser->state != GUAC_PARSE_ERROR) { /* Add any available data to buffer */ int parsed = guac_parser_append(parser, unparsed_start, unparsed_end - unparsed_start); /* Read more data if not enough data to parse */ if (parsed == 0 && parser->state != GUAC_PARSE_ERROR) { int retval; /* If no space left to read, fail */ if (unparsed_end == buffer_end) { /* Shift backward if possible */ if (instr_start != parser->__instructionbuf) { int i; /* Shift buffer */ int offset = instr_start - parser->__instructionbuf; memmove(parser->__instructionbuf, instr_start, unparsed_end - instr_start); /* Update tracking pointers */ unparsed_end -= offset; unparsed_start -= offset; instr_start = parser->__instructionbuf; /* Update parsed elements, if any */ for (i=0; i < parser->__elementc; i++) parser->__elementv[i] -= offset; } /* Otherwise, no memory to read */ else { guac_error = GUAC_STATUS_NO_MEMORY; guac_error_message = "Instruction too long"; return -1; } } /* No instruction yet? Get more data ... */ retval = guac_socket_select(socket, usec_timeout); if (retval <= 0) return -1; /* Attempt to fill buffer */ retval = guac_socket_read(socket, unparsed_end, buffer_end - unparsed_end); /* Set guac_error if read unsuccessful */ if (retval < 0) { guac_error = GUAC_STATUS_SEE_ERRNO; guac_error_message = "Error filling instruction buffer"; return -1; } /* EOF */ if (retval == 0) { guac_error = GUAC_STATUS_CLOSED; guac_error_message = "End of stream reached while " "reading instruction"; return -1; } /* Update internal buffer */ unparsed_end += retval; } /* If data was parsed, advance buffer */ else unparsed_start += parsed; } /* end while parsing data */ /* Fail on error */ if (parser->state == GUAC_PARSE_ERROR) { guac_error = GUAC_STATUS_PROTOCOL_ERROR; guac_error_message = "Instruction parse error"; return -1; } parser->__instructionbuf_unparsed_start = unparsed_start; parser->__instructionbuf_unparsed_end = unparsed_end; return 0; }
/* 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; } } }
/* Returns new instruction if one exists, or NULL if no more instructions. */ guac_instruction* guac_instruction_read(guac_socket* socket, int usec_timeout) { char* unparsed_end = socket->__instructionbuf_unparsed_end; char* unparsed_start = socket->__instructionbuf_unparsed_start; char* instr_start = socket->__instructionbuf_unparsed_start; char* buffer_end = socket->__instructionbuf + sizeof(socket->__instructionbuf); guac_instruction* instruction = guac_instruction_alloc(); while (instruction->state != GUAC_INSTRUCTION_PARSE_COMPLETE && instruction->state != GUAC_INSTRUCTION_PARSE_ERROR) { /* Add any available data to buffer */ int parsed = guac_instruction_append(instruction, unparsed_start, unparsed_end - unparsed_start); /* Read more data if not enough data to parse */ if (parsed == 0) { int retval; /* If no space left to read, fail */ if (unparsed_end == buffer_end) { /* Shift backward if possible */ if (instr_start != socket->__instructionbuf) { int i; /* Shift buffer */ int offset = instr_start - socket->__instructionbuf; memmove(socket->__instructionbuf, instr_start, unparsed_end - instr_start); /* Update tracking pointers */ unparsed_end -= offset; unparsed_start -= offset; instr_start = socket->__instructionbuf; /* Update parsed elements, if any */ for (i=0; i<instruction->__elementc; i++) instruction->__elementv[i] -= offset; } /* Otherwise, no memory to read */ else { guac_error = GUAC_STATUS_NO_MEMORY; guac_error_message = "Instruction too long"; return NULL; } } /* No instruction yet? Get more data ... */ retval = guac_socket_select(socket, usec_timeout); if (retval <= 0) return NULL; /* Attempt to fill buffer */ retval = guac_socket_read(socket, unparsed_end, buffer_end - unparsed_end); /* Set guac_error if read unsuccessful */ if (retval < 0) { guac_error = GUAC_STATUS_SEE_ERRNO; guac_error_message = "Error filling instruction buffer"; return NULL; } /* EOF */ if (retval == 0) { guac_error = GUAC_STATUS_NO_INPUT; guac_error_message = "End of stream reached while " "reading instruction"; return NULL; } /* Update internal buffer */ unparsed_end += retval; } /* If data was parsed, advance buffer */ else unparsed_start += parsed; } /* end while parsing data */ /* Fail on error */ if (instruction->state == GUAC_INSTRUCTION_PARSE_ERROR) { guac_error = GUAC_STATUS_BAD_ARGUMENT; guac_error_message = "Instruction parse error"; return NULL; } socket->__instructionbuf_unparsed_start = unparsed_start; socket->__instructionbuf_unparsed_end = unparsed_end; return instruction; }