Merge pull request #45 from glyptodon/object-api

GUAC-1172: Implement libguac side of Guacamole protocol objects.
This commit is contained in:
James Muehlner 2015-06-23 19:37:40 -07:00
commit 6bfd3e46e0
11 changed files with 510 additions and 1 deletions

View File

@ -41,6 +41,8 @@ libguacinc_HEADERS = \
guacamole/instruction-types.h \ guacamole/instruction-types.h \
guacamole/layer.h \ guacamole/layer.h \
guacamole/layer-types.h \ guacamole/layer-types.h \
guacamole/object.h \
guacamole/object-types.h \
guacamole/plugin-constants.h \ guacamole/plugin-constants.h \
guacamole/plugin.h \ guacamole/plugin.h \
guacamole/plugin-types.h \ guacamole/plugin-types.h \

View File

@ -25,6 +25,7 @@
#include "client.h" #include "client.h"
#include "client-handlers.h" #include "client-handlers.h"
#include "instruction.h" #include "instruction.h"
#include "object.h"
#include "protocol.h" #include "protocol.h"
#include "stream.h" #include "stream.h"
#include "timestamp.h" #include "timestamp.h"
@ -47,6 +48,8 @@ __guac_instruction_handler_mapping __guac_instruction_handler_map[] = {
{"ack", __guac_handle_ack}, {"ack", __guac_handle_ack},
{"blob", __guac_handle_blob}, {"blob", __guac_handle_blob},
{"end", __guac_handle_end}, {"end", __guac_handle_end},
{"get", __guac_handle_get},
{"put", __guac_handle_put},
{NULL, NULL} {NULL, NULL}
}; };
@ -319,6 +322,77 @@ int __guac_handle_end(guac_client* client, guac_instruction* instruction) {
return result; return result;
} }
int __guac_handle_get(guac_client* client, guac_instruction* instruction) {
guac_object* object;
/* Validate object index */
int object_index = atoi(instruction->argv[0]);
if (object_index < 0 || object_index >= GUAC_CLIENT_MAX_OBJECTS)
return 0;
object = &(client->__objects[object_index]);
/* Validate initialization of object */
if (object->index == GUAC_CLIENT_UNDEFINED_OBJECT_INDEX)
return 0;
/* Call object handler if defined */
if (object->get_handler)
return object->get_handler(client, object,
instruction->argv[1] /* name */
);
/* Fall back to global handler if defined */
if (client->get_handler)
return client->get_handler(client, object,
instruction->argv[1] /* name */
);
return 0;
}
int __guac_handle_put(guac_client* client, guac_instruction* instruction) {
guac_object* object;
/* Validate object index */
int object_index = atoi(instruction->argv[0]);
if (object_index < 0 || object_index >= GUAC_CLIENT_MAX_OBJECTS)
return 0;
object = &(client->__objects[object_index]);
/* Validate initialization of object */
if (object->index == GUAC_CLIENT_UNDEFINED_OBJECT_INDEX)
return 0;
/* Pull corresponding stream */
int stream_index = atoi(instruction->argv[1]);
guac_stream* stream = __init_input_stream(client, stream_index);
if (stream == NULL)
return 0;
/* Call object handler if defined */
if (object->put_handler)
return object->put_handler(client, object, stream,
instruction->argv[2], /* mimetype */
instruction->argv[3] /* name */
);
/* Fall back to global handler if defined */
if (client->put_handler)
return client->put_handler(client, object, stream,
instruction->argv[2], /* mimetype */
instruction->argv[3] /* name */
);
/* Otherwise, abort */
guac_protocol_send_ack(client->socket, stream,
"Object write unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
return 0;
}
int __guac_handle_disconnect(guac_client* client, guac_instruction* instruction) { int __guac_handle_disconnect(guac_client* client, guac_instruction* instruction) {
guac_client_stop(client); guac_client_stop(client);
return 0; return 0;

View File

@ -122,6 +122,20 @@ int __guac_handle_blob(guac_client* client, guac_instruction* instruction);
*/ */
int __guac_handle_end(guac_client* client, guac_instruction* instruction); int __guac_handle_end(guac_client* client, guac_instruction* instruction);
/**
* Internal initial handler for the get instruction. When a get instruction
* is received, this handler will be called. The client's get handler will
* be invoked if defined.
*/
int __guac_handle_get(guac_client* client, guac_instruction* instruction);
/**
* Internal initial handler for the put instruction. When a put instruction
* is received, this handler will be called. The client's put handler will
* be invoked if defined.
*/
int __guac_handle_put(guac_client* client, guac_instruction* instruction);
/** /**
* Internal initial handler for the size instruction. When a size instruction * Internal initial handler for the size instruction. When a size instruction
* is received, this handler will be called. The client's size handler will * is received, this handler will be called. The client's size handler will

View File

@ -27,6 +27,7 @@
#include "error.h" #include "error.h"
#include "instruction.h" #include "instruction.h"
#include "layer.h" #include "layer.h"
#include "object.h"
#include "pool.h" #include "pool.h"
#include "protocol.h" #include "protocol.h"
#include "socket.h" #include "socket.h"
@ -124,6 +125,39 @@ void guac_client_free_stream(guac_client* client, guac_stream* stream) {
} }
guac_object* guac_client_alloc_object(guac_client* client) {
guac_object* allocd_object;
int object_index;
/* Refuse to allocate beyond maximum */
if (client->__object_pool->active == GUAC_CLIENT_MAX_OBJECTS)
return NULL;
/* Allocate object */
object_index = guac_pool_next_int(client->__object_pool);
/* Initialize object */
allocd_object = &(client->__objects[object_index]);
allocd_object->index = object_index;
allocd_object->data = NULL;
allocd_object->get_handler = NULL;
allocd_object->put_handler = NULL;
return allocd_object;
}
void guac_client_free_object(guac_client* client, guac_object* object) {
/* Release index to pool */
guac_pool_free_int(client->__object_pool, object->index);
/* Mark object as undefined */
object->index = GUAC_CLIENT_UNDEFINED_OBJECT_INDEX;
}
/** /**
* Returns a newly allocated string containing a guaranteed-unique connection * Returns a newly allocated string containing a guaranteed-unique connection
* identifier string which is 37 characters long and begins with a '$' * identifier string which is 37 characters long and begins with a '$'
@ -216,7 +250,7 @@ guac_client* guac_client_alloc() {
/* Allocate stream pool */ /* Allocate stream pool */
client->__stream_pool = guac_pool_alloc(0); client->__stream_pool = guac_pool_alloc(0);
/* Initialze streams */ /* Initialize streams */
client->__input_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS); client->__input_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS);
client->__output_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS); client->__output_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS);
@ -225,6 +259,14 @@ guac_client* guac_client_alloc() {
client->__output_streams[i].index = GUAC_CLIENT_CLOSED_STREAM_INDEX; client->__output_streams[i].index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
} }
/* Allocate object pool */
client->__object_pool = guac_pool_alloc(0);
/* Initialize objects */
client->__objects = malloc(sizeof(guac_object) * GUAC_CLIENT_MAX_OBJECTS);
for (i=0; i<GUAC_CLIENT_MAX_OBJECTS; i++)
client->__objects[i].index = GUAC_CLIENT_UNDEFINED_OBJECT_INDEX;
return client; return client;
} }
@ -249,6 +291,12 @@ void guac_client_free(guac_client* client) {
/* Free stream pool */ /* Free stream pool */
guac_pool_free(client->__stream_pool); guac_pool_free(client->__stream_pool);
/* Free objects */
free(client->__objects);
/* Free object pool */
guac_pool_free(client->__object_pool);
free(client); free(client);
} }

