diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am index dc96a878..a6240802 100644 --- a/src/libguac/Makefile.am +++ b/src/libguac/Makefile.am @@ -54,6 +54,7 @@ libguacinc_HEADERS = \ guacamole/pool.h \ guacamole/pool-types.h \ guacamole/protocol.h \ + guacamole/protocol-constants.h \ guacamole/protocol-types.h \ guacamole/socket-constants.h \ guacamole/socket.h \ diff --git a/src/libguac/client.c b/src/libguac/client.c index 80eb4ea7..64404534 100644 --- a/src/libguac/client.c +++ b/src/libguac/client.c @@ -516,6 +516,26 @@ int guac_client_get_processing_lag(guac_client* client) { } +void guac_client_stream_argv(guac_client* client, guac_socket* socket, + const char* mimetype, const char* name, const char* value) { + + /* Allocate new stream for argument value */ + guac_stream* stream = guac_client_alloc_stream(client); + + /* Declare stream as containing connection parameter data */ + guac_protocol_send_argv(socket, stream, mimetype, name); + + /* Write parameter data */ + guac_protocol_send_blobs(socket, stream, value, strlen(value)); + + /* Terminate stream */ + guac_protocol_send_end(socket, stream); + + /* Free allocated stream */ + guac_client_free_stream(client, stream); + +} + void guac_client_stream_png(guac_client* client, guac_socket* socket, guac_composite_mode mode, const guac_layer* layer, int x, int y, cairo_surface_t* surface) { diff --git a/src/libguac/encode-jpeg.c b/src/libguac/encode-jpeg.c index 1aeb23bb..8e145d86 100644 --- a/src/libguac/encode-jpeg.c +++ b/src/libguac/encode-jpeg.c @@ -59,7 +59,7 @@ typedef struct guac_jpeg_destination_mgr { /** * The output buffer. */ - unsigned char buffer[6048]; + unsigned char buffer[GUAC_PROTOCOL_BLOB_MAX_LENGTH]; } guac_jpeg_destination_mgr; diff --git a/src/libguac/encode-png.c b/src/libguac/encode-png.c index 7d4b6c7f..20d2f5de 100644 --- a/src/libguac/encode-png.c +++ b/src/libguac/encode-png.c @@ -56,7 +56,7 @@ typedef struct guac_png_write_state { /** * Buffer of pending PNG data. */ - char buffer[6048]; + char buffer[GUAC_PROTOCOL_BLOB_MAX_LENGTH]; /** * The number of bytes currently stored in the buffer. diff --git a/src/libguac/encode-webp.c b/src/libguac/encode-webp.c index 5c2237d6..43c5a00c 100644 --- a/src/libguac/encode-webp.c +++ b/src/libguac/encode-webp.c @@ -52,7 +52,7 @@ typedef struct guac_webp_stream_writer { /** * Buffer of pending WebP data. */ - char buffer[6048]; + char buffer[GUAC_PROTOCOL_BLOB_MAX_LENGTH]; /** * The number of bytes currently stored in the buffer. diff --git a/src/libguac/guacamole/client.h b/src/libguac/guacamole/client.h index 2d01573c..88d1f416 100644 --- a/src/libguac/guacamole/client.h +++ b/src/libguac/guacamole/client.h @@ -548,6 +548,33 @@ int guac_client_load_plugin(guac_client* client, const char* protocol); */ int guac_client_get_processing_lag(guac_client* client); +/** + * Streams the given connection parameter value over an argument value stream + * ("argv" instruction), exposing the current value of the named connection + * parameter to all users of the given client. The argument value stream will + * be automatically allocated and freed. + * + * @param client + * The Guacamole client for which the argument value stream should be + * allocated. + * + * @param socket + * The socket over which instructions associated with the argument value + * stream should be sent. + * + * @param mimetype + * The mimetype of the data within the connection parameter value being + * sent. + * + * @param name + * The name of the connection parameter being sent. + * + * @param value + * The current value of the connection parameter being sent. + */ +void guac_client_stream_argv(guac_client* client, guac_socket* socket, + const char* mimetype, const char* name, const char* value); + /** * Streams the image data of the given surface over an image stream ("img" * instruction) as PNG-encoded data. The image stream will be automatically diff --git a/src/libguac/guacamole/protocol-constants.h b/src/libguac/guacamole/protocol-constants.h new file mode 100644 index 00000000..afa67bb9 --- /dev/null +++ b/src/libguac/guacamole/protocol-constants.h @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_PROTOCOL_CONSTANTS_H +#define GUAC_PROTOCOL_CONSTANTS_H + +/** + * Constants related to the Guacamole protocol. + * + * @file protocol-constants.h + */ + +/** + * This defines the overall protocol version that this build of libguac + * supports. The protocol version is used to provide compatibility between + * potentially different versions of Guacamole server and clients. The + * version number is a MAJOR_MINOR_PATCH version that matches the versioning + * used throughout the components of the Guacamole project. This version + * will not necessarily increment with the other components, unless additional + * functionality is introduced that affects compatibility. + * + * This version is passed by the __guac_protocol_send_args() function from the + * server to the client during the client/server handshake. + */ +#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_1_0" + +/** + * The maximum number of bytes that should be sent in any one blob instruction + * to ensure the instruction does not exceed the maximum allowed instruction + * size. + * + * @see GUAC_INSTRUCTION_MAX_LENGTH + */ +#define GUAC_PROTOCOL_BLOB_MAX_LENGTH 6048 + +#endif + diff --git a/src/libguac/guacamole/protocol.h b/src/libguac/guacamole/protocol.h index 0b462b4c..f4e02880 100644 --- a/src/libguac/guacamole/protocol.h +++ b/src/libguac/guacamole/protocol.h @@ -30,6 +30,7 @@ #include "layer-types.h" #include "object-types.h" +#include "protocol-constants.h" #include "protocol-types.h" #include "socket-types.h" #include "stream-types.h" @@ -38,20 +39,6 @@ #include #include -/** - * This defines the overall protocol version that this build of libguac - * supports. The protocol version is used to provide compatibility between - * potentially different versions of Guacamole server and clients. The - * version number is a MAJOR_MINOR_PATCH version that matches the versioning - * used throughout the components of the Guacamole project. This version - * will not necessarily increment with the other components, unless additional - * functionality is introduced that affects compatibility. - * - * This version is passed by the __guac_protocol_send_args() function from the - * server to the client during the client/server handshake. - */ -#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_1_0" - /* CONTROL INSTRUCTIONS */ /** @@ -448,6 +435,37 @@ int guac_protocol_send_pipe(guac_socket* socket, const guac_stream* stream, int guac_protocol_send_blob(guac_socket* socket, const guac_stream* stream, const void* data, int count); +/** + * Sends a series of blob instructions, splitting the given data across the + * number of instructions required to ensure the size of each blob does not + * exceed GUAC_PROTOCOL_BLOB_MAX_LENGTH. If the size of data provided is zero, + * no blob instructions are sent. + * + * If an error occurs sending any blob instruction, a non-zero value is + * returned, guac_error is set appropriately, and no further blobs are sent. + * + * @see GUAC_PROTOCOL_BLOB_MAX_LENGTH + * + * @param socket + * The guac_socket connection to use to send the blob instructions. + * + * @param stream + * The stream to associate with each blob sent. + * + * @param data + * The data which should be sent using the required number of blob + * instructions. + * + * @param count + * The number of bytes within the given buffer of data that must be + * written. + * + * @return + * Zero on success, non-zero on error. + */ +int guac_protocol_send_blobs(guac_socket* socket, const guac_stream* stream, + const void* data, int count); + /** * Sends an end instruction over the given guac_socket connection. * @@ -932,6 +950,31 @@ int guac_protocol_send_size(guac_socket* socket, const guac_layer* layer, /* TEXT INSTRUCTIONS */ +/** + * Sends an argv instruction over the given guac_socket connection. + * + * If an error occurs sending the instruction, a non-zero value is + * returned, and guac_error is set appropriately. + * + * @param socket + * The guac_socket connection to use to send the connection parameter + * value. + * + * @param stream + * The stream to use to send the connection parameter value. + * + * @param mimetype + * The mimetype of the connection parameter value being sent. + * + * @param name + * The name of the connection parameter whose current value is being sent. + * + * @return + * Zero on success, non-zero on error. + */ +int guac_protocol_send_argv(guac_socket* socket, guac_stream* stream, + const char* mimetype, const char* name); + /** * Sends a clipboard instruction over the given guac_socket connection. * diff --git a/src/libguac/guacamole/user.h b/src/libguac/guacamole/user.h index f920c325..702160fa 100644 --- a/src/libguac/guacamole/user.h +++ b/src/libguac/guacamole/user.h @@ -676,6 +676,32 @@ guac_object* guac_user_alloc_object(guac_user* user); */ void guac_user_free_object(guac_user* user, guac_object* object); +/** + * Streams the given connection parameter value over an argument value stream + * ("argv" instruction), exposing the current value of the named connection + * parameter to the given user. The argument value stream will be automatically + * allocated and freed. + * + * @param user + * The Guacamole user who should receive the connection parameter value. + * + * @param socket + * The socket over which instructions associated with the argument value + * stream should be sent. + * + * @param mimetype + * The mimetype of the data within the connection parameter value being + * sent. + * + * @param name + * The name of the connection parameter being sent. + * + * @param value + * The current value of the connection parameter being sent. + */ +void guac_user_stream_argv(guac_user* user, guac_socket* socket, + const char* mimetype, const char* name, const char* value); + /** * Streams the image data of the given surface over an image stream ("img" * instruction) as PNG-encoded data. The image stream will be automatically diff --git a/src/libguac/protocol.c b/src/libguac/protocol.c index 46a9858e..ff73a558 100644 --- a/src/libguac/protocol.c +++ b/src/libguac/protocol.c @@ -125,6 +125,26 @@ int guac_protocol_send_args(guac_socket* socket, const char** args) { } +int guac_protocol_send_argv(guac_socket* socket, guac_stream* stream, + const char* mimetype, const char* name) { + + int ret_val; + + guac_socket_instruction_begin(socket); + ret_val = + guac_socket_write_string(socket, "4.argv,") + || __guac_socket_write_length_int(socket, stream->index) + || guac_socket_write_string(socket, ",") + || __guac_socket_write_length_string(socket, mimetype) + || guac_socket_write_string(socket, ",") + || __guac_socket_write_length_string(socket, name) + || guac_socket_write_string(socket, ";"); + + guac_socket_instruction_end(socket); + return ret_val; + +} + int guac_protocol_send_arc(guac_socket* socket, const guac_layer* layer, int x, int y, int radius, double startAngle, double endAngle, int negative) { @@ -195,6 +215,33 @@ int guac_protocol_send_blob(guac_socket* socket, const guac_stream* stream, } +int guac_protocol_send_blobs(guac_socket* socket, const guac_stream* stream, + const void* data, int count) { + + int ret_val = 0; + + /* Send blob instructions while data remains and instructions are being + * sent successfully */ + while (count > 0 && ret_val == 0) { + + /* Limit blob size to maximum allowed */ + int blob_size = count; + if (blob_size > GUAC_PROTOCOL_BLOB_MAX_LENGTH) + blob_size = GUAC_PROTOCOL_BLOB_MAX_LENGTH; + + /* Send next blob of data */ + ret_val = guac_protocol_send_blob(socket, stream, data, blob_size); + + /* Advance to next blob */ + data = (const char*) data + blob_size; + count -= blob_size; + + } + + return ret_val; + +} + int guac_protocol_send_body(guac_socket* socket, const guac_object* object, const guac_stream* stream, const char* mimetype, const char* name) { diff --git a/src/libguac/raw_encoder.c b/src/libguac/raw_encoder.c index 9086a37b..888bde79 100644 --- a/src/libguac/raw_encoder.c +++ b/src/libguac/raw_encoder.c @@ -121,25 +121,8 @@ static void raw_encoder_flush_handler(guac_audio_stream* audio) { guac_socket* socket = audio->client->socket; guac_stream* stream = audio->stream; - unsigned char* current = state->buffer; - int remaining = state->written; - /* Flush all data in buffer as blobs */ - while (remaining > 0) { - - /* Determine size of blob to be written */ - int chunk_size = remaining; - if (chunk_size > 6048) - chunk_size = 6048; - - /* Send audio data */ - guac_protocol_send_blob(socket, stream, current, chunk_size); - - /* Advance to next blob */ - current += chunk_size; - remaining -= chunk_size; - - } + guac_protocol_send_blobs(socket, stream, state->buffer, state->written); /* All data has been flushed */ state->written = 0; diff --git a/src/libguac/user.c b/src/libguac/user.c index 9145b275..aaa51bb9 100644 --- a/src/libguac/user.c +++ b/src/libguac/user.c @@ -229,6 +229,26 @@ void guac_user_log(guac_user* user, guac_client_log_level level, } +void guac_user_stream_argv(guac_user* user, guac_socket* socket, + const char* mimetype, const char* name, const char* value) { + + /* Allocate new stream for argument value */ + guac_stream* stream = guac_user_alloc_stream(user); + + /* Declare stream as containing connection parameter data */ + guac_protocol_send_argv(socket, stream, mimetype, name); + + /* Write parameter data */ + guac_protocol_send_blobs(socket, stream, value, strlen(value)); + + /* Terminate stream */ + guac_protocol_send_end(socket, stream); + + /* Free allocated stream */ + guac_user_free_stream(user, stream); + +} + void guac_user_stream_png(guac_user* user, guac_socket* socket, guac_composite_mode mode, const guac_layer* layer, int x, int y, cairo_surface_t* surface) {