Merge pull request #109 from glyptodon/fake-merge-screen-sharing-001-libguac
GUAC-1389: Refactor libguac to allow screen sharing.
This commit is contained in:
commit
03f9cde27c
14
Makefile.am
14
Makefile.am
@ -37,32 +37,30 @@ DIST_SUBDIRS = \
|
||||
|
||||
SUBDIRS = \
|
||||
src/libguac \
|
||||
src/common \
|
||||
src/guacd \
|
||||
tests
|
||||
|
||||
if ENABLE_COMMON_SSH
|
||||
SUBDIRS += src/common-ssh
|
||||
#SUBDIRS += src/common-ssh
|
||||
endif
|
||||
|
||||
if ENABLE_TERMINAL
|
||||
SUBDIRS += src/terminal
|
||||
#SUBDIRS += src/terminal
|
||||
endif
|
||||
|
||||
if ENABLE_RDP
|
||||
SUBDIRS += src/protocols/rdp
|
||||
#SUBDIRS += src/protocols/rdp
|
||||
endif
|
||||
|
||||
if ENABLE_SSH
|
||||
SUBDIRS += src/protocols/ssh
|
||||
#SUBDIRS += src/protocols/ssh
|
||||
endif
|
||||
|
||||
if ENABLE_TELNET
|
||||
SUBDIRS += src/protocols/telnet
|
||||
#SUBDIRS += src/protocols/telnet
|
||||
endif
|
||||
|
||||
if ENABLE_VNC
|
||||
SUBDIRS += src/protocols/vnc
|
||||
#SUBDIRS += src/protocols/vnc
|
||||
endif
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
@ -38,16 +38,15 @@ libguacinc_HEADERS = \
|
||||
guacamole/error.h \
|
||||
guacamole/error-types.h \
|
||||
guacamole/hash.h \
|
||||
guacamole/instruction-constants.h \
|
||||
guacamole/instruction.h \
|
||||
guacamole/instruction-types.h \
|
||||
guacamole/layer.h \
|
||||
guacamole/layer-types.h \
|
||||
guacamole/object.h \
|
||||
guacamole/object-types.h \
|
||||
guacamole/parser-constants.h \
|
||||
guacamole/parser.h \
|
||||
guacamole/parser-types.h \
|
||||
guacamole/plugin-constants.h \
|
||||
guacamole/plugin.h \
|
||||
guacamole/plugin-types.h \
|
||||
guacamole/pool.h \
|
||||
guacamole/pool-types.h \
|
||||
guacamole/protocol.h \
|
||||
@ -60,26 +59,30 @@ libguacinc_HEADERS = \
|
||||
guacamole/stream-types.h \
|
||||
guacamole/timestamp.h \
|
||||
guacamole/timestamp-types.h \
|
||||
guacamole/unicode.h
|
||||
guacamole/unicode.h \
|
||||
guacamole/user.h \
|
||||
guacamole/user-constants.h \
|
||||
guacamole/user-fntypes.h \
|
||||
guacamole/user-types.h
|
||||
|
||||
noinst_HEADERS = \
|
||||
client-handlers.h \
|
||||
id.h \
|
||||
encode-jpeg.h \
|
||||
encode-png.h \
|
||||
palette.h \
|
||||
user-handlers.h \
|
||||
raw_encoder.h
|
||||
|
||||
libguac_la_SOURCES = \
|
||||
audio.c \
|
||||
client.c \
|
||||
client-handlers.c \
|
||||
encode-jpeg.c \
|
||||
encode-png.c \
|
||||
error.c \
|
||||
hash.c \
|
||||
instruction.c \
|
||||
id.c \
|
||||
palette.c \
|
||||
plugin.c \
|
||||
parser.c \
|
||||
pool.c \
|
||||
protocol.c \
|
||||
raw_encoder.c \
|
||||
@ -87,7 +90,9 @@ libguac_la_SOURCES = \
|
||||
socket-fd.c \
|
||||
socket-nest.c \
|
||||
timestamp.c \
|
||||
unicode.c
|
||||
unicode.c \
|
||||
user.c \
|
||||
user-handlers.c
|
||||
|
||||
# Compile WebP support if available
|
||||
if ENABLE_WEBP
|
||||
|
@ -28,51 +28,74 @@
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/stream.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Assigns a new audio encoder to the given guac_audio_stream based on the
|
||||
* audio mimetypes declared as supported by the given user. If no audio encoder
|
||||
* can be found, no new audio encoder is assigned, and the existing encoder is
|
||||
* left untouched (if any).
|
||||
*
|
||||
* @param owner
|
||||
* The user whose supported audio mimetypes should determine the audio
|
||||
* encoder selected. It is expected that this user will be the owner of
|
||||
* the connection.
|
||||
*
|
||||
* @param data
|
||||
* The guac_audio_stream to which the new encoder should be assigned.
|
||||
* Existing properties set on this audio stream, such as the bits per
|
||||
* sample, may affect the encoder chosen.
|
||||
*
|
||||
* @return
|
||||
* The assigned audio encoder. If no new audio encoder can be assigned,
|
||||
* this will be the currently-assigned audio encoder (which may be NULL).
|
||||
*/
|
||||
static void* guac_audio_assign_encoder(guac_user* owner, void* data) {
|
||||
|
||||
int i;
|
||||
|
||||
guac_audio_stream* audio = (guac_audio_stream*) data;
|
||||
int bps = audio->bps;
|
||||
|
||||
/* If there is no owner, do not attempt to assign a new encoder */
|
||||
if (owner == NULL)
|
||||
return audio->encoder;
|
||||
|
||||
/* For each supported mimetype, check for an associated encoder */
|
||||
for (i=0; owner->info.audio_mimetypes[i] != NULL; i++) {
|
||||
|
||||
const char* mimetype = owner->info.audio_mimetypes[i];
|
||||
|
||||
/* If 16-bit raw audio is supported, done. */
|
||||
if (bps == 16 && strcmp(mimetype, raw16_encoder->mimetype) == 0) {
|
||||
audio->encoder = raw16_encoder;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If 8-bit raw audio is supported, done. */
|
||||
if (bps == 8 && strcmp(mimetype, raw8_encoder->mimetype) == 0) {
|
||||
audio->encoder = raw8_encoder;
|
||||
break;
|
||||
}
|
||||
|
||||
} /* end for each mimetype */
|
||||
|
||||
/* Return assigned encoder, if any */
|
||||
return audio->encoder;
|
||||
|
||||
}
|
||||
|
||||
guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
|
||||
guac_audio_encoder* encoder, int rate, int channels, int bps) {
|
||||
|
||||
guac_audio_stream* audio;
|
||||
|
||||
/* Choose an encoding if not specified */
|
||||
if (encoder == NULL) {
|
||||
|
||||
int i;
|
||||
|
||||
/* For each supported mimetype, check for an associated encoder */
|
||||
for (i=0; client->info.audio_mimetypes[i] != NULL; i++) {
|
||||
|
||||
const char* mimetype = client->info.audio_mimetypes[i];
|
||||
|
||||
/* If 16-bit raw audio is supported, done. */
|
||||
if (bps == 16 && strcmp(mimetype, raw16_encoder->mimetype) == 0) {
|
||||
encoder = raw16_encoder;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If 8-bit raw audio is supported, done. */
|
||||
if (bps == 8 && strcmp(mimetype, raw8_encoder->mimetype) == 0) {
|
||||
encoder = raw8_encoder;
|
||||
break;
|
||||
}
|
||||
|
||||
} /* end for each mimetype */
|
||||
|
||||
/* If still no encoder could be found, fail */
|
||||
if (encoder == NULL)
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
/* Allocate stream */
|
||||
audio = (guac_audio_stream*) calloc(1, sizeof(guac_audio_stream));
|
||||
audio->client = client;
|
||||
|
||||
/* Assign encoder */
|
||||
audio->encoder = encoder;
|
||||
audio->stream = guac_client_alloc_stream(client);
|
||||
|
||||
/* Load PCM properties */
|
||||
@ -80,6 +103,13 @@ guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
|
||||
audio->channels = channels;
|
||||
audio->bps = bps;
|
||||
|
||||
/* Assign encoder for owner, abort if no encoder can be found */
|
||||
if (!guac_client_for_owner(client, guac_audio_assign_encoder, audio)) {
|
||||
guac_client_free_stream(client, audio->stream);
|
||||
free(audio);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Call handler, if defined */
|
||||
if (audio->encoder->begin_handler)
|
||||
audio->encoder->begin_handler(audio);
|
||||
@ -118,6 +148,16 @@ void guac_audio_stream_reset(guac_audio_stream* audio,
|
||||
|
||||
}
|
||||
|
||||
void guac_audio_stream_add_user(guac_audio_stream* audio, guac_user* user) {
|
||||
|
||||
guac_audio_encoder* encoder = audio->encoder;
|
||||
|
||||
/* Notify encoder that a new user is present */
|
||||
if (encoder->join_handler)
|
||||
encoder->join_handler(audio, user);
|
||||
|
||||
}
|
||||
|
||||
void guac_audio_stream_free(guac_audio_stream* audio) {
|
||||
|
||||
/* Flush stream encoding */
|
||||
|
@ -1,400 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "client-handlers.h"
|
||||
#include "instruction.h"
|
||||
#include "object.h"
|
||||
#include "protocol.h"
|
||||
#include "stream.h"
|
||||
#include "timestamp.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Guacamole instruction handler map */
|
||||
|
||||
__guac_instruction_handler_mapping __guac_instruction_handler_map[] = {
|
||||
{"sync", __guac_handle_sync},
|
||||
{"mouse", __guac_handle_mouse},
|
||||
{"key", __guac_handle_key},
|
||||
{"clipboard", __guac_handle_clipboard},
|
||||
{"disconnect", __guac_handle_disconnect},
|
||||
{"size", __guac_handle_size},
|
||||
{"file", __guac_handle_file},
|
||||
{"pipe", __guac_handle_pipe},
|
||||
{"ack", __guac_handle_ack},
|
||||
{"blob", __guac_handle_blob},
|
||||
{"end", __guac_handle_end},
|
||||
{"get", __guac_handle_get},
|
||||
{"put", __guac_handle_put},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int64_t __guac_parse_int(const char* str) {
|
||||
|
||||
int sign = 1;
|
||||
int64_t num = 0;
|
||||
|
||||
for (; *str != '\0'; str++) {
|
||||
|
||||
if (*str == '-')
|
||||
sign = -sign;
|
||||
else
|
||||
num = num * 10 + (*str - '0');
|
||||
|
||||
}
|
||||
|
||||
return num * sign;
|
||||
|
||||
}
|
||||
|
||||
/* Guacamole instruction handlers */
|
||||
|
||||
int __guac_handle_sync(guac_client* client, guac_instruction* instruction) {
|
||||
guac_timestamp timestamp = __guac_parse_int(instruction->argv[0]);
|
||||
|
||||
/* Error if timestamp is in future */
|
||||
if (timestamp > client->last_sent_timestamp)
|
||||
return -1;
|
||||
|
||||
client->last_received_timestamp = timestamp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_mouse(guac_client* client, guac_instruction* instruction) {
|
||||
if (client->mouse_handler)
|
||||
return client->mouse_handler(
|
||||
client,
|
||||
atoi(instruction->argv[0]), /* x */
|
||||
atoi(instruction->argv[1]), /* y */
|
||||
atoi(instruction->argv[2]) /* mask */
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_key(guac_client* client, guac_instruction* instruction) {
|
||||
if (client->key_handler)
|
||||
return client->key_handler(
|
||||
client,
|
||||
atoi(instruction->argv[0]), /* keysym */
|
||||
atoi(instruction->argv[1]) /* pressed */
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static guac_stream* __get_input_stream(guac_client* client, int stream_index) {
|
||||
|
||||
/* Validate stream index */
|
||||
if (stream_index < 0 || stream_index >= GUAC_CLIENT_MAX_STREAMS) {
|
||||
|
||||
guac_stream dummy_stream;
|
||||
dummy_stream.index = stream_index;
|
||||
|
||||
guac_protocol_send_ack(client->socket, &dummy_stream,
|
||||
"Invalid stream index", GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &(client->__input_streams[stream_index]);
|
||||
|
||||
}
|
||||
|
||||
static guac_stream* __get_open_input_stream(guac_client* client, int stream_index) {
|
||||
|
||||
guac_stream* stream = __get_input_stream(client, stream_index);
|
||||
|
||||
/* Fail if no such stream */
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Validate initialization of stream */
|
||||
if (stream->index == GUAC_CLIENT_CLOSED_STREAM_INDEX) {
|
||||
|
||||
guac_stream dummy_stream;
|
||||
dummy_stream.index = stream_index;
|
||||
|
||||
guac_protocol_send_ack(client->socket, &dummy_stream,
|
||||
"Invalid stream index", GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stream;
|
||||
|
||||
}
|
||||
|
||||
static guac_stream* __init_input_stream(guac_client* client, int stream_index) {
|
||||
|
||||
guac_stream* stream = __get_input_stream(client, stream_index);
|
||||
|
||||
/* Fail if no such stream */
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Initialize stream */
|
||||
stream->index = stream_index;
|
||||
stream->data = NULL;
|
||||
stream->ack_handler = NULL;
|
||||
stream->blob_handler = NULL;
|
||||
stream->end_handler = NULL;
|
||||
|
||||
return stream;
|
||||
|
||||
}
|
||||
|
||||
int __guac_handle_clipboard(guac_client* client, guac_instruction* instruction) {
|
||||
|
||||
/* Pull corresponding stream */
|
||||
int stream_index = atoi(instruction->argv[0]);
|
||||
guac_stream* stream = __init_input_stream(client, stream_index);
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* If supported, call handler */
|
||||
if (client->clipboard_handler)
|
||||
return client->clipboard_handler(
|
||||
client,
|
||||
stream,
|
||||
instruction->argv[1] /* mimetype */
|
||||
);
|
||||
|
||||
/* Otherwise, abort */
|
||||
guac_protocol_send_ack(client->socket, stream,
|
||||
"Clipboard unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int __guac_handle_size(guac_client* client, guac_instruction* instruction) {
|
||||
if (client->size_handler)
|
||||
return client->size_handler(
|
||||
client,
|
||||
atoi(instruction->argv[0]), /* width */
|
||||
atoi(instruction->argv[1]) /* height */
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_file(guac_client* client, guac_instruction* instruction) {
|
||||
|
||||
/* Pull corresponding stream */
|
||||
int stream_index = atoi(instruction->argv[0]);
|
||||
guac_stream* stream = __init_input_stream(client, stream_index);
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* If supported, call handler */
|
||||
if (client->file_handler)
|
||||
return client->file_handler(
|
||||
client,
|
||||
stream,
|
||||
instruction->argv[1], /* mimetype */
|
||||
instruction->argv[2] /* filename */
|
||||
);
|
||||
|
||||
/* Otherwise, abort */
|
||||
guac_protocol_send_ack(client->socket, stream,
|
||||
"File transfer unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_pipe(guac_client* client, guac_instruction* instruction) {
|
||||
|
||||
/* Pull corresponding stream */
|
||||
int stream_index = atoi(instruction->argv[0]);
|
||||
guac_stream* stream = __init_input_stream(client, stream_index);
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* If supported, call handler */
|
||||
if (client->pipe_handler)
|
||||
return client->pipe_handler(
|
||||
client,
|
||||
stream,
|
||||
instruction->argv[1], /* mimetype */
|
||||
instruction->argv[2] /* name */
|
||||
);
|
||||
|
||||
/* Otherwise, abort */
|
||||
guac_protocol_send_ack(client->socket, stream,
|
||||
"Named pipes unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_ack(guac_client* client, guac_instruction* instruction) {
|
||||
|
||||
guac_stream* stream;
|
||||
|
||||
/* Validate stream index */
|
||||
int stream_index = atoi(instruction->argv[0]);
|
||||
if (stream_index < 0 || stream_index >= GUAC_CLIENT_MAX_STREAMS)
|
||||
return 0;
|
||||
|
||||
stream = &(client->__output_streams[stream_index]);
|
||||
|
||||
/* Validate initialization of stream */
|
||||
if (stream->index == GUAC_CLIENT_CLOSED_STREAM_INDEX)
|
||||
return 0;
|
||||
|
||||
/* Call stream handler if defined */
|
||||
if (stream->ack_handler)
|
||||
return stream->ack_handler(client, stream, instruction->argv[1],
|
||||
atoi(instruction->argv[2]));
|
||||
|
||||
/* Fall back to global handler if defined */
|
||||
if (client->ack_handler)
|
||||
return client->ack_handler(client, stream, instruction->argv[1],
|
||||
atoi(instruction->argv[2]));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_blob(guac_client* client, guac_instruction* instruction) {
|
||||
|
||||
int stream_index = atoi(instruction->argv[0]);
|
||||
guac_stream* stream = __get_open_input_stream(client, stream_index);
|
||||
|
||||
/* Fail if no such stream */
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* Call stream handler if defined */
|
||||
if (stream->blob_handler) {
|
||||
int length = guac_protocol_decode_base64(instruction->argv[1]);
|
||||
return stream->blob_handler(client, stream, instruction->argv[1],
|
||||
length);
|
||||
}
|
||||
|
||||
/* Fall back to global handler if defined */
|
||||
if (client->blob_handler) {
|
||||
int length = guac_protocol_decode_base64(instruction->argv[1]);
|
||||
return client->blob_handler(client, stream, instruction->argv[1],
|
||||
length);
|
||||
}
|
||||
|
||||
guac_protocol_send_ack(client->socket, stream,
|
||||
"File transfer unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_end(guac_client* client, guac_instruction* instruction) {
|
||||
|
||||
int result = 0;
|
||||
int stream_index = atoi(instruction->argv[0]);
|
||||
guac_stream* stream = __get_open_input_stream(client, stream_index);
|
||||
|
||||
/* Fail if no such stream */
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* Call stream handler if defined */
|
||||
if (stream->end_handler)
|
||||
result = stream->end_handler(client, stream);
|
||||
|
||||
/* Fall back to global handler if defined */
|
||||
if (client->end_handler)
|
||||
result = client->end_handler(client, stream);
|
||||
|
||||
/* Mark stream as closed */
|
||||
stream->index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
|
||||
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) {
|
||||
guac_client_stop(client);
|
||||
return 0;
|
||||
}
|
||||
|
@ -23,29 +23,22 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "client-handlers.h"
|
||||
#include "encode-jpeg.h"
|
||||
#include "encode-png.h"
|
||||
#include "encode-webp.h"
|
||||
#include "error.h"
|
||||
#include "instruction.h"
|
||||
#include "id.h"
|
||||
#include "layer.h"
|
||||
#include "object.h"
|
||||
#include "pool.h"
|
||||
#include "plugin.h"
|
||||
#include "protocol.h"
|
||||
#include "socket.h"
|
||||
#include "stream.h"
|
||||
#include "timestamp.h"
|
||||
#include "user.h"
|
||||
|
||||
#ifdef ENABLE_WEBP
|
||||
#include "encode-webp.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OSSP_UUID_H
|
||||
#include <ossp/uuid.h>
|
||||
#else
|
||||
#include <uuid.h>
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -57,6 +50,23 @@ guac_layer __GUAC_DEFAULT_LAYER = {
|
||||
|
||||
const guac_layer* GUAC_DEFAULT_LAYER = &__GUAC_DEFAULT_LAYER;
|
||||
|
||||
/**
|
||||
* Single chunk of data, to be broadcast to all users.
|
||||
*/
|
||||
typedef struct __write_chunk {
|
||||
|
||||
/**
|
||||
* The buffer to write.
|
||||
*/
|
||||
const void* buffer;
|
||||
|
||||
/**
|
||||
* The number of bytes in the buffer.
|
||||
*/
|
||||
size_t length;
|
||||
|
||||
} __write_chunk;
|
||||
|
||||
guac_layer* guac_client_alloc_layer(guac_client* client) {
|
||||
|
||||
/* Init new layer */
|
||||
@ -109,9 +119,9 @@ guac_stream* guac_client_alloc_stream(guac_client* client) {
|
||||
/* Allocate stream */
|
||||
stream_index = guac_pool_next_int(client->__stream_pool);
|
||||
|
||||
/* Initialize stream */
|
||||
/* Initialize stream with odd index (even indices are user-level) */
|
||||
allocd_stream = &(client->__output_streams[stream_index]);
|
||||
allocd_stream->index = stream_index;
|
||||
allocd_stream->index = (stream_index * 2) + 1;
|
||||
allocd_stream->data = NULL;
|
||||
allocd_stream->ack_handler = NULL;
|
||||
allocd_stream->blob_handler = NULL;
|
||||
@ -124,107 +134,262 @@ guac_stream* guac_client_alloc_stream(guac_client* client) {
|
||||
void guac_client_free_stream(guac_client* client, guac_stream* stream) {
|
||||
|
||||
/* Release index to pool */
|
||||
guac_pool_free_int(client->__stream_pool, stream->index);
|
||||
guac_pool_free_int(client->__stream_pool, (stream->index - 1) / 2);
|
||||
|
||||
/* Mark stream as closed */
|
||||
stream->index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
|
||||
|
||||
}
|
||||
|
||||
guac_object* guac_client_alloc_object(guac_client* client) {
|
||||
/**
|
||||
* Callback which handles read requests on the broadcast socket. This callback
|
||||
* always fails, as the broadcast socket is write-only; it cannot be read.
|
||||
*
|
||||
* @param socket
|
||||
* The broadcast socket to read from.
|
||||
*
|
||||
* @param buf
|
||||
* The buffer into which data should be read.
|
||||
*
|
||||
* @param count
|
||||
* The number of bytes to attempt to read.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes read, or -1 if an error occurs. This implementation
|
||||
* always returns -1, as the broadcast socket is write-only and cannot be
|
||||
* read.
|
||||
*/
|
||||
static ssize_t __guac_socket_broadcast_read_handler(guac_socket* socket,
|
||||
void* buf, size_t count) {
|
||||
|
||||
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;
|
||||
/* Broadcast socket reads are not allowed */
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a newly allocated string containing a guaranteed-unique connection
|
||||
* identifier string which is 37 characters long and begins with a '$'
|
||||
* character. If an error occurs, NULL is returned, and no memory is
|
||||
* allocated.
|
||||
* Callback invoked by guac_client_foreach_user() which write a given chunk of
|
||||
* data to that user's socket. If the write attempt fails, the user is
|
||||
* signalled to stop with guac_user_stop().
|
||||
*
|
||||
* @param user
|
||||
* The user that the chunk of data should be written to.
|
||||
*
|
||||
* @param data
|
||||
* A pointer to a __write_chunk which describes the data to be written.
|
||||
*
|
||||
* @return
|
||||
* Always NULL.
|
||||
*/
|
||||
static char* __guac_generate_connection_id() {
|
||||
static void* __write_chunk_callback(guac_user* user, void* data) {
|
||||
|
||||
char* buffer;
|
||||
char* identifier;
|
||||
size_t identifier_length;
|
||||
__write_chunk* chunk = (__write_chunk*) data;
|
||||
|
||||
uuid_t* uuid;
|
||||
/* Attempt write, disconnect on failure */
|
||||
if (guac_socket_write(user->socket, chunk->buffer, chunk->length))
|
||||
guac_user_stop(user);
|
||||
|
||||
/* Attempt to create UUID object */
|
||||
if (uuid_create(&uuid) != UUID_RC_OK) {
|
||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||
guac_error_message = "Could not allocate memory for UUID";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Generate random UUID */
|
||||
if (uuid_make(uuid, UUID_MAKE_V4) != UUID_RC_OK) {
|
||||
uuid_destroy(uuid);
|
||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||
guac_error_message = "UUID generation failed";
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket write handler which operates on each of the sockets of all connected
|
||||
* users. This write handler will always succeed, but any failing user-specific
|
||||
* writes will invoke guac_user_stop() on the failing user.
|
||||
*
|
||||
* @param socket
|
||||
* The socket to which the given data must be written.
|
||||
*
|
||||
* @param buf
|
||||
* The buffer containing the data to write.
|
||||
*
|
||||
* @param count
|
||||
* The number of bytes to attempt to write from the given buffer.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes written, or -1 if an error occurs. This handler will
|
||||
* always succeed, and thus will always return the exact number of bytes
|
||||
* specified by count.
|
||||
*/
|
||||
static ssize_t __guac_socket_broadcast_write_handler(guac_socket* socket,
|
||||
const void* buf, size_t count) {
|
||||
|
||||
guac_client* client = (guac_client*) socket->data;
|
||||
|
||||
/* Build chunk */
|
||||
__write_chunk chunk;
|
||||
chunk.buffer = buf;
|
||||
chunk.length = count;
|
||||
|
||||
/* Broadcast chunk to all users */
|
||||
guac_client_foreach_user(client, __write_chunk_callback, &chunk);
|
||||
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback which is invoked by guac_client_foreach_user() to flush all
|
||||
* pending data on the given user's socket. If an error occurs while flushing
|
||||
* a user's socket, that user is signalled to stop with guac_user_stop().
|
||||
*
|
||||
* @param user
|
||||
* The user whose socket should be flushed.
|
||||
*
|
||||
* @param data
|
||||
* Arbitrary data passed to guac_client_foreach_user(). This is not needed
|
||||
* by this callback, and should be left as NULL.
|
||||
*
|
||||
* @return
|
||||
* Always NULL.
|
||||
*/
|
||||
static void* __flush_callback(guac_user* user, void* data) {
|
||||
|
||||
/* Attempt flush, disconnect on failure */
|
||||
if (guac_socket_flush(user->socket))
|
||||
guac_user_stop(user);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate buffer for future formatted ID */
|
||||
buffer = malloc(UUID_LEN_STR + 2);
|
||||
if (buffer == NULL) {
|
||||
uuid_destroy(uuid);
|
||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||
guac_error_message = "Could not allocate memory for connection ID";
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket flush handler which operates on each of the sockets of all connected
|
||||
* users. This flush handler will always succeed, but any failing user-specific
|
||||
* flush will invoke guac_user_stop() on the failing user.
|
||||
*
|
||||
* @param socket
|
||||
* The broadcast socket to flush.
|
||||
*
|
||||
* @return
|
||||
* Zero if the flush operation succeeds, non-zero if the operation fails.
|
||||
* This handler will always succeed, and thus will always return zero.
|
||||
*/
|
||||
static ssize_t __guac_socket_broadcast_flush_handler(guac_socket* socket) {
|
||||
|
||||
guac_client* client = (guac_client*) socket->data;
|
||||
|
||||
/* Flush all users */
|
||||
guac_client_foreach_user(client, __flush_callback, NULL);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback which is invoked by guac_client_foreach_user() to lock the given
|
||||
* user's socket in preparation for the beginning of a Guacamole protocol
|
||||
* instruction.
|
||||
*
|
||||
* @param user
|
||||
* The user whose socket should be locked.
|
||||
*
|
||||
* @param data
|
||||
* Arbitrary data passed to guac_client_foreach_user(). This is not needed
|
||||
* by this callback, and should be left as NULL.
|
||||
*
|
||||
* @return
|
||||
* Always NULL.
|
||||
*/
|
||||
static void* __lock_callback(guac_user* user, void* data) {
|
||||
|
||||
/* Lock socket */
|
||||
guac_socket_instruction_begin(user->socket);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
identifier = &(buffer[1]);
|
||||
identifier_length = UUID_LEN_STR + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket lock handler which acquires the socket locks of all connected users.
|
||||
* Socket-level locks are acquired in preparation for the beginning of a new
|
||||
* Guacamole instruction to ensure that parallel writes are only interleaved at
|
||||
* instruction boundaries.
|
||||
*
|
||||
* @param socket
|
||||
* The broadcast socket to lock.
|
||||
*/
|
||||
static void __guac_socket_broadcast_lock_handler(guac_socket* socket) {
|
||||
|
||||
guac_client* client = (guac_client*) socket->data;
|
||||
|
||||
/* Lock sockets of all users */
|
||||
guac_client_foreach_user(client, __lock_callback, NULL);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback which is invoked by guac_client_foreach_user() to unlock the given
|
||||
* user's socket at the end of a Guacamole protocol instruction.
|
||||
*
|
||||
* @param user
|
||||
* The user whose socket should be unlocked.
|
||||
*
|
||||
* @param data
|
||||
* Arbitrary data passed to guac_client_foreach_user(). This is not needed
|
||||
* by this callback, and should be left as NULL.
|
||||
*
|
||||
* @return
|
||||
* Always NULL.
|
||||
*/
|
||||
static void* __unlock_callback(guac_user* user, void* data) {
|
||||
|
||||
/* Unlock socket */
|
||||
guac_socket_instruction_end(user->socket);
|
||||
|
||||
/* Build connection ID from UUID */
|
||||
if (uuid_export(uuid, UUID_FMT_STR, &identifier, &identifier_length) != UUID_RC_OK) {
|
||||
free(buffer);
|
||||
uuid_destroy(uuid);
|
||||
guac_error = GUAC_STATUS_INTERNAL_ERROR;
|
||||
guac_error_message = "Conversion of UUID to connection ID failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uuid_destroy(uuid);
|
||||
}
|
||||
|
||||
buffer[0] = '$';
|
||||
buffer[UUID_LEN_STR + 1] = '\0';
|
||||
return buffer;
|
||||
/**
|
||||
* Socket unlock handler which releases the socket locks of all connected users.
|
||||
* Socket-level locks are released after a Guacamole instruction has finished
|
||||
* being written.
|
||||
*
|
||||
* @param socket
|
||||
* The broadcast socket to unlock.
|
||||
*/
|
||||
static void __guac_socket_broadcast_unlock_handler(guac_socket* socket) {
|
||||
|
||||
guac_client* client = (guac_client*) socket->data;
|
||||
|
||||
/* Unlock sockets of all users */
|
||||
guac_client_foreach_user(client, __unlock_callback, NULL);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback which handles select operations on the broadcast socket, waiting
|
||||
* for data to become available such that the next read operation will not
|
||||
* block. This callback always fails, as the broadcast socket is write-only; it
|
||||
* cannot be read.
|
||||
*
|
||||
* @param socket
|
||||
* The broadcast socket to wait for.
|
||||
*
|
||||
* @param usec_timeout
|
||||
* The maximum amount of time to wait for data, in microseconds, or -1 to
|
||||
* potentially wait forever.
|
||||
*
|
||||
* @return
|
||||
* A positive value on success, zero if the timeout elapsed and no data is
|
||||
* available, or a negative value if an error occurs. This implementation
|
||||
* always returns -1, as the broadcast socket is write-only and cannot be
|
||||
* read.
|
||||
*/
|
||||
static int __guac_socket_broadcast_select_handler(guac_socket* socket,
|
||||
int usec_timeout) {
|
||||
|
||||
/* Selecting the broadcast socket is not possible */
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
guac_client* guac_client_alloc() {
|
||||
|
||||
int i;
|
||||
pthread_rwlockattr_t lock_attributes;
|
||||
|
||||
/* Allocate new client */
|
||||
guac_client* client = malloc(sizeof(guac_client));
|
||||
@ -237,13 +402,11 @@ guac_client* guac_client_alloc() {
|
||||
/* Init new client */
|
||||
memset(client, 0, sizeof(guac_client));
|
||||
|
||||
client->last_received_timestamp =
|
||||
client->state = GUAC_CLIENT_RUNNING;
|
||||
client->last_sent_timestamp = guac_timestamp_current();
|
||||
|
||||
client->state = GUAC_CLIENT_RUNNING;
|
||||
|
||||
/* Generate ID */
|
||||
client->connection_id = __guac_generate_connection_id();
|
||||
client->connection_id = guac_generate_id(GUAC_CLIENT_ID_PREFIX);
|
||||
if (client->connection_id == NULL) {
|
||||
free(client);
|
||||
return NULL;
|
||||
@ -257,21 +420,30 @@ guac_client* guac_client_alloc() {
|
||||
client->__stream_pool = guac_pool_alloc(0);
|
||||
|
||||
/* Initialize streams */
|
||||
client->__input_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS);
|
||||
client->__output_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS);
|
||||
|
||||
for (i=0; i<GUAC_CLIENT_MAX_STREAMS; i++) {
|
||||
client->__input_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;
|
||||
/* Init locks */
|
||||
pthread_rwlockattr_init(&lock_attributes);
|
||||
pthread_rwlockattr_setpshared(&lock_attributes, PTHREAD_PROCESS_SHARED);
|
||||
|
||||
pthread_rwlock_init(&(client->__users_lock), &lock_attributes);
|
||||
|
||||
/* Set up socket to broadcast to all users */
|
||||
guac_socket* socket = guac_socket_alloc();
|
||||
client->socket = socket;
|
||||
socket->data = client;
|
||||
|
||||
socket->read_handler = __guac_socket_broadcast_read_handler;
|
||||
socket->write_handler = __guac_socket_broadcast_write_handler;
|
||||
socket->select_handler = __guac_socket_broadcast_select_handler;
|
||||
socket->flush_handler = __guac_socket_broadcast_flush_handler;
|
||||
socket->lock_handler = __guac_socket_broadcast_lock_handler;
|
||||
socket->unlock_handler = __guac_socket_broadcast_unlock_handler;
|
||||
|
||||
return client;
|
||||
|
||||
@ -279,6 +451,10 @@ guac_client* guac_client_alloc() {
|
||||
|
||||
void guac_client_free(guac_client* client) {
|
||||
|
||||
/* Remove all users */
|
||||
while (client->__users != NULL)
|
||||
guac_client_remove_user(client, client->__users);
|
||||
|
||||
if (client->free_handler) {
|
||||
|
||||
/* FIXME: Errors currently ignored... */
|
||||
@ -291,37 +467,19 @@ void guac_client_free(guac_client* client) {
|
||||
guac_pool_free(client->__layer_pool);
|
||||
|
||||
/* Free streams */
|
||||
free(client->__input_streams);
|
||||
free(client->__output_streams);
|
||||
|
||||
/* Free stream pool */
|
||||
guac_pool_free(client->__stream_pool);
|
||||
|
||||
/* Free objects */
|
||||
free(client->__objects);
|
||||
|
||||
/* Free object pool */
|
||||
guac_pool_free(client->__object_pool);
|
||||
|
||||
free(client);
|
||||
}
|
||||
|
||||
int guac_client_handle_instruction(guac_client* client, guac_instruction* instruction) {
|
||||
|
||||
/* For each defined instruction */
|
||||
__guac_instruction_handler_mapping* current = __guac_instruction_handler_map;
|
||||
while (current->opcode != NULL) {
|
||||
|
||||
/* If recognized, call handler */
|
||||
if (strcmp(instruction->opcode, current->opcode) == 0)
|
||||
return current->handler(client, instruction);
|
||||
|
||||
current++;
|
||||
/* Close associated plugin */
|
||||
if (client->__plugin_handle != NULL) {
|
||||
if (dlclose(client->__plugin_handle))
|
||||
guac_client_log(client, GUAC_LOG_ERROR, "Unable to close plugin: %s", dlerror());
|
||||
}
|
||||
|
||||
/* If unrecognized, ignore */
|
||||
return 0;
|
||||
|
||||
pthread_rwlock_destroy(&(client->__users_lock));
|
||||
free(client);
|
||||
}
|
||||
|
||||
void vguac_client_log(guac_client* client, guac_client_log_level level,
|
||||
@ -381,6 +539,203 @@ void guac_client_abort(guac_client* client, guac_protocol_status status,
|
||||
|
||||
}
|
||||
|
||||
int guac_client_add_user(guac_client* client, guac_user* user, int argc, char** argv) {
|
||||
|
||||
int retval = 0;
|
||||
|
||||
pthread_rwlock_wrlock(&(client->__users_lock));
|
||||
|
||||
/* Call handler, if defined */
|
||||
if (client->join_handler)
|
||||
retval = client->join_handler(user, argc, argv);
|
||||
|
||||
/* Add to list if join was successful */
|
||||
if (retval == 0) {
|
||||
|
||||
user->__prev = NULL;
|
||||
user->__next = client->__users;
|
||||
|
||||
if (client->__users != NULL)
|
||||
client->__users->__prev = user;
|
||||
|
||||
client->__users = user;
|
||||
client->connected_users++;
|
||||
|
||||
/* Update owner pointer if user is owner */
|
||||
if (user->owner)
|
||||
client->__owner = user;
|
||||
|
||||
}
|
||||
|
||||
pthread_rwlock_unlock(&(client->__users_lock));
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
void guac_client_remove_user(guac_client* client, guac_user* user) {
|
||||
|
||||
pthread_rwlock_wrlock(&(client->__users_lock));
|
||||
|
||||
/* Call handler, if defined */
|
||||
if (user->leave_handler)
|
||||
user->leave_handler(user);
|
||||
else if (client->leave_handler)
|
||||
client->leave_handler(user);
|
||||
|
||||
/* Update prev / head */
|
||||
if (user->__prev != NULL)
|
||||
user->__prev->__next = user->__next;
|
||||
else
|
||||
client->__users = user->__next;
|
||||
|
||||
/* Update next */
|
||||
if (user->__next != NULL)
|
||||
user->__next->__prev = user->__prev;
|
||||
|
||||
client->connected_users--;
|
||||
|
||||
/* Update owner pointer if user was owner */
|
||||
if (user->owner)
|
||||
client->__owner = NULL;
|
||||
|
||||
pthread_rwlock_unlock(&(client->__users_lock));
|
||||
|
||||
}
|
||||
|
||||
void guac_client_foreach_user(guac_client* client, guac_user_callback* callback, void* data) {
|
||||
|
||||
guac_user* current;
|
||||
|
||||
pthread_rwlock_rdlock(&(client->__users_lock));
|
||||
|
||||
/* Call function on each user */
|
||||
current = client->__users;
|
||||
while (current != NULL) {
|
||||
callback(current, data);
|
||||
current = current->__next;
|
||||
}
|
||||
|
||||
pthread_rwlock_unlock(&(client->__users_lock));
|
||||
|
||||
}
|
||||
|
||||
void* guac_client_for_owner(guac_client* client, guac_user_callback* callback,
|
||||
void* data) {
|
||||
|
||||
void* retval;
|
||||
|
||||
pthread_rwlock_rdlock(&(client->__users_lock));
|
||||
|
||||
/* Invoke callback with current owner */
|
||||
retval = callback(client->__owner, data);
|
||||
|
||||
pthread_rwlock_unlock(&(client->__users_lock));
|
||||
|
||||
/* Return value from callback */
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
int guac_client_end_frame(guac_client* client) {
|
||||
|
||||
/* Update and send timestamp */
|
||||
client->last_sent_timestamp = guac_timestamp_current();
|
||||
return guac_protocol_send_sync(client->socket, client->last_sent_timestamp);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty NULL-terminated array of argument names.
|
||||
*/
|
||||
const char* __GUAC_CLIENT_NO_ARGS[] = { NULL };
|
||||
|
||||
int guac_client_load_plugin(guac_client* client, const char* protocol) {
|
||||
|
||||
/* Reference to dlopen()'d plugin */
|
||||
void* client_plugin_handle;
|
||||
|
||||
/* Pluggable client */
|
||||
char protocol_lib[GUAC_PROTOCOL_LIBRARY_LIMIT] =
|
||||
GUAC_PROTOCOL_LIBRARY_PREFIX;
|
||||
|
||||
/* Type-pun for the sake of dlsym() - cannot typecast a void* to a function
|
||||
* pointer otherwise */
|
||||
union {
|
||||
guac_client_init_handler* client_init;
|
||||
void* obj;
|
||||
} alias;
|
||||
|
||||
/* Add protocol and .so suffix to protocol_lib */
|
||||
strncat(protocol_lib, protocol, GUAC_PROTOCOL_NAME_LIMIT-1);
|
||||
strcat(protocol_lib, GUAC_PROTOCOL_LIBRARY_SUFFIX);
|
||||
|
||||
/* Load client plugin */
|
||||
client_plugin_handle = dlopen(protocol_lib, RTLD_LAZY);
|
||||
if (!client_plugin_handle) {
|
||||
guac_error = GUAC_STATUS_NOT_FOUND;
|
||||
guac_error_message = dlerror();
|
||||
return -1;
|
||||
}
|
||||
|
||||
dlerror(); /* Clear errors */
|
||||
|
||||
/* Get init function */
|
||||
alias.obj = dlsym(client_plugin_handle, "guac_client_init");
|
||||
|
||||
/* Fail if cannot find guac_client_init */
|
||||
if (dlerror() != NULL) {
|
||||
guac_error = GUAC_STATUS_INTERNAL_ERROR;
|
||||
guac_error_message = dlerror();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Init client */
|
||||
client->args = __GUAC_CLIENT_NO_ARGS;
|
||||
client->__plugin_handle = client_plugin_handle;
|
||||
|
||||
return alias.client_init(client);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the provided approximate processing lag, taking into account the
|
||||
* processing lag of the given user.
|
||||
*
|
||||
* @param user
|
||||
* The guac_user to use to update the approximate processing lag.
|
||||
*
|
||||
* @param data
|
||||
* Pointer to an int containing the current approximate processing lag.
|
||||
* The int will be updated according to the processing lag of the given
|
||||
* user.
|
||||
*
|
||||
* @return
|
||||
* Always NULL.
|
||||
*/
|
||||
static void* __calculate_lag(guac_user* user, void* data) {
|
||||
|
||||
int* processing_lag = (int*) data;
|
||||
|
||||
/* Simply find maximum */
|
||||
if (user->processing_lag > *processing_lag)
|
||||
*processing_lag = user->processing_lag;
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
int guac_client_get_processing_lag(guac_client* client) {
|
||||
|
||||
int processing_lag = 0;
|
||||
|
||||
/* Approximate the processing lag of all users */
|
||||
guac_client_foreach_user(client, __calculate_lag, &processing_lag);
|
||||
|
||||
return processing_lag;
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -448,25 +803,45 @@ void guac_client_stream_webp(guac_client* client, guac_socket* socket,
|
||||
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WEBP
|
||||
/**
|
||||
* Callback which is invoked by guac_client_supports_webp() for each user
|
||||
* associated with the given client, thus updating an overall support flag
|
||||
* describing the WebP support state for the client as a whole.
|
||||
*
|
||||
* @param user
|
||||
* The user to check for WebP support.
|
||||
*
|
||||
* @param data
|
||||
* Pointer to an int containing the current WebP support status for the
|
||||
* client associated with the given user. This flag will be 0 if any user
|
||||
* already checked has lacked WebP support, or 1 otherwise.
|
||||
*
|
||||
* @return
|
||||
* Always NULL.
|
||||
*/
|
||||
static void* __webp_support_callback(guac_user* user, void* data) {
|
||||
|
||||
int* webp_supported = (int*) data;
|
||||
|
||||
/* Check whether current user supports WebP */
|
||||
if (*webp_supported)
|
||||
*webp_supported = guac_user_supports_webp(user);
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
int guac_client_supports_webp(guac_client* client) {
|
||||
|
||||
#ifdef ENABLE_WEBP
|
||||
char** mimetype = client->info.image_mimetypes;
|
||||
int webp_supported = 1;
|
||||
|
||||
/* Search for WebP mimetype in list of supported image mimetypes */
|
||||
while (*mimetype != NULL) {
|
||||
/* WebP is supported for entire client only if each user supports it */
|
||||
guac_client_foreach_user(client, __webp_support_callback, &webp_supported);
|
||||
|
||||
/* If WebP mimetype found, no need to search further */
|
||||
if (strcmp(*mimetype, "image/webp") == 0)
|
||||
return 1;
|
||||
|
||||
/* Next mimetype */
|
||||
mimetype++;
|
||||
|
||||
}
|
||||
|
||||
/* Client does not support WebP */
|
||||
return 0;
|
||||
return webp_supported;
|
||||
#else
|
||||
/* Support for WebP is completely absent */
|
||||
return 0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Glyptodon LLC
|
||||
* Copyright (C) 2016 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
|
||||
@ -20,8 +20,8 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __GUAC_AUDIO_FNTYPES_H
|
||||
#define __GUAC_AUDIO_FNTYPES_H
|
||||
#ifndef GUAC_AUDIO_FNTYPES_H
|
||||
#define GUAC_AUDIO_FNTYPES_H
|
||||
|
||||
/**
|
||||
* Function type definitions related to simple streaming audio.
|
||||
@ -30,24 +30,59 @@
|
||||
*/
|
||||
|
||||
#include "audio-types.h"
|
||||
#include "user-types.h"
|
||||
|
||||
/**
|
||||
* Handler which is called when the audio stream is opened.
|
||||
*
|
||||
* @param audio
|
||||
* The audio stream being opened.
|
||||
*/
|
||||
typedef void guac_audio_encoder_begin_handler(guac_audio_stream* audio);
|
||||
|
||||
/**
|
||||
* Handler which is called when the audio stream needs to be flushed.
|
||||
*
|
||||
* @param audio
|
||||
* The audio stream being flushed.
|
||||
*/
|
||||
typedef void guac_audio_encoder_flush_handler(guac_audio_stream* audio);
|
||||
|
||||
/**
|
||||
* Handler which is called when the audio stream is closed.
|
||||
*
|
||||
* @param audio
|
||||
* The audio stream being closed.
|
||||
*/
|
||||
typedef void guac_audio_encoder_end_handler(guac_audio_stream* audio);
|
||||
|
||||
/**
|
||||
* Handler which is called when PCM data is written to the audio stream.
|
||||
* Handler which is called when a new user has joined the Guacamole
|
||||
* connection associated with the audio stream.
|
||||
*
|
||||
* @param audio
|
||||
* The audio stream associated with the Guacamole connection being
|
||||
* joined.
|
||||
*
|
||||
* @param user
|
||||
* The user that joined the connection.
|
||||
*/
|
||||
typedef void guac_audio_encoder_join_handler(guac_audio_stream* audio,
|
||||
guac_user* user);
|
||||
|
||||
/**
|
||||
* Handler which is called when PCM data is written to the audio stream. The
|
||||
* format of the PCM data is dictated by the properties of the audio stream.
|
||||
*
|
||||
* @param audio
|
||||
* The audio stream to which data is being written.
|
||||
*
|
||||
* @param pcm_data
|
||||
* A buffer containing the raw PCM data to be written.
|
||||
*
|
||||
* @param length
|
||||
* The number of bytes within the buffer that should be written to the
|
||||
* audio stream.
|
||||
*/
|
||||
typedef void guac_audio_encoder_write_handler(guac_audio_stream* audio,
|
||||
const unsigned char* pcm_data, int length);
|
||||
|
@ -64,6 +64,12 @@ struct guac_audio_encoder {
|
||||
*/
|
||||
guac_audio_encoder_end_handler* end_handler;
|
||||
|
||||
/**
|
||||
* Handler which will be called when a new user joins the Guacamole
|
||||
* connection associated with an audio stream.
|
||||
*/
|
||||
guac_audio_encoder_join_handler* join_handler;
|
||||
|
||||
};
|
||||
|
||||
struct guac_audio_stream {
|
||||
@ -108,15 +114,24 @@ struct guac_audio_stream {
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocates a new audio stream which encodes audio data using the given
|
||||
* encoder. If NULL is specified for the encoder, an appropriate encoder
|
||||
* will be selected based on the encoders built into libguac and the level
|
||||
* of client support. The PCM format specified here (via rate, channels, and
|
||||
* Allocates a new audio stream at the client level which encodes audio data
|
||||
* using the given encoder. If NULL is specified for the encoder, an
|
||||
* appropriate encoder will be selected based on the encoders built into
|
||||
* libguac and the level of support declared by the owner associated with the
|
||||
* given guac_client. The PCM format specified here (via rate, channels, and
|
||||
* bps) must be the format used for all PCM data provided to the audio stream.
|
||||
* The format may only be changed using guac_audio_stream_reset().
|
||||
*
|
||||
* If a new user joins the connection after the audio stream is created, that
|
||||
* user will not be aware of the existence of the audio stream, and
|
||||
* guac_audio_stream_add_user() will need to be invoked to recreate the stream
|
||||
* for the new user.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client for which this audio stream is being allocated.
|
||||
* The guac_client for which this audio stream is being allocated. Only the
|
||||
* connection owner is used to determine the level of audio support, and it
|
||||
* is currently assumed that all other joining users on the connection will
|
||||
* have the same level of audio support.
|
||||
*
|
||||
* @param encoder
|
||||
* The guac_audio_encoder to use when encoding audio, or NULL if libguac
|
||||
@ -135,7 +150,8 @@ struct guac_audio_stream {
|
||||
*
|
||||
* @return
|
||||
* The newly allocated guac_audio_stream, or NULL if no audio stream could
|
||||
* be allocated due to lack of client support.
|
||||
* be allocated due to lack of support on the part of the connecting
|
||||
* Guacamole client.
|
||||
*/
|
||||
guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
|
||||
guac_audio_encoder* encoder, int rate, int channels, int bps);
|
||||
@ -168,6 +184,21 @@ guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
|
||||
void guac_audio_stream_reset(guac_audio_stream* audio,
|
||||
guac_audio_encoder* encoder, int rate, int channels, int bps);
|
||||
|
||||
/**
|
||||
* Notifies the given audio stream that a user has joined the connection. The
|
||||
* audio stream itself may need to be restarted. and the audio stream will need
|
||||
* to be created for the new user to ensure they can properly handle future
|
||||
* data received along the stream.
|
||||
*
|
||||
* @param audio
|
||||
* The guac_audio_stream associated with the Guacamole connection being
|
||||
* joined.
|
||||
*
|
||||
* @param user
|
||||
* The user that has joined the Guacamole connection.
|
||||
*/
|
||||
void guac_audio_stream_add_user(guac_audio_stream* audio, guac_user* user);
|
||||
|
||||
/**
|
||||
* Closes and frees the given audio stream.
|
||||
*
|
||||
|
@ -30,7 +30,8 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* The maximum number of inbound streams supported by any one guac_client.
|
||||
* The maximum number of inbound or outbound streams supported by any one
|
||||
* guac_client.
|
||||
*/
|
||||
#define GUAC_CLIENT_MAX_STREAMS 64
|
||||
|
||||
@ -40,26 +41,9 @@
|
||||
#define GUAC_CLIENT_CLOSED_STREAM_INDEX -1
|
||||
|
||||
/**
|
||||
* The maximum number of objects supported by any one guac_client.
|
||||
* The character prefix which identifies a client ID.
|
||||
*/
|
||||
#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"
|
||||
#define GUAC_CLIENT_ID_PREFIX '$'
|
||||
|
||||
/**
|
||||
* The flag set in the mouse button mask when the left mouse button is down.
|
||||
|
@ -34,102 +34,53 @@
|
||||
#include "object-types.h"
|
||||
#include "protocol-types.h"
|
||||
#include "stream-types.h"
|
||||
#include "user-types.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* Handler for server messages (where "server" refers to the server that
|
||||
* the proxy client is connected to).
|
||||
*/
|
||||
typedef int guac_client_handle_messages(guac_client* client);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole mouse events.
|
||||
*/
|
||||
typedef int guac_client_mouse_handler(guac_client* client, int x, int y, int button_mask);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole key events.
|
||||
*/
|
||||
typedef int guac_client_key_handler(guac_client* client, int keysym, int pressed);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole clipboard events.
|
||||
*/
|
||||
typedef int guac_client_clipboard_handler(guac_client* client, guac_stream* stream,
|
||||
char* mimetype);
|
||||
/**
|
||||
* Handler for Guacamole screen size events.
|
||||
*/
|
||||
typedef int guac_client_size_handler(guac_client* client,
|
||||
int width, int height);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole file transfer events.
|
||||
*/
|
||||
typedef int guac_client_file_handler(guac_client* client, guac_stream* stream,
|
||||
char* mimetype, char* filename);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole pipe events.
|
||||
*/
|
||||
typedef int guac_client_pipe_handler(guac_client* client, guac_stream* stream,
|
||||
char* mimetype, char* name);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole stream blob events.
|
||||
*/
|
||||
typedef int guac_client_blob_handler(guac_client* client, guac_stream* stream,
|
||||
void* data, int length);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole stream ack events.
|
||||
*/
|
||||
typedef int guac_client_ack_handler(guac_client* client, guac_stream* stream,
|
||||
char* error, guac_protocol_status status);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole stream end events.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
typedef int guac_client_audio_handler(guac_client* client, char* mimetype);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole video format events.
|
||||
*/
|
||||
typedef int guac_client_video_handler(guac_client* client, char* mimetype);
|
||||
|
||||
/**
|
||||
* Handler for freeing up any extra data allocated by the client
|
||||
* implementation.
|
||||
*
|
||||
* @param client
|
||||
* The client whose extra data should be freed (if any).
|
||||
*
|
||||
* @return
|
||||
* Zero if the data was successfully freed, non-zero if an error prevents
|
||||
* the data from being freed.
|
||||
*/
|
||||
typedef int guac_client_free_handler(guac_client* client);
|
||||
|
||||
/**
|
||||
* Handler for logging messages
|
||||
* Handler for logging messages related to a given guac_client instance.
|
||||
*
|
||||
* @param client
|
||||
* The client related to the message being logged.
|
||||
*
|
||||
* @param level
|
||||
* The log level at which to log the given message.
|
||||
*
|
||||
* @param format
|
||||
* A printf-style format string, defining the message to be logged.
|
||||
*
|
||||
* @param args
|
||||
* The va_list containing the arguments to be used when filling the
|
||||
* conversion specifiers ("%s", "%i", etc.) within the format string.
|
||||
*/
|
||||
typedef void guac_client_log_handler(guac_client* client, guac_client_log_level level, const char* format, va_list args);
|
||||
typedef void guac_client_log_handler(guac_client* client,
|
||||
guac_client_log_level level, const char* format, va_list args);
|
||||
|
||||
/**
|
||||
* Handler which should initialize the given guac_client.
|
||||
* The entry point of a client plugin which must initialize the given
|
||||
* guac_client. In practice, this function will be called "guac_client_init".
|
||||
*
|
||||
* @param client
|
||||
* The guac_client that must be initialized.
|
||||
*
|
||||
* @return
|
||||
* Zero on success, non-zero if initialization fails for any reason.
|
||||
*/
|
||||
typedef int guac_client_init_handler(guac_client* client, int argc, char** argv);
|
||||
typedef int guac_client_init_handler(guac_client* client);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -87,11 +87,5 @@ typedef enum guac_client_log_level {
|
||||
|
||||
} guac_client_log_level;
|
||||
|
||||
/**
|
||||
* Information exposed by the remote client during the connection handshake
|
||||
* which can be used by a client plugin.
|
||||
*/
|
||||
typedef struct guac_client_info guac_client_info;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -32,73 +32,34 @@
|
||||
#include "client-fntypes.h"
|
||||
#include "client-types.h"
|
||||
#include "client-constants.h"
|
||||
#include "instruction-types.h"
|
||||
#include "layer-types.h"
|
||||
#include "object-types.h"
|
||||
#include "pool-types.h"
|
||||
#include "socket-types.h"
|
||||
#include "stream-types.h"
|
||||
#include "timestamp-types.h"
|
||||
#include "user-fntypes.h"
|
||||
#include "user-types.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct guac_client_info {
|
||||
|
||||
/**
|
||||
* The number of pixels the remote client requests for the display width.
|
||||
* This need not be honored by a client plugin implementation, but if the
|
||||
* underlying protocol of the client plugin supports dynamic sizing of the
|
||||
* screen, honoring the display size request is recommended.
|
||||
*/
|
||||
int optimal_width;
|
||||
|
||||
/**
|
||||
* The number of pixels the remote client requests for the display height.
|
||||
* This need not be honored by a client plugin implementation, but if the
|
||||
* underlying protocol of the client plugin supports dynamic sizing of the
|
||||
* screen, honoring the display size request is recommended.
|
||||
*/
|
||||
int optimal_height;
|
||||
|
||||
/**
|
||||
* NULL-terminated array of client-supported audio mimetypes. If the client
|
||||
* does not support audio at all, this will be NULL.
|
||||
*/
|
||||
char** audio_mimetypes;
|
||||
|
||||
/**
|
||||
* NULL-terminated array of client-supported video mimetypes. If the client
|
||||
* does not support video at all, this will be NULL.
|
||||
*/
|
||||
char** video_mimetypes;
|
||||
|
||||
/**
|
||||
* NULL-terminated array of client-supported image mimetypes. Though all
|
||||
* supported image mimetypes will be listed here, it can be safely assumed
|
||||
* that all clients will support at least "image/png" and "image/jpeg".
|
||||
*/
|
||||
char** image_mimetypes;
|
||||
|
||||
/**
|
||||
* The DPI of the physical remote display if configured for the optimal
|
||||
* width/height combination described here. This need not be honored by
|
||||
* a client plugin implementation, but if the underlying protocol of the
|
||||
* client plugin supports dynamic sizing of the screen, honoring the
|
||||
* stated resolution of the display size request is recommended.
|
||||
*/
|
||||
int optimal_resolution;
|
||||
|
||||
};
|
||||
|
||||
struct guac_client {
|
||||
|
||||
/**
|
||||
* The guac_socket structure to be used to communicate with the web-client.
|
||||
* It is expected that the implementor of any Guacamole proxy client will
|
||||
* The guac_socket structure to be used to communicate with all connected
|
||||
* web-clients (users). Unlike the user-level guac_socket, this guac_socket
|
||||
* will broadcast instructions to all connected users simultaneously. It
|
||||
* is expected that the implementor of any Guacamole proxy client will
|
||||
* provide their own mechanism of I/O for their protocol. The guac_socket
|
||||
* structure is used only to communicate conveniently with the Guacamole
|
||||
* web-client.
|
||||
*
|
||||
* Because this socket broadcasts to all connected users, this socket MUST
|
||||
* NOT be used within the same thread as a "leave" or "join" handler. Doing
|
||||
* so results in undefined behavior, including possible segfaults.
|
||||
*/
|
||||
guac_socket* socket;
|
||||
|
||||
@ -110,24 +71,6 @@ struct guac_client {
|
||||
*/
|
||||
guac_client_state state;
|
||||
|
||||
/**
|
||||
* The time (in milliseconds) of receipt of the last sync message from
|
||||
* the client.
|
||||
*/
|
||||
guac_timestamp last_received_timestamp;
|
||||
|
||||
/**
|
||||
* The time (in milliseconds) that the last sync message was sent to the
|
||||
* client.
|
||||
*/
|
||||
guac_timestamp last_sent_timestamp;
|
||||
|
||||
/**
|
||||
* Information structure containing properties exposed by the remote
|
||||
* client during the initial handshake process.
|
||||
*/
|
||||
guac_client_info info;
|
||||
|
||||
/**
|
||||
* Arbitrary reference to proxy client-specific data. Implementors of a
|
||||
* Guacamole proxy client can store any data they want here, which can then
|
||||
@ -136,197 +79,10 @@ struct guac_client {
|
||||
void* data;
|
||||
|
||||
/**
|
||||
* Handler for server messages. If set, this function will be called
|
||||
* occasionally by the Guacamole proxy to give the client a chance to
|
||||
* handle messages from whichever server it is connected to.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int handle_messages(guac_client* client);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* client->handle_messages = handle_messages;
|
||||
* }
|
||||
* @endcode
|
||||
* The time (in milliseconds) that the last sync message was sent to the
|
||||
* client.
|
||||
*/
|
||||
guac_client_handle_messages* handle_messages;
|
||||
|
||||
/**
|
||||
* Handler for mouse events sent by the Gaucamole web-client.
|
||||
*
|
||||
* The handler takes the integer mouse X and Y coordinates, as well as
|
||||
* a button mask containing the bitwise OR of all button values currently
|
||||
* being pressed. Those values are:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Button</th> <th>Value</th></tr>
|
||||
* <tr><td>Left</td> <td>1</td></tr>
|
||||
* <tr><td>Middle</td> <td>2</td></tr>
|
||||
* <tr><td>Right</td> <td>4</td></tr>
|
||||
* <tr><td>Scrollwheel Up</td> <td>8</td></tr>
|
||||
* <tr><td>Scrollwheel Down</td><td>16</td></tr>
|
||||
* </table>
|
||||
|
||||
* Example:
|
||||
* @code
|
||||
* int mouse_handler(guac_client* client, int x, int y, int button_mask);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* client->mouse_handler = mouse_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_mouse_handler* mouse_handler;
|
||||
|
||||
/**
|
||||
* Handler for key events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes the integer X11 keysym associated with the key
|
||||
* being pressed/released, and an integer representing whether the key
|
||||
* is being pressed (1) or released (0).
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int key_handler(guac_client* client, int keysym, int pressed);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* client->key_handler = key_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_key_handler* key_handler;
|
||||
|
||||
/**
|
||||
* Handler for clipboard events sent by the Guacamole web-client. This
|
||||
* handler will be called whenever the web-client sets the data of the
|
||||
* clipboard.
|
||||
*
|
||||
* The handler takes a guac_stream, which contains the stream index and
|
||||
* will persist through the duration of the transfer, and the mimetype
|
||||
* of the data being transferred.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int clipboard_handler(guac_client* client, guac_stream* stream,
|
||||
* char* mimetype);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* client->clipboard_handler = clipboard_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_clipboard_handler* clipboard_handler;
|
||||
|
||||
/**
|
||||
* Handler for size events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes an integer width and integer height, representing
|
||||
* the current visible screen area of the client.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int size_handler(guac_client* client, int width, int height);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* client->size_handler = size_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_size_handler* size_handler;
|
||||
|
||||
/**
|
||||
* Handler for file events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes a guac_stream which contains the stream index and
|
||||
* will persist through the duration of the transfer, the mimetype of
|
||||
* the file being transferred, and the filename.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int file_handler(guac_client* client, guac_stream* stream,
|
||||
* char* mimetype, char* filename);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* client->file_handler = file_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_file_handler* file_handler;
|
||||
|
||||
/**
|
||||
* Handler for pipe events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes a guac_stream which contains the stream index and
|
||||
* will persist through the duration of the transfer, the mimetype of
|
||||
* the data being transferred, and the pipe name.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int pipe_handler(guac_client* client, guac_stream* stream,
|
||||
* char* mimetype, char* name);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* client->pipe_handler = pipe_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_pipe_handler* pipe_handler;
|
||||
|
||||
/**
|
||||
* Handler for ack events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes a guac_stream which contains the stream index and
|
||||
* will persist through the duration of the transfer, a string containing
|
||||
* the error or status message, and a status code.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int ack_handler(guac_client* client, guac_stream* stream,
|
||||
* char* error, guac_protocol_status status);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* client->ack_handler = ack_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_ack_handler* ack_handler;
|
||||
|
||||
/**
|
||||
* Handler for blob events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes a guac_stream which contains the stream index and
|
||||
* will persist through the duration of the transfer, an arbitrary buffer
|
||||
* containing the blob, and the length of the blob.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int blob_handler(guac_client* client, guac_stream* stream,
|
||||
* void* data, int length);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* client->blob_handler = blob_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_blob_handler* blob_handler;
|
||||
|
||||
/**
|
||||
* Handler for stream end events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes only a guac_stream which contains the stream index.
|
||||
* This guac_stream will be disposed of immediately after this event is
|
||||
* finished.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int end_handler(guac_client* client, guac_stream* stream);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* client->end_handler = end_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_end_handler* end_handler;
|
||||
guac_timestamp last_sent_timestamp;
|
||||
|
||||
/**
|
||||
* Handler for freeing data when the client is being unloaded.
|
||||
@ -344,7 +100,7 @@ struct guac_client {
|
||||
* @code
|
||||
* int free_handler(guac_client* client);
|
||||
*
|
||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
||||
* int guac_client_init(guac_client* client) {
|
||||
* client->free_handler = free_handler;
|
||||
* }
|
||||
* @endcode
|
||||
@ -375,46 +131,6 @@ struct guac_client {
|
||||
*/
|
||||
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.
|
||||
* Note that because guac_pool always gives non-negative indices starting
|
||||
@ -435,25 +151,11 @@ struct guac_client {
|
||||
guac_pool* __stream_pool;
|
||||
|
||||
/**
|
||||
* All available output streams (data going to connected client).
|
||||
* All available client-level output streams (data going to all connected
|
||||
* users).
|
||||
*/
|
||||
guac_stream* __output_streams;
|
||||
|
||||
/**
|
||||
* All available input streams (data coming from connected client).
|
||||
*/
|
||||
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
|
||||
* be used within the Guacamole protocol to refer to this connection.
|
||||
@ -463,6 +165,105 @@ struct guac_client {
|
||||
*/
|
||||
char* connection_id;
|
||||
|
||||
/**
|
||||
* Lock which is acquired when the users list is being manipulated, or when
|
||||
* the users list is being iterated.
|
||||
*/
|
||||
pthread_rwlock_t __users_lock;
|
||||
|
||||
/**
|
||||
* The first user within the list of all connected users, or NULL if no
|
||||
* users are currently connected.
|
||||
*/
|
||||
guac_user* __users;
|
||||
|
||||
/**
|
||||
* The user that first created this connection. This user will also have
|
||||
* their "owner" flag set to a non-zero value. If the owner has left the
|
||||
* connection, this will be NULL.
|
||||
*/
|
||||
guac_user* __owner;
|
||||
|
||||
/**
|
||||
* The number of currently-connected users. This value may include inactive
|
||||
* users if cleanup of those users has not yet finished.
|
||||
*/
|
||||
int connected_users;
|
||||
|
||||
/**
|
||||
* Handler for join events, called whenever a new user is joining an active
|
||||
* connection. Note that because users may leave the connection at any
|
||||
* time, a reference to a guac_user can become invalid at any time and
|
||||
* should never be maintained outside the scope of a function invoked by
|
||||
* libguac to which that guac_user was passed (the scope in which the
|
||||
* guac_user reference is guaranteed to be valid) UNLESS that reference is
|
||||
* properly invalidated within the leave_handler.
|
||||
*
|
||||
* The handler is given a pointer to a newly-allocated guac_user which
|
||||
* must then be initialized, if needed.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int join_handler(guac_user* user, int argc, char** argv);
|
||||
*
|
||||
* int guac_client_init(guac_client* client) {
|
||||
* client->join_handler = join_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_join_handler* join_handler;
|
||||
|
||||
/**
|
||||
* Handler for leave events, called whenever a new user is leaving an
|
||||
* active connection.
|
||||
*
|
||||
* The handler is given a pointer to the leaving guac_user whose custom
|
||||
* data and associated resources must now be freed, if any.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int leave_handler(guac_user* user);
|
||||
*
|
||||
* int guac_client_init(guac_client* client) {
|
||||
* client->leave_handler = leave_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_leave_handler* leave_handler;
|
||||
|
||||
/**
|
||||
* NULL-terminated array of all arguments accepted by this client , in
|
||||
* order. New users will specify these arguments when they join the
|
||||
* connection, and the values of those arguments will be made available to
|
||||
* the function initializing newly-joined users.
|
||||
*
|
||||
* The guac_client_init entry point is expected to initialize this, if
|
||||
* arguments are expected.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* const char* __my_args[] = {
|
||||
* "hostname",
|
||||
* "port",
|
||||
* "username",
|
||||
* "password",
|
||||
* NULL
|
||||
* };
|
||||
*
|
||||
* int guac_client_init(guac_client* client) {
|
||||
* client->args = __my_args;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
const char** args;
|
||||
|
||||
/**
|
||||
* Handle to the dlopen()'d plugin, which should be given to dlclose() when
|
||||
* this client is freed. This is only assigned if guac_client_load_plugin()
|
||||
* is used.
|
||||
*/
|
||||
void* __plugin_handle;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@ -480,25 +281,6 @@ guac_client* guac_client_alloc();
|
||||
*/
|
||||
void guac_client_free(guac_client* client);
|
||||
|
||||
/**
|
||||
* Call the appropriate handler defined by the given client for the given
|
||||
* instruction. A comparison is made between the instruction opcode and the
|
||||
* initial handler lookup table defined in client-handlers.c. The intial
|
||||
* handlers will in turn call the client's handler (if defined).
|
||||
*
|
||||
* @param client
|
||||
* The proxy client whose handlers should be called.
|
||||
*
|
||||
* @param instruction
|
||||
* The instruction to pass to the proxy client via the appropriate handler.
|
||||
*
|
||||
* @return
|
||||
* Non-negative if the instruction was handled successfully, or negative
|
||||
* if an error occurred.
|
||||
*/
|
||||
int guac_client_handle_instruction(guac_client* client,
|
||||
guac_instruction* instruction);
|
||||
|
||||
/**
|
||||
* Writes a message in the log used by the given client. The logger used will
|
||||
* normally be defined by guacd (or whichever program loads the proxy client)
|
||||
@ -604,8 +386,11 @@ void guac_client_free_layer(guac_client* client, guac_layer* layer);
|
||||
* Allocates a new stream. An arbitrary index is automatically assigned
|
||||
* if no previously-allocated stream is available for use.
|
||||
*
|
||||
* @param client The proxy client to allocate the layer buffer for.
|
||||
* @return The next available stream, or a newly allocated stream.
|
||||
* @param client
|
||||
* The client to allocate the stream for.
|
||||
*
|
||||
* @return
|
||||
* The next available stream, or a newly allocated stream.
|
||||
*/
|
||||
guac_stream* guac_client_alloc_stream(guac_client* client);
|
||||
|
||||
@ -613,34 +398,134 @@ guac_stream* guac_client_alloc_stream(guac_client* client);
|
||||
* Returns the given stream to the pool of available streams, such that it
|
||||
* can be reused by any subsequent call to guac_client_alloc_stream().
|
||||
*
|
||||
* @param client The proxy client to return the buffer to.
|
||||
* @param stream The stream to return to the pool of available stream.
|
||||
* @param client
|
||||
* The client to return the stream to.
|
||||
*
|
||||
* @param stream
|
||||
* The stream to return to the pool of available 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.
|
||||
* Adds the given user to the internal list of connected users. Future writes
|
||||
* to the broadcast socket stored within guac_client will also write to this
|
||||
* user. The join handler of this guac_client will be called.
|
||||
*
|
||||
* @param client
|
||||
* The proxy client to allocate the object for.
|
||||
*
|
||||
* @return
|
||||
* The next available object, or a newly allocated object.
|
||||
* @param client The proxy client to add the user to.
|
||||
* @param user The user to add.
|
||||
* @param argc The number of arguments to pass to the new user.
|
||||
* @param argv An array of strings containing the argument values being passed.
|
||||
* @return Zero if the user was added successfully, non-zero if the user could
|
||||
* not join the connection.
|
||||
*/
|
||||
guac_object* guac_client_alloc_object(guac_client* client);
|
||||
int guac_client_add_user(guac_client* client, guac_user* user, int argc, char** argv);
|
||||
|
||||
/**
|
||||
* 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().
|
||||
* Removes the given user, removing the user from the internally-tracked list
|
||||
* of connected users, and calling any appropriate leave handler.
|
||||
*
|
||||
* @param client The proxy client to return the buffer to.
|
||||
* @param user The user to remove.
|
||||
*/
|
||||
void guac_client_remove_user(guac_client* client, guac_user* user);
|
||||
|
||||
/**
|
||||
* Calls the given function on all currently-connected users of the given
|
||||
* client. The function will be given a reference to a guac_user and the
|
||||
* specified arbitrary data. The value returned by the callback will be
|
||||
* ignored.
|
||||
*
|
||||
* This function is reentrant, but the user list MUST NOT be manipulated
|
||||
* within the same thread as a callback to this function. Though the callback
|
||||
* MAY invoke guac_client_foreach_user(), doing so should not be necessary, and
|
||||
* may indicate poor design choices.
|
||||
*
|
||||
* Because this function loops through all connected users, this function MUST
|
||||
* NOT be invoked within the same thread as a "leave" or "join" handler. Doing
|
||||
* so results in undefined behavior, including possible segfaults.
|
||||
*
|
||||
* @param client
|
||||
* The proxy client to return the object to.
|
||||
* The client whose users should be iterated.
|
||||
*
|
||||
* @param object
|
||||
* The object to return to the pool of available object.
|
||||
* @param callback
|
||||
* The function to call for each user.
|
||||
*
|
||||
* @param data
|
||||
* Arbitrary data to pass to the callback each time it is invoked.
|
||||
*/
|
||||
void guac_client_free_object(guac_client* client, guac_object* object);
|
||||
void guac_client_foreach_user(guac_client* client,
|
||||
guac_user_callback* callback, void* data);
|
||||
|
||||
/**
|
||||
* Retrieves the connected user that is marked as the owner. The owner of a
|
||||
* connection is the user that established the initial connection that created
|
||||
* the connection (the first user to connect and join).
|
||||
*
|
||||
* Calls the given function on with the currently-connected user that is marked
|
||||
* as the owner. The owner of a connection is the user that established the
|
||||
* initial connection that created the connection (the first user to connect
|
||||
* and join). The function will be given a reference to a guac_user and the
|
||||
* specified arbitrary data. If the owner has since left the connection, the
|
||||
* function will instead be invoked with NULL as the guac_user. The value
|
||||
* returned by the callback will be returned by this function.
|
||||
*
|
||||
* This function is reentrant, but the user list MUST NOT be manipulated
|
||||
* within the same thread as a callback to this function.
|
||||
*
|
||||
* Because this function depends on a consistent list of connected users, this
|
||||
* function MUST NOT be invoked within the same thread as a "leave" or "join"
|
||||
* handler. Doing so results in undefined behavior, including possible
|
||||
* segfaults.
|
||||
*
|
||||
* @param client
|
||||
* The client to retrieve the owner from.
|
||||
*
|
||||
* @return
|
||||
* The value returned by the callback.
|
||||
*/
|
||||
void* guac_client_for_owner(guac_client* client, guac_user_callback* callback,
|
||||
void* data);
|
||||
|
||||
/**
|
||||
* Marks the end of the current frame by sending a "sync" instruction to
|
||||
* all connected users. This instruction will contain the current timestamp.
|
||||
* The last_sent_timestamp member of guac_client will be updated accordingly.
|
||||
*
|
||||
* If an error occurs sending the instruction, a non-zero value is
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param client The guac_client which has finished a frame.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_client_end_frame(guac_client* client);
|
||||
|
||||
/**
|
||||
* Initializes the given guac_client using the initialization routine provided
|
||||
* by the plugin corresponding to the named protocol. This will automatically
|
||||
* invoke guac_client_init within the plugin for the given protocol.
|
||||
*
|
||||
* Note that the connection will likely not be established until the first
|
||||
* user (the "owner") is added to the client.
|
||||
*
|
||||
* @param client The guac_client to initialize.
|
||||
* @param protocol The name of the protocol to use.
|
||||
* @return Zero if initialization was successful, non-zero otherwise.
|
||||
*/
|
||||
int guac_client_load_plugin(guac_client* client, const char* protocol);
|
||||
|
||||
/**
|
||||
* Calculates and returns the approximate processing lag experienced by the
|
||||
* pool of users. The processing lag is the difference in time between server
|
||||
* and client due purely to data processing and excluding network delays.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client to calculate the processing lag of.
|
||||
*
|
||||
* @return
|
||||
* The approximate processing lag of the pool of users associated with the
|
||||
* given guac_client, in milliseconds.
|
||||
*/
|
||||
int guac_client_get_processing_lag(guac_client* client);
|
||||
|
||||
/**
|
||||
* Streams the image data of the given surface over an image stream ("img"
|
||||
@ -648,7 +533,7 @@ void guac_client_free_object(guac_client* client, guac_object* object);
|
||||
* allocated and freed.
|
||||
*
|
||||
* @param client
|
||||
* The Guacamole client from which the image stream should be allocated.
|
||||
* The Guacamole client for which the image stream should be allocated.
|
||||
*
|
||||
* @param socket
|
||||
* The socket over which instructions associated with the image stream
|
||||
@ -681,7 +566,7 @@ void guac_client_stream_png(guac_client* client, guac_socket* socket,
|
||||
* will be automatically allocated and freed.
|
||||
*
|
||||
* @param client
|
||||
* The Guacamole client from which the image stream should be allocated.
|
||||
* The Guacamole client for which the image stream should be allocated.
|
||||
*
|
||||
* @param socket
|
||||
* The socket over which instructions associated with the image stream
|
||||
@ -705,8 +590,9 @@ void guac_client_stream_png(guac_client* client, guac_socket* socket,
|
||||
* A Cairo surface containing the image data to be streamed.
|
||||
*
|
||||
* @param quality
|
||||
* The JPEG image quality, which must be an integer value between 0 and
|
||||
* 100 inclusive.
|
||||
* The JPEG image quality, which must be an integer value between 0 and 100
|
||||
* inclusive. Larger values indicate improving quality at the expense of
|
||||
* larger file size.
|
||||
*/
|
||||
void guac_client_stream_jpeg(guac_client* client, guac_socket* socket,
|
||||
guac_composite_mode mode, const guac_layer* layer, int x, int y,
|
||||
@ -720,7 +606,7 @@ void guac_client_stream_jpeg(guac_client* client, guac_socket* socket,
|
||||
* guac_client_supports_webp() prior to calling this function.
|
||||
*
|
||||
* @param client
|
||||
* The Guacamole client from which the image stream should be allocated.
|
||||
* The Guacamole client for whom the image stream should be allocated.
|
||||
*
|
||||
* @param socket
|
||||
* The socket over which instructions associated with the image stream
|
||||
@ -758,15 +644,15 @@ void guac_client_stream_webp(guac_client* client, guac_socket* socket,
|
||||
cairo_surface_t* surface, int quality, int lossless);
|
||||
|
||||
/**
|
||||
* Returns whether the given client supports WebP. If the client does not
|
||||
* support WebP, or the server cannot encode WebP images, zero is returned.
|
||||
* Returns whether all users of the given client support WebP. If any user does
|
||||
* not support WebP, or the server cannot encode WebP images, zero is returned.
|
||||
*
|
||||
* @param client
|
||||
* The Guacamole client to check for WebP support.
|
||||
* The Guacamole client whose users should be checked for WebP support.
|
||||
*
|
||||
* @return
|
||||
* Non-zero if the given client claims to support WebP and the server has
|
||||
* been built with WebP support, zero otherwise.
|
||||
* Non-zero if the all users of the given client claim to support WebP and
|
||||
* the server has been built with WebP support, zero otherwise.
|
||||
*/
|
||||
int guac_client_supports_webp(guac_client* client);
|
||||
|
||||
|
@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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_INSTRUCTION_H
|
||||
#define _GUAC_INSTRUCTION_H
|
||||
|
||||
/**
|
||||
* Provides functions and structures for reading, writing, and manipulating
|
||||
* Guacamole instructions.
|
||||
*
|
||||
* @file instruction.h
|
||||
*/
|
||||
|
||||
#include "instruction-types.h"
|
||||
#include "instruction-constants.h"
|
||||
#include "socket-types.h"
|
||||
|
||||
struct guac_instruction {
|
||||
|
||||
/**
|
||||
* The opcode of the instruction.
|
||||
*/
|
||||
char* opcode;
|
||||
|
||||
/**
|
||||
* The number of arguments passed to this instruction.
|
||||
*/
|
||||
int argc;
|
||||
|
||||
/**
|
||||
* Array of all arguments passed to this instruction.
|
||||
*/
|
||||
char** argv;
|
||||
|
||||
/**
|
||||
* The parse state of the instruction.
|
||||
*/
|
||||
guac_instruction_parse_state state;
|
||||
|
||||
/**
|
||||
* The length of the current element, if known.
|
||||
*/
|
||||
int __element_length;
|
||||
|
||||
/**
|
||||
* The number of elements currently parsed.
|
||||
*/
|
||||
int __elementc;
|
||||
|
||||
/**
|
||||
* All currently parsed elements.
|
||||
*/
|
||||
char* __elementv[GUAC_INSTRUCTION_MAX_ELEMENTS];
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocates a new instruction. Each instruction contains within itself the
|
||||
* necessary facilities to parse instruction data.
|
||||
*
|
||||
* @return The newly allocated instruction, or NULL if an error occurs during
|
||||
* allocation, in which case guac_error will be set appropriately.
|
||||
*/
|
||||
guac_instruction* guac_instruction_alloc();
|
||||
|
||||
/**
|
||||
* Resets the parse state and contents of the given instruction, such that the
|
||||
* memory of that instruction can be reused for another parse cycle.
|
||||
*
|
||||
* @param instruction The instruction to reset.
|
||||
*/
|
||||
void guac_instruction_reset(guac_instruction* instruction);
|
||||
|
||||
/**
|
||||
* Appends data from the given buffer to the given instruction. The data will
|
||||
* be appended, if possible, to this instruction as a reference and thus the
|
||||
* buffer must remain valid throughout the life of the instruction. This
|
||||
* function may modify the contents of the buffer when those contents are
|
||||
* part of an element within the instruction being read.
|
||||
*
|
||||
* @param instruction The instruction to append data to.
|
||||
* @param buffer A buffer containing data that should be appended to this
|
||||
* instruction.
|
||||
* @param length The number of bytes available for appending within the buffer.
|
||||
* @return The number of bytes appended to this instruction, which may be
|
||||
* zero if more data is needed.
|
||||
*/
|
||||
int guac_instruction_append(guac_instruction* instruction,
|
||||
void* buffer, int length);
|
||||
|
||||
/**
|
||||
* Frees all memory allocated to the given instruction.
|
||||
*
|
||||
* @param instruction The instruction to free.
|
||||
*/
|
||||
void guac_instruction_free(guac_instruction* instruction);
|
||||
|
||||
/**
|
||||
* Returns whether new instruction data is available on the given guac_socket
|
||||
* connection for parsing.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param usec_timeout The maximum number of microseconds to wait before
|
||||
* giving up.
|
||||
* @return A positive value if data is available, negative on error, or
|
||||
* zero if no data is currently available.
|
||||
*/
|
||||
int guac_instruction_waiting(guac_socket* socket, int usec_timeout);
|
||||
|
||||
/**
|
||||
* Reads a single instruction from the given guac_socket connection.
|
||||
*
|
||||
* If an error occurs reading the instruction, NULL is returned,
|
||||
* and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param usec_timeout The maximum number of microseconds to wait before
|
||||
* giving up.
|
||||
* @return A new instruction if data was successfully read, NULL on
|
||||
* error or if the instruction could not be read completely
|
||||
* because the timeout elapsed, in which case guac_error will be
|
||||
* set to GUAC_STATUS_INPUT_TIMEOUT and subsequent calls to
|
||||
* guac_protocol_read_instruction() will return the parsed instruction
|
||||
* once enough data is available.
|
||||
*/
|
||||
guac_instruction* guac_instruction_read(guac_socket* socket, int usec_timeout);
|
||||
|
||||
/**
|
||||
* Reads a single instruction with the given opcode from the given guac_socket
|
||||
* connection.
|
||||
*
|
||||
* If an error occurs reading the instruction, NULL is returned,
|
||||
* and guac_error is set appropriately.
|
||||
*
|
||||
* If the instruction read is not the expected instruction, NULL is returned,
|
||||
* and guac_error is set to GUAC_STATUS_BAD_STATE.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param usec_timeout The maximum number of microseconds to wait before
|
||||
* giving up.
|
||||
* @param opcode The opcode of the instruction to read.
|
||||
* @return A new instruction if an instruction with the given opcode was read,
|
||||
* NULL otherwise. If an instruction was read, but the instruction had
|
||||
* a different opcode, NULL is returned and guac_error is set to
|
||||
* GUAC_STATUS_BAD_STATE.
|
||||
*/
|
||||
guac_instruction* guac_instruction_expect(guac_socket* socket,
|
||||
int usec_timeout, const char* opcode);
|
||||
|
||||
#endif
|
||||
|
@ -29,8 +29,8 @@
|
||||
* @file object.h
|
||||
*/
|
||||
|
||||
#include "client-fntypes.h"
|
||||
#include "object-types.h"
|
||||
#include "user-fntypes.h"
|
||||
|
||||
struct guac_object {
|
||||
|
||||
@ -54,18 +54,18 @@ struct guac_object {
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int get_handler(guac_client* client, guac_object* object,
|
||||
* int get_handler(guac_user* user, guac_object* object,
|
||||
* char* name);
|
||||
*
|
||||
* int some_function(guac_client* client) {
|
||||
* int some_function(guac_user* user) {
|
||||
*
|
||||
* guac_object* object = guac_client_alloc_object(client);
|
||||
* guac_object* object = guac_user_alloc_object(user);
|
||||
* object->get_handler = get_handler;
|
||||
*
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_get_handler* get_handler;
|
||||
guac_user_get_handler* get_handler;
|
||||
|
||||
/**
|
||||
* Handler for put events sent by the Guacamole web-client.
|
||||
@ -77,18 +77,18 @@ struct guac_object {
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int put_handler(guac_client* client, guac_object* object,
|
||||
* int put_handler(guac_user* user, guac_object* object,
|
||||
* guac_stream* stream, char* mimetype, char* name);
|
||||
*
|
||||
* int some_function(guac_client* client) {
|
||||
* int some_function(guac_user* user) {
|
||||
*
|
||||
* guac_object* object = guac_client_alloc_object(client);
|
||||
* guac_object* object = guac_user_alloc_object(user);
|
||||
* object->put_handler = put_handler;
|
||||
*
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_put_handler* put_handler;
|
||||
guac_user_put_handler* put_handler;
|
||||
|
||||
};
|
||||
|
||||
|
@ -20,13 +20,13 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _GUAC_INSTRUCTION_CONSTANTS_H
|
||||
#define _GUAC_INSTRUCTION_CONSTANTS_H
|
||||
#ifndef _GUAC_PARSER_CONSTANTS_H
|
||||
#define _GUAC_PARSER_CONSTANTS_H
|
||||
|
||||
/**
|
||||
* Constants related to Guacamole instructions.
|
||||
* Constants related to the Guacamole protocol parser.
|
||||
*
|
||||
* @file instruction-constants.h
|
||||
* @file parser-constants.h
|
||||
*/
|
||||
|
||||
/**
|
@ -21,48 +21,49 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _GUAC_INSTRUCTION_TYPES_H
|
||||
#define _GUAC_INSTRUCTION_TYPES_H
|
||||
#ifndef _GUAC_PARSER_TYPES_H
|
||||
#define _GUAC_PARSER_TYPES_H
|
||||
|
||||
/**
|
||||
* Type definitions related to Guacamole instructions.
|
||||
* Type definitions related to parsing the Guacamole protocol.
|
||||
*
|
||||
* @file instruction-types.h
|
||||
* @file parser-types.h
|
||||
*/
|
||||
|
||||
/**
|
||||
* All possible states of the instruction parser.
|
||||
*/
|
||||
typedef enum guac_instruction_parse_state {
|
||||
typedef enum guac_parse_state {
|
||||
|
||||
/**
|
||||
* The parser is currently waiting for data to complete the length prefix
|
||||
* of the current element of the instruction.
|
||||
*/
|
||||
GUAC_INSTRUCTION_PARSE_LENGTH,
|
||||
GUAC_PARSE_LENGTH,
|
||||
|
||||
/**
|
||||
* The parser has finished reading the length prefix and is currently
|
||||
* waiting for data to complete the content of the instruction.
|
||||
*/
|
||||
GUAC_INSTRUCTION_PARSE_CONTENT,
|
||||
GUAC_PARSE_CONTENT,
|
||||
|
||||
/**
|
||||
* The instruction has been fully parsed.
|
||||
*/
|
||||
GUAC_INSTRUCTION_PARSE_COMPLETE,
|
||||
GUAC_PARSE_COMPLETE,
|
||||
|
||||
/**
|
||||
* The instruction cannot be parsed because of a protocol error.
|
||||
*/
|
||||
GUAC_INSTRUCTION_PARSE_ERROR
|
||||
GUAC_PARSE_ERROR
|
||||
|
||||
} guac_instruction_parse_state;
|
||||
} guac_parse_state;
|
||||
|
||||
/**
|
||||
* Represents a single instruction within the Guacamole protocol.
|
||||
* A Guacamole protocol parser, which reads individual instructions, filling
|
||||
* its own internal structure with the most recently read instruction data.
|
||||
*/
|
||||
typedef struct guac_instruction guac_instruction;
|
||||
typedef struct guac_parser guac_parser;
|
||||
|
||||
#endif
|
||||
|
192
src/libguac/guacamole/parser.h
Normal file
192
src/libguac/guacamole/parser.h
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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_PARSER_H
|
||||
#define _GUAC_PARSER_H
|
||||
|
||||
/**
|
||||
* Provides functions and structures for parsing the Guacamole protocol.
|
||||
*
|
||||
* @file parser.h
|
||||
*/
|
||||
|
||||
#include "parser-types.h"
|
||||
#include "parser-constants.h"
|
||||
#include "socket-types.h"
|
||||
|
||||
struct guac_parser {
|
||||
|
||||
/**
|
||||
* The opcode of the instruction.
|
||||
*/
|
||||
char* opcode;
|
||||
|
||||
/**
|
||||
* The number of arguments passed to this instruction.
|
||||
*/
|
||||
int argc;
|
||||
|
||||
/**
|
||||
* Array of all arguments passed to this instruction.
|
||||
*/
|
||||
char** argv;
|
||||
|
||||
/**
|
||||
* The parse state of the instruction.
|
||||
*/
|
||||
guac_parse_state state;
|
||||
|
||||
/**
|
||||
* The length of the current element, if known.
|
||||
*/
|
||||
int __element_length;
|
||||
|
||||
/**
|
||||
* The number of elements currently parsed.
|
||||
*/
|
||||
int __elementc;
|
||||
|
||||
/**
|
||||
* All currently parsed elements.
|
||||
*/
|
||||
char* __elementv[GUAC_INSTRUCTION_MAX_ELEMENTS];
|
||||
|
||||
/**
|
||||
* Pointer to the first character of the current in-progress instruction
|
||||
* within the buffer.
|
||||
*/
|
||||
char* __instructionbuf_unparsed_start;
|
||||
|
||||
/**
|
||||
* Pointer to the first unused section of the instruction buffer.
|
||||
*/
|
||||
char* __instructionbuf_unparsed_end;
|
||||
|
||||
/**
|
||||
* The instruction buffer. This is essentially the input buffer,
|
||||
* provided as a convenience to be used to buffer instructions until
|
||||
* those instructions are complete and ready to be parsed.
|
||||
*/
|
||||
char __instructionbuf[32768];
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocates a new parser.
|
||||
*
|
||||
* @return The newly allocated parser, or NULL if an error occurs during
|
||||
* allocation, in which case guac_error will be set appropriately.
|
||||
*/
|
||||
guac_parser* guac_parser_alloc();
|
||||
|
||||
/**
|
||||
* Appends data from the given buffer to the given parser. The data will be
|
||||
* appended, if possible, to the in-progress instruction as a reference and
|
||||
* thus the buffer must remain valid throughout the life of the current
|
||||
* instruction. This function may modify the contents of the buffer when those
|
||||
* contents are part of an element within the instruction being read.
|
||||
*
|
||||
* @param parser The parser to append data to.
|
||||
* @param buffer A buffer containing data that should be appended to this
|
||||
* parser.
|
||||
* @param length The number of bytes available for appending within the buffer.
|
||||
* @return The number of bytes appended to this parser, which may be
|
||||
* zero if more data is needed.
|
||||
*/
|
||||
int guac_parser_append(guac_parser* parser, void* buffer, int length);
|
||||
|
||||
/**
|
||||
* Returns the number of unparsed bytes stored in the given parser's internal
|
||||
* buffers.
|
||||
*
|
||||
* @param parser The parser to return the length of.
|
||||
* @return The number of unparsed bytes stored in the given parser.
|
||||
*/
|
||||
int guac_parser_length(guac_parser* parser);
|
||||
|
||||
/**
|
||||
* Removes up to length bytes from internal buffer of unparsed bytes, storing
|
||||
* them in the given buffer.
|
||||
*
|
||||
* @param parser The parser to remove unparsed bytes from.
|
||||
* @param buffer The buffer to store the unparsed bytes within.
|
||||
* @param length The length of the given buffer.
|
||||
* @return The number of bytes stored in the given buffer.
|
||||
*/
|
||||
int guac_parser_shift(guac_parser* parser, void* buffer, int length);
|
||||
|
||||
/**
|
||||
* Frees all memory allocated to the given parser.
|
||||
*
|
||||
* @param parser The parser to free.
|
||||
*/
|
||||
void guac_parser_free(guac_parser* parser);
|
||||
|
||||
/**
|
||||
* Reads a single instruction from the given guac_socket connection. This
|
||||
* may result in additional data being read from the guac_socket, stored
|
||||
* internally within a buffer for future parsing. Future calls to
|
||||
* guac_parser_read() will read from the interal buffer before reading
|
||||
* from the guac_socket. Data from the internal buffer can be removed
|
||||
* and used elsewhere through guac_parser_shift().
|
||||
*
|
||||
* If an error occurs reading the instruction, non-zero is returned,
|
||||
* and guac_error is set appropriately.
|
||||
*
|
||||
* @param parser The guac_parser to read instruction data from.
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param usec_timeout The maximum number of microseconds to wait before
|
||||
* giving up.
|
||||
* @return Zero if an instruction was read within the time allowed, or
|
||||
* non-zero if no instruction could be read. If the instruction
|
||||
* could not be read completely because the timeout elapsed, in
|
||||
* which case guac_error will be set to GUAC_STATUS_INPUT_TIMEOUT
|
||||
* and additional calls to guac_parser_read() will be required.
|
||||
*/
|
||||
int guac_parser_read(guac_parser* parser, guac_socket* socket, int usec_timeout);
|
||||
|
||||
/**
|
||||
* Reads a single instruction from the given guac_socket. This operates
|
||||
* identically to guac_parser_read(), except that an error is returned if
|
||||
* the expected opcode is not received.
|
||||
*
|
||||
* If an error occurs reading the instruction, NULL is returned,
|
||||
* and guac_error is set appropriately.
|
||||
*
|
||||
* If the instruction read is not the expected instruction, NULL is returned,
|
||||
* and guac_error is set to GUAC_STATUS_BAD_STATE.
|
||||
*
|
||||
* @param parser The guac_parser to read instruction data from.
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param usec_timeout The maximum number of microseconds to wait before
|
||||
* giving up.
|
||||
* @param opcode The opcode of the instruction to read.
|
||||
* @return Zero if an instruction with the given opcode was read, non-zero
|
||||
* otherwise. If an instruction was read, but the instruction had a
|
||||
* different opcode, non-zero is returned and guac_error is set to
|
||||
* GUAC_STATUS_BAD_STATE.
|
||||
*/
|
||||
int guac_parser_expect(guac_parser* parser, guac_socket* socket, int usec_timeout, const char* opcode);
|
||||
|
||||
#endif
|
||||
|
@ -23,9 +23,7 @@
|
||||
#ifndef _GUAC_PLUGIN_H
|
||||
#define _GUAC_PLUGIN_H
|
||||
|
||||
#include "client-types.h"
|
||||
#include "plugin-constants.h"
|
||||
#include "plugin-types.h"
|
||||
|
||||
/**
|
||||
* Provides functions and structures required for handling a client plugin.
|
||||
@ -33,60 +31,4 @@
|
||||
* @file plugin.h
|
||||
*/
|
||||
|
||||
struct guac_client_plugin {
|
||||
|
||||
/**
|
||||
* Reference to dlopen'd client plugin.
|
||||
*/
|
||||
void* __client_plugin_handle;
|
||||
|
||||
/**
|
||||
* Reference to the init handler of this client plugin. This
|
||||
* function will be called when the client plugin is started.
|
||||
*/
|
||||
guac_client_init_handler* init_handler;
|
||||
|
||||
/**
|
||||
* NULL-terminated array of all arguments accepted by this client
|
||||
* plugin, in order. The values of these arguments will be passed
|
||||
* to the init_handler if the client plugin is started.
|
||||
*/
|
||||
const char** args;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the plugin which provides support for the given protocol, if it
|
||||
* exists.
|
||||
*
|
||||
* @param protocol The name of the protocol to retrieve the client plugin
|
||||
* for.
|
||||
* @return The client plugin supporting the given protocol, or NULL if
|
||||
* an error occurs or no such plugin exists.
|
||||
*/
|
||||
guac_client_plugin* guac_client_plugin_open(const char* protocol);
|
||||
|
||||
/**
|
||||
* Close the given plugin, releasing all associated resources. This function
|
||||
* must be called after use of a client plugin is finished.
|
||||
*
|
||||
* @param plugin The client plugin to close.
|
||||
* @return Zero on success, non-zero if an error occurred while releasing
|
||||
* the resources associated with the plugin.
|
||||
*/
|
||||
int guac_client_plugin_close(guac_client_plugin* plugin);
|
||||
|
||||
/**
|
||||
* Initializes the given guac_client using the initialization routine provided
|
||||
* by the given guac_client_plugin.
|
||||
*
|
||||
* @param plugin The client plugin to use to initialize the new client.
|
||||
* @param client The guac_client to initialize.
|
||||
* @param argc The number of arguments being passed to the client.
|
||||
* @param argv All arguments to be passed to the client.
|
||||
* @return Zero if initialization was successful, non-zero otherwise.
|
||||
*/
|
||||
int guac_client_plugin_init_client(guac_client_plugin* plugin,
|
||||
guac_client* client, int argc, char** argv);
|
||||
|
||||
#endif
|
||||
|
@ -32,6 +32,8 @@
|
||||
|
||||
#include "pool-types.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
struct guac_pool {
|
||||
|
||||
/**
|
||||
@ -62,6 +64,11 @@ struct guac_pool {
|
||||
*/
|
||||
guac_pool_int* __tail;
|
||||
|
||||
/**
|
||||
* Lock which is acquired when the pool is being modified or accessed.
|
||||
*/
|
||||
pthread_mutex_t __lock;
|
||||
|
||||
};
|
||||
|
||||
struct guac_pool_int {
|
||||
@ -99,21 +106,29 @@ void guac_pool_free(guac_pool* pool);
|
||||
/**
|
||||
* Returns the next available integer from the given guac_pool. All integers
|
||||
* returned are non-negative, and are returned in sequences, starting from 0.
|
||||
* This operation is threadsafe.
|
||||
*
|
||||
* @param pool The guac_pool to retrieve an integer from.
|
||||
* @return The next available integer, which may be either an integer not yet
|
||||
* @param pool
|
||||
* The guac_pool to retrieve an integer from.
|
||||
*
|
||||
* @return
|
||||
* The next available integer, which may be either an integer not yet
|
||||
* returned by a call to guac_pool_next_int, or an integer which was
|
||||
* previosly returned, but has since been freed.
|
||||
* previously returned, but has since been freed.
|
||||
*/
|
||||
int guac_pool_next_int(guac_pool* pool);
|
||||
|
||||
/**
|
||||
* Frees the given integer back into the given guac_pool. The integer given
|
||||
* will be available for future calls to guac_pool_next_int.
|
||||
* will be available for future calls to guac_pool_next_int. This operation is
|
||||
* threadsafe.
|
||||
*
|
||||
* @param pool The guac_pool to free the given integer into.
|
||||
* @param value The integer which should be returned to the given pool, such
|
||||
* that it can be received by a future call to guac_pool_next_int.
|
||||
* @param pool
|
||||
* The guac_pool to free the given integer into.
|
||||
*
|
||||
* @param value
|
||||
* The integer which should be returned to the given pool, such that it can
|
||||
* be received by a future call to guac_pool_next_int.
|
||||
*/
|
||||
void guac_pool_free_int(guac_pool* pool, int value);
|
||||
|
||||
|
@ -49,11 +49,11 @@ typedef ssize_t guac_socket_read_handler(guac_socket* socket,
|
||||
/**
|
||||
* Generic write handler for socket write operations, modeled after the standard
|
||||
* POSIX write() function. When set within a guac_socket, a handler of this type
|
||||
* will be called when data needs to be write into the socket.
|
||||
* will be called when data needs to be written to the socket.
|
||||
*
|
||||
* @param socket The guac_socket being written to.
|
||||
* @param buf The arbitrary buffer containing data to be written.
|
||||
* @param count The maximum number of bytes to write from the buffer.
|
||||
* @param count The maximum number of bytes to written to the buffer.
|
||||
* @return The number of bytes written, or -1 if an error occurs.
|
||||
*/
|
||||
typedef ssize_t guac_socket_write_handler(guac_socket* socket,
|
||||
@ -72,6 +72,39 @@ typedef ssize_t guac_socket_write_handler(guac_socket* socket,
|
||||
*/
|
||||
typedef int guac_socket_select_handler(guac_socket* socket, int usec_timeout);
|
||||
|
||||
/**
|
||||
* Generic flush handler for socket flush operations. This function is not
|
||||
* modeled after any POSIX function. When set within a guac_socket, a handler
|
||||
* of this type will be called when guac_socket_flush() is called.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket being flushed.
|
||||
*
|
||||
* @return
|
||||
* Zero on success, or non-zero if an error occurs during flush.
|
||||
*/
|
||||
typedef ssize_t guac_socket_flush_handler(guac_socket* socket);
|
||||
|
||||
/**
|
||||
* When set within a guac_socket, a handler of this type will be called
|
||||
* whenever exclusive access to the guac_socket is required, such as when
|
||||
* guac_socket_instruction_begin() is called.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket to which exclusive access is required.
|
||||
*/
|
||||
typedef void guac_socket_lock_handler(guac_socket* socket);
|
||||
|
||||
/**
|
||||
* When set within a guac_socket, a handler of this type will be called
|
||||
* whenever exclusive access to the guac_socket is no longer required, such as
|
||||
* when guac_socket_instruction_end() is called.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket to which exclusive access is no longer required.
|
||||
*/
|
||||
typedef void guac_socket_unlock_handler(guac_socket* socket);
|
||||
|
||||
/**
|
||||
* Generic handler for the closing of a socket, modeled after the standard
|
||||
* POSIX close() function. When set within a guac_socket, a handler of this type
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
* 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
|
||||
@ -52,13 +52,28 @@ struct guac_socket {
|
||||
|
||||
/**
|
||||
* Handler which will be called whenever data is written to this socket.
|
||||
* Note that because guac_socket automatically buffers written data, this
|
||||
* handler might only get called when the socket is flushed.
|
||||
*/
|
||||
guac_socket_write_handler* write_handler;
|
||||
|
||||
/**
|
||||
* Handler which will be called whenever guac_socket_select is invoked
|
||||
* Handler which will be called whenever this socket needs to be flushed.
|
||||
*/
|
||||
guac_socket_flush_handler* flush_handler;
|
||||
|
||||
/**
|
||||
* Handler which will be called whenever a socket needs to be acquired for
|
||||
* exclusive access, such as when an instruction is about to be written.
|
||||
*/
|
||||
guac_socket_lock_handler* lock_handler;
|
||||
|
||||
/**
|
||||
* Handler which will be called whenever exclusive access to a socket is
|
||||
* being released, such as when an instruction has finished being written.
|
||||
*/
|
||||
guac_socket_unlock_handler* unlock_handler;
|
||||
|
||||
/**
|
||||
* Handler which will be called whenever guac_socket_select() is invoked
|
||||
* on this socket.
|
||||
*/
|
||||
guac_socket_select_handler* select_handler;
|
||||
@ -90,52 +105,6 @@ struct guac_socket {
|
||||
*/
|
||||
int __ready_buf[3];
|
||||
|
||||
/**
|
||||
* The number of bytes currently in the main write buffer.
|
||||
*/
|
||||
int __written;
|
||||
|
||||
/**
|
||||
* The main write buffer. Bytes written go here before being flushed
|
||||
* to the open file descriptor.
|
||||
*/
|
||||
char __out_buf[GUAC_SOCKET_OUTPUT_BUFFER_SIZE];
|
||||
|
||||
/**
|
||||
* Pointer to the first character of the current in-progress instruction
|
||||
* within the buffer.
|
||||
*/
|
||||
char* __instructionbuf_unparsed_start;
|
||||
|
||||
/**
|
||||
* Pointer to the first unused section of the instruction buffer.
|
||||
*/
|
||||
char* __instructionbuf_unparsed_end;
|
||||
|
||||
/**
|
||||
* The instruction buffer. This is essentially the input buffer,
|
||||
* provided as a convenience to be used to buffer instructions until
|
||||
* those instructions are complete and ready to be parsed.
|
||||
*/
|
||||
char __instructionbuf[32768];
|
||||
|
||||
/**
|
||||
* Whether instructions should be guaranteed atomic across threads using
|
||||
* locks. By default, thread safety is disabled on sockets.
|
||||
*/
|
||||
int __threadsafe_instructions;
|
||||
|
||||
/**
|
||||
* Lock which is acquired when an instruction is being written, and
|
||||
* released when the instruction is finished being written.
|
||||
*/
|
||||
pthread_mutex_t __instruction_write_lock;
|
||||
|
||||
/**
|
||||
* Lock which is acquired when the buffer is being modified or flushed.
|
||||
*/
|
||||
pthread_mutex_t __buffer_lock;
|
||||
|
||||
/**
|
||||
* Whether automatic keep-alive is enabled.
|
||||
*/
|
||||
@ -164,66 +133,36 @@ guac_socket* guac_socket_alloc();
|
||||
*/
|
||||
void guac_socket_free(guac_socket* socket);
|
||||
|
||||
/**
|
||||
* Declares that the given socket must behave in a threadsafe way. Calling
|
||||
* this function on a socket guarantees that the socket will send instructions
|
||||
* atomically. Without automatic threadsafe sockets, multiple threads writing
|
||||
* to the same socket must ensure that instructions will not potentially
|
||||
* overlap.
|
||||
*
|
||||
* @param socket The guac_socket to declare as threadsafe.
|
||||
*/
|
||||
void guac_socket_require_threadsafe(guac_socket* socket);
|
||||
|
||||
/**
|
||||
* Declares that the given socket must automatically send a keep-alive ping
|
||||
* to ensure neither side of the socket times out while the socket is open.
|
||||
* This ping will take the form of a "nop" instruction. Enabling keep-alive
|
||||
* automatically enables threadsafety.
|
||||
* This ping will take the form of a "nop" instruction.
|
||||
*
|
||||
* @param socket The guac_socket to declare as requiring an automatic
|
||||
* keep-alive ping.
|
||||
* @param socket
|
||||
* The guac_socket to declare as requiring an automatic keep-alive ping.
|
||||
*/
|
||||
void guac_socket_require_keep_alive(guac_socket* socket);
|
||||
|
||||
/**
|
||||
* Marks the beginning of a Guacamole protocol instruction. If threadsafety
|
||||
* is enabled on the socket, other instructions will be blocked from sending
|
||||
* until this instruction is complete.
|
||||
* Marks the beginning of a Guacamole protocol instruction.
|
||||
*
|
||||
* @param socket The guac_socket beginning an instruction.
|
||||
* @param socket
|
||||
* The guac_socket beginning an instruction.
|
||||
*/
|
||||
void guac_socket_instruction_begin(guac_socket* socket);
|
||||
|
||||
/**
|
||||
* Marks the end of a Guacamole protocol instruction. If threadsafety
|
||||
* is enabled on the socket, other instructions will be allowed to send.
|
||||
* Marks the end of a Guacamole protocol instruction.
|
||||
*
|
||||
* @param socket The guac_socket ending an instruction.
|
||||
* @param socket
|
||||
* The guac_socket ending an instruction.
|
||||
*/
|
||||
void guac_socket_instruction_end(guac_socket* socket);
|
||||
|
||||
/**
|
||||
* Marks the beginning of a socket's buffer modification. If threadsafety is
|
||||
* enabled on the socket, other functions which modify the buffer will be
|
||||
* blocked until this modification is complete.
|
||||
*
|
||||
* @param socket The guac_socket whose buffer is being updated.
|
||||
*/
|
||||
void guac_socket_update_buffer_begin(guac_socket* socket);
|
||||
|
||||
/**
|
||||
* Marks the end of a socket's buffer modification. If threadsafety is enabled
|
||||
* on the socket, other functions which modify the buffer will now be allowed
|
||||
* to continue.
|
||||
*
|
||||
* @param socket The guac_socket whose buffer is done being updated.
|
||||
*/
|
||||
void guac_socket_update_buffer_end(guac_socket* socket);
|
||||
|
||||
/**
|
||||
* Allocates and initializes a new guac_socket object with the given open
|
||||
* file descriptor.
|
||||
* file descriptor. The file descriptor will be automatically closed when
|
||||
* the allocated guac_socket is freed.
|
||||
*
|
||||
* If an error occurs while allocating the guac_socket object, NULL is returned,
|
||||
* and guac_error is set appropriately.
|
||||
@ -237,7 +176,8 @@ guac_socket* guac_socket_open(int fd);
|
||||
|
||||
/**
|
||||
* Allocates and initializes a new guac_socket which writes all data via
|
||||
* nest instructions to the given existing, open guac_socket.
|
||||
* nest instructions to the given existing, open guac_socket. Freeing the
|
||||
* returned guac_socket has no effect on the underlying, nested guac_socket.
|
||||
*
|
||||
* If an error occurs while allocating the guac_socket object, NULL is returned,
|
||||
* and guac_error is set appropriately.
|
||||
@ -268,9 +208,7 @@ ssize_t guac_socket_write_int(guac_socket* socket, int64_t i);
|
||||
/**
|
||||
* Writes the given string to the given guac_socket object. The data
|
||||
* written may be buffered until the buffer is flushed automatically or
|
||||
* manually. Note that if the string can contain characters used
|
||||
* internally by the Guacamole protocol (commas, semicolons, or
|
||||
* backslashes) it will need to be escaped.
|
||||
* manually.
|
||||
*
|
||||
* If an error occurs while writing, a non-zero value is returned, and
|
||||
* guac_error is set appropriately.
|
||||
@ -284,9 +222,9 @@ ssize_t guac_socket_write_string(guac_socket* socket, const char* str);
|
||||
/**
|
||||
* Writes the given binary data to the given guac_socket object as base64-
|
||||
* encoded data. The data written may be buffered until the buffer is flushed
|
||||
* automatically or manually. Beware that because base64 data is buffered
|
||||
* automatically or manually. Beware that, because base64 data is buffered
|
||||
* on top of the write buffer already used, a call to guac_socket_flush_base64()
|
||||
* must be made before non-base64 writes (or writes of an independent block of
|
||||
* MUST be made before non-base64 writes (or writes of an independent block of
|
||||
* base64 data) can be made.
|
||||
*
|
||||
* If an error occurs while writing, a non-zero value is returned, and
|
||||
@ -300,8 +238,8 @@ ssize_t guac_socket_write_string(guac_socket* socket, const char* str);
|
||||
ssize_t guac_socket_write_base64(guac_socket* socket, const void* buf, size_t count);
|
||||
|
||||
/**
|
||||
* Writes the given data to the specified socket. The data written is not
|
||||
* buffered, and will be sent immediately.
|
||||
* Writes the given data to the specified socket. The data written may be
|
||||
* buffered until the buffer is flushed automatically or manually.
|
||||
*
|
||||
* If an error occurs while writing, a non-zero value is returned, and
|
||||
* guac_error is set appropriately.
|
||||
|
@ -29,7 +29,7 @@
|
||||
* @file stream.h
|
||||
*/
|
||||
|
||||
#include "client-fntypes.h"
|
||||
#include "user-fntypes.h"
|
||||
#include "stream-types.h"
|
||||
|
||||
struct guac_stream {
|
||||
@ -53,21 +53,21 @@ struct guac_stream {
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int ack_handler(guac_client* client, guac_stream* stream,
|
||||
* int ack_handler(guac_user* user, guac_stream* stream,
|
||||
* char* error, guac_protocol_status status);
|
||||
*
|
||||
* int some_function(guac_client* client) {
|
||||
* int some_function(guac_user* user) {
|
||||
*
|
||||
* guac_stream* stream = guac_client_alloc_stream(client);
|
||||
* guac_stream* stream = guac_user_alloc_stream(user);
|
||||
* stream->ack_handler = ack_handler;
|
||||
*
|
||||
* guac_protocol_send_clipboard(client->socket,
|
||||
* guac_protocol_send_clipboard(user->socket,
|
||||
* stream, "text/plain");
|
||||
*
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_ack_handler* ack_handler;
|
||||
guac_user_ack_handler* ack_handler;
|
||||
|
||||
/**
|
||||
* Handler for blob events sent by the Guacamole web-client.
|
||||
@ -78,16 +78,16 @@ struct guac_stream {
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int blob_handler(guac_client* client, guac_stream* stream,
|
||||
* int blob_handler(guac_user* user, guac_stream* stream,
|
||||
* void* data, int length);
|
||||
*
|
||||
* int my_clipboard_handler(guac_client* client, guac_stream* stream,
|
||||
* int my_clipboard_handler(guac_user* user, guac_stream* stream,
|
||||
* char* mimetype) {
|
||||
* stream->blob_handler = blob_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_blob_handler* blob_handler;
|
||||
guac_user_blob_handler* blob_handler;
|
||||
|
||||
/**
|
||||
* Handler for stream end events sent by the Guacamole web-client.
|
||||
@ -98,15 +98,15 @@ struct guac_stream {
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int end_handler(guac_client* client, guac_stream* stream);
|
||||
* int end_handler(guac_user* user, guac_stream* stream);
|
||||
*
|
||||
* int my_clipboard_handler(guac_client* client, guac_stream* stream,
|
||||
* int my_clipboard_handler(guac_user* user, guac_stream* stream,
|
||||
* char* mimetype) {
|
||||
* stream->end_handler = end_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_client_end_handler* end_handler;
|
||||
guac_user_end_handler* end_handler;
|
||||
|
||||
};
|
||||
|
||||
|
@ -37,9 +37,18 @@
|
||||
* calls. The return value from a single call will not have any useful
|
||||
* (or defined) meaning.
|
||||
*
|
||||
* @return An arbitrary millisecond timestamp.
|
||||
* @return
|
||||
* An arbitrary millisecond timestamp.
|
||||
*/
|
||||
guac_timestamp guac_timestamp_current();
|
||||
|
||||
/**
|
||||
* Sleeps for the given number of milliseconds.
|
||||
*
|
||||
* @param duration
|
||||
* The number of milliseconds to sleep.
|
||||
*/
|
||||
void guac_timestamp_msleep(int duration);
|
||||
|
||||
#endif
|
||||
|
||||
|
71
src/libguac/guacamole/user-constants.h
Normal file
71
src/libguac/guacamole/user-constants.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_USER_CONSTANTS_H
|
||||
#define _GUAC_USER_CONSTANTS_H
|
||||
|
||||
/**
|
||||
* Constants related to the Guacamole user structure, guac_user.
|
||||
*
|
||||
* @file user-constants.h
|
||||
*/
|
||||
|
||||
/**
|
||||
* The character prefix which identifies a user ID.
|
||||
*/
|
||||
#define GUAC_USER_ID_PREFIX '@'
|
||||
|
||||
/**
|
||||
* The maximum number of inbound or outbound streams supported by any one
|
||||
* guac_user.
|
||||
*/
|
||||
#define GUAC_USER_MAX_STREAMS 64
|
||||
|
||||
/**
|
||||
* The index of a closed stream.
|
||||
*/
|
||||
#define GUAC_USER_CLOSED_STREAM_INDEX -1
|
||||
|
||||
/**
|
||||
* The maximum number of objects supported by any one guac_client.
|
||||
*/
|
||||
#define GUAC_USER_MAX_OBJECTS 64
|
||||
|
||||
/**
|
||||
* The index of an object which has not been defined.
|
||||
*/
|
||||
#define GUAC_USER_UNDEFINED_OBJECT_INDEX -1
|
||||
|
||||
/**
|
||||
* The stream name reserved for the root of a Guacamole protocol object.
|
||||
*/
|
||||
#define GUAC_USER_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_USER_STREAM_INDEX_MIMETYPE "application/vnd.glyptodon.guacamole.stream-index+json"
|
||||
|
||||
#endif
|
||||
|
408
src/libguac/guacamole/user-fntypes.h
Normal file
408
src/libguac/guacamole/user-fntypes.h
Normal file
@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_USER_FNTYPES_H
|
||||
#define _GUAC_USER_FNTYPES_H
|
||||
|
||||
/**
|
||||
* Function type definitions related to the guac_user object.
|
||||
*
|
||||
* @file user-fntypes.h
|
||||
*/
|
||||
|
||||
#include "object-types.h"
|
||||
#include "protocol-types.h"
|
||||
#include "stream-types.h"
|
||||
#include "timestamp-types.h"
|
||||
#include "user-types.h"
|
||||
|
||||
/**
|
||||
* Callback which relates to a single guac_user at a time, along with arbitrary
|
||||
* data.
|
||||
*
|
||||
* @see guac_client_foreach_user()
|
||||
* @see guac_client_for_owner()
|
||||
*
|
||||
* @param user
|
||||
* The user for which this callback was invoked. Depending on whether
|
||||
* guac_client_foreach_user() or guac_client_for_owner() was called, this
|
||||
* will either be the current user as the "foreach" iteration continues,
|
||||
* or the owner of the connection. If guac_client_for_owner() was called
|
||||
* for a connection which has no owner, this may be NULL.
|
||||
*
|
||||
* @param data
|
||||
* The arbitrary data passed to guac_client_foreach_user() or
|
||||
* guac_client_for_owner().
|
||||
*
|
||||
* @return
|
||||
* An arbitrary return value, the semantics of which are determined by the
|
||||
* implementation of the callback and the manner of its user. In the case
|
||||
* of a callback provided to guac_client_foreach_user(), this value is
|
||||
* always discarded.
|
||||
*/
|
||||
typedef void* guac_user_callback(guac_user* user, void* data);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole mouse events, invoked when a "mouse" instruction has
|
||||
* been received from a user.
|
||||
*
|
||||
* @param user
|
||||
* The user that sent the mouse event.
|
||||
*
|
||||
* @param x
|
||||
* The X coordinate of the mouse within the display when the event
|
||||
* occurred, in pixels. This value is not guaranteed to be within the
|
||||
* bounds of the display area.
|
||||
*
|
||||
* @param y
|
||||
* The Y coordinate of the mouse within the display when the event
|
||||
* occurred, in pixels. This value is not guaranteed to be within the
|
||||
* bounds of the display area.
|
||||
*
|
||||
* @param button_mask
|
||||
* An integer value representing the current state of each button, where
|
||||
* the Nth bit within the integer is set to 1 if and only if the Nth mouse
|
||||
* button is currently pressed. The lowest-order bit is the left mouse
|
||||
* button, followed by the middle button, right button, and finally the up
|
||||
* and down buttons of the scroll wheel.
|
||||
*
|
||||
* @see GUAC_CLIENT_MOUSE_LEFT
|
||||
* @see GUAC_CLIENT_MOUSE_MIDDLE
|
||||
* @see GUAC_CLIENT_MOUSE_RIGHT
|
||||
* @see GUAC_CLIENT_MOUSE_SCROLL_UP
|
||||
* @see GUAC_CLIENT_MOUSE_SCROLL_DOWN
|
||||
*
|
||||
* @return
|
||||
* Zero if the mouse event was handled successfully, or non-zero if an
|
||||
* error occurred.
|
||||
*/
|
||||
typedef int guac_user_mouse_handler(guac_user* user, int x, int y,
|
||||
int button_mask);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole key events, invoked when a "key" event has been
|
||||
* received from a user.
|
||||
*
|
||||
* @param user
|
||||
* The user that sent the key event.
|
||||
*
|
||||
* @param keysym
|
||||
* The X11 keysym of the key that was pressed or released.
|
||||
*
|
||||
* @param pressed
|
||||
* Non-zero if the key represented by the given keysym is currently
|
||||
* pressed, zero if it is released.
|
||||
*
|
||||
* @return
|
||||
* Zero if the key event was handled successfully, or non-zero if an error
|
||||
* occurred.
|
||||
*/
|
||||
typedef int guac_user_key_handler(guac_user* user, int keysym, int pressed);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole clipboard streams received from a user. Each such
|
||||
* clipboard stream begins when the user sends a "clipboard" instruction. To
|
||||
* handle received data along this stream, implementations of this handler
|
||||
* must assign blob and end handlers to the given stream object.
|
||||
*
|
||||
* @param user
|
||||
* The user that opened the clipboard stream.
|
||||
*
|
||||
* @param stream
|
||||
* The stream object allocated by libguac to represent the clipboard stream
|
||||
* opened by the user.
|
||||
*
|
||||
* @param mimetype
|
||||
* The mimetype of the data that will be sent along the stream.
|
||||
*
|
||||
* @return
|
||||
* Zero if the opening of the clipboard stream has been handled
|
||||
* successfully, or non-zero if an error occurs.
|
||||
*/
|
||||
typedef int guac_user_clipboard_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole size events, invoked when a "size" instruction has
|
||||
* been received from a user. A "size" instruction indicates that the desired
|
||||
* display size has changed.
|
||||
*
|
||||
* @param user
|
||||
* The user whose desired display size has changed.
|
||||
*
|
||||
* @param width
|
||||
* The desired width of the display, in pixels.
|
||||
*
|
||||
* @param height
|
||||
* The desired height of the display, in pixels.
|
||||
*
|
||||
* @return
|
||||
* Zero if the size event has been successfully handled, non-zero
|
||||
* otherwise.
|
||||
*/
|
||||
typedef int guac_user_size_handler(guac_user* user,
|
||||
int width, int height);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole file streams received from a user. Each such file
|
||||
* stream begins when the user sends a "file" instruction. To handle received
|
||||
* data along this stream, implementations of this handler must assign blob and
|
||||
* end handlers to the given stream object.
|
||||
*
|
||||
* @param user
|
||||
* The user that opened the file stream.
|
||||
*
|
||||
* @param stream
|
||||
* The stream object allocated by libguac to represent the file stream
|
||||
* opened by the user.
|
||||
*
|
||||
* @param mimetype
|
||||
* The mimetype of the data that will be sent along the stream.
|
||||
*
|
||||
* @param filename
|
||||
* The name of the file being transferred.
|
||||
*
|
||||
* @return
|
||||
* Zero if the opening of the file stream has been handled successfully, or
|
||||
* non-zero if an error occurs.
|
||||
*/
|
||||
typedef int guac_user_file_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype, char* filename);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole pipe streams received from a user. Pipe streams are
|
||||
* unidirectional, arbitrary, named pipes. Each such pipe stream begins when
|
||||
* the user sends a "pipe" instruction. To handle received data along this
|
||||
* stream, implementations of this handler must assign blob and end handlers to
|
||||
* the given stream object.
|
||||
*
|
||||
* @param user
|
||||
* The user that opened the pipe stream.
|
||||
*
|
||||
* @param stream
|
||||
* The stream object allocated by libguac to represent the pipe stream
|
||||
* opened by the user.
|
||||
*
|
||||
* @param mimetype
|
||||
* The mimetype of the data that will be sent along the stream.
|
||||
*
|
||||
* @param name
|
||||
* The arbitrary name assigned to this pipe. It is up to the implementation
|
||||
* of this handler and the application containing the Guacamole client to
|
||||
* determine the semantics of a pipe stream having this name.
|
||||
*
|
||||
* @return
|
||||
* Zero if the opening of the pipe stream has been handled successfully, or
|
||||
* non-zero if an error occurs.
|
||||
*/
|
||||
typedef int guac_user_pipe_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype, char* name);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole stream blobs. Each blob originates from a "blob"
|
||||
* instruction which was associated with a previously-created stream.
|
||||
*
|
||||
* @param user
|
||||
* The user that is sending this blob of data along the stream.
|
||||
*
|
||||
* @param stream
|
||||
* The stream along which the blob was received. The semantics associated
|
||||
* with this stream are determined by the manner of its creation.
|
||||
*
|
||||
* @param data
|
||||
* The blob of data received.
|
||||
*
|
||||
* @param length
|
||||
* The number of bytes within the blob of data received.
|
||||
*
|
||||
* @return
|
||||
* Zero if the blob of data was successfully handled, non-zero otherwise.
|
||||
*/
|
||||
typedef int guac_user_blob_handler(guac_user* user, guac_stream* stream,
|
||||
void* data, int length);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole stream "ack" instructions. A user will send "ack"
|
||||
* instructions to acknowledge the successful receipt of blobs along a stream
|
||||
* opened by the server, or to notify of errors. An "ack" with an error status
|
||||
* implicitly closes the stream.
|
||||
*
|
||||
* @param user
|
||||
* The user sending the "ack" instruction.
|
||||
*
|
||||
* @param stream
|
||||
* The stream for which the "ack" was received.
|
||||
*
|
||||
* @param error
|
||||
* An arbitrary, human-readable message describing the error that
|
||||
* occurred, if any. If no error occurs, this will likely be blank,
|
||||
* "SUCCESS", or similar. This value exists for the sake of readability,
|
||||
* not for the sake of data interchange.
|
||||
*
|
||||
* @param status
|
||||
* GUAC_PROTOCOL_STATUS_SUCCESS if the blob was received and handled
|
||||
* successfully, or a different status code describing the problem if an
|
||||
* error occurred and the stream has been implicitly closed.
|
||||
*
|
||||
* @return
|
||||
* Zero if the "ack" message was successfully handled, non-zero otherwise.
|
||||
*/
|
||||
typedef int guac_user_ack_handler(guac_user* user, guac_stream* stream,
|
||||
char* error, guac_protocol_status status);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole stream "end" instructions. End instructions are sent
|
||||
* by the user when a stream is closing because its end has been reached.
|
||||
*
|
||||
* @param user
|
||||
* The user that sent the "end" instruction.
|
||||
*
|
||||
* @param stream
|
||||
* The stream that is being closed.
|
||||
*
|
||||
* @return
|
||||
* Zero if the end-of-stream condition has been sucessfully handled,
|
||||
* non-zero otherwise.
|
||||
*/
|
||||
typedef int guac_user_end_handler(guac_user* user, guac_stream* stream);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole join events. A join event is fired by the
|
||||
* guac_client whenever a guac_user joins the connection. There is no
|
||||
* instruction associated with a join event.
|
||||
*
|
||||
* Implementations of the join handler MUST NOT use the client-level
|
||||
* broadcast socket, nor invoke guac_client_foreach_user() or
|
||||
* guac_client_for_owner(). Doing so will result in undefined behavior,
|
||||
* including segfaults.
|
||||
*
|
||||
* @param user
|
||||
* The user joining the connection. The guac_client associated with the
|
||||
* connection will already be populated within the user object.
|
||||
*
|
||||
* @param argc
|
||||
* The number of arguments stored within argv.
|
||||
*
|
||||
* @param argv
|
||||
* An array of all arguments provided by the user when they joined. These
|
||||
* arguments must correspond to the argument names declared when the
|
||||
* guac_client was initialized. If the number of arguments does not match
|
||||
* the number of argument names declared, then the joining user has
|
||||
* violated the Guacamole protocol.
|
||||
*
|
||||
* @return
|
||||
* Zero if the user has been successfully initialized and should be allowed
|
||||
* to join the connection, non-zero otherwise.
|
||||
*/
|
||||
typedef int guac_user_join_handler(guac_user* user, int argc, char** argv);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole leave events. A leave event is fired by the
|
||||
* guac_client whenever a guac_user leaves the connection. There is no
|
||||
* instruction associated with a leave event.
|
||||
*
|
||||
* Implementations of the leave handler MUST NOT use the client-level
|
||||
* broadcast socket, nor invoke guac_client_foreach_user() or
|
||||
* guac_client_for_owner(). Doing so will result in undefined behavior,
|
||||
* including segfaults.
|
||||
*
|
||||
* @param user
|
||||
* The user that has left the connection.
|
||||
*
|
||||
* @return
|
||||
* Zero if the leave event has been successfully handled, non-zero
|
||||
* otherwise.
|
||||
*/
|
||||
typedef int guac_user_leave_handler(guac_user* user);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole sync events. A sync event is fired by the
|
||||
* guac_client whenever a guac_user responds to a "sync" instruction. Sync
|
||||
* instructions are sent by the Guacamole server to mark the logical end of a
|
||||
* frame, and to inform the Guacamole client that all data up to a particular
|
||||
* point in time has been sent. The response from the Guacamole client
|
||||
* similarly indicates that all data received up to a particular point in
|
||||
* server time has been handled.
|
||||
*
|
||||
* @param user
|
||||
* The user that sent the "sync" instruction.
|
||||
*
|
||||
* @param timestamp
|
||||
* The timestamp contained within the sync instruction.
|
||||
*
|
||||
* @return
|
||||
* Zero if the sync event has been handled successfully, non-zero
|
||||
* otherwise.
|
||||
*/
|
||||
typedef int guac_user_sync_handler(guac_user* user, guac_timestamp timestamp);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole object get requests. The semantics of the stream
|
||||
* which will be created in response to the request are determined by the type
|
||||
* of the object and the name of the stream requested. It is up to the
|
||||
* implementation of this handler to then respond with a "body" instruction
|
||||
* that begins the requested stream.
|
||||
*
|
||||
* @param user
|
||||
* The user requesting read access to the stream having the given name.
|
||||
*
|
||||
* @param object
|
||||
* The object from which the given named stream is being requested.
|
||||
*
|
||||
* @param name
|
||||
* The name of the stream being requested.
|
||||
*
|
||||
* @return
|
||||
* Zero if the get request was successfully handled, non-zero otherwise.
|
||||
*/
|
||||
typedef int guac_user_get_handler(guac_user* user, guac_object* object,
|
||||
char* name);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole object put requests. Put requests implicitly create a
|
||||
* stream, the semantics of which are determined by the type of the object
|
||||
* and the name of the stream requested.
|
||||
*
|
||||
* @param user
|
||||
* The user requesting write access to the stream having the given name.
|
||||
*
|
||||
* @param object
|
||||
* The object from which the given named stream is being requested.
|
||||
*
|
||||
* @param stream
|
||||
* The stream along which the blobs which should be written to the named
|
||||
* stream will be received.
|
||||
*
|
||||
* @param mimetype
|
||||
* The mimetype of the data that will be received along the given stream.
|
||||
*
|
||||
* @param name
|
||||
* The name of the stream being requested.
|
||||
*
|
||||
* @return
|
||||
* Zero if the put request was successfully handled, non-zero otherwise.
|
||||
*/
|
||||
typedef int guac_user_put_handler(guac_user* user, guac_object* object,
|
||||
guac_stream* stream, char* mimetype, char* name);
|
||||
|
||||
#endif
|
||||
|
@ -20,21 +20,26 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _GUAC_PLUGIN_TYPES_H
|
||||
#define _GUAC_PLUGIN_TYPES_H
|
||||
#ifndef _GUAC_USER_TYPES_H
|
||||
#define _GUAC_USER_TYPES_H
|
||||
|
||||
/**
|
||||
* Type definitions related to client plugins.
|
||||
* Type definitions related to the guac_user object.
|
||||
*
|
||||
* @file plugin-types.h
|
||||
* @file user-types.h
|
||||
*/
|
||||
|
||||
/**
|
||||
* A handle to a client plugin, containing enough information about the
|
||||
* plugin to complete the initial protocol handshake and instantiate a new
|
||||
* client supporting the protocol provided by the client plugin.
|
||||
* Representation of a physical connection within a larger logical connection
|
||||
* which may be shared. Logical connections are represented by guac_client.
|
||||
*/
|
||||
typedef struct guac_client_plugin guac_client_plugin;
|
||||
typedef struct guac_user guac_user;
|
||||
|
||||
/**
|
||||
* Information exposed by the remote client during the connection handshake
|
||||
* which can be used by a client plugin.
|
||||
*/
|
||||
typedef struct guac_user_info guac_user_info;
|
||||
|
||||
#endif
|
||||
|
847
src/libguac/guacamole/user.h
Normal file
847
src/libguac/guacamole/user.h
Normal file
@ -0,0 +1,847 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_USER_H
|
||||
#define _GUAC_USER_H
|
||||
|
||||
/**
|
||||
* Defines the guac_user object, which represents a physical connection
|
||||
* within a larger, possibly shared, logical connection represented by a
|
||||
* guac_client.
|
||||
*
|
||||
* @file user.h
|
||||
*/
|
||||
|
||||
#include "client-types.h"
|
||||
#include "layer-types.h"
|
||||
#include "pool-types.h"
|
||||
#include "socket-types.h"
|
||||
#include "stream-types.h"
|
||||
#include "timestamp-types.h"
|
||||
#include "user-constants.h"
|
||||
#include "user-fntypes.h"
|
||||
#include "user-types.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct guac_user_info {
|
||||
|
||||
/**
|
||||
* The number of pixels the remote client requests for the display width.
|
||||
* This need not be honored by a client plugin implementation, but if the
|
||||
* underlying protocol of the client plugin supports dynamic sizing of the
|
||||
* screen, honoring the display size request is recommended.
|
||||
*/
|
||||
int optimal_width;
|
||||
|
||||
/**
|
||||
* The number of pixels the remote client requests for the display height.
|
||||
* This need not be honored by a client plugin implementation, but if the
|
||||
* underlying protocol of the client plugin supports dynamic sizing of the
|
||||
* screen, honoring the display size request is recommended.
|
||||
*/
|
||||
int optimal_height;
|
||||
|
||||
/**
|
||||
* NULL-terminated array of client-supported audio mimetypes. If the client
|
||||
* does not support audio at all, this will be NULL.
|
||||
*/
|
||||
const char** audio_mimetypes;
|
||||
|
||||
/**
|
||||
* NULL-terminated array of client-supported video mimetypes. If the client
|
||||
* does not support video at all, this will be NULL.
|
||||
*/
|
||||
const char** video_mimetypes;
|
||||
|
||||
/**
|
||||
* NULL-terminated array of client-supported image mimetypes. Though all
|
||||
* supported image mimetypes will be listed here, it can be safely assumed
|
||||
* that all clients will support at least "image/png" and "image/jpeg".
|
||||
*/
|
||||
const char** image_mimetypes;
|
||||
|
||||
/**
|
||||
* The DPI of the physical remote display if configured for the optimal
|
||||
* width/height combination described here. This need not be honored by
|
||||
* a client plugin implementation, but if the underlying protocol of the
|
||||
* client plugin supports dynamic sizing of the screen, honoring the
|
||||
* stated resolution of the display size request is recommended.
|
||||
*/
|
||||
int optimal_resolution;
|
||||
|
||||
};
|
||||
|
||||
struct guac_user {
|
||||
|
||||
/**
|
||||
* The guac_client to which this user belongs.
|
||||
*/
|
||||
guac_client* client;
|
||||
|
||||
/**
|
||||
* This user's actual socket. Data written to this socket will
|
||||
* be received by this user alone, and data sent by this specific
|
||||
* user will be received by this socket.
|
||||
*/
|
||||
guac_socket* socket;
|
||||
|
||||
/**
|
||||
* The unique identifier allocated for this user, which may be used within
|
||||
* the Guacamole protocol to refer to this user. This identifier is
|
||||
* guaranteed to be unique from all existing connections and users, and
|
||||
* will not collide with any available protocol names.
|
||||
*/
|
||||
char* user_id;
|
||||
|
||||
/**
|
||||
* Non-zero if this user is the owner of the associated connection, zero
|
||||
* otherwise. The owner is the user which created the connection.
|
||||
*/
|
||||
int owner;
|
||||
|
||||
/**
|
||||
* Non-zero if this user is active (connected), and zero otherwise. When
|
||||
* the user is created, this will be set to a non-zero value. If an event
|
||||
* occurs which requires that the user disconnect, or the user has
|
||||
* disconnected, this will be reset to zero.
|
||||
*/
|
||||
int active;
|
||||
|
||||
/**
|
||||
* The previous user in the group of users within the same logical
|
||||
* connection. This is currently only used internally by guac_client to
|
||||
* track its set of connected users. To iterate connected users, use
|
||||
* guac_client_foreach_user().
|
||||
*/
|
||||
guac_user* __prev;
|
||||
|
||||
/**
|
||||
* The next user in the group of users within the same logical connection.
|
||||
* This is currently only used internally by guac_client to track its set
|
||||
* of connected users. To iterate connected users, use
|
||||
* guac_client_foreach_user().
|
||||
*/
|
||||
guac_user* __next;
|
||||
|
||||
/**
|
||||
* The time (in milliseconds) of receipt of the last sync message from
|
||||
* the user.
|
||||
*/
|
||||
guac_timestamp last_received_timestamp;
|
||||
|
||||
/**
|
||||
* The duration of the last frame rendered by the user, in milliseconds.
|
||||
* This duration will include network and processing lag, and thus should
|
||||
* be slightly higher than the true frame duration.
|
||||
*/
|
||||
int last_frame_duration;
|
||||
|
||||
/**
|
||||
* The overall lag experienced by the user relative to the stream of
|
||||
* frames, roughly excluding network lag.
|
||||
*/
|
||||
int processing_lag;
|
||||
|
||||
/**
|
||||
* Information structure containing properties exposed by the remote
|
||||
* user during the initial handshake process.
|
||||
*/
|
||||
guac_user_info info;
|
||||
|
||||
/**
|
||||
* Pool of stream indices.
|
||||
*/
|
||||
guac_pool* __stream_pool;
|
||||
|
||||
/**
|
||||
* All available output streams (data going to connected user).
|
||||
*/
|
||||
guac_stream* __output_streams;
|
||||
|
||||
/**
|
||||
* All available input streams (data coming from connected user).
|
||||
*/
|
||||
guac_stream* __input_streams;
|
||||
|
||||
/**
|
||||
* Pool of object indices.
|
||||
*/
|
||||
guac_pool* __object_pool;
|
||||
|
||||
/**
|
||||
* All available objects (arbitrary sets of named streams).
|
||||
*/
|
||||
guac_object* __objects;
|
||||
|
||||
/**
|
||||
* Arbitrary user-specific data.
|
||||
*/
|
||||
void* data;
|
||||
|
||||
/**
|
||||
* Handler for mouse events sent by the Gaucamole web-client.
|
||||
*
|
||||
* The handler takes the integer mouse X and Y coordinates, as well as
|
||||
* a button mask containing the bitwise OR of all button values currently
|
||||
* being pressed. Those values are:
|
||||
*
|
||||
* <table>
|
||||
* <tr><th>Button</th> <th>Value</th></tr>
|
||||
* <tr><td>Left</td> <td>1</td></tr>
|
||||
* <tr><td>Middle</td> <td>2</td></tr>
|
||||
* <tr><td>Right</td> <td>4</td></tr>
|
||||
* <tr><td>Scrollwheel Up</td> <td>8</td></tr>
|
||||
* <tr><td>Scrollwheel Down</td><td>16</td></tr>
|
||||
* </table>
|
||||
|
||||
* Example:
|
||||
* @code
|
||||
* int mouse_handler(guac_user* user, int x, int y, int button_mask);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->mouse_handler = mouse_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_mouse_handler* mouse_handler;
|
||||
|
||||
/**
|
||||
* Handler for key events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes the integer X11 keysym associated with the key
|
||||
* being pressed/released, and an integer representing whether the key
|
||||
* is being pressed (1) or released (0).
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int key_handler(guac_user* user, int keysym, int pressed);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->key_handler = key_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_key_handler* key_handler;
|
||||
|
||||
/**
|
||||
* Handler for clipboard events sent by the Guacamole web-client. This
|
||||
* handler will be called whenever the web-client sets the data of the
|
||||
* clipboard.
|
||||
*
|
||||
* The handler takes a guac_stream, which contains the stream index and
|
||||
* will persist through the duration of the transfer, and the mimetype
|
||||
* of the data being transferred.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int clipboard_handler(guac_user* user, guac_stream* stream,
|
||||
* char* mimetype);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->clipboard_handler = clipboard_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_clipboard_handler* clipboard_handler;
|
||||
|
||||
/**
|
||||
* Handler for size events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes an integer width and integer height, representing
|
||||
* the current visible screen area of the client.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int size_handler(guac_user* user, int width, int height);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->size_handler = size_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_size_handler* size_handler;
|
||||
|
||||
/**
|
||||
* Handler for file events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes a guac_stream which contains the stream index and
|
||||
* will persist through the duration of the transfer, the mimetype of
|
||||
* the file being transferred, and the filename.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int file_handler(guac_user* user, guac_stream* stream,
|
||||
* char* mimetype, char* filename);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->file_handler = file_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_file_handler* file_handler;
|
||||
|
||||
/**
|
||||
* Handler for pipe events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes a guac_stream which contains the stream index and
|
||||
* will persist through the duration of the transfer, the mimetype of
|
||||
* the data being transferred, and the pipe name.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int pipe_handler(guac_user* user, guac_stream* stream,
|
||||
* char* mimetype, char* name);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->pipe_handler = pipe_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_pipe_handler* pipe_handler;
|
||||
|
||||
/**
|
||||
* Handler for ack events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes a guac_stream which contains the stream index and
|
||||
* will persist through the duration of the transfer, a string containing
|
||||
* the error or status message, and a status code.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int ack_handler(guac_user* user, guac_stream* stream,
|
||||
* char* error, guac_protocol_status status);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->ack_handler = ack_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_ack_handler* ack_handler;
|
||||
|
||||
/**
|
||||
* Handler for blob events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes a guac_stream which contains the stream index and
|
||||
* will persist through the duration of the transfer, an arbitrary buffer
|
||||
* containing the blob, and the length of the blob.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int blob_handler(guac_user* user, guac_stream* stream,
|
||||
* void* data, int length);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->blob_handler = blob_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_blob_handler* blob_handler;
|
||||
|
||||
/**
|
||||
* Handler for stream end events sent by the Guacamole web-client.
|
||||
*
|
||||
* The handler takes only a guac_stream which contains the stream index.
|
||||
* This guac_stream will be disposed of immediately after this event is
|
||||
* finished.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int end_handler(guac_user* user, guac_stream* stream);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->end_handler = end_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_end_handler* end_handler;
|
||||
|
||||
/**
|
||||
* Handler for sync events sent by the Guacamole web-client. Sync events
|
||||
* are used to track per-user latency.
|
||||
*
|
||||
* The handler takes only a guac_timestamp which contains the timestamp
|
||||
* received from the user. Latency can be determined by comparing this
|
||||
* timestamp against the last_sent_timestamp of guac_client.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int sync_handler(guac_user* user, guac_timestamp timestamp);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->sync_handler = sync_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_sync_handler* sync_handler;
|
||||
|
||||
/**
|
||||
* Handler for leave events fired by the guac_client when a guac_user
|
||||
* is leaving an active connection.
|
||||
*
|
||||
* The handler takes only a guac_user which will be the guac_user that
|
||||
* left the connection. This guac_user will be disposed of immediately
|
||||
* after this event is finished.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* int leave_handler(guac_user* user);
|
||||
*
|
||||
* int my_join_handler(guac_user* user, int argv, char** argv) {
|
||||
* user->leave_handler = leave_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_leave_handler* leave_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_user* user, guac_object* object,
|
||||
* char* name);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->get_handler = get_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_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_user* user, guac_object* object,
|
||||
* guac_stream* stream, char* mimetype, char* name);
|
||||
*
|
||||
* int guac_user_init(guac_user* user, int argc, char** argv) {
|
||||
* user->put_handler = put_handler;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
guac_user_put_handler* put_handler;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocates a new, blank user, not associated with any specific client or
|
||||
* socket.
|
||||
*
|
||||
* @return The newly allocated guac_user, or NULL if allocation failed.
|
||||
*/
|
||||
guac_user* guac_user_alloc();
|
||||
|
||||
/**
|
||||
* Frees the given user and all associated resources.
|
||||
*
|
||||
* @param user The guac_user to free.
|
||||
*/
|
||||
void guac_user_free(guac_user* user);
|
||||
|
||||
/**
|
||||
* Call the appropriate handler defined by the given user for the given
|
||||
* instruction. A comparison is made between the instruction opcode and the
|
||||
* initial handler lookup table defined in user-handlers.c. The intial handlers
|
||||
* will in turn call the user's handler (if defined).
|
||||
*
|
||||
* @param user
|
||||
* The user whose handlers should be called.
|
||||
*
|
||||
* @param opcode
|
||||
* The opcode of the instruction to pass to the user via the appropriate
|
||||
* handler.
|
||||
*
|
||||
* @param argc
|
||||
* The number of arguments which are part of the instruction.
|
||||
*
|
||||
* @param argv
|
||||
* An array of all arguments which are part of the instruction.
|
||||
*
|
||||
* @return
|
||||
* Non-negative if the instruction was handled successfully, or negative
|
||||
* if an error occurred.
|
||||
*/
|
||||
int guac_user_handle_instruction(guac_user* user, const char* opcode,
|
||||
int argc, char** argv);
|
||||
|
||||
/**
|
||||
* Allocates a new stream. An arbitrary index is automatically assigned
|
||||
* if no previously-allocated stream is available for use.
|
||||
*
|
||||
* @param user The user to allocate the stream for.
|
||||
* @return The next available stream, or a newly allocated stream.
|
||||
*/
|
||||
guac_stream* guac_user_alloc_stream(guac_user* user);
|
||||
|
||||
/**
|
||||
* Returns the given stream to the pool of available streams, such that it
|
||||
* can be reused by any subsequent call to guac_user_alloc_stream().
|
||||
*
|
||||
* @param user The user to return the stream to.
|
||||
* @param stream The stream to return to the pool of available stream.
|
||||
*/
|
||||
void guac_user_free_stream(guac_user* user, guac_stream* stream);
|
||||
|
||||
/**
|
||||
* Signals the given user that it must disconnect, or advises cooperating
|
||||
* services that the given user is no longer connected.
|
||||
*
|
||||
* @param user The user to stop.
|
||||
*/
|
||||
void guac_user_stop(guac_user* user);
|
||||
|
||||
/**
|
||||
* Signals the given user to stop gracefully, while also signalling via the
|
||||
* Guacamole protocol that an error has occurred. Note that this is a completely
|
||||
* cooperative signal, and can be ignored by the user or the hosting
|
||||
* daemon. The message given will be logged to the system logs.
|
||||
*
|
||||
* @param user The user to signal to stop.
|
||||
* @param status The status to send over the Guacamole protocol.
|
||||
* @param format A printf-style format string to log.
|
||||
* @param ... Arguments to use when filling the format string for printing.
|
||||
*/
|
||||
void guac_user_abort(guac_user* user, guac_protocol_status status,
|
||||
const char* format, ...);
|
||||
|
||||
/**
|
||||
* Signals the given user to stop gracefully, while also signalling via the
|
||||
* Guacamole protocol that an error has occurred. Note that this is a completely
|
||||
* cooperative signal, and can be ignored by the user or the hosting
|
||||
* daemon. The message given will be logged to the system logs.
|
||||
*
|
||||
* @param user The user to signal to stop.
|
||||
* @param status The status to send over the Guacamole protocol.
|
||||
* @param format A printf-style format string to log.
|
||||
* @param ap The va_list containing the arguments to be used when filling the
|
||||
* format string for printing.
|
||||
*/
|
||||
void vguac_user_abort(guac_user* user, guac_protocol_status status,
|
||||
const char* format, va_list ap);
|
||||
|
||||
/**
|
||||
* Writes a message in the log used by the given user. The logger used will
|
||||
* normally be defined by guacd (or whichever program loads the user)
|
||||
* by setting the logging handlers of the user when it is loaded.
|
||||
*
|
||||
* @param user The user logging this message.
|
||||
* @param level The level at which to log this message.
|
||||
* @param format A printf-style format string to log.
|
||||
* @param ... Arguments to use when filling the format string for printing.
|
||||
*/
|
||||
void guac_user_log(guac_user* user, guac_client_log_level level,
|
||||
const char* format, ...);
|
||||
|
||||
/**
|
||||
* Writes a message in the log used by the given user. The logger used will
|
||||
* normally be defined by guacd (or whichever program loads the user)
|
||||
* by setting the logging handlers of the user when it is loaded.
|
||||
*
|
||||
* @param user The user logging this message.
|
||||
* @param level The level at which to log this message.
|
||||
* @param format A printf-style format string to log.
|
||||
* @param ap The va_list containing the arguments to be used when filling the
|
||||
* format string for printing.
|
||||
*/
|
||||
void vguac_user_log(guac_user* user, guac_client_log_level level,
|
||||
const char* format, va_list ap);
|
||||
|
||||
/**
|
||||
* Allocates a new object. An arbitrary index is automatically assigned
|
||||
* if no previously-allocated object is available for use.
|
||||
*
|
||||
* @param user
|
||||
* The user to allocate the object for.
|
||||
*
|
||||
* @return
|
||||
* The next available object, or a newly allocated object.
|
||||
*/
|
||||
guac_object* guac_user_alloc_object(guac_user* user);
|
||||
|
||||
/**
|
||||
* Returns the given object to the pool of available objects, such that it
|
||||
* can be reused by any subsequent call to guac_user_alloc_object().
|
||||
*
|
||||
* @param user
|
||||
* The user to return the object to.
|
||||
*
|
||||
* @param object
|
||||
* The object to return to the pool of available object.
|
||||
*/
|
||||
void guac_user_free_object(guac_user* user, guac_object* object);
|
||||
|
||||
/**
|
||||
* Streams the image data of the given surface over an image stream ("img"
|
||||
* instruction) as PNG-encoded data. The image stream will be automatically
|
||||
* allocated and freed.
|
||||
*
|
||||
* @param user
|
||||
* The Guacamole user for whom the image stream should be allocated.
|
||||
*
|
||||
* @param socket
|
||||
* The socket over which instructions associated with the image stream
|
||||
* should be sent.
|
||||
*
|
||||
* @param mode
|
||||
* The composite mode to use when rendering the image over the given layer.
|
||||
*
|
||||
* @param layer
|
||||
* The destination layer.
|
||||
*
|
||||
* @param x
|
||||
* The X coordinate of the upper-left corner of the destination rectangle
|
||||
* within the given layer.
|
||||
*
|
||||
* @param y
|
||||
* The Y coordinate of the upper-left corner of the destination rectangle
|
||||
* within the given layer.
|
||||
*
|
||||
* @param surface
|
||||
* A Cairo surface containing the image data to be streamed.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Streams the image data of the given surface over an image stream ("img"
|
||||
* instruction) as JPEG-encoded data at the given quality. The image stream
|
||||
* will be automatically allocated and freed.
|
||||
*
|
||||
* @param user
|
||||
* The Guacamole user for whom the image stream should be allocated.
|
||||
*
|
||||
* @param socket
|
||||
* The socket over which instructions associated with the image stream
|
||||
* should be sent.
|
||||
*
|
||||
* @param mode
|
||||
* The composite mode to use when rendering the image over the given layer.
|
||||
*
|
||||
* @param layer
|
||||
* The destination layer.
|
||||
*
|
||||
* @param x
|
||||
* The X coordinate of the upper-left corner of the destination rectangle
|
||||
* within the given layer.
|
||||
*
|
||||
* @param y
|
||||
* The Y coordinate of the upper-left corner of the destination rectangle
|
||||
* within the given layer.
|
||||
*
|
||||
* @param surface
|
||||
* A Cairo surface containing the image data to be streamed.
|
||||
*
|
||||
* @param quality
|
||||
* The JPEG image quality, which must be an integer value between 0 and 100
|
||||
* inclusive. Larger values indicate improving quality at the expense of
|
||||
* larger file size.
|
||||
*/
|
||||
void guac_user_stream_jpeg(guac_user* user, guac_socket* socket,
|
||||
guac_composite_mode mode, const guac_layer* layer, int x, int y,
|
||||
cairo_surface_t* surface, int quality);
|
||||
|
||||
/**
|
||||
* Streams the image data of the given surface over an image stream ("img"
|
||||
* instruction) as WebP-encoded data at the given quality. The image stream
|
||||
* will be automatically allocated and freed. If the server does not support
|
||||
* WebP, this function has no effect, so be sure to check the result of
|
||||
* guac_user_supports_webp() or guac_client_supports_webp() prior to calling
|
||||
* this function.
|
||||
*
|
||||
* @param user
|
||||
* The Guacamole user for whom the image stream should be allocated.
|
||||
*
|
||||
* @param socket
|
||||
* The socket over which instructions associated with the image stream
|
||||
* should be sent.
|
||||
*
|
||||
* @param mode
|
||||
* The composite mode to use when rendering the image over the given layer.
|
||||
*
|
||||
* @param layer
|
||||
* The destination layer.
|
||||
*
|
||||
* @param x
|
||||
* The X coordinate of the upper-left corner of the destination rectangle
|
||||
* within the given layer.
|
||||
*
|
||||
* @param y
|
||||
* The Y coordinate of the upper-left corner of the destination rectangle
|
||||
* within the given layer.
|
||||
*
|
||||
* @param surface
|
||||
* A Cairo surface containing the image data to be streamed.
|
||||
*
|
||||
* @param quality
|
||||
* The WebP image quality, which must be an integer value between 0 and 100
|
||||
* inclusive. For lossy images, larger values indicate improving quality at
|
||||
* the expense of larger file size. For lossless images, this dictates the
|
||||
* quality of compression, with larger values producing smaller files at
|
||||
* the expense of speed.
|
||||
*
|
||||
* @param lossless
|
||||
* Zero to encode a lossy image, non-zero to encode losslessly.
|
||||
*/
|
||||
void guac_user_stream_webp(guac_user* user, guac_socket* socket,
|
||||
guac_composite_mode mode, const guac_layer* layer, int x, int y,
|
||||
cairo_surface_t* surface, int quality, int lossless);
|
||||
|
||||
/**
|
||||
* Returns whether the given user supports WebP. If the user does not
|
||||
* support WebP, or the server cannot encode WebP images, zero is returned.
|
||||
*
|
||||
* @param user
|
||||
* The Guacamole user to check for WebP support.
|
||||
*
|
||||
* @return
|
||||
* Non-zero if the given user claims to support WebP and the server has
|
||||
* been built with WebP support, zero otherwise.
|
||||
*/
|
||||
int guac_user_supports_webp(guac_user* user);
|
||||
|
||||
/**
|
||||
* Automatically handles a single argument received from a joining user,
|
||||
* returning a newly-allocated string containing that value. If the argument
|
||||
* provided by the user is blank, a newly-allocated string containing the
|
||||
* default value is returned.
|
||||
*
|
||||
* @param user
|
||||
* The user joining the connection and providing the given arguments.
|
||||
*
|
||||
* @param arg_names
|
||||
* A NULL-terminated array of argument names, corresponding to the provided
|
||||
* array of argument values. This array must be exactly the same size as
|
||||
* the argument value array, with one additional entry for the NULL
|
||||
* terminator.
|
||||
*
|
||||
* @param argv
|
||||
* An array of all argument values, corresponding to the provided array of
|
||||
* argument names. This array must be exactly the same size as the argument
|
||||
* name array, with the exception of the NULL terminator.
|
||||
*
|
||||
* @param index
|
||||
* The index of the entry in both the arg_names and argv arrays which
|
||||
* corresponds to the argument being parsed.
|
||||
*
|
||||
* @param default_value
|
||||
* The value to return if the provided argument is blank. If this value is
|
||||
* not NULL, the returned value will be a newly-allocated string containing
|
||||
* this value.
|
||||
*
|
||||
* @return
|
||||
* A newly-allocated string containing the provided argument value. If the
|
||||
* provided argument value is blank, this newly-allocated string will
|
||||
* contain the default value. If the default value is NULL and the provided
|
||||
* argument value is blank, no string will be allocated and NULL is
|
||||
* returned.
|
||||
*/
|
||||
char* guac_user_parse_args_string(guac_user* user, const char** arg_names,
|
||||
const char** argv, int index, const char* default_value);
|
||||
|
||||
/**
|
||||
* Automatically handles a single integer argument received from a joining
|
||||
* user, returning the integer value of that argument. If the argument provided
|
||||
* by the user is blank or invalid, the default value is returned.
|
||||
*
|
||||
* @param user
|
||||
* The user joining the connection and providing the given arguments.
|
||||
*
|
||||
* @param arg_names
|
||||
* A NULL-terminated array of argument names, corresponding to the provided
|
||||
* array of argument values. This array must be exactly the same size as
|
||||
* the argument value array, with one additional entry for the NULL
|
||||
* terminator.
|
||||
*
|
||||
* @param argv
|
||||
* An array of all argument values, corresponding to the provided array of
|
||||
* argument names. This array must be exactly the same size as the argument
|
||||
* name array, with the exception of the NULL terminator.
|
||||
*
|
||||
* @param index
|
||||
* The index of the entry in both the arg_names and argv arrays which
|
||||
* corresponds to the argument being parsed.
|
||||
*
|
||||
* @param default_value
|
||||
* The value to return if the provided argument is blank or invalid.
|
||||
*
|
||||
* @return
|
||||
* The integer value parsed from the provided argument value, or the
|
||||
* default value if the provided argument value is blank or invalid.
|
||||
*/
|
||||
int guac_user_parse_args_int(guac_user* user, const char** arg_names,
|
||||
const char** argv, int index, int default_value);
|
||||
|
||||
/**
|
||||
* Automatically handles a single boolean argument received from a joining
|
||||
* user, returning the value of that argument (either 1 for true or 0 for
|
||||
* false). Only "true" and "false" are legitimate values for a boolean
|
||||
* argument. If the argument provided by the user is blank or invalid, the
|
||||
* default value is returned.
|
||||
*
|
||||
* @param user
|
||||
* The user joining the connection and providing the given arguments.
|
||||
*
|
||||
* @param arg_names
|
||||
* A NULL-terminated array of argument names, corresponding to the provided
|
||||
* array of argument values. This array must be exactly the same size as
|
||||
* the argument value array, with one additional entry for the NULL
|
||||
* terminator.
|
||||
*
|
||||
* @param argv
|
||||
* An array of all argument values, corresponding to the provided array of
|
||||
* argument names. This array must be exactly the same size as the argument
|
||||
* name array, with the exception of the NULL terminator.
|
||||
*
|
||||
* @param index
|
||||
* The index of the entry in both the arg_names and argv arrays which
|
||||
* corresponds to the argument being parsed.
|
||||
*
|
||||
* @param default_value
|
||||
* The value to return if the provided argument is blank or invalid.
|
||||
*
|
||||
* @return
|
||||
* true (1) if the provided argument value is "true", false (0) if the
|
||||
* provided argument value is "false", or the default value if the provided
|
||||
* argument value is blank or invalid.
|
||||
*/
|
||||
int guac_user_parse_args_boolean(guac_user* user, const char** arg_names,
|
||||
const char** argv, int index, int default_value);
|
||||
|
||||
#endif
|
||||
|
88
src/libguac/id.c
Normal file
88
src/libguac/id.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "id.h"
|
||||
#include "error.h"
|
||||
|
||||
#ifdef HAVE_OSSP_UUID_H
|
||||
#include <ossp/uuid.h>
|
||||
#else
|
||||
#include <uuid.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
char* guac_generate_id(char prefix) {
|
||||
|
||||
char* buffer;
|
||||
char* identifier;
|
||||
size_t identifier_length;
|
||||
|
||||
uuid_t* uuid;
|
||||
|
||||
/* Attempt to create UUID object */
|
||||
if (uuid_create(&uuid) != UUID_RC_OK) {
|
||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||
guac_error_message = "Could not allocate memory for UUID";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Generate random UUID */
|
||||
if (uuid_make(uuid, UUID_MAKE_V4) != UUID_RC_OK) {
|
||||
uuid_destroy(uuid);
|
||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||
guac_error_message = "UUID generation failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate buffer for future formatted ID */
|
||||
buffer = malloc(UUID_LEN_STR + 2);
|
||||
if (buffer == NULL) {
|
||||
uuid_destroy(uuid);
|
||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||
guac_error_message = "Could not allocate memory for connection ID";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
identifier = &(buffer[1]);
|
||||
identifier_length = UUID_LEN_STR + 1;
|
||||
|
||||
/* Build connection ID from UUID */
|
||||
if (uuid_export(uuid, UUID_FMT_STR, &identifier, &identifier_length) != UUID_RC_OK) {
|
||||
free(buffer);
|
||||
uuid_destroy(uuid);
|
||||
guac_error = GUAC_STATUS_INTERNAL_ERROR;
|
||||
guac_error_message = "Conversion of UUID to connection ID failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uuid_destroy(uuid);
|
||||
|
||||
buffer[0] = prefix;
|
||||
buffer[UUID_LEN_STR + 1] = '\0';
|
||||
return buffer;
|
||||
|
||||
}
|
||||
|
||||
|
40
src/libguac/id.h
Normal file
40
src/libguac/id.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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_ID_H
|
||||
#define __GUAC_ID_H
|
||||
|
||||
/**
|
||||
* Generates a guaranteed-unique identifier which is a total of 37 characters
|
||||
* long, having the given single-character prefix. The resulting identifier
|
||||
* must be freed with a call to free() when no longer needed. If an error
|
||||
* occurs, NULL is returned, no memory is allocated, and guac_error is set
|
||||
* appropriately.
|
||||
*
|
||||
* @param prefix The single-character prefix to use.
|
||||
* @return A newly-allocated unique identifier with the given prefix, or
|
||||
* NULL if the identifier could not be generated.
|
||||
*/
|
||||
char* guac_generate_id(char prefix);
|
||||
|
||||
#endif
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "error.h"
|
||||
#include "instruction.h"
|
||||
#include "parser.h"
|
||||
#include "socket.h"
|
||||
#include "unicode.h"
|
||||
|
||||
@ -31,46 +31,49 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
guac_instruction* guac_instruction_alloc() {
|
||||
static void guac_parser_reset(guac_parser* parser) {
|
||||
parser->opcode = NULL;
|
||||
parser->argc = 0;
|
||||
parser->state = GUAC_PARSE_LENGTH;
|
||||
parser->__elementc = 0;
|
||||
parser->__element_length = 0;
|
||||
}
|
||||
|
||||
/* Allocate space for instruction */
|
||||
guac_instruction* instruction = malloc(sizeof(guac_instruction));
|
||||
if (instruction == NULL) {
|
||||
guac_parser* guac_parser_alloc() {
|
||||
|
||||
/* Allocate space for parser */
|
||||
guac_parser* parser = malloc(sizeof(guac_parser));
|
||||
if (parser == NULL) {
|
||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||
guac_error_message = "Insufficient memory to allocate instruction";
|
||||
guac_error_message = "Insufficient memory to allocate parser";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
guac_instruction_reset(instruction);
|
||||
return instruction;
|
||||
/* Init parse start/end markers */
|
||||
parser->__instructionbuf_unparsed_start = parser->__instructionbuf;
|
||||
parser->__instructionbuf_unparsed_end = parser->__instructionbuf;
|
||||
|
||||
guac_parser_reset(parser);
|
||||
return parser;
|
||||
|
||||
}
|
||||
|
||||
void guac_instruction_reset(guac_instruction* instruction) {
|
||||
instruction->opcode = NULL;
|
||||
instruction->argc = 0;
|
||||
instruction->state = GUAC_INSTRUCTION_PARSE_LENGTH;
|
||||
instruction->__elementc = 0;
|
||||
instruction->__element_length = 0;
|
||||
}
|
||||
|
||||
int guac_instruction_append(guac_instruction* instr,
|
||||
void* buffer, int length) {
|
||||
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 (instr->__elementc == GUAC_INSTRUCTION_MAX_ELEMENTS
|
||||
&& instr->state != GUAC_INSTRUCTION_PARSE_COMPLETE) {
|
||||
instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
|
||||
if (parser->__elementc == GUAC_INSTRUCTION_MAX_ELEMENTS
|
||||
&& parser->state != GUAC_PARSE_COMPLETE) {
|
||||
parser->state = GUAC_PARSE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse element length */
|
||||
if (instr->state == GUAC_INSTRUCTION_PARSE_LENGTH) {
|
||||
if (parser->state == GUAC_PARSE_LENGTH) {
|
||||
|
||||
int parsed_length = instr->__element_length;
|
||||
int parsed_length = parser->__element_length;
|
||||
while (bytes_parsed < length) {
|
||||
|
||||
/* Pull next character */
|
||||
@ -83,14 +86,14 @@ int guac_instruction_append(guac_instruction* instr,
|
||||
|
||||
/* If period, switch to parsing content */
|
||||
else if (c == '.') {
|
||||
instr->__elementv[instr->__elementc++] = char_buffer;
|
||||
instr->state = GUAC_INSTRUCTION_PARSE_CONTENT;
|
||||
parser->__elementv[parser->__elementc++] = char_buffer;
|
||||
parser->state = GUAC_PARSE_CONTENT;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If not digit, parse error */
|
||||
else {
|
||||
instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
|
||||
parser->state = GUAC_PARSE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -98,19 +101,19 @@ int guac_instruction_append(guac_instruction* instr,
|
||||
|
||||
/* If too long, parse error */
|
||||
if (parsed_length > GUAC_INSTRUCTION_MAX_LENGTH) {
|
||||
instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
|
||||
parser->state = GUAC_PARSE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save length */
|
||||
instr->__element_length = parsed_length;
|
||||
parser->__element_length = parsed_length;
|
||||
|
||||
} /* end parse length */
|
||||
|
||||
/* Parse element content */
|
||||
if (instr->state == GUAC_INSTRUCTION_PARSE_CONTENT) {
|
||||
if (parser->state == GUAC_PARSE_CONTENT) {
|
||||
|
||||
while (bytes_parsed < length && instr->__element_length >= 0) {
|
||||
while (bytes_parsed < length && parser->__element_length >= 0) {
|
||||
|
||||
/* Get length of current character */
|
||||
char c = *char_buffer;
|
||||
@ -124,35 +127,35 @@ int guac_instruction_append(guac_instruction* instr,
|
||||
bytes_parsed += char_length;
|
||||
|
||||
/* If end of element, handle terminator */
|
||||
if (instr->__element_length == 0) {
|
||||
if (parser->__element_length == 0) {
|
||||
|
||||
*char_buffer = '\0';
|
||||
|
||||
/* If semicolon, store end-of-instruction */
|
||||
if (c == ';') {
|
||||
instr->state = GUAC_INSTRUCTION_PARSE_COMPLETE;
|
||||
instr->opcode = instr->__elementv[0];
|
||||
instr->argv = &(instr->__elementv[1]);
|
||||
instr->argc = instr->__elementc - 1;
|
||||
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 == ',') {
|
||||
instr->state = GUAC_INSTRUCTION_PARSE_LENGTH;
|
||||
parser->state = GUAC_PARSE_LENGTH;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Otherwise, parse error */
|
||||
else {
|
||||
instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
|
||||
parser->state = GUAC_PARSE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* end if end of element */
|
||||
|
||||
/* Advance to next character */
|
||||
instr->__element_length--;
|
||||
parser->__element_length--;
|
||||
char_buffer += char_length;
|
||||
|
||||
}
|
||||
@ -163,27 +166,25 @@ int guac_instruction_append(guac_instruction* instr,
|
||||
|
||||
}
|
||||
|
||||
/* Returns new instruction if one exists, or NULL if no more instructions. */
|
||||
guac_instruction* guac_instruction_read(guac_socket* socket,
|
||||
int usec_timeout) {
|
||||
int guac_parser_read(guac_parser* parser, 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);
|
||||
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);
|
||||
|
||||
guac_instruction* instruction = guac_instruction_alloc();
|
||||
/* Begin next instruction if previous was ended */
|
||||
if (parser->state == GUAC_PARSE_COMPLETE)
|
||||
guac_parser_reset(parser);
|
||||
|
||||
while (instruction->state != GUAC_INSTRUCTION_PARSE_COMPLETE
|
||||
&& instruction->state != GUAC_INSTRUCTION_PARSE_ERROR) {
|
||||
while (parser->state != GUAC_PARSE_COMPLETE
|
||||
&& parser->state != GUAC_PARSE_ERROR) {
|
||||
|
||||
/* Add any available data to buffer */
|
||||
int parsed = guac_instruction_append(instruction, unparsed_start,
|
||||
unparsed_end - unparsed_start);
|
||||
int parsed = guac_parser_append(parser, unparsed_start, unparsed_end - unparsed_start);
|
||||
|
||||
/* Read more data if not enough data to parse */
|
||||
if (parsed == 0) {
|
||||
if (parsed == 0 && parser->state != GUAC_PARSE_ERROR) {
|
||||
|
||||
int retval;
|
||||
|
||||
@ -191,23 +192,23 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
||||
if (unparsed_end == buffer_end) {
|
||||
|
||||
/* Shift backward if possible */
|
||||
if (instr_start != socket->__instructionbuf) {
|
||||
if (instr_start != parser->__instructionbuf) {
|
||||
|
||||
int i;
|
||||
|
||||
/* Shift buffer */
|
||||
int offset = instr_start - socket->__instructionbuf;
|
||||
memmove(socket->__instructionbuf, instr_start,
|
||||
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 = socket->__instructionbuf;
|
||||
instr_start = parser->__instructionbuf;
|
||||
|
||||
/* Update parsed elements, if any */
|
||||
for (i=0; i<instruction->__elementc; i++)
|
||||
instruction->__elementv[i] -= offset;
|
||||
for (i=0; i < parser->__elementc; i++)
|
||||
parser->__elementv[i] -= offset;
|
||||
|
||||
}
|
||||
|
||||
@ -215,7 +216,7 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
||||
else {
|
||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||
guac_error_message = "Instruction too long";
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@ -223,7 +224,7 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
||||
/* No instruction yet? Get more data ... */
|
||||
retval = guac_socket_select(socket, usec_timeout);
|
||||
if (retval <= 0)
|
||||
return NULL;
|
||||
return -1;
|
||||
|
||||
/* Attempt to fill buffer */
|
||||
retval = guac_socket_read(socket, unparsed_end,
|
||||
@ -233,7 +234,7 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
||||
if (retval < 0) {
|
||||
guac_error = GUAC_STATUS_SEE_ERRNO;
|
||||
guac_error_message = "Error filling instruction buffer";
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@ -241,7 +242,7 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
||||
guac_error = GUAC_STATUS_CLOSED;
|
||||
guac_error_message = "End of stream reached while "
|
||||
"reading instruction";
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Update internal buffer */
|
||||
@ -256,55 +257,65 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
||||
} /* end while parsing data */
|
||||
|
||||
/* Fail on error */
|
||||
if (instruction->state == GUAC_INSTRUCTION_PARSE_ERROR) {
|
||||
if (parser->state == GUAC_PARSE_ERROR) {
|
||||
guac_error = GUAC_STATUS_PROTOCOL_ERROR;
|
||||
guac_error_message = "Instruction parse error";
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
socket->__instructionbuf_unparsed_start = unparsed_start;
|
||||
socket->__instructionbuf_unparsed_end = unparsed_end;
|
||||
return instruction;
|
||||
parser->__instructionbuf_unparsed_start = unparsed_start;
|
||||
parser->__instructionbuf_unparsed_end = unparsed_end;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
guac_instruction* guac_instruction_expect(guac_socket* socket, int usec_timeout,
|
||||
const char* opcode) {
|
||||
int guac_parser_expect(guac_parser* parser, guac_socket* socket, int usec_timeout, const char* opcode) {
|
||||
|
||||
guac_instruction* instruction;
|
||||
|
||||
/* Wait for data until timeout */
|
||||
if (guac_instruction_waiting(socket, usec_timeout) <= 0)
|
||||
return NULL;
|
||||
|
||||
/* Read available instruction */
|
||||
instruction = guac_instruction_read(socket, usec_timeout);
|
||||
if (instruction == NULL)
|
||||
return NULL;
|
||||
/* Read next instruction */
|
||||
if (guac_parser_read(parser, socket, usec_timeout) != 0)
|
||||
return -1;
|
||||
|
||||
/* Validate instruction */
|
||||
if (strcmp(instruction->opcode, opcode) != 0) {
|
||||
if (strcmp(parser->opcode, opcode) != 0) {
|
||||
guac_error = GUAC_STATUS_PROTOCOL_ERROR;
|
||||
guac_error_message = "Instruction read did not have expected opcode";
|
||||
guac_instruction_free(instruction);
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return instruction if valid */
|
||||
return instruction;
|
||||
/* Return non-zero only if valid instruction read */
|
||||
return parser->state != GUAC_PARSE_COMPLETE;
|
||||
|
||||
}
|
||||
|
||||
void guac_instruction_free(guac_instruction* instruction) {
|
||||
free(instruction);
|
||||
int guac_parser_length(guac_parser* parser) {
|
||||
|
||||
char* unparsed_end = parser->__instructionbuf_unparsed_end;
|
||||
char* unparsed_start = parser->__instructionbuf_unparsed_start;
|
||||
|
||||
return unparsed_end - unparsed_start;
|
||||
|
||||
}
|
||||
|
||||
int guac_instruction_waiting(guac_socket* socket, int usec_timeout) {
|
||||
int guac_parser_shift(guac_parser* parser, void* buffer, int length) {
|
||||
|
||||
if (socket->__instructionbuf_unparsed_end >
|
||||
socket->__instructionbuf_unparsed_start)
|
||||
return 1;
|
||||
char* copy_end = parser->__instructionbuf_unparsed_end;
|
||||
char* copy_start = parser->__instructionbuf_unparsed_start;
|
||||
|
||||
/* Contain copy region within length */
|
||||
if (copy_end - copy_start > length)
|
||||
copy_end = copy_start + length;
|
||||
|
||||
/* Copy buffer */
|
||||
length = copy_end - copy_start;
|
||||
memcpy(buffer, copy_start, length);
|
||||
|
||||
parser->__instructionbuf_unparsed_start = copy_end;
|
||||
|
||||
return length;
|
||||
|
||||
return guac_socket_select(socket, usec_timeout);
|
||||
}
|
||||
|
||||
void guac_parser_free(guac_parser* parser) {
|
||||
free(parser);
|
||||
}
|
||||
|
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "error.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
guac_client_plugin* guac_client_plugin_open(const char* protocol) {
|
||||
|
||||
guac_client_plugin* plugin;
|
||||
|
||||
/* Reference to dlopen()'d plugin */
|
||||
void* client_plugin_handle;
|
||||
|
||||
/* Client args description */
|
||||
const char** client_args;
|
||||
|
||||
/* Pluggable client */
|
||||
char protocol_lib[GUAC_PROTOCOL_LIBRARY_LIMIT] =
|
||||
GUAC_PROTOCOL_LIBRARY_PREFIX;
|
||||
|
||||
union {
|
||||
guac_client_init_handler* client_init;
|
||||
void* obj;
|
||||
} alias;
|
||||
|
||||
/* Add protocol and .so suffix to protocol_lib */
|
||||
strncat(protocol_lib, protocol, GUAC_PROTOCOL_NAME_LIMIT-1);
|
||||
strcat(protocol_lib, GUAC_PROTOCOL_LIBRARY_SUFFIX);
|
||||
|
||||
/* Load client plugin */
|
||||
client_plugin_handle = dlopen(protocol_lib, RTLD_LAZY);
|
||||
if (!client_plugin_handle) {
|
||||
guac_error = GUAC_STATUS_NOT_FOUND;
|
||||
guac_error_message = dlerror();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dlerror(); /* Clear errors */
|
||||
|
||||
/* Get init function */
|
||||
alias.obj = dlsym(client_plugin_handle, "guac_client_init");
|
||||
|
||||
/* Fail if cannot find guac_client_init */
|
||||
if (dlerror() != NULL) {
|
||||
guac_error = GUAC_STATUS_INTERNAL_ERROR;
|
||||
guac_error_message = dlerror();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get usage strig */
|
||||
client_args = (const char**) dlsym(client_plugin_handle, "GUAC_CLIENT_ARGS");
|
||||
|
||||
/* Fail if cannot find GUAC_CLIENT_ARGS */
|
||||
if (dlerror() != NULL) {
|
||||
guac_error = GUAC_STATUS_INTERNAL_ERROR;
|
||||
guac_error_message = dlerror();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate plugin */
|
||||
plugin = malloc(sizeof(guac_client_plugin));
|
||||
if (plugin == NULL) {
|
||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||
guac_error_message = "Could not allocate memory for client plugin";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Init and return plugin */
|
||||
plugin->__client_plugin_handle = client_plugin_handle;
|
||||
plugin->init_handler = alias.client_init;
|
||||
plugin->args = client_args;
|
||||
return plugin;
|
||||
|
||||
}
|
||||
|
||||
int guac_client_plugin_close(guac_client_plugin* plugin) {
|
||||
|
||||
/* Unload client plugin */
|
||||
if (dlclose(plugin->__client_plugin_handle)) {
|
||||
guac_error = GUAC_STATUS_INTERNAL_ERROR;
|
||||
guac_error_message = dlerror();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Free plugin handle */
|
||||
free(plugin);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_client_plugin_init_client(guac_client_plugin* plugin,
|
||||
guac_client* client, int argc, char** argv) {
|
||||
|
||||
return plugin->init_handler(client, argc, argv);
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
guac_pool* guac_pool_alloc(int size) {
|
||||
|
||||
pthread_mutexattr_t lock_attributes;
|
||||
guac_pool* pool = malloc(sizeof(guac_pool));
|
||||
|
||||
/* If unable to allocate, just return NULL. */
|
||||
@ -41,6 +42,11 @@ guac_pool* guac_pool_alloc(int size) {
|
||||
pool->__head = NULL;
|
||||
pool->__tail = NULL;
|
||||
|
||||
/* Init lock */
|
||||
pthread_mutexattr_init(&lock_attributes);
|
||||
pthread_mutexattr_setpshared(&lock_attributes, PTHREAD_PROCESS_SHARED);
|
||||
pthread_mutex_init(&(pool->__lock), &lock_attributes);
|
||||
|
||||
return pool;
|
||||
|
||||
}
|
||||
@ -57,6 +63,9 @@ void guac_pool_free(guac_pool* pool) {
|
||||
free(old);
|
||||
}
|
||||
|
||||
/* Destroy lock */
|
||||
pthread_mutex_destroy(&(pool->__lock));
|
||||
|
||||
/* Free pool */
|
||||
free(pool);
|
||||
|
||||
@ -66,11 +75,17 @@ int guac_pool_next_int(guac_pool* pool) {
|
||||
|
||||
int value;
|
||||
|
||||
/* Acquire exclusive access */
|
||||
pthread_mutex_lock(&(pool->__lock));
|
||||
|
||||
pool->active++;
|
||||
|
||||
/* If more integers are needed, return a new one. */
|
||||
if (pool->__head == NULL || pool->__next_value < pool->min_size)
|
||||
return pool->__next_value++;
|
||||
if (pool->__head == NULL || pool->__next_value < pool->min_size) {
|
||||
value = pool->__next_value++;
|
||||
pthread_mutex_unlock(&(pool->__lock));
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Otherwise, remove first integer. */
|
||||
value = pool->__head->value;
|
||||
@ -90,6 +105,7 @@ int guac_pool_next_int(guac_pool* pool) {
|
||||
}
|
||||
|
||||
/* Return retrieved value. */
|
||||
pthread_mutex_unlock(&(pool->__lock));
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -100,6 +116,9 @@ void guac_pool_free_int(guac_pool* pool, int value) {
|
||||
pool_int->value = value;
|
||||
pool_int->__next = NULL;
|
||||
|
||||
/* Acquire exclusive access */
|
||||
pthread_mutex_lock(&(pool->__lock));
|
||||
|
||||
pool->active--;
|
||||
|
||||
/* If pool empty, store as sole entry. */
|
||||
@ -112,5 +131,8 @@ void guac_pool_free_int(guac_pool* pool, int value) {
|
||||
pool->__tail = pool_int;
|
||||
}
|
||||
|
||||
/* Value has been freed */
|
||||
pthread_mutex_unlock(&(pool->__lock));
|
||||
|
||||
}
|
||||
|
||||
|
@ -29,14 +29,15 @@
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static void raw_encoder_begin_handler(guac_audio_stream* audio) {
|
||||
static void raw_encoder_send_audio(guac_audio_stream* audio,
|
||||
guac_socket* socket) {
|
||||
|
||||
raw_encoder_state* state;
|
||||
char mimetype[256];
|
||||
|
||||
/* Produce mimetype string from format info */
|
||||
@ -44,7 +45,16 @@ static void raw_encoder_begin_handler(guac_audio_stream* audio) {
|
||||
audio->bps, audio->rate, audio->channels);
|
||||
|
||||
/* Associate stream */
|
||||
guac_protocol_send_audio(audio->client->socket, audio->stream, mimetype);
|
||||
guac_protocol_send_audio(socket, audio->stream, mimetype);
|
||||
|
||||
}
|
||||
|
||||
static void raw_encoder_begin_handler(guac_audio_stream* audio) {
|
||||
|
||||
raw_encoder_state* state;
|
||||
|
||||
/* Broadcast existence of stream */
|
||||
raw_encoder_send_audio(audio, audio->client->socket);
|
||||
|
||||
/* Allocate and init encoder state */
|
||||
audio->data = state = malloc(sizeof(raw_encoder_state));
|
||||
@ -55,9 +65,13 @@ static void raw_encoder_begin_handler(guac_audio_stream* audio) {
|
||||
|
||||
state->buffer = malloc(state->length);
|
||||
|
||||
guac_client_log(audio->client, GUAC_LOG_DEBUG,
|
||||
"Using raw encoder (%s) with a %i byte buffer.",
|
||||
mimetype, state->length);
|
||||
}
|
||||
|
||||
static void raw_encoder_join_handler(guac_audio_stream* audio,
|
||||
guac_user* user) {
|
||||
|
||||
/* Notify user of existence of stream */
|
||||
raw_encoder_send_audio(audio, user->socket);
|
||||
|
||||
}
|
||||
|
||||
@ -143,6 +157,7 @@ guac_audio_encoder _raw8_encoder = {
|
||||
.begin_handler = raw_encoder_begin_handler,
|
||||
.write_handler = raw_encoder_write_handler,
|
||||
.flush_handler = raw_encoder_flush_handler,
|
||||
.join_handler = raw_encoder_join_handler,
|
||||
.end_handler = raw_encoder_end_handler
|
||||
};
|
||||
|
||||
@ -152,6 +167,7 @@ guac_audio_encoder _raw16_encoder = {
|
||||
.begin_handler = raw_encoder_begin_handler,
|
||||
.write_handler = raw_encoder_write_handler,
|
||||
.flush_handler = raw_encoder_flush_handler,
|
||||
.join_handler = raw_encoder_join_handler,
|
||||
.end_handler = raw_encoder_end_handler
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
* 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
|
||||
@ -25,9 +25,11 @@
|
||||
#include "error.h"
|
||||
#include "socket.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -37,34 +39,69 @@
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
typedef struct __guac_socket_fd_data {
|
||||
/**
|
||||
* Data associated with an open socket which writes to a file descriptor.
|
||||
*/
|
||||
typedef struct guac_socket_fd_data {
|
||||
|
||||
/**
|
||||
* The associated file descriptor;
|
||||
*/
|
||||
int fd;
|
||||
|
||||
} __guac_socket_fd_data;
|
||||
/**
|
||||
* The number of bytes currently in the main write buffer.
|
||||
*/
|
||||
int written;
|
||||
|
||||
ssize_t __guac_socket_fd_read_handler(guac_socket* socket,
|
||||
void* buf, size_t count) {
|
||||
/**
|
||||
* The main write buffer. Bytes written go here before being flushed
|
||||
* to the open file descriptor.
|
||||
*/
|
||||
char out_buf[GUAC_SOCKET_OUTPUT_BUFFER_SIZE];
|
||||
|
||||
__guac_socket_fd_data* data = (__guac_socket_fd_data*) socket->data;
|
||||
/**
|
||||
* Lock which is acquired when an instruction is being written, and
|
||||
* released when the instruction is finished being written.
|
||||
*/
|
||||
pthread_mutex_t socket_lock;
|
||||
|
||||
/* Read from socket */
|
||||
int retval = read(data->fd, buf, count);
|
||||
/**
|
||||
* Lock which protects access to the internal buffer of this socket,
|
||||
* guaranteeing atomicity of writes and flushes.
|
||||
*/
|
||||
pthread_mutex_t buffer_lock;
|
||||
|
||||
/* Record errors in guac_error */
|
||||
if (retval < 0) {
|
||||
guac_error = GUAC_STATUS_SEE_ERRNO;
|
||||
guac_error_message = "Error reading data from socket";
|
||||
}
|
||||
} guac_socket_fd_data;
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
ssize_t __guac_socket_fd_write_handler(guac_socket* socket,
|
||||
/**
|
||||
* Writes the entire contents of the given buffer to the file descriptor
|
||||
* associated with the given socket, retrying as necessary until the whole
|
||||
* buffer is written, and aborting if an error occurs.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket associated with the file descriptor to which the given
|
||||
* buffer should be written.
|
||||
*
|
||||
* @param buf
|
||||
* The buffer of data to write to the given guac_socket.
|
||||
*
|
||||
* @param count
|
||||
* The number of bytes within the given buffer.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes written, which will be exactly the size of the given
|
||||
* buffer, or a negative value if an error occurs.
|
||||
*/
|
||||
ssize_t guac_socket_fd_write(guac_socket* socket,
|
||||
const void* buf, size_t count) {
|
||||
|
||||
__guac_socket_fd_data* data = (__guac_socket_fd_data*) socket->data;
|
||||
guac_socket_fd_data* data = (guac_socket_fd_data*) socket->data;
|
||||
const char* buffer = buf;
|
||||
|
||||
/* Write until completely written */
|
||||
while (count > 0) {
|
||||
|
||||
int retval;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
@ -79,14 +116,229 @@ ssize_t __guac_socket_fd_write_handler(guac_socket* socket,
|
||||
if (retval < 0) {
|
||||
guac_error = GUAC_STATUS_SEE_ERRNO;
|
||||
guac_error_message = "Error writing data to socket";
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Advance buffer as data retval */
|
||||
buffer += retval;
|
||||
count -= retval;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to read from the underlying file descriptor of the given
|
||||
* guac_socket, populating the given buffer.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket being read from.
|
||||
*
|
||||
* @param buf
|
||||
* The arbitrary buffer which we must populate with data.
|
||||
*
|
||||
* @param count
|
||||
* The maximum number of bytes to read into the buffer.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes read, or -1 if an error occurs.
|
||||
*/
|
||||
static ssize_t guac_socket_fd_read_handler(guac_socket* socket,
|
||||
void* buf, size_t count) {
|
||||
|
||||
guac_socket_fd_data* data = (guac_socket_fd_data*) socket->data;
|
||||
|
||||
/* Read from socket */
|
||||
int retval = read(data->fd, buf, count);
|
||||
|
||||
/* Record errors in guac_error */
|
||||
if (retval < 0) {
|
||||
guac_error = GUAC_STATUS_SEE_ERRNO;
|
||||
guac_error_message = "Error reading data from socket";
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
int __guac_socket_fd_select_handler(guac_socket* socket, int usec_timeout) {
|
||||
/**
|
||||
* Flushes the contents of the output buffer of the given socket immediately,
|
||||
* without first locking access to the output buffer. This function must ONLY
|
||||
* be called if the buffer lock has already been acquired.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket to flush.
|
||||
*
|
||||
* @return
|
||||
* Zero if the flush operation was successful, non-zero otherwise.
|
||||
*/
|
||||
static ssize_t guac_socket_fd_flush(guac_socket* socket) {
|
||||
|
||||
__guac_socket_fd_data* data = (__guac_socket_fd_data*) socket->data;
|
||||
guac_socket_fd_data* data = (guac_socket_fd_data*) socket->data;
|
||||
|
||||
/* Flush remaining bytes in buffer */
|
||||
if (data->written > 0) {
|
||||
|
||||
/* Write ALL bytes in buffer immediately */
|
||||
if (guac_socket_fd_write(socket, data->out_buf, data->written))
|
||||
return 1;
|
||||
|
||||
data->written = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the internal buffer of the given guac_socket, writing all data
|
||||
* to the underlying file descriptor.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket to flush.
|
||||
*
|
||||
* @return
|
||||
* Zero if the flush operation was successful, non-zero otherwise.
|
||||
*/
|
||||
static ssize_t guac_socket_fd_flush_handler(guac_socket* socket) {
|
||||
|
||||
int retval;
|
||||
guac_socket_fd_data* data = (guac_socket_fd_data*) socket->data;
|
||||
|
||||
/* Acquire exclusive access to buffer */
|
||||
pthread_mutex_lock(&(data->buffer_lock));
|
||||
|
||||
/* Flush contents of buffer */
|
||||
retval = guac_socket_fd_flush(socket);
|
||||
|
||||
/* Relinquish exclusive access to buffer */
|
||||
pthread_mutex_unlock(&(data->buffer_lock));
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the contents of the buffer to the output buffer of the given socket,
|
||||
* flushing the output buffer as necessary, without first locking access to the
|
||||
* output buffer. This function must ONLY be called if the buffer lock has
|
||||
* already been acquired.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket to write the given buffer to.
|
||||
*
|
||||
* @param buf
|
||||
* The buffer to write to the given socket.
|
||||
*
|
||||
* @param count
|
||||
* The number of bytes in the given buffer.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes written, or a negative value if an error occurs
|
||||
* during write.
|
||||
*/
|
||||
static ssize_t guac_socket_fd_write_buffered(guac_socket* socket,
|
||||
const void* buf, size_t count) {
|
||||
|
||||
size_t original_count = count;
|
||||
const char* current = buf;
|
||||
guac_socket_fd_data* data = (guac_socket_fd_data*) socket->data;
|
||||
|
||||
/* Append to buffer, flush if necessary */
|
||||
while (count > 0) {
|
||||
|
||||
int chunk_size;
|
||||
int remaining = sizeof(data->out_buf) - data->written;
|
||||
|
||||
/* If no space left in buffer, flush and retry */
|
||||
if (remaining == 0) {
|
||||
|
||||
/* Abort if error occurs during flush */
|
||||
if (guac_socket_fd_flush(socket))
|
||||
return -1;
|
||||
|
||||
/* Retry buffer append */
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* Calculate size of chunk to be written to buffer */
|
||||
chunk_size = count;
|
||||
if (chunk_size > remaining)
|
||||
chunk_size = remaining;
|
||||
|
||||
/* Update output buffer */
|
||||
memcpy(data->out_buf + data->written, current, chunk_size);
|
||||
data->written += chunk_size;
|
||||
|
||||
/* Update provided buffer */
|
||||
current += chunk_size;
|
||||
count -= chunk_size;
|
||||
|
||||
}
|
||||
|
||||
/* All bytes have been written, possibly some to the internal buffer */
|
||||
return original_count;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the provided data to the internal buffer for future writing. The
|
||||
* actual write attempt will occur only upon flush, or when the internal buffer
|
||||
* is full.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket being write to.
|
||||
*
|
||||
* @param buf
|
||||
* The arbitrary buffer containing the data to be written.
|
||||
*
|
||||
* @param count
|
||||
* The number of bytes contained within the buffer.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes written, or -1 if an error occurs.
|
||||
*/
|
||||
static ssize_t guac_socket_fd_write_handler(guac_socket* socket,
|
||||
const void* buf, size_t count) {
|
||||
|
||||
int retval;
|
||||
guac_socket_fd_data* data = (guac_socket_fd_data*) socket->data;
|
||||
|
||||
/* Acquire exclusive access to buffer */
|
||||
pthread_mutex_lock(&(data->buffer_lock));
|
||||
|
||||
/* Write provided data to buffer */
|
||||
retval = guac_socket_fd_write_buffered(socket, buf, count);
|
||||
|
||||
/* Relinquish exclusive access to buffer */
|
||||
pthread_mutex_unlock(&(data->buffer_lock));
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for data on the underlying file desriptor of the given socket to
|
||||
* become available such that the next read operation will not block.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket to wait for.
|
||||
*
|
||||
* @param usec_timeout
|
||||
* The maximum amount of time to wait for data, in microseconds, or -1 to
|
||||
* potentially wait forever.
|
||||
*
|
||||
* @return
|
||||
* A positive value on success, zero if the timeout elapsed and no data is
|
||||
* available, or a negative value if an error occurs.
|
||||
*/
|
||||
static int guac_socket_fd_select_handler(guac_socket* socket,
|
||||
int usec_timeout) {
|
||||
|
||||
guac_socket_fd_data* data = (guac_socket_fd_data*) socket->data;
|
||||
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
@ -122,20 +374,91 @@ int __guac_socket_fd_select_handler(guac_socket* socket, int usec_timeout) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees all implementation-specific data associated with the given socket, but
|
||||
* not the socket object itself.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket whose associated data should be freed.
|
||||
*
|
||||
* @return
|
||||
* Zero if the data was successfully freed, non-zero otherwise. This
|
||||
* implementation always succeeds, and will always return zero.
|
||||
*/
|
||||
static int guac_socket_fd_free_handler(guac_socket* socket) {
|
||||
|
||||
guac_socket_fd_data* data = (guac_socket_fd_data*) socket->data;
|
||||
|
||||
/* Destroy locks */
|
||||
pthread_mutex_destroy(&(data->socket_lock));
|
||||
pthread_mutex_destroy(&(data->buffer_lock));
|
||||
|
||||
/* Close file descriptor */
|
||||
close(data->fd);
|
||||
|
||||
free(data);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires exclusive access to the given socket.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket to which exclusive access is required.
|
||||
*/
|
||||
static void guac_socket_fd_lock_handler(guac_socket* socket) {
|
||||
|
||||
guac_socket_fd_data* data = (guac_socket_fd_data*) socket->data;
|
||||
|
||||
/* Acquire exclusive access to socket */
|
||||
pthread_mutex_lock(&(data->socket_lock));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Relinquishes exclusive access to the given socket.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket to which exclusive access is no longer required.
|
||||
*/
|
||||
static void guac_socket_fd_unlock_handler(guac_socket* socket) {
|
||||
|
||||
guac_socket_fd_data* data = (guac_socket_fd_data*) socket->data;
|
||||
|
||||
/* Relinquish exclusive access to socket */
|
||||
pthread_mutex_unlock(&(data->socket_lock));
|
||||
|
||||
}
|
||||
|
||||
guac_socket* guac_socket_open(int fd) {
|
||||
|
||||
pthread_mutexattr_t lock_attributes;
|
||||
|
||||
/* Allocate socket and associated data */
|
||||
guac_socket* socket = guac_socket_alloc();
|
||||
__guac_socket_fd_data* data = malloc(sizeof(__guac_socket_fd_data));
|
||||
guac_socket_fd_data* data = malloc(sizeof(guac_socket_fd_data));
|
||||
|
||||
/* Store file descriptor as socket data */
|
||||
data->fd = fd;
|
||||
data->written = 0;
|
||||
socket->data = data;
|
||||
|
||||
pthread_mutexattr_init(&lock_attributes);
|
||||
pthread_mutexattr_setpshared(&lock_attributes, PTHREAD_PROCESS_SHARED);
|
||||
|
||||
/* Init locks */
|
||||
pthread_mutex_init(&(data->socket_lock), &lock_attributes);
|
||||
pthread_mutex_init(&(data->buffer_lock), &lock_attributes);
|
||||
|
||||
/* Set read/write handlers */
|
||||
socket->read_handler = __guac_socket_fd_read_handler;
|
||||
socket->write_handler = __guac_socket_fd_write_handler;
|
||||
socket->select_handler = __guac_socket_fd_select_handler;
|
||||
socket->read_handler = guac_socket_fd_read_handler;
|
||||
socket->write_handler = guac_socket_fd_write_handler;
|
||||
socket->select_handler = guac_socket_fd_select_handler;
|
||||
socket->lock_handler = guac_socket_fd_lock_handler;
|
||||
socket->unlock_handler = guac_socket_fd_unlock_handler;
|
||||
socket->flush_handler = guac_socket_fd_flush_handler;
|
||||
socket->free_handler = guac_socket_fd_free_handler;
|
||||
|
||||
return socket;
|
||||
|
||||
|
@ -105,6 +105,27 @@ ssize_t __guac_socket_nest_write_handler(guac_socket* socket,
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees all implementation-specific data associated with the given socket, but
|
||||
* not the socket object itself.
|
||||
*
|
||||
* @param socket
|
||||
* The guac_socket whose associated data should be freed.
|
||||
*
|
||||
* @return
|
||||
* Zero if the data was successfully freed, non-zero otherwise. This
|
||||
* implementation always succeeds, and will always return zero.
|
||||
*/
|
||||
static int __guac_socket_nest_free_handler(guac_socket* socket) {
|
||||
|
||||
/* Free associated data */
|
||||
__guac_socket_nest_data* data = (__guac_socket_nest_data*) socket->data;
|
||||
free(data);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
guac_socket* guac_socket_nest(guac_socket* parent, int index) {
|
||||
|
||||
/* Allocate socket and associated data */
|
||||
@ -115,8 +136,9 @@ guac_socket* guac_socket_nest(guac_socket* parent, int index) {
|
||||
data->parent = parent;
|
||||
socket->data = data;
|
||||
|
||||
/* Set write handler */
|
||||
/* Set write and free handlers */
|
||||
socket->write_handler = __guac_socket_nest_write_handler;
|
||||
socket->free_handler = __guac_socket_nest_free_handler;
|
||||
|
||||
return socket;
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -138,7 +139,6 @@ int guac_socket_select(guac_socket* socket, int usec_timeout) {
|
||||
|
||||
guac_socket* guac_socket_alloc() {
|
||||
|
||||
pthread_mutexattr_t lock_attributes;
|
||||
guac_socket* socket = malloc(sizeof(guac_socket));
|
||||
|
||||
/* If no memory available, return with error */
|
||||
@ -149,46 +149,28 @@ guac_socket* guac_socket_alloc() {
|
||||
}
|
||||
|
||||
socket->__ready = 0;
|
||||
socket->__written = 0;
|
||||
socket->data = NULL;
|
||||
socket->state = GUAC_SOCKET_OPEN;
|
||||
socket->last_write_timestamp = guac_timestamp_current();
|
||||
|
||||
/* Init members */
|
||||
socket->__instructionbuf_unparsed_start = socket->__instructionbuf;
|
||||
socket->__instructionbuf_unparsed_end = socket->__instructionbuf;
|
||||
|
||||
/* Default to unsafe threading */
|
||||
socket->__threadsafe_instructions = 0;
|
||||
|
||||
/* No keep alive ping by default */
|
||||
socket->__keep_alive_enabled = 0;
|
||||
|
||||
pthread_mutexattr_init(&lock_attributes);
|
||||
pthread_mutexattr_setpshared(&lock_attributes, PTHREAD_PROCESS_SHARED);
|
||||
|
||||
pthread_mutex_init(&(socket->__instruction_write_lock), &lock_attributes);
|
||||
pthread_mutex_init(&(socket->__buffer_lock), &lock_attributes);
|
||||
|
||||
/* No handlers yet */
|
||||
socket->read_handler = NULL;
|
||||
socket->write_handler = NULL;
|
||||
socket->select_handler = NULL;
|
||||
socket->free_handler = NULL;
|
||||
socket->flush_handler = NULL;
|
||||
socket->lock_handler = NULL;
|
||||
socket->unlock_handler = NULL;
|
||||
|
||||
return socket;
|
||||
|
||||
}
|
||||
|
||||
void guac_socket_require_threadsafe(guac_socket* socket) {
|
||||
socket->__threadsafe_instructions = 1;
|
||||
}
|
||||
|
||||
void guac_socket_require_keep_alive(guac_socket* socket) {
|
||||
|
||||
/* Keep-alive thread requires a threadsafe socket */
|
||||
guac_socket_require_threadsafe(socket);
|
||||
|
||||
/* Start keep-alive thread */
|
||||
socket->__keep_alive_enabled = 1;
|
||||
pthread_create(&(socket->__keep_alive_thread), NULL,
|
||||
@ -198,44 +180,28 @@ void guac_socket_require_keep_alive(guac_socket* socket) {
|
||||
|
||||
void guac_socket_instruction_begin(guac_socket* socket) {
|
||||
|
||||
/* Lock writes if threadsafety enabled */
|
||||
if (socket->__threadsafe_instructions)
|
||||
pthread_mutex_lock(&(socket->__instruction_write_lock));
|
||||
/* Call instruction begin handler if defined */
|
||||
if (socket->lock_handler)
|
||||
socket->lock_handler(socket);
|
||||
|
||||
}
|
||||
|
||||
void guac_socket_instruction_end(guac_socket* socket) {
|
||||
|
||||
/* Unlock writes if threadsafety enabled */
|
||||
if (socket->__threadsafe_instructions)
|
||||
pthread_mutex_unlock(&(socket->__instruction_write_lock));
|
||||
|
||||
}
|
||||
|
||||
void guac_socket_update_buffer_begin(guac_socket* socket) {
|
||||
|
||||
/* Lock if threadsafety enabled */
|
||||
if (socket->__threadsafe_instructions)
|
||||
pthread_mutex_lock(&(socket->__buffer_lock));
|
||||
|
||||
}
|
||||
|
||||
void guac_socket_update_buffer_end(guac_socket* socket) {
|
||||
|
||||
/* Unlock if threadsafety enabled */
|
||||
if (socket->__threadsafe_instructions)
|
||||
pthread_mutex_unlock(&(socket->__buffer_lock));
|
||||
/* Call instruction end handler if defined */
|
||||
if (socket->unlock_handler)
|
||||
socket->unlock_handler(socket);
|
||||
|
||||
}
|
||||
|
||||
void guac_socket_free(guac_socket* socket) {
|
||||
|
||||
guac_socket_flush(socket);
|
||||
|
||||
/* Call free handler if defined */
|
||||
if (socket->free_handler)
|
||||
socket->free_handler(socket);
|
||||
|
||||
guac_socket_flush(socket);
|
||||
|
||||
/* Mark as closed */
|
||||
socket->state = GUAC_SOCKET_CLOSED;
|
||||
|
||||
@ -243,91 +209,92 @@ void guac_socket_free(guac_socket* socket) {
|
||||
if (socket->__keep_alive_enabled)
|
||||
pthread_join(socket->__keep_alive_thread, NULL);
|
||||
|
||||
pthread_mutex_destroy(&(socket->__instruction_write_lock));
|
||||
free(socket);
|
||||
}
|
||||
|
||||
ssize_t guac_socket_write_int(guac_socket* socket, int64_t i) {
|
||||
|
||||
char buffer[128];
|
||||
snprintf(buffer, sizeof(buffer), "%"PRIi64, i);
|
||||
return guac_socket_write_string(socket, buffer);
|
||||
int length;
|
||||
|
||||
/* Write provided integer as a string */
|
||||
length = snprintf(buffer, sizeof(buffer), "%"PRIi64, i);
|
||||
return guac_socket_write(socket, buffer, length);
|
||||
|
||||
}
|
||||
|
||||
ssize_t guac_socket_write_string(guac_socket* socket, const char* str) {
|
||||
|
||||
char* __out_buf = socket->__out_buf;
|
||||
|
||||
guac_socket_update_buffer_begin(socket);
|
||||
|
||||
for (; *str != '\0'; str++) {
|
||||
|
||||
__out_buf[socket->__written++] = *str;
|
||||
|
||||
/* Flush when necessary, return on error. Note that we must flush within 4 bytes of boundary because
|
||||
* __guac_socket_write_base64_triplet ALWAYS writes four bytes, and would otherwise potentially overflow
|
||||
* the buffer. */
|
||||
if (socket->__written > GUAC_SOCKET_OUTPUT_BUFFER_SIZE - 4) {
|
||||
|
||||
if (guac_socket_write(socket, __out_buf, socket->__written)) {
|
||||
guac_socket_update_buffer_end(socket);
|
||||
/* Write contents of string */
|
||||
if (guac_socket_write(socket, str, strlen(str)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
socket->__written = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guac_socket_update_buffer_end(socket);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
ssize_t __guac_socket_write_base64_triplet(guac_socket* socket, int a, int b, int c) {
|
||||
ssize_t __guac_socket_write_base64_triplet(guac_socket* socket,
|
||||
int a, int b, int c) {
|
||||
|
||||
char* __out_buf = socket->__out_buf;
|
||||
char output[4];
|
||||
|
||||
/* Byte 1 */
|
||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[(a & 0xFC) >> 2]; /* [AAAAAA]AABBBB BBBBCC CCCCCC */
|
||||
/* Byte 0:[AAAAAA] AABBBB BBBBCC CCCCCC */
|
||||
output[0] = __guac_socket_BASE64_CHARACTERS[(a & 0xFC) >> 2];
|
||||
|
||||
if (b >= 0) {
|
||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[((a & 0x03) << 4) | ((b & 0xF0) >> 4)]; /* AAAAAA[AABBBB]BBBBCC CCCCCC */
|
||||
|
||||
/* Byte 1: AAAAAA [AABBBB] BBBBCC CCCCCC */
|
||||
output[1] = __guac_socket_BASE64_CHARACTERS[((a & 0x03) << 4) | ((b & 0xF0) >> 4)];
|
||||
|
||||
/*
|
||||
* Bytes 2 and 3, zero characters of padding:
|
||||
*
|
||||
* AAAAAA AABBBB [BBBBCC] CCCCCC
|
||||
* AAAAAA AABBBB BBBBCC [CCCCCC]
|
||||
*/
|
||||
if (c >= 0) {
|
||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[((b & 0x0F) << 2) | ((c & 0xC0) >> 6)]; /* AAAAAA AABBBB[BBBBCC]CCCCCC */
|
||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[c & 0x3F]; /* AAAAAA AABBBB BBBBCC[CCCCCC] */
|
||||
output[2] = __guac_socket_BASE64_CHARACTERS[((b & 0x0F) << 2) | ((c & 0xC0) >> 6)];
|
||||
output[3] = __guac_socket_BASE64_CHARACTERS[c & 0x3F];
|
||||
}
|
||||
|
||||
/*
|
||||
* Bytes 2 and 3, one character of padding:
|
||||
*
|
||||
* AAAAAA AABBBB [BBBB--] ------
|
||||
* AAAAAA AABBBB BBBB-- [------]
|
||||
*/
|
||||
else {
|
||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[((b & 0x0F) << 2)]; /* AAAAAA AABBBB[BBBB--]------ */
|
||||
__out_buf[socket->__written++] = '='; /* AAAAAA AABBBB BBBB--[------] */
|
||||
output[2] = __guac_socket_BASE64_CHARACTERS[((b & 0x0F) << 2)];
|
||||
output[3] = '=';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Bytes 1, 2, and 3, two characters of padding:
|
||||
*
|
||||
* AAAAAA [AA----] ------ ------
|
||||
* AAAAAA AA---- [------] ------
|
||||
* AAAAAA AA---- ------ [------]
|
||||
*/
|
||||
else {
|
||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[((a & 0x03) << 4)]; /* AAAAAA[AA----]------ ------ */
|
||||
__out_buf[socket->__written++] = '='; /* AAAAAA AA----[------]------ */
|
||||
__out_buf[socket->__written++] = '='; /* AAAAAA AA---- ------[------] */
|
||||
output[1] = __guac_socket_BASE64_CHARACTERS[((a & 0x03) << 4)];
|
||||
output[2] = '=';
|
||||
output[3] = '=';
|
||||
}
|
||||
|
||||
/* At this point, 4 bytes have been socket->__written */
|
||||
|
||||
/* Flush when necessary, return on error */
|
||||
if (socket->__written > GUAC_SOCKET_OUTPUT_BUFFER_SIZE - 4) {
|
||||
|
||||
if (guac_socket_write(socket, __out_buf, socket->__written))
|
||||
/* At this point, 4 base64 bytes have been written */
|
||||
if (guac_socket_write(socket, output, 4))
|
||||
return -1;
|
||||
|
||||
socket->__written = 0;
|
||||
}
|
||||
|
||||
/* If no second byte was provided, only one byte was written */
|
||||
if (b < 0)
|
||||
return 1;
|
||||
|
||||
/* If no third byte was provided, only two bytes were written */
|
||||
if (c < 0)
|
||||
return 2;
|
||||
|
||||
/* Otherwise, three bytes were written */
|
||||
return 3;
|
||||
|
||||
}
|
||||
@ -359,37 +326,25 @@ ssize_t guac_socket_write_base64(guac_socket* socket, const void* buf, size_t co
|
||||
const unsigned char* char_buf = (const unsigned char*) buf;
|
||||
const unsigned char* end = char_buf + count;
|
||||
|
||||
guac_socket_update_buffer_begin(socket);
|
||||
while (char_buf < end) {
|
||||
|
||||
retval = __guac_socket_write_base64_byte(socket, *(char_buf++));
|
||||
if (retval < 0) {
|
||||
guac_socket_update_buffer_end(socket);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guac_socket_update_buffer_end(socket);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
ssize_t guac_socket_flush(guac_socket* socket) {
|
||||
|
||||
/* Flush remaining bytes in buffer */
|
||||
guac_socket_update_buffer_begin(socket);
|
||||
if (socket->__written > 0) {
|
||||
/* If handler defined, call it. */
|
||||
if (socket->flush_handler)
|
||||
return socket->flush_handler(socket);
|
||||
|
||||
if (guac_socket_write(socket, socket->__out_buf, socket->__written)) {
|
||||
guac_socket_update_buffer_end(socket);
|
||||
return 1;
|
||||
}
|
||||
|
||||
socket->__written = 0;
|
||||
}
|
||||
|
||||
guac_socket_update_buffer_end(socket);
|
||||
/* Otherwise, do nothing */
|
||||
return 0;
|
||||
|
||||
}
|
||||
@ -399,18 +354,14 @@ ssize_t guac_socket_flush_base64(guac_socket* socket) {
|
||||
int retval;
|
||||
|
||||
/* Flush triplet to output buffer */
|
||||
guac_socket_update_buffer_begin(socket);
|
||||
while (socket->__ready > 0) {
|
||||
|
||||
retval = __guac_socket_write_base64_byte(socket, -1);
|
||||
if (retval < 0) {
|
||||
guac_socket_update_buffer_end(socket);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guac_socket_update_buffer_end(socket);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
@ -56,3 +56,16 @@ guac_timestamp guac_timestamp_current() {
|
||||
|
||||
}
|
||||
|
||||
void guac_timestamp_msleep(int duration) {
|
||||
|
||||
/* Split milliseconds into equivalent seconds + nanoseconds */
|
||||
struct timespec sleep_period = {
|
||||
.tv_sec = duration / 1000,
|
||||
.tv_nsec = (duration % 1000) * 1000000
|
||||
};
|
||||
|
||||
/* Sleep for specified interval */
|
||||
nanosleep(&sleep_period, NULL);
|
||||
|
||||
}
|
||||
|
||||
|
525
src/libguac/user-handlers.c
Normal file
525
src/libguac/user-handlers.c
Normal file
@ -0,0 +1,525 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "object.h"
|
||||
#include "protocol.h"
|
||||
#include "stream.h"
|
||||
#include "timestamp.h"
|
||||
#include "user.h"
|
||||
#include "user-handlers.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Guacamole instruction handler map */
|
||||
|
||||
__guac_instruction_handler_mapping __guac_instruction_handler_map[] = {
|
||||
{"sync", __guac_handle_sync},
|
||||
{"mouse", __guac_handle_mouse},
|
||||
{"key", __guac_handle_key},
|
||||
{"clipboard", __guac_handle_clipboard},
|
||||
{"disconnect", __guac_handle_disconnect},
|
||||
{"size", __guac_handle_size},
|
||||
{"file", __guac_handle_file},
|
||||
{"pipe", __guac_handle_pipe},
|
||||
{"ack", __guac_handle_ack},
|
||||
{"blob", __guac_handle_blob},
|
||||
{"end", __guac_handle_end},
|
||||
{"get", __guac_handle_get},
|
||||
{"put", __guac_handle_put},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a 64-bit integer from the given string. It is assumed that the string
|
||||
* will contain only decimal digits, with an optional leading minus sign.
|
||||
* The result of parsing a string which does not conform to this pattern is
|
||||
* undefined.
|
||||
*
|
||||
* @param str
|
||||
* The string to parse, which must contain only decimal digits and an
|
||||
* optional leading minus sign.
|
||||
*
|
||||
* @return
|
||||
* The 64-bit integer value represented by the given string.
|
||||
*/
|
||||
static int64_t __guac_parse_int(const char* str) {
|
||||
|
||||
int sign = 1;
|
||||
int64_t num = 0;
|
||||
|
||||
for (; *str != '\0'; str++) {
|
||||
|
||||
if (*str == '-')
|
||||
sign = -sign;
|
||||
else
|
||||
num = num * 10 + (*str - '0');
|
||||
|
||||
}
|
||||
|
||||
return num * sign;
|
||||
|
||||
}
|
||||
|
||||
/* Guacamole instruction handlers */
|
||||
|
||||
int __guac_handle_sync(guac_user* user, int argc, char** argv) {
|
||||
|
||||
int frame_duration;
|
||||
|
||||
guac_timestamp current = guac_timestamp_current();
|
||||
guac_timestamp timestamp = __guac_parse_int(argv[0]);
|
||||
|
||||
/* Error if timestamp is in future */
|
||||
if (timestamp > user->client->last_sent_timestamp)
|
||||
return -1;
|
||||
|
||||
/* Update stored timestamp */
|
||||
user->last_received_timestamp = timestamp;
|
||||
|
||||
/* Calculate length of frame, including network and processing lag */
|
||||
frame_duration = current - timestamp;
|
||||
|
||||
/* Update lag statistics if at least one frame has been rendered */
|
||||
if (user->last_frame_duration != 0) {
|
||||
|
||||
/* Approximate processing lag by summing the frame duration deltas */
|
||||
int processing_lag = user->processing_lag + frame_duration
|
||||
- user->last_frame_duration;
|
||||
|
||||
/* Adjust back to zero if cumulative error leads to a negative value */
|
||||
if (processing_lag < 0)
|
||||
processing_lag = 0;
|
||||
|
||||
user->processing_lag = processing_lag;
|
||||
|
||||
}
|
||||
|
||||
/* Record duration of frame */
|
||||
user->last_frame_duration = frame_duration;
|
||||
|
||||
if (user->sync_handler)
|
||||
return user->sync_handler(user, timestamp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_mouse(guac_user* user, int argc, char** argv) {
|
||||
if (user->mouse_handler)
|
||||
return user->mouse_handler(
|
||||
user,
|
||||
atoi(argv[0]), /* x */
|
||||
atoi(argv[1]), /* y */
|
||||
atoi(argv[2]) /* mask */
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_key(guac_user* user, int argc, char** argv) {
|
||||
if (user->key_handler)
|
||||
return user->key_handler(
|
||||
user,
|
||||
atoi(argv[0]), /* keysym */
|
||||
atoi(argv[1]) /* pressed */
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the existing user-level input stream having the given index. These
|
||||
* will be streams which were created by the remotely-connected user. If the
|
||||
* index is invalid or too large, this function will automatically respond with
|
||||
* an "ack" instruction containing an appropriate error code.
|
||||
*
|
||||
* @param user
|
||||
* The user associated with the stream being retrieved.
|
||||
*
|
||||
* @param stream_index
|
||||
* The index of the stream to retrieve.
|
||||
*
|
||||
* @return
|
||||
* The stream associated with the given user and having the given index,
|
||||
* or NULL if the index is invalid.
|
||||
*/
|
||||
static guac_stream* __get_input_stream(guac_user* user, int stream_index) {
|
||||
|
||||
/* Validate stream index */
|
||||
if (stream_index < 0 || stream_index >= GUAC_USER_MAX_STREAMS) {
|
||||
|
||||
guac_stream dummy_stream;
|
||||
dummy_stream.index = stream_index;
|
||||
|
||||
guac_protocol_send_ack(user->socket, &dummy_stream,
|
||||
"Invalid stream index", GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &(user->__input_streams[stream_index]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the existing, in-progress (open) user-level input stream having
|
||||
* the given index. These will be streams which were created by the
|
||||
* remotely-connected user. If the index is invalid, too large, or the stream
|
||||
* is closed, this function will automatically respond with an "ack"
|
||||
* instruction containing an appropriate error code.
|
||||
*
|
||||
* @param user
|
||||
* The user associated with the stream being retrieved.
|
||||
*
|
||||
* @param stream_index
|
||||
* The index of the stream to retrieve.
|
||||
*
|
||||
* @return
|
||||
* The in-progress (open)stream associated with the given user and having
|
||||
* the given index, or NULL if the index is invalid or the stream is
|
||||
* closed.
|
||||
*/
|
||||
static guac_stream* __get_open_input_stream(guac_user* user, int stream_index) {
|
||||
|
||||
guac_stream* stream = __get_input_stream(user, stream_index);
|
||||
|
||||
/* Fail if no such stream */
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Validate initialization of stream */
|
||||
if (stream->index == GUAC_USER_CLOSED_STREAM_INDEX) {
|
||||
|
||||
guac_stream dummy_stream;
|
||||
dummy_stream.index = stream_index;
|
||||
|
||||
guac_protocol_send_ack(user->socket, &dummy_stream,
|
||||
"Invalid stream index", GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stream;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes and returns a new user-level input stream having the given
|
||||
* index, clearing any values that may have been assigned by a past use of the
|
||||
* underlying stream object storage. If the stream was already open, it will
|
||||
* first be closed and its end handlers invoked as if explicitly closed by the
|
||||
* user.
|
||||
*
|
||||
* @param user
|
||||
* The user associated with the stream being initialized.
|
||||
*
|
||||
* @param stream_index
|
||||
* The index of the stream to initialized.
|
||||
*
|
||||
* @return
|
||||
* A new initialized user-level input stream having the given index, or
|
||||
* NULL if the index is invalid.
|
||||
*/
|
||||
static guac_stream* __init_input_stream(guac_user* user, int stream_index) {
|
||||
|
||||
guac_stream* stream = __get_input_stream(user, stream_index);
|
||||
|
||||
/* Fail if no such stream */
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Force end of previous stream if open */
|
||||
if (stream->index != GUAC_USER_CLOSED_STREAM_INDEX) {
|
||||
|
||||
/* Call stream handler if defined */
|
||||
if (stream->end_handler)
|
||||
stream->end_handler(user, stream);
|
||||
|
||||
/* Fall back to global handler if defined */
|
||||
else if (user->end_handler)
|
||||
user->end_handler(user, stream);
|
||||
|
||||
}
|
||||
|
||||
/* Initialize stream */
|
||||
stream->index = stream_index;
|
||||
stream->data = NULL;
|
||||
stream->ack_handler = NULL;
|
||||
stream->blob_handler = NULL;
|
||||
stream->end_handler = NULL;
|
||||
|
||||
return stream;
|
||||
|
||||
}
|
||||
|
||||
int __guac_handle_clipboard(guac_user* user, int argc, char** argv) {
|
||||
|
||||
/* Pull corresponding stream */
|
||||
int stream_index = atoi(argv[0]);
|
||||
guac_stream* stream = __init_input_stream(user, stream_index);
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* If supported, call handler */
|
||||
if (user->clipboard_handler)
|
||||
return user->clipboard_handler(
|
||||
user,
|
||||
stream,
|
||||
argv[1] /* mimetype */
|
||||
);
|
||||
|
||||
/* Otherwise, abort */
|
||||
guac_protocol_send_ack(user->socket, stream,
|
||||
"Clipboard unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int __guac_handle_size(guac_user* user, int argc, char** argv) {
|
||||
if (user->size_handler)
|
||||
return user->size_handler(
|
||||
user,
|
||||
atoi(argv[0]), /* width */
|
||||
atoi(argv[1]) /* height */
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_file(guac_user* user, int argc, char** argv) {
|
||||
|
||||
/* Pull corresponding stream */
|
||||
int stream_index = atoi(argv[0]);
|
||||
guac_stream* stream = __init_input_stream(user, stream_index);
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* If supported, call handler */
|
||||
if (user->file_handler)
|
||||
return user->file_handler(
|
||||
user,
|
||||
stream,
|
||||
argv[1], /* mimetype */
|
||||
argv[2] /* filename */
|
||||
);
|
||||
|
||||
/* Otherwise, abort */
|
||||
guac_protocol_send_ack(user->socket, stream,
|
||||
"File transfer unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_pipe(guac_user* user, int argc, char** argv) {
|
||||
|
||||
/* Pull corresponding stream */
|
||||
int stream_index = atoi(argv[0]);
|
||||
guac_stream* stream = __init_input_stream(user, stream_index);
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* If supported, call handler */
|
||||
if (user->pipe_handler)
|
||||
return user->pipe_handler(
|
||||
user,
|
||||
stream,
|
||||
argv[1], /* mimetype */
|
||||
argv[2] /* name */
|
||||
);
|
||||
|
||||
/* Otherwise, abort */
|
||||
guac_protocol_send_ack(user->socket, stream,
|
||||
"Named pipes unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_ack(guac_user* user, int argc, char** argv) {
|
||||
|
||||
guac_stream* stream;
|
||||
|
||||
/* Parse stream index */
|
||||
int stream_index = atoi(argv[0]);
|
||||
|
||||
/* Ignore indices of client-level streams */
|
||||
if (stream_index % 2 != 0)
|
||||
return 0;
|
||||
|
||||
/* Determine index within user-level array of streams */
|
||||
stream_index /= 2;
|
||||
|
||||
/* Validate stream index */
|
||||
if (stream_index < 0 || stream_index >= GUAC_USER_MAX_STREAMS)
|
||||
return 0;
|
||||
|
||||
stream = &(user->__output_streams[stream_index]);
|
||||
|
||||
/* Validate initialization of stream */
|
||||
if (stream->index == GUAC_USER_CLOSED_STREAM_INDEX)
|
||||
return 0;
|
||||
|
||||
/* Call stream handler if defined */
|
||||
if (stream->ack_handler)
|
||||
return stream->ack_handler(user, stream, argv[1],
|
||||
atoi(argv[2]));
|
||||
|
||||
/* Fall back to global handler if defined */
|
||||
if (user->ack_handler)
|
||||
return user->ack_handler(user, stream, argv[1],
|
||||
atoi(argv[2]));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_blob(guac_user* user, int argc, char** argv) {
|
||||
|
||||
int stream_index = atoi(argv[0]);
|
||||
guac_stream* stream = __get_open_input_stream(user, stream_index);
|
||||
|
||||
/* Fail if no such stream */
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* Call stream handler if defined */
|
||||
if (stream->blob_handler) {
|
||||
int length = guac_protocol_decode_base64(argv[1]);
|
||||
return stream->blob_handler(user, stream, argv[1],
|
||||
length);
|
||||
}
|
||||
|
||||
/* Fall back to global handler if defined */
|
||||
if (user->blob_handler) {
|
||||
int length = guac_protocol_decode_base64(argv[1]);
|
||||
return user->blob_handler(user, stream, argv[1],
|
||||
length);
|
||||
}
|
||||
|
||||
guac_protocol_send_ack(user->socket, stream,
|
||||
"File transfer unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_end(guac_user* user, int argc, char** argv) {
|
||||
|
||||
int result = 0;
|
||||
int stream_index = atoi(argv[0]);
|
||||
guac_stream* stream = __get_open_input_stream(user, stream_index);
|
||||
|
||||
/* Fail if no such stream */
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* Call stream handler if defined */
|
||||
if (stream->end_handler)
|
||||
result = stream->end_handler(user, stream);
|
||||
|
||||
/* Fall back to global handler if defined */
|
||||
else if (user->end_handler)
|
||||
result = user->end_handler(user, stream);
|
||||
|
||||
/* Mark stream as closed */
|
||||
stream->index = GUAC_USER_CLOSED_STREAM_INDEX;
|
||||
return result;
|
||||
}
|
||||
|
||||
int __guac_handle_get(guac_user* user, int argc, char** argv) {
|
||||
|
||||
guac_object* object;
|
||||
|
||||
/* Validate object index */
|
||||
int object_index = atoi(argv[0]);
|
||||
if (object_index < 0 || object_index >= GUAC_USER_MAX_OBJECTS)
|
||||
return 0;
|
||||
|
||||
object = &(user->__objects[object_index]);
|
||||
|
||||
/* Validate initialization of object */
|
||||
if (object->index == GUAC_USER_UNDEFINED_OBJECT_INDEX)
|
||||
return 0;
|
||||
|
||||
/* Call object handler if defined */
|
||||
if (object->get_handler)
|
||||
return object->get_handler(
|
||||
user,
|
||||
object,
|
||||
argv[1] /* name */
|
||||
);
|
||||
|
||||
/* Fall back to global handler if defined */
|
||||
if (user->get_handler)
|
||||
return user->get_handler(
|
||||
user,
|
||||
object,
|
||||
argv[1] /* name */
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_put(guac_user* user, int argc, char** argv) {
|
||||
|
||||
guac_object* object;
|
||||
|
||||
/* Validate object index */
|
||||
int object_index = atoi(argv[0]);
|
||||
if (object_index < 0 || object_index >= GUAC_USER_MAX_OBJECTS)
|
||||
return 0;
|
||||
|
||||
object = &(user->__objects[object_index]);
|
||||
|
||||
/* Validate initialization of object */
|
||||
if (object->index == GUAC_USER_UNDEFINED_OBJECT_INDEX)
|
||||
return 0;
|
||||
|
||||
/* Pull corresponding stream */
|
||||
int stream_index = atoi(argv[1]);
|
||||
guac_stream* stream = __init_input_stream(user, stream_index);
|
||||
if (stream == NULL)
|
||||
return 0;
|
||||
|
||||
/* Call object handler if defined */
|
||||
if (object->put_handler)
|
||||
return object->put_handler(
|
||||
user,
|
||||
object,
|
||||
stream,
|
||||
argv[2], /* mimetype */
|
||||
argv[3] /* name */
|
||||
);
|
||||
|
||||
/* Fall back to global handler if defined */
|
||||
if (user->put_handler)
|
||||
return user->put_handler(
|
||||
user,
|
||||
object,
|
||||
stream,
|
||||
argv[2], /* mimetype */
|
||||
argv[3] /* name */
|
||||
);
|
||||
|
||||
/* Otherwise, abort */
|
||||
guac_protocol_send_ack(user->socket, stream,
|
||||
"Object write unsupported", GUAC_PROTOCOL_STATUS_UNSUPPORTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_handle_disconnect(guac_user* user, int argc, char** argv) {
|
||||
guac_user_stop(user);
|
||||
return 0;
|
||||
}
|
||||
|
@ -21,26 +21,41 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _GUAC_CLIENT_HANDLERS__H
|
||||
#define _GUAC_CLIENT_HANDLERS__H
|
||||
#ifndef _GUAC_USER_HANDLERS__H
|
||||
#define _GUAC_USER_HANDLERS__H
|
||||
|
||||
/**
|
||||
* Provides initial handler functions and a lookup structure for automatically
|
||||
* handling client instructions. This is used only internally within libguac,
|
||||
* and is not installed along with the library.
|
||||
* handling instructions received from each user. This is used only internally
|
||||
* within libguac, and is not installed along with the library.
|
||||
*
|
||||
* @file client-handlers.h
|
||||
* @file user-handlers.h
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "instruction.h"
|
||||
#include "timestamp.h"
|
||||
|
||||
/**
|
||||
* Internal handler for Guacamole instructions.
|
||||
* Internal handler for Guacamole instructions. Instruction handlers will be
|
||||
* invoked when their corresponding instructions are received. The mapping
|
||||
* of instruction opcode to handler is defined by the
|
||||
* __guac_instruction_handler_map array.
|
||||
*
|
||||
* @param user
|
||||
* The user that sent the instruction.
|
||||
*
|
||||
* @param argc
|
||||
* The number of arguments in argv.
|
||||
*
|
||||
* @param argv
|
||||
* The arguments included with the instruction, excluding the opcode.
|
||||
*
|
||||
* @return
|
||||
* Zero if the instruction was successfully handled, non-zero otherwise.
|
||||
*/
|
||||
typedef int __guac_instruction_handler(guac_client* client, guac_instruction* copied);
|
||||
typedef int __guac_instruction_handler(guac_user* user, int argc, char** argv);
|
||||
|
||||
/**
|
||||
* Structure mapping an instruction opcode to an instruction handler.
|
||||
@ -64,91 +79,92 @@ typedef struct __guac_instruction_handler_mapping {
|
||||
* is received, this handler will be called. Sync instructions are automatically
|
||||
* handled, thus there is no client handler for sync instruction.
|
||||
*/
|
||||
int __guac_handle_sync(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_sync;
|
||||
|
||||
/**
|
||||
* Internal initial handler for the mouse instruction. When a mouse instruction
|
||||
* is received, this handler will be called. The client's mouse handler will
|
||||
* be invoked if defined.
|
||||
*/
|
||||
int __guac_handle_mouse(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_mouse;
|
||||
|
||||
/**
|
||||
* Internal initial handler for the key instruction. When a key instruction
|
||||
* is received, this handler will be called. The client's key handler will
|
||||
* be invoked if defined.
|
||||
*/
|
||||
int __guac_handle_key(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_key;
|
||||
|
||||
/**
|
||||
* Internal initial handler for the clipboard instruction. When a clipboard instruction
|
||||
* is received, this handler will be called. The client's clipboard handler will
|
||||
* be invoked if defined.
|
||||
* Internal initial handler for the clipboard instruction. When a clipboard
|
||||
* instruction is received, this handler will be called. The client's clipboard
|
||||
* handler will be invoked if defined.
|
||||
*/
|
||||
int __guac_handle_clipboard(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_clipboard;
|
||||
|
||||
/**
|
||||
* Internal initial handler for the file instruction. When a file instruction
|
||||
* is received, this handler will be called. The client's file handler will
|
||||
* be invoked if defined.
|
||||
*/
|
||||
int __guac_handle_file(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_file;
|
||||
|
||||
/**
|
||||
* Internal initial handler for the pipe instruction. When a pipe instruction
|
||||
* is received, this handler will be called. The client's pipe handler will
|
||||
* be invoked if defined.
|
||||
*/
|
||||
int __guac_handle_pipe(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_pipe;
|
||||
|
||||
/**
|
||||
* Internal initial handler for the ack instruction. When a ack instruction
|
||||
* is received, this handler will be called. The client's ack handler will
|
||||
* be invoked if defined.
|
||||
*/
|
||||
int __guac_handle_ack(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_ack;
|
||||
|
||||
/**
|
||||
* Internal initial handler for the blob instruction. When a blob instruction
|
||||
* is received, this handler will be called. The client's blob handler will
|
||||
* be invoked if defined.
|
||||
*/
|
||||
int __guac_handle_blob(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_blob;
|
||||
|
||||
/**
|
||||
* Internal initial handler for the end instruction. When a end instruction
|
||||
* is received, this handler will be called. The client's end handler will
|
||||
* be invoked if defined.
|
||||
*/
|
||||
int __guac_handle_end(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_end;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
__guac_instruction_handler __guac_handle_get;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
__guac_instruction_handler __guac_handle_put;
|
||||
|
||||
/**
|
||||
* Internal initial handler for the size instruction. When a size instruction
|
||||
* is received, this handler will be called. The client's size handler will
|
||||
* be invoked if defined.
|
||||
*/
|
||||
int __guac_handle_size(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_size;
|
||||
|
||||
/**
|
||||
* Internal initial handler for the disconnect instruction. When a disconnect instruction
|
||||
* is received, this handler will be called. Disconnect instructions are automatically
|
||||
* handled, thus there is no client handler for disconnect instruction.
|
||||
* Internal initial handler for the disconnect instruction. When a disconnect
|
||||
* instruction is received, this handler will be called. Disconnect
|
||||
* instructions are automatically handled, thus there is no client handler for
|
||||
* disconnect instruction.
|
||||
*/
|
||||
int __guac_handle_disconnect(guac_client* client, guac_instruction* instruction);
|
||||
__guac_instruction_handler __guac_handle_disconnect;
|
||||
|
||||
/**
|
||||
* Instruction handler mapping table. This is a NULL-terminated array of
|
442
src/libguac/user.c
Normal file
442
src/libguac/user.c
Normal file
@ -0,0 +1,442 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "encode-jpeg.h"
|
||||
#include "encode-png.h"
|
||||
#include "encode-webp.h"
|
||||
#include "id.h"
|
||||
#include "object.h"
|
||||
#include "pool.h"
|
||||
#include "protocol.h"
|
||||
#include "socket.h"
|
||||
#include "stream.h"
|
||||
#include "timestamp.h"
|
||||
#include "user.h"
|
||||
#include "user-handlers.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
guac_user* guac_user_alloc() {
|
||||
|
||||
guac_user* user = calloc(1, sizeof(guac_user));
|
||||
int i;
|
||||
|
||||
/* Generate ID */
|
||||
user->user_id = guac_generate_id(GUAC_USER_ID_PREFIX);
|
||||
if (user->user_id == NULL) {
|
||||
free(user);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
user->last_received_timestamp = guac_timestamp_current();
|
||||
user->last_frame_duration = 0;
|
||||
user->processing_lag = 0;
|
||||
user->active = 1;
|
||||
|
||||
/* Allocate stream pool */
|
||||
user->__stream_pool = guac_pool_alloc(0);
|
||||
|
||||
/* Initialze streams */
|
||||
user->__input_streams = malloc(sizeof(guac_stream) * GUAC_USER_MAX_STREAMS);
|
||||
user->__output_streams = malloc(sizeof(guac_stream) * GUAC_USER_MAX_STREAMS);
|
||||
|
||||
for (i=0; i<GUAC_USER_MAX_STREAMS; i++) {
|
||||
user->__input_streams[i].index = GUAC_USER_CLOSED_STREAM_INDEX;
|
||||
user->__output_streams[i].index = GUAC_USER_CLOSED_STREAM_INDEX;
|
||||
}
|
||||
|
||||
/* Allocate object pool */
|
||||
user->__object_pool = guac_pool_alloc(0);
|
||||
|
||||
/* Initialize objects */
|
||||
user->__objects = malloc(sizeof(guac_object) * GUAC_USER_MAX_OBJECTS);
|
||||
for (i=0; i<GUAC_USER_MAX_OBJECTS; i++)
|
||||
user->__objects[i].index = GUAC_USER_UNDEFINED_OBJECT_INDEX;
|
||||
|
||||
return user;
|
||||
|
||||
}
|
||||
|
||||
void guac_user_free(guac_user* user) {
|
||||
|
||||
/* Free streams */
|
||||
free(user->__input_streams);
|
||||
free(user->__output_streams);
|
||||
|
||||
/* Free stream pool */
|
||||
guac_pool_free(user->__stream_pool);
|
||||
|
||||
/* Free objects */
|
||||
free(user->__objects);
|
||||
|
||||
/* Free object pool */
|
||||
guac_pool_free(user->__object_pool);
|
||||
|
||||
/* Clean up user */
|
||||
free(user->user_id);
|
||||
free(user);
|
||||
|
||||
}
|
||||
|
||||
guac_stream* guac_user_alloc_stream(guac_user* user) {
|
||||
|
||||
guac_stream* allocd_stream;
|
||||
int stream_index;
|
||||
|
||||
/* Refuse to allocate beyond maximum */
|
||||
if (user->__stream_pool->active == GUAC_USER_MAX_STREAMS)
|
||||
return NULL;
|
||||
|
||||
/* Allocate stream */
|
||||
stream_index = guac_pool_next_int(user->__stream_pool);
|
||||
|
||||
/* Initialize stream with even index (odd indices are client-level) */
|
||||
allocd_stream = &(user->__output_streams[stream_index]);
|
||||
allocd_stream->index = stream_index * 2;
|
||||
allocd_stream->data = NULL;
|
||||
allocd_stream->ack_handler = NULL;
|
||||
allocd_stream->blob_handler = NULL;
|
||||
allocd_stream->end_handler = NULL;
|
||||
|
||||
return allocd_stream;
|
||||
|
||||
}
|
||||
|
||||
void guac_user_free_stream(guac_user* user, guac_stream* stream) {
|
||||
|
||||
/* Release index to pool */
|
||||
guac_pool_free_int(user->__stream_pool, stream->index / 2);
|
||||
|
||||
/* Mark stream as closed */
|
||||
stream->index = GUAC_USER_CLOSED_STREAM_INDEX;
|
||||
|
||||
}
|
||||
|
||||
guac_object* guac_user_alloc_object(guac_user* user) {
|
||||
|
||||
guac_object* allocd_object;
|
||||
int object_index;
|
||||
|
||||
/* Refuse to allocate beyond maximum */
|
||||
if (user->__object_pool->active == GUAC_USER_MAX_OBJECTS)
|
||||
return NULL;
|
||||
|
||||
/* Allocate object */
|
||||
object_index = guac_pool_next_int(user->__object_pool);
|
||||
|
||||
/* Initialize object */
|
||||
allocd_object = &(user->__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_user_free_object(guac_user* user, guac_object* object) {
|
||||
|
||||
/* Release index to pool */
|
||||
guac_pool_free_int(user->__object_pool, object->index);
|
||||
|
||||
/* Mark object as undefined */
|
||||
object->index = GUAC_USER_UNDEFINED_OBJECT_INDEX;
|
||||
|
||||
}
|
||||
|
||||
int guac_user_handle_instruction(guac_user* user, const char* opcode, int argc, char** argv) {
|
||||
|
||||
/* For each defined instruction */
|
||||
__guac_instruction_handler_mapping* current = __guac_instruction_handler_map;
|
||||
while (current->opcode != NULL) {
|
||||
|
||||
/* If recognized, call handler */
|
||||
if (strcmp(opcode, current->opcode) == 0)
|
||||
return current->handler(user, argc, argv);
|
||||
|
||||
current++;
|
||||
}
|
||||
|
||||
/* If unrecognized, ignore */
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void guac_user_stop(guac_user* user) {
|
||||
user->active = 0;
|
||||
}
|
||||
|
||||
void vguac_user_abort(guac_user* user, guac_protocol_status status,
|
||||
const char* format, va_list ap) {
|
||||
|
||||
/* Only relevant if user is active */
|
||||
if (user->active) {
|
||||
|
||||
/* Log detail of error */
|
||||
vguac_user_log(user, GUAC_LOG_ERROR, format, ap);
|
||||
|
||||
/* Send error immediately, limit information given */
|
||||
guac_protocol_send_error(user->socket, "Aborted. See logs.", status);
|
||||
guac_socket_flush(user->socket);
|
||||
|
||||
/* Stop user */
|
||||
guac_user_stop(user);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void guac_user_abort(guac_user* user, guac_protocol_status status,
|
||||
const char* format, ...) {
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
vguac_user_abort(user, status, format, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
}
|
||||
|
||||
void vguac_user_log(guac_user* user, guac_client_log_level level,
|
||||
const char* format, va_list ap) {
|
||||
|
||||
vguac_client_log(user->client, level, format, ap);
|
||||
|
||||
}
|
||||
|
||||
void guac_user_log(guac_user* user, guac_client_log_level level,
|
||||
const char* format, ...) {
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
vguac_client_log(user->client, level, format, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
/* Allocate new stream for image */
|
||||
guac_stream* stream = guac_user_alloc_stream(user);
|
||||
|
||||
/* Declare stream as containing image data */
|
||||
guac_protocol_send_img(socket, stream, mode, layer, "image/png", x, y);
|
||||
|
||||
/* Write PNG data */
|
||||
guac_png_write(socket, stream, surface);
|
||||
|
||||
/* Terminate stream */
|
||||
guac_protocol_send_end(socket, stream);
|
||||
|
||||
/* Free allocated stream */
|
||||
guac_user_free_stream(user, stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_user_stream_jpeg(guac_user* user, guac_socket* socket,
|
||||
guac_composite_mode mode, const guac_layer* layer, int x, int y,
|
||||
cairo_surface_t* surface, int quality) {
|
||||
|
||||
/* Allocate new stream for image */
|
||||
guac_stream* stream = guac_user_alloc_stream(user);
|
||||
|
||||
/* Declare stream as containing image data */
|
||||
guac_protocol_send_img(socket, stream, mode, layer, "image/jpeg", x, y);
|
||||
|
||||
/* Write JPEG data */
|
||||
guac_jpeg_write(socket, stream, surface, quality);
|
||||
|
||||
/* Terminate stream */
|
||||
guac_protocol_send_end(socket, stream);
|
||||
|
||||
/* Free allocated stream */
|
||||
guac_user_free_stream(user, stream);
|
||||
|
||||
}
|
||||
|
||||
void guac_user_stream_webp(guac_user* user, guac_socket* socket,
|
||||
guac_composite_mode mode, const guac_layer* layer, int x, int y,
|
||||
cairo_surface_t* surface, int quality, int lossless) {
|
||||
|
||||
#ifdef ENABLE_WEBP
|
||||
/* Allocate new stream for image */
|
||||
guac_stream* stream = guac_user_alloc_stream(user);
|
||||
|
||||
/* Declare stream as containing image data */
|
||||
guac_protocol_send_img(socket, stream, mode, layer, "image/webp", x, y);
|
||||
|
||||
/* Write WebP data */
|
||||
guac_webp_write(socket, stream, surface, quality, lossless);
|
||||
|
||||
/* Terminate stream */
|
||||
guac_protocol_send_end(socket, stream);
|
||||
|
||||
/* Free allocated stream */
|
||||
guac_user_free_stream(user, stream);
|
||||
#else
|
||||
/* Do nothing if WebP support is not built in */
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int guac_user_supports_webp(guac_user* user) {
|
||||
|
||||
#ifdef ENABLE_WEBP
|
||||
const char** mimetype = user->info.image_mimetypes;
|
||||
|
||||
/* Search for WebP mimetype in list of supported image mimetypes */
|
||||
while (*mimetype != NULL) {
|
||||
|
||||
/* If WebP mimetype found, no need to search further */
|
||||
if (strcmp(*mimetype, "image/webp") == 0)
|
||||
return 1;
|
||||
|
||||
/* Next mimetype */
|
||||
mimetype++;
|
||||
|
||||
}
|
||||
|
||||
/* User does not support WebP */
|
||||
return 0;
|
||||
#else
|
||||
/* Support for WebP is completely absent */
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
char* guac_user_parse_args_string(guac_user* user, const char** arg_names,
|
||||
const char** argv, int index, const char* default_value) {
|
||||
|
||||
/* Pull parameter value from argv */
|
||||
const char* value = argv[index];
|
||||
|
||||
/* Use default value if blank */
|
||||
if (value[0] == 0) {
|
||||
|
||||
/* NULL is a completely legal default value */
|
||||
if (default_value == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Log use of default */
|
||||
guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using "
|
||||
"default value of \"%s\".", arg_names[index], default_value);
|
||||
|
||||
return strdup(default_value);
|
||||
|
||||
}
|
||||
|
||||
/* Otherwise use provided value */
|
||||
return strdup(value);
|
||||
|
||||
}
|
||||
|
||||
int guac_user_parse_args_int(guac_user* user, const char** arg_names,
|
||||
const char** argv, int index, int default_value) {
|
||||
|
||||
char* parse_end;
|
||||
long parsed_value;
|
||||
|
||||
/* Pull parameter value from argv */
|
||||
const char* value = argv[index];
|
||||
|
||||
/* Use default value if blank */
|
||||
if (value[0] == 0) {
|
||||
|
||||
/* Log use of default */
|
||||
guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using "
|
||||
"default value of %i.", arg_names[index], default_value);
|
||||
|
||||
return default_value;
|
||||
|
||||
}
|
||||
|
||||
/* Parse value, checking for errors */
|
||||
errno = 0;
|
||||
parsed_value = strtol(value, &parse_end, 10);
|
||||
|
||||
/* Ensure parsed value is within the legal range of an int */
|
||||
if (parsed_value < INT_MIN || parsed_value > INT_MAX)
|
||||
errno = ERANGE;
|
||||
|
||||
/* Resort to default if input is invalid */
|
||||
if (errno != 0 || *parse_end != '\0') {
|
||||
|
||||
/* Log use of default */
|
||||
guac_user_log(user, GUAC_LOG_WARNING, "Specified value \"%s\" for "
|
||||
"parameter \"%s\" is not a valid integer. Using default value "
|
||||
"of %i.", value, arg_names[index], default_value);
|
||||
|
||||
return default_value;
|
||||
|
||||
}
|
||||
|
||||
/* Parsed successfully */
|
||||
return parsed_value;
|
||||
|
||||
}
|
||||
|
||||
int guac_user_parse_args_boolean(guac_user* user, const char** arg_names,
|
||||
const char** argv, int index, int default_value) {
|
||||
|
||||
/* Pull parameter value from argv */
|
||||
const char* value = argv[index];
|
||||
|
||||
/* Use default value if blank */
|
||||
if (value[0] == 0) {
|
||||
|
||||
/* Log use of default */
|
||||
guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using "
|
||||
"default value of %i.", arg_names[index], default_value);
|
||||
|
||||
return default_value;
|
||||
|
||||
}
|
||||
|
||||
/* Parse string "true" as true */
|
||||
if (strcmp(value, "true") == 0)
|
||||
return 1;
|
||||
|
||||
/* Parse string "false" as false */
|
||||
if (strcmp(value, "false") == 0)
|
||||
return 0;
|
||||
|
||||
/* All other values are invalid */
|
||||
guac_user_log(user, GUAC_LOG_WARNING, "Parameter \"%s\" must be either "
|
||||
"\"true\" or \"false\". Using default value.", arg_names[index]);
|
||||
|
||||
return default_value;
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ check_PROGRAMS = test_libguac
|
||||
|
||||
noinst_HEADERS = \
|
||||
client/client_suite.h \
|
||||
common/common_suite.h \
|
||||
protocol/suite.h \
|
||||
util/util_suite.h
|
||||
|
||||
@ -37,10 +36,6 @@ test_libguac_SOURCES = \
|
||||
client/client_suite.c \
|
||||
client/buffer_pool.c \
|
||||
client/layer_pool.c \
|
||||
common/common_suite.c \
|
||||
common/guac_iconv.c \
|
||||
common/guac_string.c \
|
||||
common/guac_rect.c \
|
||||
protocol/suite.c \
|
||||
protocol/base64_decode.c \
|
||||
protocol/instruction_parse.c \
|
||||
@ -53,11 +48,9 @@ test_libguac_SOURCES = \
|
||||
|
||||
test_libguac_CFLAGS = \
|
||||
-Werror -Wall -pedantic \
|
||||
@COMMON_INCLUDE@ \
|
||||
@LIBGUAC_INCLUDE@
|
||||
|
||||
test_libguac_LDADD = \
|
||||
@COMMON_LTLIB@ \
|
||||
@CUNIT_LIBS@ \
|
||||
@LIBGUAC_LTLIB@
|
||||
|
||||
|
@ -29,13 +29,13 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <CUnit/Basic.h>
|
||||
#include <guacamole/instruction.h>
|
||||
#include <guacamole/parser.h>
|
||||
|
||||
void test_instruction_parse() {
|
||||
|
||||
/* Allocate instruction space */
|
||||
guac_instruction* instruction = guac_instruction_alloc();
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction);
|
||||
/* Allocate parser */
|
||||
guac_parser* parser = guac_parser_alloc();
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(parser);
|
||||
|
||||
/* Instruction input */
|
||||
char buffer[] = "4.test,8.testdata,5.zxcvb,13.guacamoletest;XXXXXXXXXXXXXXXXXX";
|
||||
@ -46,7 +46,7 @@ void test_instruction_parse() {
|
||||
while (remaining > 18) {
|
||||
|
||||
/* Parse more data */
|
||||
int parsed = guac_instruction_append(instruction, current, remaining);
|
||||
int parsed = guac_parser_append(parser, current, remaining);
|
||||
if (parsed == 0)
|
||||
break;
|
||||
|
||||
@ -56,24 +56,24 @@ void test_instruction_parse() {
|
||||
}
|
||||
|
||||
CU_ASSERT_EQUAL(remaining, 18);
|
||||
CU_ASSERT_EQUAL(instruction->state, GUAC_INSTRUCTION_PARSE_COMPLETE);
|
||||
CU_ASSERT_EQUAL(parser->state, GUAC_PARSE_COMPLETE);
|
||||
|
||||
/* Parse is complete - no more data should be read */
|
||||
CU_ASSERT_EQUAL(guac_instruction_append(instruction, current, 18), 0);
|
||||
CU_ASSERT_EQUAL(instruction->state, GUAC_INSTRUCTION_PARSE_COMPLETE);
|
||||
CU_ASSERT_EQUAL(guac_parser_append(parser, current, 18), 0);
|
||||
CU_ASSERT_EQUAL(parser->state, GUAC_PARSE_COMPLETE);
|
||||
|
||||
/* Validate resulting structure */
|
||||
CU_ASSERT_EQUAL(instruction->argc, 3);
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->opcode);
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->argv[0]);
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->argv[1]);
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->argv[2]);
|
||||
CU_ASSERT_EQUAL(parser->argc, 3);
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(parser->opcode);
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(parser->argv[0]);
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(parser->argv[1]);
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(parser->argv[2]);
|
||||
|
||||
/* Validate resulting content */
|
||||
CU_ASSERT_STRING_EQUAL(instruction->opcode, "test");
|
||||
CU_ASSERT_STRING_EQUAL(instruction->argv[0], "testdata");
|
||||
CU_ASSERT_STRING_EQUAL(instruction->argv[1], "zxcvb");
|
||||
CU_ASSERT_STRING_EQUAL(instruction->argv[2], "guacamoletest");
|
||||
CU_ASSERT_STRING_EQUAL(parser->opcode, "test");
|
||||
CU_ASSERT_STRING_EQUAL(parser->argv[0], "testdata");
|
||||
CU_ASSERT_STRING_EQUAL(parser->argv[1], "zxcvb");
|
||||
CU_ASSERT_STRING_EQUAL(parser->argv[2], "guacamoletest");
|
||||
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
#include <CUnit/Basic.h>
|
||||
#include <guacamole/error.h>
|
||||
#include <guacamole/instruction.h>
|
||||
#include <guacamole/parser.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
|
||||
@ -71,7 +71,7 @@ void test_instruction_read() {
|
||||
else {
|
||||
|
||||
guac_socket* socket;
|
||||
guac_instruction* instruction;
|
||||
guac_parser* parser;
|
||||
|
||||
close(wfd);
|
||||
|
||||
@ -79,28 +79,30 @@ void test_instruction_read() {
|
||||
socket = guac_socket_open(rfd);
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(socket);
|
||||
|
||||
/* Allocate parser */
|
||||
parser = guac_parser_alloc();
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(parser);
|
||||
|
||||
/* Read instruction */
|
||||
instruction = guac_instruction_read(socket, 1000000);
|
||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction);
|
||||
CU_ASSERT_EQUAL_FATAL(guac_parser_read(parser, socket, 1000000), 0);
|
||||
|
||||
/* Validate contents */
|
||||
CU_ASSERT_STRING_EQUAL(instruction->opcode, "test");
|
||||
CU_ASSERT_EQUAL_FATAL(instruction->argc, 3);
|
||||
CU_ASSERT_STRING_EQUAL(instruction->argv[0], "a" UTF8_4 "b");
|
||||
CU_ASSERT_STRING_EQUAL(instruction->argv[1], "12345");
|
||||
CU_ASSERT_STRING_EQUAL(instruction->argv[2], "a" UTF8_8 "c");
|
||||
CU_ASSERT_STRING_EQUAL(parser->opcode, "test");
|
||||
CU_ASSERT_EQUAL_FATAL(parser->argc, 3);
|
||||
CU_ASSERT_STRING_EQUAL(parser->argv[0], "a" UTF8_4 "b");
|
||||
CU_ASSERT_STRING_EQUAL(parser->argv[1], "12345");
|
||||
CU_ASSERT_STRING_EQUAL(parser->argv[2], "a" UTF8_8 "c");
|
||||
|
||||
/* Read another instruction */
|
||||
guac_instruction_free(instruction);
|
||||
instruction = guac_instruction_read(socket, 1000000);
|
||||
CU_ASSERT_EQUAL_FATAL(guac_parser_read(parser, socket, 1000000), 0);
|
||||
|
||||
/* Validate contents */
|
||||
CU_ASSERT_STRING_EQUAL(instruction->opcode, "test2");
|
||||
CU_ASSERT_EQUAL_FATAL(instruction->argc, 2);
|
||||
CU_ASSERT_STRING_EQUAL(instruction->argv[0], "hellohello");
|
||||
CU_ASSERT_STRING_EQUAL(instruction->argv[1], "worldworldworld");
|
||||
CU_ASSERT_STRING_EQUAL(parser->opcode, "test2");
|
||||
CU_ASSERT_EQUAL_FATAL(parser->argc, 2);
|
||||
CU_ASSERT_STRING_EQUAL(parser->argv[0], "hellohello");
|
||||
CU_ASSERT_STRING_EQUAL(parser->argv[1], "worldworldworld");
|
||||
|
||||
guac_instruction_free(instruction);
|
||||
guac_parser_free(parser);
|
||||
guac_socket_free(socket);
|
||||
|
||||
}
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
#include <CUnit/Basic.h>
|
||||
#include <guacamole/error.h>
|
||||
#include <guacamole/instruction.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
#include <CUnit/Basic.h>
|
||||
#include <guacamole/error.h>
|
||||
#include <guacamole/instruction.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
|
||||
|
@ -39,7 +39,6 @@ int main() {
|
||||
register_protocol_suite();
|
||||
register_client_suite();
|
||||
register_util_suite();
|
||||
register_common_suite();
|
||||
|
||||
/* Run tests */
|
||||
CU_basic_set_mode(CU_BRM_VERBOSE);
|
||||
|
Loading…
Reference in New Issue
Block a user