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 = \
|
SUBDIRS = \
|
||||||
src/libguac \
|
src/libguac \
|
||||||
src/common \
|
|
||||||
src/guacd \
|
|
||||||
tests
|
tests
|
||||||
|
|
||||||
if ENABLE_COMMON_SSH
|
if ENABLE_COMMON_SSH
|
||||||
SUBDIRS += src/common-ssh
|
#SUBDIRS += src/common-ssh
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if ENABLE_TERMINAL
|
if ENABLE_TERMINAL
|
||||||
SUBDIRS += src/terminal
|
#SUBDIRS += src/terminal
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if ENABLE_RDP
|
if ENABLE_RDP
|
||||||
SUBDIRS += src/protocols/rdp
|
#SUBDIRS += src/protocols/rdp
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if ENABLE_SSH
|
if ENABLE_SSH
|
||||||
SUBDIRS += src/protocols/ssh
|
#SUBDIRS += src/protocols/ssh
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if ENABLE_TELNET
|
if ENABLE_TELNET
|
||||||
SUBDIRS += src/protocols/telnet
|
#SUBDIRS += src/protocols/telnet
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if ENABLE_VNC
|
if ENABLE_VNC
|
||||||
SUBDIRS += src/protocols/vnc
|
#SUBDIRS += src/protocols/vnc
|
||||||
endif
|
endif
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
|
@ -38,16 +38,15 @@ libguacinc_HEADERS = \
|
|||||||
guacamole/error.h \
|
guacamole/error.h \
|
||||||
guacamole/error-types.h \
|
guacamole/error-types.h \
|
||||||
guacamole/hash.h \
|
guacamole/hash.h \
|
||||||
guacamole/instruction-constants.h \
|
|
||||||
guacamole/instruction.h \
|
|
||||||
guacamole/instruction-types.h \
|
|
||||||
guacamole/layer.h \
|
guacamole/layer.h \
|
||||||
guacamole/layer-types.h \
|
guacamole/layer-types.h \
|
||||||
guacamole/object.h \
|
guacamole/object.h \
|
||||||
guacamole/object-types.h \
|
guacamole/object-types.h \
|
||||||
|
guacamole/parser-constants.h \
|
||||||
|
guacamole/parser.h \
|
||||||
|
guacamole/parser-types.h \
|
||||||
guacamole/plugin-constants.h \
|
guacamole/plugin-constants.h \
|
||||||
guacamole/plugin.h \
|
guacamole/plugin.h \
|
||||||
guacamole/plugin-types.h \
|
|
||||||
guacamole/pool.h \
|
guacamole/pool.h \
|
||||||
guacamole/pool-types.h \
|
guacamole/pool-types.h \
|
||||||
guacamole/protocol.h \
|
guacamole/protocol.h \
|
||||||
@ -60,26 +59,30 @@ libguacinc_HEADERS = \
|
|||||||
guacamole/stream-types.h \
|
guacamole/stream-types.h \
|
||||||
guacamole/timestamp.h \
|
guacamole/timestamp.h \
|
||||||
guacamole/timestamp-types.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 = \
|
noinst_HEADERS = \
|
||||||
client-handlers.h \
|
id.h \
|
||||||
encode-jpeg.h \
|
encode-jpeg.h \
|
||||||
encode-png.h \
|
encode-png.h \
|
||||||
palette.h \
|
palette.h \
|
||||||
|
user-handlers.h \
|
||||||
raw_encoder.h
|
raw_encoder.h
|
||||||
|
|
||||||
libguac_la_SOURCES = \
|
libguac_la_SOURCES = \
|
||||||
audio.c \
|
audio.c \
|
||||||
client.c \
|
client.c \
|
||||||
client-handlers.c \
|
|
||||||
encode-jpeg.c \
|
encode-jpeg.c \
|
||||||
encode-png.c \
|
encode-png.c \
|
||||||
error.c \
|
error.c \
|
||||||
hash.c \
|
hash.c \
|
||||||
instruction.c \
|
id.c \
|
||||||
palette.c \
|
palette.c \
|
||||||
plugin.c \
|
parser.c \
|
||||||
pool.c \
|
pool.c \
|
||||||
protocol.c \
|
protocol.c \
|
||||||
raw_encoder.c \
|
raw_encoder.c \
|
||||||
@ -87,7 +90,9 @@ libguac_la_SOURCES = \
|
|||||||
socket-fd.c \
|
socket-fd.c \
|
||||||
socket-nest.c \
|
socket-nest.c \
|
||||||
timestamp.c \
|
timestamp.c \
|
||||||
unicode.c
|
unicode.c \
|
||||||
|
user.c \
|
||||||
|
user-handlers.c
|
||||||
|
|
||||||
# Compile WebP support if available
|
# Compile WebP support if available
|
||||||
if ENABLE_WEBP
|
if ENABLE_WEBP
|
||||||
|
@ -28,51 +28,74 @@
|
|||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
||||||
#include <guacamole/stream.h>
|
#include <guacamole/stream.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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_stream* guac_audio_stream_alloc(guac_client* client,
|
||||||
guac_audio_encoder* encoder, int rate, int channels, int bps) {
|
guac_audio_encoder* encoder, int rate, int channels, int bps) {
|
||||||
|
|
||||||
guac_audio_stream* audio;
|
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 */
|
/* Allocate stream */
|
||||||
audio = (guac_audio_stream*) calloc(1, sizeof(guac_audio_stream));
|
audio = (guac_audio_stream*) calloc(1, sizeof(guac_audio_stream));
|
||||||
audio->client = client;
|
audio->client = client;
|
||||||
|
|
||||||
/* Assign encoder */
|
|
||||||
audio->encoder = encoder;
|
|
||||||
audio->stream = guac_client_alloc_stream(client);
|
audio->stream = guac_client_alloc_stream(client);
|
||||||
|
|
||||||
/* Load PCM properties */
|
/* Load PCM properties */
|
||||||
@ -80,6 +103,13 @@ guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
|
|||||||
audio->channels = channels;
|
audio->channels = channels;
|
||||||
audio->bps = bps;
|
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 */
|
/* Call handler, if defined */
|
||||||
if (audio->encoder->begin_handler)
|
if (audio->encoder->begin_handler)
|
||||||
audio->encoder->begin_handler(audio);
|
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) {
|
void guac_audio_stream_free(guac_audio_stream* audio) {
|
||||||
|
|
||||||
/* Flush stream encoding */
|
/* 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 "config.h"
|
||||||
|
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "client-handlers.h"
|
|
||||||
#include "encode-jpeg.h"
|
#include "encode-jpeg.h"
|
||||||
#include "encode-png.h"
|
#include "encode-png.h"
|
||||||
|
#include "encode-webp.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "instruction.h"
|
#include "id.h"
|
||||||
#include "layer.h"
|
#include "layer.h"
|
||||||
#include "object.h"
|
|
||||||
#include "pool.h"
|
#include "pool.h"
|
||||||
|
#include "plugin.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
#include "timestamp.h"
|
#include "timestamp.h"
|
||||||
|
#include "user.h"
|
||||||
|
|
||||||
#ifdef ENABLE_WEBP
|
#include <dlfcn.h>
|
||||||
#include "encode-webp.h"
|
#include <pthread.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_OSSP_UUID_H
|
|
||||||
#include <ossp/uuid.h>
|
|
||||||
#else
|
|
||||||
#include <uuid.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -57,6 +50,23 @@ guac_layer __GUAC_DEFAULT_LAYER = {
|
|||||||
|
|
||||||
const guac_layer* GUAC_DEFAULT_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) {
|
guac_layer* guac_client_alloc_layer(guac_client* client) {
|
||||||
|
|
||||||
/* Init new layer */
|
/* Init new layer */
|
||||||
@ -109,9 +119,9 @@ guac_stream* guac_client_alloc_stream(guac_client* client) {
|
|||||||
/* Allocate stream */
|
/* Allocate stream */
|
||||||
stream_index = guac_pool_next_int(client->__stream_pool);
|
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 = &(client->__output_streams[stream_index]);
|
||||||
allocd_stream->index = stream_index;
|
allocd_stream->index = (stream_index * 2) + 1;
|
||||||
allocd_stream->data = NULL;
|
allocd_stream->data = NULL;
|
||||||
allocd_stream->ack_handler = NULL;
|
allocd_stream->ack_handler = NULL;
|
||||||
allocd_stream->blob_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) {
|
void guac_client_free_stream(guac_client* client, guac_stream* stream) {
|
||||||
|
|
||||||
/* Release index to pool */
|
/* 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 */
|
/* Mark stream as closed */
|
||||||
stream->index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
|
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;
|
/* Broadcast socket reads are not allowed */
|
||||||
int object_index;
|
return -1;
|
||||||
|
|
||||||
/* Refuse to allocate beyond maximum */
|
|
||||||
if (client->__object_pool->active == GUAC_CLIENT_MAX_OBJECTS)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Allocate object */
|
|
||||||
object_index = guac_pool_next_int(client->__object_pool);
|
|
||||||
|
|
||||||
/* Initialize object */
|
|
||||||
allocd_object = &(client->__objects[object_index]);
|
|
||||||
allocd_object->index = object_index;
|
|
||||||
allocd_object->data = NULL;
|
|
||||||
allocd_object->get_handler = NULL;
|
|
||||||
allocd_object->put_handler = NULL;
|
|
||||||
|
|
||||||
return allocd_object;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void guac_client_free_object(guac_client* client, guac_object* object) {
|
|
||||||
|
|
||||||
/* Release index to pool */
|
|
||||||
guac_pool_free_int(client->__object_pool, object->index);
|
|
||||||
|
|
||||||
/* Mark object as undefined */
|
|
||||||
object->index = GUAC_CLIENT_UNDEFINED_OBJECT_INDEX;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a newly allocated string containing a guaranteed-unique connection
|
* Callback invoked by guac_client_foreach_user() which write a given chunk of
|
||||||
* identifier string which is 37 characters long and begins with a '$'
|
* data to that user's socket. If the write attempt fails, the user is
|
||||||
* character. If an error occurs, NULL is returned, and no memory is
|
* signalled to stop with guac_user_stop().
|
||||||
* allocated.
|
*
|
||||||
|
* @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;
|
__write_chunk* chunk = (__write_chunk*) data;
|
||||||
char* identifier;
|
|
||||||
size_t identifier_length;
|
|
||||||
|
|
||||||
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 */
|
return NULL;
|
||||||
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);
|
* Socket write handler which operates on each of the sockets of all connected
|
||||||
if (buffer == NULL) {
|
* users. This write handler will always succeed, but any failing user-specific
|
||||||
uuid_destroy(uuid);
|
* writes will invoke guac_user_stop() on the failing user.
|
||||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
*
|
||||||
guac_error_message = "Could not allocate memory for connection ID";
|
* @param socket
|
||||||
return NULL;
|
* 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) {
|
||||||
|
|
||||||
identifier = &(buffer[1]);
|
guac_client* client = (guac_client*) socket->data;
|
||||||
identifier_length = UUID_LEN_STR + 1;
|
|
||||||
|
|
||||||
/* Build connection ID from UUID */
|
/* Build chunk */
|
||||||
if (uuid_export(uuid, UUID_FMT_STR, &identifier, &identifier_length) != UUID_RC_OK) {
|
__write_chunk chunk;
|
||||||
free(buffer);
|
chunk.buffer = buf;
|
||||||
uuid_destroy(uuid);
|
chunk.length = count;
|
||||||
guac_error = GUAC_STATUS_INTERNAL_ERROR;
|
|
||||||
guac_error_message = "Conversion of UUID to connection ID failed";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
uuid_destroy(uuid);
|
/* Broadcast chunk to all users */
|
||||||
|
guac_client_foreach_user(client, __write_chunk_callback, &chunk);
|
||||||
|
|
||||||
buffer[0] = '$';
|
return count;
|
||||||
buffer[UUID_LEN_STR + 1] = '\0';
|
|
||||||
return buffer;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
guac_client* guac_client_alloc() {
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
pthread_rwlockattr_t lock_attributes;
|
||||||
|
|
||||||
/* Allocate new client */
|
/* Allocate new client */
|
||||||
guac_client* client = malloc(sizeof(guac_client));
|
guac_client* client = malloc(sizeof(guac_client));
|
||||||
@ -237,13 +402,11 @@ guac_client* guac_client_alloc() {
|
|||||||
/* Init new client */
|
/* Init new client */
|
||||||
memset(client, 0, sizeof(guac_client));
|
memset(client, 0, sizeof(guac_client));
|
||||||
|
|
||||||
client->last_received_timestamp =
|
|
||||||
client->last_sent_timestamp = guac_timestamp_current();
|
|
||||||
|
|
||||||
client->state = GUAC_CLIENT_RUNNING;
|
client->state = GUAC_CLIENT_RUNNING;
|
||||||
|
client->last_sent_timestamp = guac_timestamp_current();
|
||||||
|
|
||||||
/* Generate ID */
|
/* Generate ID */
|
||||||
client->connection_id = __guac_generate_connection_id();
|
client->connection_id = guac_generate_id(GUAC_CLIENT_ID_PREFIX);
|
||||||
if (client->connection_id == NULL) {
|
if (client->connection_id == NULL) {
|
||||||
free(client);
|
free(client);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -257,21 +420,30 @@ guac_client* guac_client_alloc() {
|
|||||||
client->__stream_pool = guac_pool_alloc(0);
|
client->__stream_pool = guac_pool_alloc(0);
|
||||||
|
|
||||||
/* Initialize streams */
|
/* Initialize streams */
|
||||||
client->__input_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS);
|
|
||||||
client->__output_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS);
|
client->__output_streams = malloc(sizeof(guac_stream) * GUAC_CLIENT_MAX_STREAMS);
|
||||||
|
|
||||||
for (i=0; i<GUAC_CLIENT_MAX_STREAMS; i++) {
|
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;
|
client->__output_streams[i].index = GUAC_CLIENT_CLOSED_STREAM_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate object pool */
|
|
||||||
client->__object_pool = guac_pool_alloc(0);
|
|
||||||
|
|
||||||
/* Initialize objects */
|
/* Init locks */
|
||||||
client->__objects = malloc(sizeof(guac_object) * GUAC_CLIENT_MAX_OBJECTS);
|
pthread_rwlockattr_init(&lock_attributes);
|
||||||
for (i=0; i<GUAC_CLIENT_MAX_OBJECTS; i++)
|
pthread_rwlockattr_setpshared(&lock_attributes, PTHREAD_PROCESS_SHARED);
|
||||||
client->__objects[i].index = GUAC_CLIENT_UNDEFINED_OBJECT_INDEX;
|
|
||||||
|
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;
|
return client;
|
||||||
|
|
||||||
@ -279,6 +451,10 @@ guac_client* guac_client_alloc() {
|
|||||||
|
|
||||||
void guac_client_free(guac_client* client) {
|
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) {
|
if (client->free_handler) {
|
||||||
|
|
||||||
/* FIXME: Errors currently ignored... */
|
/* FIXME: Errors currently ignored... */
|
||||||
@ -291,37 +467,19 @@ void guac_client_free(guac_client* client) {
|
|||||||
guac_pool_free(client->__layer_pool);
|
guac_pool_free(client->__layer_pool);
|
||||||
|
|
||||||
/* Free streams */
|
/* Free streams */
|
||||||
free(client->__input_streams);
|
|
||||||
free(client->__output_streams);
|
free(client->__output_streams);
|
||||||
|
|
||||||
/* Free stream pool */
|
/* Free stream pool */
|
||||||
guac_pool_free(client->__stream_pool);
|
guac_pool_free(client->__stream_pool);
|
||||||
|
|
||||||
/* Free objects */
|
/* Close associated plugin */
|
||||||
free(client->__objects);
|
if (client->__plugin_handle != NULL) {
|
||||||
|
if (dlclose(client->__plugin_handle))
|
||||||
/* Free object pool */
|
guac_client_log(client, GUAC_LOG_ERROR, "Unable to close plugin: %s", dlerror());
|
||||||
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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If unrecognized, ignore */
|
pthread_rwlock_destroy(&(client->__users_lock));
|
||||||
return 0;
|
free(client);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void vguac_client_log(guac_client* client, guac_client_log_level level,
|
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,
|
void guac_client_stream_png(guac_client* client, guac_socket* socket,
|
||||||
guac_composite_mode mode, const guac_layer* layer, int x, int y,
|
guac_composite_mode mode, const guac_layer* layer, int x, int y,
|
||||||
cairo_surface_t* surface) {
|
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) {
|
int guac_client_supports_webp(guac_client* client) {
|
||||||
|
|
||||||
#ifdef ENABLE_WEBP
|
#ifdef ENABLE_WEBP
|
||||||
char** mimetype = client->info.image_mimetypes;
|
int webp_supported = 1;
|
||||||
|
|
||||||
/* Search for WebP mimetype in list of supported image mimetypes */
|
/* WebP is supported for entire client only if each user supports it */
|
||||||
while (*mimetype != NULL) {
|
guac_client_foreach_user(client, __webp_support_callback, &webp_supported);
|
||||||
|
|
||||||
/* If WebP mimetype found, no need to search further */
|
return webp_supported;
|
||||||
if (strcmp(*mimetype, "image/webp") == 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
/* Next mimetype */
|
|
||||||
mimetype++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Client does not support WebP */
|
|
||||||
return 0;
|
|
||||||
#else
|
#else
|
||||||
/* Support for WebP is completely absent */
|
/* Support for WebP is completely absent */
|
||||||
return 0;
|
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
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -20,8 +20,8 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __GUAC_AUDIO_FNTYPES_H
|
#ifndef GUAC_AUDIO_FNTYPES_H
|
||||||
#define __GUAC_AUDIO_FNTYPES_H
|
#define GUAC_AUDIO_FNTYPES_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function type definitions related to simple streaming audio.
|
* Function type definitions related to simple streaming audio.
|
||||||
@ -30,24 +30,59 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "audio-types.h"
|
#include "audio-types.h"
|
||||||
|
#include "user-types.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler which is called when the audio stream is opened.
|
* 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);
|
typedef void guac_audio_encoder_begin_handler(guac_audio_stream* audio);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler which is called when the audio stream needs to be flushed.
|
* 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);
|
typedef void guac_audio_encoder_flush_handler(guac_audio_stream* audio);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler which is called when the audio stream is closed.
|
* 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);
|
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,
|
typedef void guac_audio_encoder_write_handler(guac_audio_stream* audio,
|
||||||
const unsigned char* pcm_data, int length);
|
const unsigned char* pcm_data, int length);
|
||||||
|
@ -64,6 +64,12 @@ struct guac_audio_encoder {
|
|||||||
*/
|
*/
|
||||||
guac_audio_encoder_end_handler* end_handler;
|
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 {
|
struct guac_audio_stream {
|
||||||
@ -108,15 +114,24 @@ struct guac_audio_stream {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates a new audio stream which encodes audio data using the given
|
* Allocates a new audio stream at the client level which encodes audio data
|
||||||
* encoder. If NULL is specified for the encoder, an appropriate encoder
|
* using the given encoder. If NULL is specified for the encoder, an
|
||||||
* will be selected based on the encoders built into libguac and the level
|
* appropriate encoder will be selected based on the encoders built into
|
||||||
* of client support. The PCM format specified here (via rate, channels, and
|
* 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.
|
* 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().
|
* 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
|
* @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
|
* @param encoder
|
||||||
* The guac_audio_encoder to use when encoding audio, or NULL if libguac
|
* The guac_audio_encoder to use when encoding audio, or NULL if libguac
|
||||||
@ -135,7 +150,8 @@ struct guac_audio_stream {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* The newly allocated guac_audio_stream, or NULL if no audio stream could
|
* 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_stream* guac_audio_stream_alloc(guac_client* client,
|
||||||
guac_audio_encoder* encoder, int rate, int channels, int bps);
|
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,
|
void guac_audio_stream_reset(guac_audio_stream* audio,
|
||||||
guac_audio_encoder* encoder, int rate, int channels, int bps);
|
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.
|
* 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
|
#define GUAC_CLIENT_MAX_STREAMS 64
|
||||||
|
|
||||||
@ -40,26 +41,9 @@
|
|||||||
#define GUAC_CLIENT_CLOSED_STREAM_INDEX -1
|
#define GUAC_CLIENT_CLOSED_STREAM_INDEX -1
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of objects supported by any one guac_client.
|
* The character prefix which identifies a client ID.
|
||||||
*/
|
*/
|
||||||
#define GUAC_CLIENT_MAX_OBJECTS 64
|
#define GUAC_CLIENT_ID_PREFIX '$'
|
||||||
|
|
||||||
/**
|
|
||||||
* The index of an object which has not been defined.
|
|
||||||
*/
|
|
||||||
#define GUAC_CLIENT_UNDEFINED_OBJECT_INDEX -1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The stream name reserved for the root of a Guacamole protocol object.
|
|
||||||
*/
|
|
||||||
#define GUAC_CLIENT_OBJECT_ROOT_NAME "/"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The mimetype of a stream containing a map of available stream names to their
|
|
||||||
* corresponding mimetypes. The root of a Guacamole protocol object is
|
|
||||||
* guaranteed to have this type.
|
|
||||||
*/
|
|
||||||
#define GUAC_CLIENT_STREAM_INDEX_MIMETYPE "application/vnd.glyptodon.guacamole.stream-index+json"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The flag set in the mouse button mask when the left mouse button is down.
|
* The flag set in the mouse button mask when the left mouse button is down.
|
||||||
|
@ -34,102 +34,53 @@
|
|||||||
#include "object-types.h"
|
#include "object-types.h"
|
||||||
#include "protocol-types.h"
|
#include "protocol-types.h"
|
||||||
#include "stream-types.h"
|
#include "stream-types.h"
|
||||||
|
#include "user-types.h"
|
||||||
|
|
||||||
#include <stdarg.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
|
* Handler for freeing up any extra data allocated by the client
|
||||||
* implementation.
|
* 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);
|
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
|
#endif
|
||||||
|
|
||||||
|
@ -87,11 +87,5 @@ typedef enum guac_client_log_level {
|
|||||||
|
|
||||||
} 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
|
#endif
|
||||||
|
|
||||||
|
@ -32,73 +32,34 @@
|
|||||||
#include "client-fntypes.h"
|
#include "client-fntypes.h"
|
||||||
#include "client-types.h"
|
#include "client-types.h"
|
||||||
#include "client-constants.h"
|
#include "client-constants.h"
|
||||||
#include "instruction-types.h"
|
|
||||||
#include "layer-types.h"
|
#include "layer-types.h"
|
||||||
#include "object-types.h"
|
#include "object-types.h"
|
||||||
#include "pool-types.h"
|
#include "pool-types.h"
|
||||||
#include "socket-types.h"
|
#include "socket-types.h"
|
||||||
#include "stream-types.h"
|
#include "stream-types.h"
|
||||||
#include "timestamp-types.h"
|
#include "timestamp-types.h"
|
||||||
|
#include "user-fntypes.h"
|
||||||
|
#include "user-types.h"
|
||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
#include <stdarg.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 {
|
struct guac_client {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The guac_socket structure to be used to communicate with the web-client.
|
* The guac_socket structure to be used to communicate with all connected
|
||||||
* It is expected that the implementor of any Guacamole proxy client will
|
* 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
|
* provide their own mechanism of I/O for their protocol. The guac_socket
|
||||||
* structure is used only to communicate conveniently with the Guacamole
|
* structure is used only to communicate conveniently with the Guacamole
|
||||||
* web-client.
|
* 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;
|
guac_socket* socket;
|
||||||
|
|
||||||
@ -110,24 +71,6 @@ struct guac_client {
|
|||||||
*/
|
*/
|
||||||
guac_client_state state;
|
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
|
* Arbitrary reference to proxy client-specific data. Implementors of a
|
||||||
* Guacamole proxy client can store any data they want here, which can then
|
* Guacamole proxy client can store any data they want here, which can then
|
||||||
@ -136,197 +79,10 @@ struct guac_client {
|
|||||||
void* data;
|
void* data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for server messages. If set, this function will be called
|
* The time (in milliseconds) that the last sync message was sent to the
|
||||||
* occasionally by the Guacamole proxy to give the client a chance to
|
* client.
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
guac_client_handle_messages* handle_messages;
|
guac_timestamp last_sent_timestamp;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for freeing data when the client is being unloaded.
|
* Handler for freeing data when the client is being unloaded.
|
||||||
@ -344,7 +100,7 @@ struct guac_client {
|
|||||||
* @code
|
* @code
|
||||||
* int free_handler(guac_client* client);
|
* 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;
|
* client->free_handler = free_handler;
|
||||||
* }
|
* }
|
||||||
* @endcode
|
* @endcode
|
||||||
@ -375,46 +131,6 @@ struct guac_client {
|
|||||||
*/
|
*/
|
||||||
guac_client_log_handler* log_handler;
|
guac_client_log_handler* log_handler;
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for get events sent by the Guacamole web-client.
|
|
||||||
*
|
|
||||||
* The handler takes a guac_object, containing the object index which will
|
|
||||||
* persist through the duration of the transfer, and the name of the stream
|
|
||||||
* being requested. It is up to the get handler to create the required body
|
|
||||||
* stream.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
* @code
|
|
||||||
* int get_handler(guac_client* client, guac_object* object,
|
|
||||||
* char* name);
|
|
||||||
*
|
|
||||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
|
||||||
* client->get_handler = get_handler;
|
|
||||||
* }
|
|
||||||
* @endcode
|
|
||||||
*/
|
|
||||||
guac_client_get_handler* get_handler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for put events sent by the Guacamole web-client.
|
|
||||||
*
|
|
||||||
* The handler takes a guac_object and guac_stream, which each contain their
|
|
||||||
* respective indices which will persist through the duration of the
|
|
||||||
* transfer, the mimetype of the data being transferred, and the name of
|
|
||||||
* the stream within the object being written to.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
* @code
|
|
||||||
* int put_handler(guac_client* client, guac_object* object,
|
|
||||||
* guac_stream* stream, char* mimetype, char* name);
|
|
||||||
*
|
|
||||||
* int guac_client_init(guac_client* client, int argc, char** argv) {
|
|
||||||
* client->put_handler = put_handler;
|
|
||||||
* }
|
|
||||||
* @endcode
|
|
||||||
*/
|
|
||||||
guac_client_put_handler* put_handler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pool of buffer indices. Buffers are simply layers with negative indices.
|
* Pool of buffer indices. Buffers are simply layers with negative indices.
|
||||||
* Note that because guac_pool always gives non-negative indices starting
|
* Note that because guac_pool always gives non-negative indices starting
|
||||||
@ -435,25 +151,11 @@ struct guac_client {
|
|||||||
guac_pool* __stream_pool;
|
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;
|
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
|
* The unique identifier allocated for the connection, which may
|
||||||
* be used within the Guacamole protocol to refer to this connection.
|
* be used within the Guacamole protocol to refer to this connection.
|
||||||
@ -463,6 +165,105 @@ struct guac_client {
|
|||||||
*/
|
*/
|
||||||
char* connection_id;
|
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);
|
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
|
* 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)
|
* 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
|
* Allocates a new stream. An arbitrary index is automatically assigned
|
||||||
* if no previously-allocated stream is available for use.
|
* if no previously-allocated stream is available for use.
|
||||||
*
|
*
|
||||||
* @param client The proxy client to allocate the layer buffer for.
|
* @param client
|
||||||
* @return The next available stream, or a newly allocated stream.
|
* 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);
|
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
|
* 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().
|
* can be reused by any subsequent call to guac_client_alloc_stream().
|
||||||
*
|
*
|
||||||
* @param client The proxy client to return the buffer to.
|
* @param client
|
||||||
* @param stream The stream to return to the pool of available stream.
|
* 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);
|
void guac_client_free_stream(guac_client* client, guac_stream* stream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates a new object. An arbitrary index is automatically assigned
|
* Adds the given user to the internal list of connected users. Future writes
|
||||||
* if no previously-allocated object is available for use.
|
* 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
|
* @param client The proxy client to add the user to.
|
||||||
* The proxy client to allocate the object for.
|
* @param user The user to add.
|
||||||
*
|
* @param argc The number of arguments to pass to the new user.
|
||||||
* @return
|
* @param argv An array of strings containing the argument values being passed.
|
||||||
* The next available object, or a newly allocated object.
|
* @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
|
* Removes the given user, removing the user from the internally-tracked list
|
||||||
* can be reused by any subsequent call to guac_client_alloc_object().
|
* 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
|
* @param client
|
||||||
* The proxy client to return the object to.
|
* The client whose users should be iterated.
|
||||||
*
|
*
|
||||||
* @param object
|
* @param callback
|
||||||
* The object to return to the pool of available object.
|
* 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"
|
* 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.
|
* allocated and freed.
|
||||||
*
|
*
|
||||||
* @param client
|
* @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
|
* @param socket
|
||||||
* The socket over which instructions associated with the image stream
|
* 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.
|
* will be automatically allocated and freed.
|
||||||
*
|
*
|
||||||
* @param client
|
* @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
|
* @param socket
|
||||||
* The socket over which instructions associated with the image stream
|
* 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.
|
* A Cairo surface containing the image data to be streamed.
|
||||||
*
|
*
|
||||||
* @param quality
|
* @param quality
|
||||||
* The JPEG image quality, which must be an integer value between 0 and
|
* The JPEG image quality, which must be an integer value between 0 and 100
|
||||||
* 100 inclusive.
|
* inclusive. Larger values indicate improving quality at the expense of
|
||||||
|
* larger file size.
|
||||||
*/
|
*/
|
||||||
void guac_client_stream_jpeg(guac_client* client, guac_socket* socket,
|
void guac_client_stream_jpeg(guac_client* client, guac_socket* socket,
|
||||||
guac_composite_mode mode, const guac_layer* layer, int x, int y,
|
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.
|
* guac_client_supports_webp() prior to calling this function.
|
||||||
*
|
*
|
||||||
* @param client
|
* @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
|
* @param socket
|
||||||
* The socket over which instructions associated with the image stream
|
* 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);
|
cairo_surface_t* surface, int quality, int lossless);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given client supports WebP. If the client does not
|
* Returns whether all users of the given client support WebP. If any user does
|
||||||
* support WebP, or the server cannot encode WebP images, zero is returned.
|
* not support WebP, or the server cannot encode WebP images, zero is returned.
|
||||||
*
|
*
|
||||||
* @param client
|
* @param client
|
||||||
* The Guacamole client to check for WebP support.
|
* The Guacamole client whose users should be checked for WebP support.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* Non-zero if the given client claims to support WebP and the server has
|
* Non-zero if the all users of the given client claim to support WebP and
|
||||||
* been built with WebP support, zero otherwise.
|
* the server has been built with WebP support, zero otherwise.
|
||||||
*/
|
*/
|
||||||
int guac_client_supports_webp(guac_client* client);
|
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
|
* @file object.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "client-fntypes.h"
|
|
||||||
#include "object-types.h"
|
#include "object-types.h"
|
||||||
|
#include "user-fntypes.h"
|
||||||
|
|
||||||
struct guac_object {
|
struct guac_object {
|
||||||
|
|
||||||
@ -54,18 +54,18 @@ struct guac_object {
|
|||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* @code
|
* @code
|
||||||
* int get_handler(guac_client* client, guac_object* object,
|
* int get_handler(guac_user* user, guac_object* object,
|
||||||
* char* name);
|
* 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;
|
* object->get_handler = get_handler;
|
||||||
*
|
*
|
||||||
* }
|
* }
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
guac_client_get_handler* get_handler;
|
guac_user_get_handler* get_handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for put events sent by the Guacamole web-client.
|
* Handler for put events sent by the Guacamole web-client.
|
||||||
@ -77,18 +77,18 @@ struct guac_object {
|
|||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* @code
|
* @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);
|
* 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;
|
* object->put_handler = put_handler;
|
||||||
*
|
*
|
||||||
* }
|
* }
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
guac_client_put_handler* put_handler;
|
guac_user_put_handler* put_handler;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,13 +20,13 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _GUAC_INSTRUCTION_CONSTANTS_H
|
#ifndef _GUAC_PARSER_CONSTANTS_H
|
||||||
#define _GUAC_INSTRUCTION_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
|
#ifndef _GUAC_PARSER_TYPES_H
|
||||||
#define _GUAC_INSTRUCTION_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.
|
* 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
|
* The parser is currently waiting for data to complete the length prefix
|
||||||
* of the current element of the instruction.
|
* 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
|
* The parser has finished reading the length prefix and is currently
|
||||||
* waiting for data to complete the content of the instruction.
|
* waiting for data to complete the content of the instruction.
|
||||||
*/
|
*/
|
||||||
GUAC_INSTRUCTION_PARSE_CONTENT,
|
GUAC_PARSE_CONTENT,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The instruction has been fully parsed.
|
* The instruction has been fully parsed.
|
||||||
*/
|
*/
|
||||||
GUAC_INSTRUCTION_PARSE_COMPLETE,
|
GUAC_PARSE_COMPLETE,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The instruction cannot be parsed because of a protocol error.
|
* 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
|
#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
|
#ifndef _GUAC_PLUGIN_H
|
||||||
#define _GUAC_PLUGIN_H
|
#define _GUAC_PLUGIN_H
|
||||||
|
|
||||||
#include "client-types.h"
|
|
||||||
#include "plugin-constants.h"
|
#include "plugin-constants.h"
|
||||||
#include "plugin-types.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides functions and structures required for handling a client plugin.
|
* Provides functions and structures required for handling a client plugin.
|
||||||
@ -33,60 +31,4 @@
|
|||||||
* @file plugin.h
|
* @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
|
#endif
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
#include "pool-types.h"
|
#include "pool-types.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
struct guac_pool {
|
struct guac_pool {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,6 +64,11 @@ struct guac_pool {
|
|||||||
*/
|
*/
|
||||||
guac_pool_int* __tail;
|
guac_pool_int* __tail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock which is acquired when the pool is being modified or accessed.
|
||||||
|
*/
|
||||||
|
pthread_mutex_t __lock;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct guac_pool_int {
|
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
|
* Returns the next available integer from the given guac_pool. All integers
|
||||||
* returned are non-negative, and are returned in sequences, starting from 0.
|
* 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.
|
* @param pool
|
||||||
* @return The next available integer, which may be either an integer not yet
|
* The guac_pool to retrieve an integer from.
|
||||||
* returned by a call to guac_pool_next_int, or an integer which was
|
*
|
||||||
* previosly returned, but has since been freed.
|
* @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
|
||||||
|
* previously returned, but has since been freed.
|
||||||
*/
|
*/
|
||||||
int guac_pool_next_int(guac_pool* pool);
|
int guac_pool_next_int(guac_pool* pool);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frees the given integer back into the given guac_pool. The integer given
|
* 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 pool
|
||||||
* @param value The integer which should be returned to the given pool, such
|
* The guac_pool to free the given integer into.
|
||||||
* that it can be received by a future call to guac_pool_next_int.
|
*
|
||||||
|
* @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);
|
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
|
* 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
|
* 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 socket The guac_socket being written to.
|
||||||
* @param buf The arbitrary buffer containing data to be written.
|
* @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.
|
* @return The number of bytes written, or -1 if an error occurs.
|
||||||
*/
|
*/
|
||||||
typedef ssize_t guac_socket_write_handler(guac_socket* socket,
|
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);
|
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
|
* 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
|
* 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
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* 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.
|
* 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;
|
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.
|
* on this socket.
|
||||||
*/
|
*/
|
||||||
guac_socket_select_handler* select_handler;
|
guac_socket_select_handler* select_handler;
|
||||||
@ -90,52 +105,6 @@ struct guac_socket {
|
|||||||
*/
|
*/
|
||||||
int __ready_buf[3];
|
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.
|
* Whether automatic keep-alive is enabled.
|
||||||
*/
|
*/
|
||||||
@ -164,66 +133,36 @@ guac_socket* guac_socket_alloc();
|
|||||||
*/
|
*/
|
||||||
void guac_socket_free(guac_socket* socket);
|
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
|
* 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.
|
* 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
|
* This ping will take the form of a "nop" instruction.
|
||||||
* automatically enables threadsafety.
|
|
||||||
*
|
*
|
||||||
* @param socket The guac_socket to declare as requiring an automatic
|
* @param socket
|
||||||
* keep-alive ping.
|
* The guac_socket to declare as requiring an automatic keep-alive ping.
|
||||||
*/
|
*/
|
||||||
void guac_socket_require_keep_alive(guac_socket* socket);
|
void guac_socket_require_keep_alive(guac_socket* socket);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the beginning of a Guacamole protocol instruction. If threadsafety
|
* Marks the beginning of a Guacamole protocol instruction.
|
||||||
* is enabled on the socket, other instructions will be blocked from sending
|
|
||||||
* until this instruction is complete.
|
|
||||||
*
|
*
|
||||||
* @param socket The guac_socket beginning an instruction.
|
* @param socket
|
||||||
|
* The guac_socket beginning an instruction.
|
||||||
*/
|
*/
|
||||||
void guac_socket_instruction_begin(guac_socket* socket);
|
void guac_socket_instruction_begin(guac_socket* socket);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the end of a Guacamole protocol instruction. If threadsafety
|
* Marks the end of a Guacamole protocol instruction.
|
||||||
* is enabled on the socket, other instructions will be allowed to send.
|
|
||||||
*
|
*
|
||||||
* @param socket The guac_socket ending an instruction.
|
* @param socket
|
||||||
|
* The guac_socket ending an instruction.
|
||||||
*/
|
*/
|
||||||
void guac_socket_instruction_end(guac_socket* socket);
|
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
|
* 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,
|
* If an error occurs while allocating the guac_socket object, NULL is returned,
|
||||||
* and guac_error is set appropriately.
|
* 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
|
* 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,
|
* If an error occurs while allocating the guac_socket object, NULL is returned,
|
||||||
* and guac_error is set appropriately.
|
* 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
|
* Writes the given string to the given guac_socket object. The data
|
||||||
* written may be buffered until the buffer is flushed automatically or
|
* written may be buffered until the buffer is flushed automatically or
|
||||||
* manually. Note that if the string can contain characters used
|
* manually.
|
||||||
* internally by the Guacamole protocol (commas, semicolons, or
|
|
||||||
* backslashes) it will need to be escaped.
|
|
||||||
*
|
*
|
||||||
* If an error occurs while writing, a non-zero value is returned, and
|
* If an error occurs while writing, a non-zero value is returned, and
|
||||||
* guac_error is set appropriately.
|
* 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-
|
* 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
|
* 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()
|
* 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.
|
* base64 data) can be made.
|
||||||
*
|
*
|
||||||
* If an error occurs while writing, a non-zero value is returned, and
|
* 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);
|
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
|
* Writes the given data to the specified socket. The data written may be
|
||||||
* buffered, and will be sent immediately.
|
* buffered until the buffer is flushed automatically or manually.
|
||||||
*
|
*
|
||||||
* If an error occurs while writing, a non-zero value is returned, and
|
* If an error occurs while writing, a non-zero value is returned, and
|
||||||
* guac_error is set appropriately.
|
* guac_error is set appropriately.
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
* @file stream.h
|
* @file stream.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "client-fntypes.h"
|
#include "user-fntypes.h"
|
||||||
#include "stream-types.h"
|
#include "stream-types.h"
|
||||||
|
|
||||||
struct guac_stream {
|
struct guac_stream {
|
||||||
@ -53,21 +53,21 @@ struct guac_stream {
|
|||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* @code
|
* @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);
|
* 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;
|
* stream->ack_handler = ack_handler;
|
||||||
*
|
*
|
||||||
* guac_protocol_send_clipboard(client->socket,
|
* guac_protocol_send_clipboard(user->socket,
|
||||||
* stream, "text/plain");
|
* stream, "text/plain");
|
||||||
*
|
*
|
||||||
* }
|
* }
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
guac_client_ack_handler* ack_handler;
|
guac_user_ack_handler* ack_handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for blob events sent by the Guacamole web-client.
|
* Handler for blob events sent by the Guacamole web-client.
|
||||||
@ -78,16 +78,16 @@ struct guac_stream {
|
|||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* @code
|
* @code
|
||||||
* int blob_handler(guac_client* client, guac_stream* stream,
|
* int blob_handler(guac_user* user, guac_stream* stream,
|
||||||
* void* data, int length);
|
* 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) {
|
* char* mimetype) {
|
||||||
* stream->blob_handler = blob_handler;
|
* stream->blob_handler = blob_handler;
|
||||||
* }
|
* }
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
guac_client_blob_handler* blob_handler;
|
guac_user_blob_handler* blob_handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for stream end events sent by the Guacamole web-client.
|
* Handler for stream end events sent by the Guacamole web-client.
|
||||||
@ -98,15 +98,15 @@ struct guac_stream {
|
|||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* @code
|
* @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) {
|
* char* mimetype) {
|
||||||
* stream->end_handler = end_handler;
|
* stream->end_handler = end_handler;
|
||||||
* }
|
* }
|
||||||
* @endcode
|
* @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
|
* calls. The return value from a single call will not have any useful
|
||||||
* (or defined) meaning.
|
* (or defined) meaning.
|
||||||
*
|
*
|
||||||
* @return An arbitrary millisecond timestamp.
|
* @return
|
||||||
|
* An arbitrary millisecond timestamp.
|
||||||
*/
|
*/
|
||||||
guac_timestamp guac_timestamp_current();
|
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
|
#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.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _GUAC_PLUGIN_TYPES_H
|
#ifndef _GUAC_USER_TYPES_H
|
||||||
#define _GUAC_PLUGIN_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
|
* Representation of a physical connection within a larger logical connection
|
||||||
* plugin to complete the initial protocol handshake and instantiate a new
|
* which may be shared. Logical connections are represented by guac_client.
|
||||||
* client supporting the protocol provided by the client plugin.
|
|
||||||
*/
|
*/
|
||||||
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
|
#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 "config.h"
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "instruction.h"
|
#include "parser.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
#include "unicode.h"
|
#include "unicode.h"
|
||||||
|
|
||||||
@ -31,46 +31,49 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.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_parser* guac_parser_alloc() {
|
||||||
guac_instruction* instruction = malloc(sizeof(guac_instruction));
|
|
||||||
if (instruction == NULL) {
|
/* Allocate space for parser */
|
||||||
|
guac_parser* parser = malloc(sizeof(guac_parser));
|
||||||
|
if (parser == NULL) {
|
||||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||||
guac_error_message = "Insufficient memory to allocate instruction";
|
guac_error_message = "Insufficient memory to allocate parser";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
guac_instruction_reset(instruction);
|
/* Init parse start/end markers */
|
||||||
return instruction;
|
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) {
|
int guac_parser_append(guac_parser* parser, void* buffer, int length) {
|
||||||
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) {
|
|
||||||
|
|
||||||
char* char_buffer = (char*) buffer;
|
char* char_buffer = (char*) buffer;
|
||||||
int bytes_parsed = 0;
|
int bytes_parsed = 0;
|
||||||
|
|
||||||
/* Do not exceed maximum number of elements */
|
/* Do not exceed maximum number of elements */
|
||||||
if (instr->__elementc == GUAC_INSTRUCTION_MAX_ELEMENTS
|
if (parser->__elementc == GUAC_INSTRUCTION_MAX_ELEMENTS
|
||||||
&& instr->state != GUAC_INSTRUCTION_PARSE_COMPLETE) {
|
&& parser->state != GUAC_PARSE_COMPLETE) {
|
||||||
instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
|
parser->state = GUAC_PARSE_ERROR;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse element length */
|
/* 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) {
|
while (bytes_parsed < length) {
|
||||||
|
|
||||||
/* Pull next character */
|
/* Pull next character */
|
||||||
@ -83,14 +86,14 @@ int guac_instruction_append(guac_instruction* instr,
|
|||||||
|
|
||||||
/* If period, switch to parsing content */
|
/* If period, switch to parsing content */
|
||||||
else if (c == '.') {
|
else if (c == '.') {
|
||||||
instr->__elementv[instr->__elementc++] = char_buffer;
|
parser->__elementv[parser->__elementc++] = char_buffer;
|
||||||
instr->state = GUAC_INSTRUCTION_PARSE_CONTENT;
|
parser->state = GUAC_PARSE_CONTENT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If not digit, parse error */
|
/* If not digit, parse error */
|
||||||
else {
|
else {
|
||||||
instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
|
parser->state = GUAC_PARSE_ERROR;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,19 +101,19 @@ int guac_instruction_append(guac_instruction* instr,
|
|||||||
|
|
||||||
/* If too long, parse error */
|
/* If too long, parse error */
|
||||||
if (parsed_length > GUAC_INSTRUCTION_MAX_LENGTH) {
|
if (parsed_length > GUAC_INSTRUCTION_MAX_LENGTH) {
|
||||||
instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
|
parser->state = GUAC_PARSE_ERROR;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save length */
|
/* Save length */
|
||||||
instr->__element_length = parsed_length;
|
parser->__element_length = parsed_length;
|
||||||
|
|
||||||
} /* end parse length */
|
} /* end parse length */
|
||||||
|
|
||||||
/* Parse element content */
|
/* 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 */
|
/* Get length of current character */
|
||||||
char c = *char_buffer;
|
char c = *char_buffer;
|
||||||
@ -124,35 +127,35 @@ int guac_instruction_append(guac_instruction* instr,
|
|||||||
bytes_parsed += char_length;
|
bytes_parsed += char_length;
|
||||||
|
|
||||||
/* If end of element, handle terminator */
|
/* If end of element, handle terminator */
|
||||||
if (instr->__element_length == 0) {
|
if (parser->__element_length == 0) {
|
||||||
|
|
||||||
*char_buffer = '\0';
|
*char_buffer = '\0';
|
||||||
|
|
||||||
/* If semicolon, store end-of-instruction */
|
/* If semicolon, store end-of-instruction */
|
||||||
if (c == ';') {
|
if (c == ';') {
|
||||||
instr->state = GUAC_INSTRUCTION_PARSE_COMPLETE;
|
parser->state = GUAC_PARSE_COMPLETE;
|
||||||
instr->opcode = instr->__elementv[0];
|
parser->opcode = parser->__elementv[0];
|
||||||
instr->argv = &(instr->__elementv[1]);
|
parser->argv = &(parser->__elementv[1]);
|
||||||
instr->argc = instr->__elementc - 1;
|
parser->argc = parser->__elementc - 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If comma, move on to next element */
|
/* If comma, move on to next element */
|
||||||
else if (c == ',') {
|
else if (c == ',') {
|
||||||
instr->state = GUAC_INSTRUCTION_PARSE_LENGTH;
|
parser->state = GUAC_PARSE_LENGTH;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise, parse error */
|
/* Otherwise, parse error */
|
||||||
else {
|
else {
|
||||||
instr->state = GUAC_INSTRUCTION_PARSE_ERROR;
|
parser->state = GUAC_PARSE_ERROR;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* end if end of element */
|
} /* end if end of element */
|
||||||
|
|
||||||
/* Advance to next character */
|
/* Advance to next character */
|
||||||
instr->__element_length--;
|
parser->__element_length--;
|
||||||
char_buffer += char_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. */
|
int guac_parser_read(guac_parser* parser, guac_socket* socket, int usec_timeout) {
|
||||||
guac_instruction* guac_instruction_read(guac_socket* socket,
|
|
||||||
int usec_timeout) {
|
|
||||||
|
|
||||||
char* unparsed_end = socket->__instructionbuf_unparsed_end;
|
char* unparsed_end = parser->__instructionbuf_unparsed_end;
|
||||||
char* unparsed_start = socket->__instructionbuf_unparsed_start;
|
char* unparsed_start = parser->__instructionbuf_unparsed_start;
|
||||||
char* instr_start = socket->__instructionbuf_unparsed_start;
|
char* instr_start = parser->__instructionbuf_unparsed_start;
|
||||||
char* buffer_end = socket->__instructionbuf
|
char* buffer_end = parser->__instructionbuf + sizeof(parser->__instructionbuf);
|
||||||
+ sizeof(socket->__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
|
while (parser->state != GUAC_PARSE_COMPLETE
|
||||||
&& instruction->state != GUAC_INSTRUCTION_PARSE_ERROR) {
|
&& parser->state != GUAC_PARSE_ERROR) {
|
||||||
|
|
||||||
/* Add any available data to buffer */
|
/* Add any available data to buffer */
|
||||||
int parsed = guac_instruction_append(instruction, unparsed_start,
|
int parsed = guac_parser_append(parser, unparsed_start, unparsed_end - unparsed_start);
|
||||||
unparsed_end - unparsed_start);
|
|
||||||
|
|
||||||
/* Read more data if not enough data to parse */
|
/* Read more data if not enough data to parse */
|
||||||
if (parsed == 0) {
|
if (parsed == 0 && parser->state != GUAC_PARSE_ERROR) {
|
||||||
|
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
@ -191,23 +192,23 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
|||||||
if (unparsed_end == buffer_end) {
|
if (unparsed_end == buffer_end) {
|
||||||
|
|
||||||
/* Shift backward if possible */
|
/* Shift backward if possible */
|
||||||
if (instr_start != socket->__instructionbuf) {
|
if (instr_start != parser->__instructionbuf) {
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Shift buffer */
|
/* Shift buffer */
|
||||||
int offset = instr_start - socket->__instructionbuf;
|
int offset = instr_start - parser->__instructionbuf;
|
||||||
memmove(socket->__instructionbuf, instr_start,
|
memmove(parser->__instructionbuf, instr_start,
|
||||||
unparsed_end - instr_start);
|
unparsed_end - instr_start);
|
||||||
|
|
||||||
/* Update tracking pointers */
|
/* Update tracking pointers */
|
||||||
unparsed_end -= offset;
|
unparsed_end -= offset;
|
||||||
unparsed_start -= offset;
|
unparsed_start -= offset;
|
||||||
instr_start = socket->__instructionbuf;
|
instr_start = parser->__instructionbuf;
|
||||||
|
|
||||||
/* Update parsed elements, if any */
|
/* Update parsed elements, if any */
|
||||||
for (i=0; i<instruction->__elementc; i++)
|
for (i=0; i < parser->__elementc; i++)
|
||||||
instruction->__elementv[i] -= offset;
|
parser->__elementv[i] -= offset;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +216,7 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
|||||||
else {
|
else {
|
||||||
guac_error = GUAC_STATUS_NO_MEMORY;
|
guac_error = GUAC_STATUS_NO_MEMORY;
|
||||||
guac_error_message = "Instruction too long";
|
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 ... */
|
/* No instruction yet? Get more data ... */
|
||||||
retval = guac_socket_select(socket, usec_timeout);
|
retval = guac_socket_select(socket, usec_timeout);
|
||||||
if (retval <= 0)
|
if (retval <= 0)
|
||||||
return NULL;
|
return -1;
|
||||||
|
|
||||||
/* Attempt to fill buffer */
|
/* Attempt to fill buffer */
|
||||||
retval = guac_socket_read(socket, unparsed_end,
|
retval = guac_socket_read(socket, unparsed_end,
|
||||||
@ -233,7 +234,7 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
|||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
guac_error = GUAC_STATUS_SEE_ERRNO;
|
guac_error = GUAC_STATUS_SEE_ERRNO;
|
||||||
guac_error_message = "Error filling instruction buffer";
|
guac_error_message = "Error filling instruction buffer";
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
@ -241,7 +242,7 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
|||||||
guac_error = GUAC_STATUS_CLOSED;
|
guac_error = GUAC_STATUS_CLOSED;
|
||||||
guac_error_message = "End of stream reached while "
|
guac_error_message = "End of stream reached while "
|
||||||
"reading instruction";
|
"reading instruction";
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update internal buffer */
|
/* Update internal buffer */
|
||||||
@ -256,55 +257,65 @@ guac_instruction* guac_instruction_read(guac_socket* socket,
|
|||||||
} /* end while parsing data */
|
} /* end while parsing data */
|
||||||
|
|
||||||
/* Fail on error */
|
/* Fail on error */
|
||||||
if (instruction->state == GUAC_INSTRUCTION_PARSE_ERROR) {
|
if (parser->state == GUAC_PARSE_ERROR) {
|
||||||
guac_error = GUAC_STATUS_PROTOCOL_ERROR;
|
guac_error = GUAC_STATUS_PROTOCOL_ERROR;
|
||||||
guac_error_message = "Instruction parse error";
|
guac_error_message = "Instruction parse error";
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
socket->__instructionbuf_unparsed_start = unparsed_start;
|
parser->__instructionbuf_unparsed_start = unparsed_start;
|
||||||
socket->__instructionbuf_unparsed_end = unparsed_end;
|
parser->__instructionbuf_unparsed_end = unparsed_end;
|
||||||
return instruction;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guac_instruction* guac_instruction_expect(guac_socket* socket, int usec_timeout,
|
int guac_parser_expect(guac_parser* parser, guac_socket* socket, int usec_timeout, const char* opcode) {
|
||||||
const char* opcode) {
|
|
||||||
|
|
||||||
guac_instruction* instruction;
|
/* Read next instruction */
|
||||||
|
if (guac_parser_read(parser, socket, usec_timeout) != 0)
|
||||||
/* Wait for data until timeout */
|
return -1;
|
||||||
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;
|
|
||||||
|
|
||||||
/* Validate instruction */
|
/* Validate instruction */
|
||||||
if (strcmp(instruction->opcode, opcode) != 0) {
|
if (strcmp(parser->opcode, opcode) != 0) {
|
||||||
guac_error = GUAC_STATUS_PROTOCOL_ERROR;
|
guac_error = GUAC_STATUS_PROTOCOL_ERROR;
|
||||||
guac_error_message = "Instruction read did not have expected opcode";
|
guac_error_message = "Instruction read did not have expected opcode";
|
||||||
guac_instruction_free(instruction);
|
return -1;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return instruction if valid */
|
/* Return non-zero only if valid instruction read */
|
||||||
return instruction;
|
return parser->state != GUAC_PARSE_COMPLETE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_instruction_free(guac_instruction* instruction) {
|
int guac_parser_length(guac_parser* parser) {
|
||||||
free(instruction);
|
|
||||||
|
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 >
|
char* copy_end = parser->__instructionbuf_unparsed_end;
|
||||||
socket->__instructionbuf_unparsed_start)
|
char* copy_start = parser->__instructionbuf_unparsed_start;
|
||||||
return 1;
|
|
||||||
|
/* 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) {
|
guac_pool* guac_pool_alloc(int size) {
|
||||||
|
|
||||||
|
pthread_mutexattr_t lock_attributes;
|
||||||
guac_pool* pool = malloc(sizeof(guac_pool));
|
guac_pool* pool = malloc(sizeof(guac_pool));
|
||||||
|
|
||||||
/* If unable to allocate, just return NULL. */
|
/* If unable to allocate, just return NULL. */
|
||||||
@ -41,6 +42,11 @@ guac_pool* guac_pool_alloc(int size) {
|
|||||||
pool->__head = NULL;
|
pool->__head = NULL;
|
||||||
pool->__tail = 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;
|
return pool;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -57,6 +63,9 @@ void guac_pool_free(guac_pool* pool) {
|
|||||||
free(old);
|
free(old);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Destroy lock */
|
||||||
|
pthread_mutex_destroy(&(pool->__lock));
|
||||||
|
|
||||||
/* Free pool */
|
/* Free pool */
|
||||||
free(pool);
|
free(pool);
|
||||||
|
|
||||||
@ -66,11 +75,17 @@ int guac_pool_next_int(guac_pool* pool) {
|
|||||||
|
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
|
/* Acquire exclusive access */
|
||||||
|
pthread_mutex_lock(&(pool->__lock));
|
||||||
|
|
||||||
pool->active++;
|
pool->active++;
|
||||||
|
|
||||||
/* If more integers are needed, return a new one. */
|
/* If more integers are needed, return a new one. */
|
||||||
if (pool->__head == NULL || pool->__next_value < pool->min_size)
|
if (pool->__head == NULL || pool->__next_value < pool->min_size) {
|
||||||
return pool->__next_value++;
|
value = pool->__next_value++;
|
||||||
|
pthread_mutex_unlock(&(pool->__lock));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
/* Otherwise, remove first integer. */
|
/* Otherwise, remove first integer. */
|
||||||
value = pool->__head->value;
|
value = pool->__head->value;
|
||||||
@ -90,6 +105,7 @@ int guac_pool_next_int(guac_pool* pool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Return retrieved value. */
|
/* Return retrieved value. */
|
||||||
|
pthread_mutex_unlock(&(pool->__lock));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +116,9 @@ void guac_pool_free_int(guac_pool* pool, int value) {
|
|||||||
pool_int->value = value;
|
pool_int->value = value;
|
||||||
pool_int->__next = NULL;
|
pool_int->__next = NULL;
|
||||||
|
|
||||||
|
/* Acquire exclusive access */
|
||||||
|
pthread_mutex_lock(&(pool->__lock));
|
||||||
|
|
||||||
pool->active--;
|
pool->active--;
|
||||||
|
|
||||||
/* If pool empty, store as sole entry. */
|
/* 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;
|
pool->__tail = pool_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Value has been freed */
|
||||||
|
pthread_mutex_unlock(&(pool->__lock));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,14 +29,15 @@
|
|||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
||||||
#include <guacamole/socket.h>
|
#include <guacamole/socket.h>
|
||||||
|
#include <guacamole/user.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.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];
|
char mimetype[256];
|
||||||
|
|
||||||
/* Produce mimetype string from format info */
|
/* 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);
|
audio->bps, audio->rate, audio->channels);
|
||||||
|
|
||||||
/* Associate stream */
|
/* 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 */
|
/* Allocate and init encoder state */
|
||||||
audio->data = state = malloc(sizeof(raw_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);
|
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,
|
.begin_handler = raw_encoder_begin_handler,
|
||||||
.write_handler = raw_encoder_write_handler,
|
.write_handler = raw_encoder_write_handler,
|
||||||
.flush_handler = raw_encoder_flush_handler,
|
.flush_handler = raw_encoder_flush_handler,
|
||||||
|
.join_handler = raw_encoder_join_handler,
|
||||||
.end_handler = raw_encoder_end_handler
|
.end_handler = raw_encoder_end_handler
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -152,6 +167,7 @@ guac_audio_encoder _raw16_encoder = {
|
|||||||
.begin_handler = raw_encoder_begin_handler,
|
.begin_handler = raw_encoder_begin_handler,
|
||||||
.write_handler = raw_encoder_write_handler,
|
.write_handler = raw_encoder_write_handler,
|
||||||
.flush_handler = raw_encoder_flush_handler,
|
.flush_handler = raw_encoder_flush_handler,
|
||||||
|
.join_handler = raw_encoder_join_handler,
|
||||||
.end_handler = raw_encoder_end_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
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -25,9 +25,11 @@
|
|||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -37,16 +39,116 @@
|
|||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#endif
|
#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;
|
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,
|
/**
|
||||||
|
* The main write buffer. Bytes written go here before being flushed
|
||||||
|
* to the open file descriptor.
|
||||||
|
*/
|
||||||
|
char out_buf[GUAC_SOCKET_OUTPUT_BUFFER_SIZE];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock which is acquired when an instruction is being written, and
|
||||||
|
* released when the instruction is finished being written.
|
||||||
|
*/
|
||||||
|
pthread_mutex_t socket_lock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock which protects access to the internal buffer of this socket,
|
||||||
|
* guaranteeing atomicity of writes and flushes.
|
||||||
|
*/
|
||||||
|
pthread_mutex_t buffer_lock;
|
||||||
|
|
||||||
|
} guac_socket_fd_data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
const char* buffer = buf;
|
||||||
|
|
||||||
|
/* Write until completely written */
|
||||||
|
while (count > 0) {
|
||||||
|
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
/* MINGW32 WINSOCK only works with send() */
|
||||||
|
retval = send(data->fd, buf, count, 0);
|
||||||
|
#else
|
||||||
|
/* Use write() for all other platforms */
|
||||||
|
retval = write(data->fd, buf, count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Record errors in guac_error */
|
||||||
|
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) {
|
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;
|
||||||
|
|
||||||
/* Read from socket */
|
/* Read from socket */
|
||||||
int retval = read(data->fd, buf, count);
|
int retval = read(data->fd, buf, count);
|
||||||
@ -61,32 +163,182 @@ ssize_t __guac_socket_fd_read_handler(guac_socket* socket,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t __guac_socket_fd_write_handler(guac_socket* socket,
|
/**
|
||||||
const void* buf, size_t count) {
|
* 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;
|
||||||
int retval;
|
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
/* Flush remaining bytes in buffer */
|
||||||
/* MINGW32 WINSOCK only works with send() */
|
if (data->written > 0) {
|
||||||
retval = send(data->fd, buf, count, 0);
|
|
||||||
#else
|
|
||||||
/* Use write() for all other platforms */
|
|
||||||
retval = write(data->fd, buf, count);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Record errors in guac_error */
|
/* Write ALL bytes in buffer immediately */
|
||||||
if (retval < 0) {
|
if (guac_socket_fd_write(socket, data->out_buf, data->written))
|
||||||
guac_error = GUAC_STATUS_SEE_ERRNO;
|
return 1;
|
||||||
guac_error_message = "Error writing data to socket";
|
|
||||||
|
data->written = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int __guac_socket_fd_select_handler(guac_socket* socket, int usec_timeout) {
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
__guac_socket_fd_data* data = (__guac_socket_fd_data*) socket->data;
|
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;
|
fd_set fds;
|
||||||
struct timeval timeout;
|
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) {
|
guac_socket* guac_socket_open(int fd) {
|
||||||
|
|
||||||
|
pthread_mutexattr_t lock_attributes;
|
||||||
|
|
||||||
/* Allocate socket and associated data */
|
/* Allocate socket and associated data */
|
||||||
guac_socket* socket = guac_socket_alloc();
|
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 */
|
/* Store file descriptor as socket data */
|
||||||
data->fd = fd;
|
data->fd = fd;
|
||||||
|
data->written = 0;
|
||||||
socket->data = data;
|
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 */
|
/* Set read/write handlers */
|
||||||
socket->read_handler = __guac_socket_fd_read_handler;
|
socket->read_handler = guac_socket_fd_read_handler;
|
||||||
socket->write_handler = __guac_socket_fd_write_handler;
|
socket->write_handler = guac_socket_fd_write_handler;
|
||||||
socket->select_handler = __guac_socket_fd_select_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;
|
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) {
|
guac_socket* guac_socket_nest(guac_socket* parent, int index) {
|
||||||
|
|
||||||
/* Allocate socket and associated data */
|
/* Allocate socket and associated data */
|
||||||
@ -115,8 +136,9 @@ guac_socket* guac_socket_nest(guac_socket* parent, int index) {
|
|||||||
data->parent = parent;
|
data->parent = parent;
|
||||||
socket->data = data;
|
socket->data = data;
|
||||||
|
|
||||||
/* Set write handler */
|
/* Set write and free handlers */
|
||||||
socket->write_handler = __guac_socket_nest_write_handler;
|
socket->write_handler = __guac_socket_nest_write_handler;
|
||||||
|
socket->free_handler = __guac_socket_nest_free_handler;
|
||||||
|
|
||||||
return socket;
|
return socket;
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -138,7 +139,6 @@ int guac_socket_select(guac_socket* socket, int usec_timeout) {
|
|||||||
|
|
||||||
guac_socket* guac_socket_alloc() {
|
guac_socket* guac_socket_alloc() {
|
||||||
|
|
||||||
pthread_mutexattr_t lock_attributes;
|
|
||||||
guac_socket* socket = malloc(sizeof(guac_socket));
|
guac_socket* socket = malloc(sizeof(guac_socket));
|
||||||
|
|
||||||
/* If no memory available, return with error */
|
/* If no memory available, return with error */
|
||||||
@ -149,46 +149,28 @@ guac_socket* guac_socket_alloc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
socket->__ready = 0;
|
socket->__ready = 0;
|
||||||
socket->__written = 0;
|
|
||||||
socket->data = NULL;
|
socket->data = NULL;
|
||||||
socket->state = GUAC_SOCKET_OPEN;
|
socket->state = GUAC_SOCKET_OPEN;
|
||||||
socket->last_write_timestamp = guac_timestamp_current();
|
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 */
|
/* No keep alive ping by default */
|
||||||
socket->__keep_alive_enabled = 0;
|
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 */
|
/* No handlers yet */
|
||||||
socket->read_handler = NULL;
|
socket->read_handler = NULL;
|
||||||
socket->write_handler = NULL;
|
socket->write_handler = NULL;
|
||||||
socket->select_handler = NULL;
|
socket->select_handler = NULL;
|
||||||
socket->free_handler = NULL;
|
socket->free_handler = NULL;
|
||||||
|
socket->flush_handler = NULL;
|
||||||
|
socket->lock_handler = NULL;
|
||||||
|
socket->unlock_handler = NULL;
|
||||||
|
|
||||||
return socket;
|
return socket;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_socket_require_threadsafe(guac_socket* socket) {
|
|
||||||
socket->__threadsafe_instructions = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void guac_socket_require_keep_alive(guac_socket* socket) {
|
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 */
|
/* Start keep-alive thread */
|
||||||
socket->__keep_alive_enabled = 1;
|
socket->__keep_alive_enabled = 1;
|
||||||
pthread_create(&(socket->__keep_alive_thread), NULL,
|
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) {
|
void guac_socket_instruction_begin(guac_socket* socket) {
|
||||||
|
|
||||||
/* Lock writes if threadsafety enabled */
|
/* Call instruction begin handler if defined */
|
||||||
if (socket->__threadsafe_instructions)
|
if (socket->lock_handler)
|
||||||
pthread_mutex_lock(&(socket->__instruction_write_lock));
|
socket->lock_handler(socket);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_socket_instruction_end(guac_socket* socket) {
|
void guac_socket_instruction_end(guac_socket* socket) {
|
||||||
|
|
||||||
/* Unlock writes if threadsafety enabled */
|
/* Call instruction end handler if defined */
|
||||||
if (socket->__threadsafe_instructions)
|
if (socket->unlock_handler)
|
||||||
pthread_mutex_unlock(&(socket->__instruction_write_lock));
|
socket->unlock_handler(socket);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_socket_free(guac_socket* socket) {
|
void guac_socket_free(guac_socket* socket) {
|
||||||
|
|
||||||
|
guac_socket_flush(socket);
|
||||||
|
|
||||||
/* Call free handler if defined */
|
/* Call free handler if defined */
|
||||||
if (socket->free_handler)
|
if (socket->free_handler)
|
||||||
socket->free_handler(socket);
|
socket->free_handler(socket);
|
||||||
|
|
||||||
guac_socket_flush(socket);
|
|
||||||
|
|
||||||
/* Mark as closed */
|
/* Mark as closed */
|
||||||
socket->state = GUAC_SOCKET_CLOSED;
|
socket->state = GUAC_SOCKET_CLOSED;
|
||||||
|
|
||||||
@ -243,91 +209,92 @@ void guac_socket_free(guac_socket* socket) {
|
|||||||
if (socket->__keep_alive_enabled)
|
if (socket->__keep_alive_enabled)
|
||||||
pthread_join(socket->__keep_alive_thread, NULL);
|
pthread_join(socket->__keep_alive_thread, NULL);
|
||||||
|
|
||||||
pthread_mutex_destroy(&(socket->__instruction_write_lock));
|
|
||||||
free(socket);
|
free(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t guac_socket_write_int(guac_socket* socket, int64_t i) {
|
ssize_t guac_socket_write_int(guac_socket* socket, int64_t i) {
|
||||||
|
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
snprintf(buffer, sizeof(buffer), "%"PRIi64, i);
|
int length;
|
||||||
return guac_socket_write_string(socket, buffer);
|
|
||||||
|
/* 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) {
|
ssize_t guac_socket_write_string(guac_socket* socket, const char* str) {
|
||||||
|
|
||||||
char* __out_buf = socket->__out_buf;
|
/* Write contents of string */
|
||||||
|
if (guac_socket_write(socket, str, strlen(str)))
|
||||||
|
return 1;
|
||||||
|
|
||||||
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);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket->__written = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
guac_socket_update_buffer_end(socket);
|
|
||||||
return 0;
|
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 */
|
/* Byte 0:[AAAAAA] AABBBB BBBBCC CCCCCC */
|
||||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[(a & 0xFC) >> 2]; /* [AAAAAA]AABBBB BBBBCC CCCCCC */
|
output[0] = __guac_socket_BASE64_CHARACTERS[(a & 0xFC) >> 2];
|
||||||
|
|
||||||
if (b >= 0) {
|
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) {
|
if (c >= 0) {
|
||||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[((b & 0x0F) << 2) | ((c & 0xC0) >> 6)]; /* AAAAAA AABBBB[BBBBCC]CCCCCC */
|
output[2] = __guac_socket_BASE64_CHARACTERS[((b & 0x0F) << 2) | ((c & 0xC0) >> 6)];
|
||||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[c & 0x3F]; /* AAAAAA AABBBB BBBBCC[CCCCCC] */
|
output[3] = __guac_socket_BASE64_CHARACTERS[c & 0x3F];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bytes 2 and 3, one character of padding:
|
||||||
|
*
|
||||||
|
* AAAAAA AABBBB [BBBB--] ------
|
||||||
|
* AAAAAA AABBBB BBBB-- [------]
|
||||||
|
*/
|
||||||
else {
|
else {
|
||||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[((b & 0x0F) << 2)]; /* AAAAAA AABBBB[BBBB--]------ */
|
output[2] = __guac_socket_BASE64_CHARACTERS[((b & 0x0F) << 2)];
|
||||||
__out_buf[socket->__written++] = '='; /* AAAAAA AABBBB BBBB--[------] */
|
output[3] = '=';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bytes 1, 2, and 3, two characters of padding:
|
||||||
|
*
|
||||||
|
* AAAAAA [AA----] ------ ------
|
||||||
|
* AAAAAA AA---- [------] ------
|
||||||
|
* AAAAAA AA---- ------ [------]
|
||||||
|
*/
|
||||||
else {
|
else {
|
||||||
__out_buf[socket->__written++] = __guac_socket_BASE64_CHARACTERS[((a & 0x03) << 4)]; /* AAAAAA[AA----]------ ------ */
|
output[1] = __guac_socket_BASE64_CHARACTERS[((a & 0x03) << 4)];
|
||||||
__out_buf[socket->__written++] = '='; /* AAAAAA AA----[------]------ */
|
output[2] = '=';
|
||||||
__out_buf[socket->__written++] = '='; /* AAAAAA AA---- ------[------] */
|
output[3] = '=';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* At this point, 4 bytes have been socket->__written */
|
/* At this point, 4 base64 bytes have been written */
|
||||||
|
if (guac_socket_write(socket, output, 4))
|
||||||
/* Flush when necessary, return on error */
|
return -1;
|
||||||
if (socket->__written > GUAC_SOCKET_OUTPUT_BUFFER_SIZE - 4) {
|
|
||||||
|
|
||||||
if (guac_socket_write(socket, __out_buf, socket->__written))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
socket->__written = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* If no second byte was provided, only one byte was written */
|
||||||
if (b < 0)
|
if (b < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
/* If no third byte was provided, only two bytes were written */
|
||||||
if (c < 0)
|
if (c < 0)
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
|
/* Otherwise, three bytes were written */
|
||||||
return 3;
|
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* char_buf = (const unsigned char*) buf;
|
||||||
const unsigned char* end = char_buf + count;
|
const unsigned char* end = char_buf + count;
|
||||||
|
|
||||||
guac_socket_update_buffer_begin(socket);
|
|
||||||
while (char_buf < end) {
|
while (char_buf < end) {
|
||||||
|
|
||||||
retval = __guac_socket_write_base64_byte(socket, *(char_buf++));
|
retval = __guac_socket_write_base64_byte(socket, *(char_buf++));
|
||||||
if (retval < 0) {
|
if (retval < 0)
|
||||||
guac_socket_update_buffer_end(socket);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guac_socket_update_buffer_end(socket);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t guac_socket_flush(guac_socket* socket) {
|
ssize_t guac_socket_flush(guac_socket* socket) {
|
||||||
|
|
||||||
/* Flush remaining bytes in buffer */
|
/* If handler defined, call it. */
|
||||||
guac_socket_update_buffer_begin(socket);
|
if (socket->flush_handler)
|
||||||
if (socket->__written > 0) {
|
return socket->flush_handler(socket);
|
||||||
|
|
||||||
if (guac_socket_write(socket, socket->__out_buf, socket->__written)) {
|
/* Otherwise, do nothing */
|
||||||
guac_socket_update_buffer_end(socket);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket->__written = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
guac_socket_update_buffer_end(socket);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -399,18 +354,14 @@ ssize_t guac_socket_flush_base64(guac_socket* socket) {
|
|||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
/* Flush triplet to output buffer */
|
/* Flush triplet to output buffer */
|
||||||
guac_socket_update_buffer_begin(socket);
|
|
||||||
while (socket->__ready > 0) {
|
while (socket->__ready > 0) {
|
||||||
|
|
||||||
retval = __guac_socket_write_base64_byte(socket, -1);
|
retval = __guac_socket_write_base64_byte(socket, -1);
|
||||||
if (retval < 0) {
|
if (retval < 0)
|
||||||
guac_socket_update_buffer_end(socket);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guac_socket_update_buffer_end(socket);
|
|
||||||
return 0;
|
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
|
#ifndef _GUAC_USER_HANDLERS__H
|
||||||
#define _GUAC_CLIENT_HANDLERS__H
|
#define _GUAC_USER_HANDLERS__H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides initial handler functions and a lookup structure for automatically
|
* Provides initial handler functions and a lookup structure for automatically
|
||||||
* handling client instructions. This is used only internally within libguac,
|
* handling instructions received from each user. This is used only internally
|
||||||
* and is not installed along with the library.
|
* within libguac, and is not installed along with the library.
|
||||||
*
|
*
|
||||||
* @file client-handlers.h
|
* @file user-handlers.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "client.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.
|
* 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
|
* is received, this handler will be called. Sync instructions are automatically
|
||||||
* handled, thus there is no client handler for sync instruction.
|
* 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
|
* Internal initial handler for the mouse instruction. When a mouse instruction
|
||||||
* is received, this handler will be called. The client's mouse handler will
|
* is received, this handler will be called. The client's mouse handler will
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the key instruction. When a key instruction
|
||||||
* is received, this handler will be called. The client's key handler will
|
* is received, this handler will be called. The client's key handler will
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the clipboard instruction. When a clipboard
|
||||||
* is received, this handler will be called. The client's clipboard handler will
|
* instruction is received, this handler will be called. The client's clipboard
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the file instruction. When a file instruction
|
||||||
* is received, this handler will be called. The client's file handler will
|
* is received, this handler will be called. The client's file handler will
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the pipe instruction. When a pipe instruction
|
||||||
* is received, this handler will be called. The client's pipe handler will
|
* is received, this handler will be called. The client's pipe handler will
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the ack instruction. When a ack instruction
|
||||||
* is received, this handler will be called. The client's ack handler will
|
* is received, this handler will be called. The client's ack handler will
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the blob instruction. When a blob instruction
|
||||||
* is received, this handler will be called. The client's blob handler will
|
* is received, this handler will be called. The client's blob handler will
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the end instruction. When a end instruction
|
||||||
* is received, this handler will be called. The client's end handler will
|
* is received, this handler will be called. The client's end handler will
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the get instruction. When a get instruction
|
||||||
* is received, this handler will be called. The client's get handler will
|
* is received, this handler will be called. The client's get handler will
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the put instruction. When a put instruction
|
||||||
* is received, this handler will be called. The client's put handler will
|
* is received, this handler will be called. The client's put handler will
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the size instruction. When a size instruction
|
||||||
* is received, this handler will be called. The client's size handler will
|
* is received, this handler will be called. The client's size handler will
|
||||||
* be invoked if defined.
|
* 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
|
* Internal initial handler for the disconnect instruction. When a disconnect
|
||||||
* is received, this handler will be called. Disconnect instructions are automatically
|
* instruction is received, this handler will be called. Disconnect
|
||||||
* handled, thus there is no client handler for disconnect instruction.
|
* 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
|
* 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 = \
|
noinst_HEADERS = \
|
||||||
client/client_suite.h \
|
client/client_suite.h \
|
||||||
common/common_suite.h \
|
|
||||||
protocol/suite.h \
|
protocol/suite.h \
|
||||||
util/util_suite.h
|
util/util_suite.h
|
||||||
|
|
||||||
@ -37,10 +36,6 @@ test_libguac_SOURCES = \
|
|||||||
client/client_suite.c \
|
client/client_suite.c \
|
||||||
client/buffer_pool.c \
|
client/buffer_pool.c \
|
||||||
client/layer_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/suite.c \
|
||||||
protocol/base64_decode.c \
|
protocol/base64_decode.c \
|
||||||
protocol/instruction_parse.c \
|
protocol/instruction_parse.c \
|
||||||
@ -53,11 +48,9 @@ test_libguac_SOURCES = \
|
|||||||
|
|
||||||
test_libguac_CFLAGS = \
|
test_libguac_CFLAGS = \
|
||||||
-Werror -Wall -pedantic \
|
-Werror -Wall -pedantic \
|
||||||
@COMMON_INCLUDE@ \
|
|
||||||
@LIBGUAC_INCLUDE@
|
@LIBGUAC_INCLUDE@
|
||||||
|
|
||||||
test_libguac_LDADD = \
|
test_libguac_LDADD = \
|
||||||
@COMMON_LTLIB@ \
|
|
||||||
@CUNIT_LIBS@ \
|
@CUNIT_LIBS@ \
|
||||||
@LIBGUAC_LTLIB@
|
@LIBGUAC_LTLIB@
|
||||||
|
|
||||||
|
@ -29,13 +29,13 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <CUnit/Basic.h>
|
#include <CUnit/Basic.h>
|
||||||
#include <guacamole/instruction.h>
|
#include <guacamole/parser.h>
|
||||||
|
|
||||||
void test_instruction_parse() {
|
void test_instruction_parse() {
|
||||||
|
|
||||||
/* Allocate instruction space */
|
/* Allocate parser */
|
||||||
guac_instruction* instruction = guac_instruction_alloc();
|
guac_parser* parser = guac_parser_alloc();
|
||||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction);
|
CU_ASSERT_PTR_NOT_NULL_FATAL(parser);
|
||||||
|
|
||||||
/* Instruction input */
|
/* Instruction input */
|
||||||
char buffer[] = "4.test,8.testdata,5.zxcvb,13.guacamoletest;XXXXXXXXXXXXXXXXXX";
|
char buffer[] = "4.test,8.testdata,5.zxcvb,13.guacamoletest;XXXXXXXXXXXXXXXXXX";
|
||||||
@ -46,7 +46,7 @@ void test_instruction_parse() {
|
|||||||
while (remaining > 18) {
|
while (remaining > 18) {
|
||||||
|
|
||||||
/* Parse more data */
|
/* Parse more data */
|
||||||
int parsed = guac_instruction_append(instruction, current, remaining);
|
int parsed = guac_parser_append(parser, current, remaining);
|
||||||
if (parsed == 0)
|
if (parsed == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -56,24 +56,24 @@ void test_instruction_parse() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CU_ASSERT_EQUAL(remaining, 18);
|
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 */
|
/* Parse is complete - no more data should be read */
|
||||||
CU_ASSERT_EQUAL(guac_instruction_append(instruction, current, 18), 0);
|
CU_ASSERT_EQUAL(guac_parser_append(parser, current, 18), 0);
|
||||||
CU_ASSERT_EQUAL(instruction->state, GUAC_INSTRUCTION_PARSE_COMPLETE);
|
CU_ASSERT_EQUAL(parser->state, GUAC_PARSE_COMPLETE);
|
||||||
|
|
||||||
/* Validate resulting structure */
|
/* Validate resulting structure */
|
||||||
CU_ASSERT_EQUAL(instruction->argc, 3);
|
CU_ASSERT_EQUAL(parser->argc, 3);
|
||||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->opcode);
|
CU_ASSERT_PTR_NOT_NULL_FATAL(parser->opcode);
|
||||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->argv[0]);
|
CU_ASSERT_PTR_NOT_NULL_FATAL(parser->argv[0]);
|
||||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->argv[1]);
|
CU_ASSERT_PTR_NOT_NULL_FATAL(parser->argv[1]);
|
||||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction->argv[2]);
|
CU_ASSERT_PTR_NOT_NULL_FATAL(parser->argv[2]);
|
||||||
|
|
||||||
/* Validate resulting content */
|
/* Validate resulting content */
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->opcode, "test");
|
CU_ASSERT_STRING_EQUAL(parser->opcode, "test");
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->argv[0], "testdata");
|
CU_ASSERT_STRING_EQUAL(parser->argv[0], "testdata");
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->argv[1], "zxcvb");
|
CU_ASSERT_STRING_EQUAL(parser->argv[1], "zxcvb");
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->argv[2], "guacamoletest");
|
CU_ASSERT_STRING_EQUAL(parser->argv[2], "guacamoletest");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
#include <CUnit/Basic.h>
|
#include <CUnit/Basic.h>
|
||||||
#include <guacamole/error.h>
|
#include <guacamole/error.h>
|
||||||
#include <guacamole/instruction.h>
|
#include <guacamole/parser.h>
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
||||||
#include <guacamole/socket.h>
|
#include <guacamole/socket.h>
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ void test_instruction_read() {
|
|||||||
else {
|
else {
|
||||||
|
|
||||||
guac_socket* socket;
|
guac_socket* socket;
|
||||||
guac_instruction* instruction;
|
guac_parser* parser;
|
||||||
|
|
||||||
close(wfd);
|
close(wfd);
|
||||||
|
|
||||||
@ -79,28 +79,30 @@ void test_instruction_read() {
|
|||||||
socket = guac_socket_open(rfd);
|
socket = guac_socket_open(rfd);
|
||||||
CU_ASSERT_PTR_NOT_NULL_FATAL(socket);
|
CU_ASSERT_PTR_NOT_NULL_FATAL(socket);
|
||||||
|
|
||||||
|
/* Allocate parser */
|
||||||
|
parser = guac_parser_alloc();
|
||||||
|
CU_ASSERT_PTR_NOT_NULL_FATAL(parser);
|
||||||
|
|
||||||
/* Read instruction */
|
/* Read instruction */
|
||||||
instruction = guac_instruction_read(socket, 1000000);
|
CU_ASSERT_EQUAL_FATAL(guac_parser_read(parser, socket, 1000000), 0);
|
||||||
CU_ASSERT_PTR_NOT_NULL_FATAL(instruction);
|
|
||||||
|
|
||||||
/* Validate contents */
|
/* Validate contents */
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->opcode, "test");
|
CU_ASSERT_STRING_EQUAL(parser->opcode, "test");
|
||||||
CU_ASSERT_EQUAL_FATAL(instruction->argc, 3);
|
CU_ASSERT_EQUAL_FATAL(parser->argc, 3);
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->argv[0], "a" UTF8_4 "b");
|
CU_ASSERT_STRING_EQUAL(parser->argv[0], "a" UTF8_4 "b");
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->argv[1], "12345");
|
CU_ASSERT_STRING_EQUAL(parser->argv[1], "12345");
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->argv[2], "a" UTF8_8 "c");
|
CU_ASSERT_STRING_EQUAL(parser->argv[2], "a" UTF8_8 "c");
|
||||||
|
|
||||||
/* Read another instruction */
|
/* Read another instruction */
|
||||||
guac_instruction_free(instruction);
|
CU_ASSERT_EQUAL_FATAL(guac_parser_read(parser, socket, 1000000), 0);
|
||||||
instruction = guac_instruction_read(socket, 1000000);
|
|
||||||
|
|
||||||
/* Validate contents */
|
/* Validate contents */
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->opcode, "test2");
|
CU_ASSERT_STRING_EQUAL(parser->opcode, "test2");
|
||||||
CU_ASSERT_EQUAL_FATAL(instruction->argc, 2);
|
CU_ASSERT_EQUAL_FATAL(parser->argc, 2);
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->argv[0], "hellohello");
|
CU_ASSERT_STRING_EQUAL(parser->argv[0], "hellohello");
|
||||||
CU_ASSERT_STRING_EQUAL(instruction->argv[1], "worldworldworld");
|
CU_ASSERT_STRING_EQUAL(parser->argv[1], "worldworldworld");
|
||||||
|
|
||||||
guac_instruction_free(instruction);
|
guac_parser_free(parser);
|
||||||
guac_socket_free(socket);
|
guac_socket_free(socket);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
|
|
||||||
#include <CUnit/Basic.h>
|
#include <CUnit/Basic.h>
|
||||||
#include <guacamole/error.h>
|
#include <guacamole/error.h>
|
||||||
#include <guacamole/instruction.h>
|
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
||||||
#include <guacamole/socket.h>
|
#include <guacamole/socket.h>
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
|
|
||||||
#include <CUnit/Basic.h>
|
#include <CUnit/Basic.h>
|
||||||
#include <guacamole/error.h>
|
#include <guacamole/error.h>
|
||||||
#include <guacamole/instruction.h>
|
|
||||||
#include <guacamole/protocol.h>
|
#include <guacamole/protocol.h>
|
||||||
#include <guacamole/socket.h>
|
#include <guacamole/socket.h>
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ int main() {
|
|||||||
register_protocol_suite();
|
register_protocol_suite();
|
||||||
register_client_suite();
|
register_client_suite();
|
||||||
register_util_suite();
|
register_util_suite();
|
||||||
register_common_suite();
|
|
||||||
|
|
||||||
/* Run tests */
|
/* Run tests */
|
||||||
CU_basic_set_mode(CU_BRM_VERBOSE);
|
CU_basic_set_mode(CU_BRM_VERBOSE);
|
||||||
|
Loading…
Reference in New Issue
Block a user