View File

@ -39,6 +39,28 @@
*/ */
#define GUAC_CLIENT_CLOSED_STREAM_INDEX -1 #define GUAC_CLIENT_CLOSED_STREAM_INDEX -1
/**
* The maximum number of objects supported by any one guac_client.
*/
#define GUAC_CLIENT_MAX_OBJECTS 64
/**
* The index of an object which has not been defined.
*/
#define GUAC_CLIENT_UNDEFINED_OBJECT_INDEX -1
/**
* The stream name reserved for the root of a Guacamole protocol object.
*/
#define GUAC_CLIENT_OBJECT_ROOT_NAME "/"
/**
* The mimetype of a stream containing a map of available stream names to their
* corresponding mimetypes. The root of a Guacamole protocol object is
* guaranteed to have this type.
*/
#define GUAC_CLIENT_STREAM_INDEX_MIMETYPE "application/vnd.glyptodon.guacamole.stream-index+json"
/** /**
* The flag set in the mouse button mask when the left mouse button is down. * The flag set in the mouse button mask when the left mouse button is down.
*/ */

View File

@ -31,6 +31,7 @@
*/ */
#include "client-types.h" #include "client-types.h"
#include "object-types.h"
#include "protocol-types.h" #include "protocol-types.h"
#include "stream-types.h" #include "stream-types.h"
@ -92,6 +93,18 @@ typedef int guac_client_ack_handler(guac_client* client, guac_stream* stream,
*/ */
typedef int guac_client_end_handler(guac_client* client, guac_stream* stream); typedef int guac_client_end_handler(guac_client* client, guac_stream* stream);
/**
* Handler for Guacamole object get events.
*/
typedef int guac_client_get_handler(guac_client* client, guac_object* object,
char* name);
/**
* Handler for Guacamole object put events.
*/
typedef int guac_client_put_handler(guac_client* client, guac_object* object,
guac_stream* stream, char* mimetype, char* name);
/** /**
* Handler for Guacamole audio format events. * Handler for Guacamole audio format events.
*/ */

View File

@ -34,6 +34,7 @@
#include "client-constants.h" #include "client-constants.h"
#include "instruction-types.h" #include "instruction-types.h"
#include "layer-types.h" #include "layer-types.h"
#include "object-types.h"
#include "pool-types.h" #include "pool-types.h"
#include "socket-types.h" #include "socket-types.h"
#include "stream-types.h" #include "stream-types.h"
@ -366,6 +367,46 @@ struct guac_client {
*/ */
guac_client_log_handler* log_handler; guac_client_log_handler* log_handler;
/**
* Handler for get events sent by the Guacamole web-client.
*
* The handler takes a guac_object, containing the object index which will
* persist through the duration of the transfer, and the name of the stream
* being requested. It is up to the get handler to create the required body
* stream.
*
* Example:
* @code
* int get_handler(guac_client* client, guac_object* object,
* char* name);
*
* int guac_client_init(guac_client* client, int argc, char** argv) {
* client->get_handler = get_handler;
* }
* @endcode
*/
guac_client_get_handler* get_handler;
/**
* Handler for put events sent by the Guacamole web-client.
*
* The handler takes a guac_object and guac_stream, which each contain their
* respective indices which will persist through the duration of the
* transfer, the mimetype of the data being transferred, and the name of
* the stream within the object being written to.
*
* Example:
* @code
* int put_handler(guac_client* client, guac_object* object,
* guac_stream* stream, char* mimetype, char* name);
*
* int guac_client_init(guac_client* client, int argc, char** argv) {
* client->put_handler = put_handler;
* }
* @endcode
*/
guac_client_put_handler* put_handler;
/** /**
* Pool of buffer indices. Buffers are simply layers with negative indices. * Pool of buffer indices. Buffers are simply layers with negative indices.
* Note that because guac_pool always gives non-negative indices starting * Note that because guac_pool always gives non-negative indices starting
@ -395,6 +436,16 @@ struct guac_client {
*/ */
guac_stream* __input_streams; guac_stream* __input_streams;
/**
* Pool of object indices.
*/
guac_pool* __object_pool;
/**
* All available objects (arbitrary sets of named streams).
*/
guac_object* __objects;
/** /**
* The unique identifier allocated for the connection, which may * The unique identifier allocated for the connection, which may
* be used within the Guacamole protocol to refer to this connection. * be used within the Guacamole protocol to refer to this connection.
@ -552,6 +603,30 @@ guac_stream* guac_client_alloc_stream(guac_client* client);
*/ */
void guac_client_free_stream(guac_client* client, guac_stream* stream); void guac_client_free_stream(guac_client* client, guac_stream* stream);
/**
* Allocates a new object. An arbitrary index is automatically assigned
* if no previously-allocated object is available for use.
*
* @param client
* The proxy client to allocate the object for.
*
* @return
* The next available object, or a newly allocated object.
*/
guac_object* guac_client_alloc_object(guac_client* client);
/**
* Returns the given object to the pool of available objects, such that it
* can be reused by any subsequent call to guac_client_alloc_object().
*
* @param client
* The proxy client to return the object to.
*
* @param object
* The object to return to the pool of available object.
*/
void guac_client_free_object(guac_client* client, guac_object* object);
/** /**
* The default Guacamole client layer, layer 0. * The default Guacamole client layer, layer 0.
*/ */

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef GUAC_OBJECT_TYPES_H
#define GUAC_OBJECT_TYPES_H
/**
* Type definitions related to Guacamole protocol objects.
*
* @file object-types.h
*/
/**
* Represents a single object within the Guacamole protocol.
*/
typedef struct guac_object guac_object;
#endif

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef GUAC_OBJECT_H
#define GUAC_OBJECT_H
/**
* Provides functions and structures required for allocating and using objects.
*
* @file object.h
*/
#include "client-fntypes.h"
#include "object-types.h"
struct guac_object {
/**
* The index of this object.
*/
int index;
/**
* Arbitrary data associated with this object.
*/
void* data;
/**
* Handler for get events sent by the Guacamole web-client.
*
* The handler takes a guac_object, containing the object index which will
* persist through the duration of the transfer, and the name of the stream
* being requested. It is up to the get handler to create the required body
* stream.
*
* Example:
* @code
* int get_handler(guac_client* client, guac_object* object,
* char* name);
*
* int some_function(guac_client* client) {
*
* guac_object* object = guac_client_alloc_object(client);
* object->get_handler = get_handler;
*
* }
* @endcode
*/
guac_client_get_handler* get_handler;
/**
* Handler for put events sent by the Guacamole web-client.
*
* The handler takes a guac_object and guac_stream, which each contain their
* respective indices which will persist through the duration of the
* transfer, the mimetype of the data being transferred, and the name of
* the stream within the object being written to.
*
* Example:
* @code
* int put_handler(guac_client* client, guac_object* object,
* guac_stream* stream, char* mimetype, char* name);
*
* int some_function(guac_client* client) {
*
* guac_object* object = guac_client_alloc_object(client);
* object->put_handler = put_handler;
*
* }
* @endcode
*/
guac_client_put_handler* put_handler;
};
#endif

View File

@ -32,6 +32,7 @@
*/ */
#include "layer-types.h" #include "layer-types.h"
#include "object-types.h"
#include "protocol-types.h" #include "protocol-types.h"
#include "socket-types.h" #include "socket-types.h"
#include "stream-types.h" #include "stream-types.h"
@ -217,6 +218,75 @@ int guac_protocol_send_select(guac_socket* socket, const char* protocol);
*/ */
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp); int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp);
/* OBJECT INSTRUCTIONS */
/**
* Sends a body 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.
*
* @param object
* The object to associated with the stream being used.
*
* @param stream
* The stream to use.
*
* @param mimetype
* The mimetype of the data being sent.
*
* @param name
* The name of the stream whose body is being sent, as requested by a "get"
* instruction.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_body(guac_socket* socket, const guac_object* object,
const guac_stream* stream, const char* mimetype, const char* name);
/**
* Sends a filesystem 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.
*
* @param object
* The object representing the filesystem being exposed.
*
* @param name
* A name describing the filesystem being exposed.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_filesystem(guac_socket* socket,
const guac_object* object, const char* name);
/**
* Sends an undefine 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.
*
* @param object
* The object being undefined.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_undefine(guac_socket* socket,
const guac_object* object);
/* MEDIA INSTRUCTIONS */ /* MEDIA INSTRUCTIONS */
/** /**

View File

@ -24,6 +24,7 @@
#include "error.h" #include "error.h"
#include "layer.h" #include "layer.h"
#include "object.h"
#include "palette.h" #include "palette.h"
#include "protocol.h" #include "protocol.h"
#include "socket.h" #include "socket.h"
@ -463,6 +464,28 @@ int guac_protocol_send_blob(guac_socket* socket, const guac_stream* stream,
} }
int guac_protocol_send_body(guac_socket* socket, const guac_object* object,
const 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.body,")
|| __guac_socket_write_length_int(socket, object->index)
|| guac_socket_write_string(socket, ",")
|| __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_cfill(guac_socket* socket, int guac_protocol_send_cfill(guac_socket* socket,
guac_composite_mode mode, const guac_layer* layer, guac_composite_mode mode, const guac_layer* layer,
int r, int g, int b, int a) { int r, int g, int b, int a) {
@ -833,6 +856,24 @@ int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream,
} }
int guac_protocol_send_filesystem(guac_socket* socket,
const guac_object* object, const char* name) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "10.filesystem,")
|| __guac_socket_write_length_int(socket, object->index)
|| 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_identity(guac_socket* socket, const guac_layer* layer) { int guac_protocol_send_identity(guac_socket* socket, const guac_layer* layer) {
int ret_val; int ret_val;
@ -1284,6 +1325,22 @@ int guac_protocol_send_transform(guac_socket* socket, const guac_layer* layer,
} }
int guac_protocol_send_undefine(guac_socket* socket,
const guac_object* object) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "8.undefine,")
|| __guac_socket_write_length_int(socket, object->index)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_video(guac_socket* socket, const guac_stream* stream, int guac_protocol_send_video(guac_socket* socket, const guac_stream* stream,
const guac_layer* layer, const char* mimetype, double duration) { const guac_layer* layer, const char* mimetype, double duration) {