Compare commits

...

35 Commits

Author SHA1 Message Date
Virtually Nick
47b9360d46
GUACAMOLE-1714: Merge update guacenc for const parameters/values introduced in FFmpeg 5.0. 2023-02-02 20:36:32 -05:00
Mike Jumper
98c2a6adcb
GUACAMOLE-377: Merge correction ensuring users receive a proper frame boundary when joining. 2023-01-24 14:25:09 -08:00
Alex Leitner
3b0a9bac75 GUACAMOLE-377: Send a sync instruction to users when synchronizing surfaces. 2023-01-23 20:55:01 +00:00
Mike Jumper
f6893ed319 Merge 1.5.0 changes back to master. 2023-01-10 21:54:05 -08:00
James Muehlner
a5214c971a
GUACAMOLE-1604: Merge version number bumps for 1.5.0. 2023-01-10 17:16:43 -08:00
Mike Jumper
ccfcef8c0f GUACAMOLE-1604: Add explicit libtool version info for libguac-terminal. 2023-01-10 17:08:15 -08:00
Mike Jumper
1a7a57ed19 GUACAMOLE-1604: Update libtool version info for libguac (interfaces added and changed).
The only changed interface here is the guac_user_info struct, which now
has a "name" member.
2023-01-10 17:07:16 -08:00
Mike Jumper
eac064bde9 GUACAMOLE-1604: Bump version number to 1.5.0. 2023-01-10 17:02:06 -08:00
Virtually Nick
4afc1d85ce Merge 1.5.0 changes back to master. 2023-01-04 15:53:11 -05:00
Virtually Nick
818b5f79df
GUACAMOLE-1538: Merge add missing documentation for libguac-terminal. 2023-01-04 15:51:05 -05:00
Mike Jumper
8ef60bfa9d GUACAMOLE-1538: Document parameters of libguac-terminal handlers. 2023-01-04 12:40:21 -08:00
Mike Jumper
d90e0e97fe GUACAMOLE-1538: Add missing documentation for libguac-terminal functions. 2023-01-04 12:22:02 -08:00
Mike Jumper
ec7964e8fb GUACAMOLE-1538: Return number of bytes written for guac_terminal_write() and guac_terminal_printf(). 2023-01-04 12:05:02 -08:00
James Muehlner
add7ce361b Merge 1.5.0 changes back to master. 2022-11-29 03:46:57 +00:00
James Muehlner
7d16f67d6d
GUACAMOLE-1293: Merge fix for double acquisition/release of rwlock. 2022-11-28 14:31:44 -08:00
Mike Jumper
e3adb97085 GUACAMOLE-1293: Do not re-acquire __users_lock while already held for writing.
Per POSIX spec, the behavior of acquiring a read lock on a rwlock that's
already acquired for writing is undefined. From the documentation for
pthread_rwlock_rdlock():

"... Results are undefined if the calling thread holds a write lock on
rwlock at the time the call is made."
2022-11-28 13:37:41 -08:00
Mike Jumper
55941823ec Merge 1.5.0 changes back to master. 2022-11-25 23:24:08 -08:00
Mike Jumper
07acce8a76
GUACAMOLE-1293: Merge support for notifying when a user has joined/left a connection. 2022-11-25 23:23:13 -08:00
Virtually Nick
5b1677f21a GUACAMOLE-1293: Fix copy-pasta and style issues; add user ID to information passed to client. 2022-11-25 21:57:44 -05:00
Virtually Nick
623c398005 GUACAMOLE-1293: Change new user info member to simply "name" to clarify its purpose. 2022-11-24 18:13:06 -05:00
Virtually Nick
aa92239edd GUACAMOLE-1293: Rename new enum values to be more consistent with existing code. 2022-11-08 07:45:38 -05:00
Virtually Nick
897712c743 GUACAMOLE-1293: Update and add debug logging. 2022-11-08 07:45:38 -05:00
Virtually Nick
02b24d0101 GUACAMOLE-1293: Simplify the assignment of strings/constants. 2022-11-08 07:45:38 -05:00
Virtually Nick
26eadc37a3 GUACAMOLE-1293: Move to status code plus arguments for msg instruction. 2022-11-08 07:45:38 -05:00
Virtually Nick
6d7156bc70 GUACAMOLE-1293: Update struct member that stores human-readable name. 2022-11-08 07:45:38 -05:00
Virtually Nick
6312e1720d GUACAMOLE-1293: Add support for notifying owner of users joining and leaving. 2022-11-08 07:45:38 -05:00
Virtually Nick
cb7ae25177 GUACAMOLE-1293: Add support for the name handshake instruction. 2022-11-08 07:45:38 -05:00
Virtually Nick
a4adb3f5c0 GUACAMOLE-1293: Add protocol support for msg instruction. 2022-11-08 07:45:38 -05:00
Dan Fandrich
5cf408ebbb GUACAMOLE-1714: Adapt to const parameters of ffmpeg 5.0. 2022-11-07 12:16:35 -08:00
Mike Jumper
3ca6bb0a61
GUACAMOLE-1708: Merge correction to missing Czech keyboard character mapping. 2022-11-06 09:05:52 -08:00
Max
457a169c49 GUACAMOLE-1708: Added Czech keyboard keymap fix missing char 2022-11-06 12:17:21 +01:00
Mike Jumper
bad381cebe
GUACAMOLE-1708: Merge RDP support for Czech keyboard layout. 2022-11-05 08:56:56 -07:00
Max
6171da6d0b GUACAMOLE-1708: Added Czech keyboard keymap for RDP 2022-11-01 21:56:23 +01:00
Mike Jumper
067f2a91a0
GUACAMOLE-1682: Merge automatic newline normalization of terminal clipboard. 2022-10-18 12:41:48 -07:00
Alex Leitner
bc52485570 GUACAMOLE-1682: Normalize conflicting newline encodings in clipboards between Linux and Windows systems for ssh sessions. 2022-10-18 19:38:56 +00:00
25 changed files with 569 additions and 40 deletions

View File

@ -117,7 +117,7 @@ error() {
## ##
usage() { usage() {
cat >&2 <<END cat >&2 <<END
guacctl 1.4.0, Apache Guacamole terminal session control utility. guacctl 1.5.0, Apache Guacamole terminal session control utility.
Usage: guacctl [OPTION] [FILE or NAME]... Usage: guacctl [OPTION] [FILE or NAME]...
-d, --download download each of the files listed. -d, --download download each of the files listed.

View File

@ -18,7 +18,7 @@
# #
AC_PREREQ([2.61]) AC_PREREQ([2.61])
AC_INIT([guacamole-server], [1.4.0]) AC_INIT([guacamole-server], [1.5.0])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])

View File

@ -166,6 +166,8 @@ void guac_common_display_free(guac_common_display* display) {
void guac_common_display_dup(guac_common_display* display, guac_user* user, void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_socket* socket) { guac_socket* socket) {
guac_client* client = user->client;
pthread_mutex_lock(&display->_lock); pthread_mutex_lock(&display->_lock);
/* Sunchronize shared cursor */ /* Sunchronize shared cursor */
@ -178,6 +180,9 @@ void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_common_display_dup_layers(display->layers, user, socket); guac_common_display_dup_layers(display->layers, user, socket);
guac_common_display_dup_layers(display->buffers, user, socket); guac_common_display_dup_layers(display->buffers, user, socket);
/* Sends a sync instruction to mark the boundary of the first frame */
guac_protocol_send_sync(socket, client->last_sent_timestamp, 1);
pthread_mutex_unlock(&display->_lock); pthread_mutex_unlock(&display->_lock);
} }
@ -384,4 +389,3 @@ void guac_common_display_free_buffer(guac_common_display* display,
pthread_mutex_unlock(&display->_lock); pthread_mutex_unlock(&display->_lock);
} }

View File

@ -213,7 +213,7 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
#endif #endif
} }
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec, AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, const AVCodec* codec,
int bitrate, int width, int height, int gop_size, int qmax, int qmin, int bitrate, int width, int height, int gop_size, int qmax, int qmin,
int pix_fmt, AVRational time_base) { int pix_fmt, AVRational time_base) {
@ -249,7 +249,7 @@ AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec,
} }
int guacenc_open_avcodec(AVCodecContext *avcodec_context, int guacenc_open_avcodec(AVCodecContext *avcodec_context,
AVCodec *codec, AVDictionary **options, const AVCodec *codec, AVDictionary **options,
AVStream* stream) { AVStream* stream) {
int ret = avcodec_open2(avcodec_context, codec, options); int ret = avcodec_open2(avcodec_context, codec, options);

View File

@ -128,7 +128,7 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame);
* The pointer to the configured AVCodecContext. * The pointer to the configured AVCodecContext.
* *
*/ */
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec, AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, const AVCodec* codec,
int bitrate, int width, int height, int gop_size, int qmax, int qmin, int bitrate, int width, int height, int gop_size, int qmax, int qmin,
int pix_fmt, AVRational time_base); int pix_fmt, AVRational time_base);
@ -158,7 +158,7 @@ AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec,
* Zero on success, a negative value on error. * Zero on success, a negative value on error.
*/ */
int guacenc_open_avcodec(AVCodecContext *avcodec_context, int guacenc_open_avcodec(AVCodecContext *avcodec_context,
AVCodec *codec, AVDictionary **options, const AVCodec *codec, AVDictionary **options,
AVStream* stream); AVStream* stream);
#endif #endif

View File

@ -47,7 +47,7 @@
guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name, guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
int width, int height, int bitrate) { int width, int height, int bitrate) {
AVOutputFormat *container_format; const AVOutputFormat *container_format;
AVFormatContext *container_format_context; AVFormatContext *container_format_context;
AVStream *video_stream; AVStream *video_stream;
int ret; int ret;
@ -63,7 +63,7 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
container_format = container_format_context->oformat; container_format = container_format_context->oformat;
/* Pull codec based on name */ /* Pull codec based on name */
AVCodec* codec = avcodec_find_encoder_by_name(codec_name); const AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
if (codec == NULL) { if (codec == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to locate codec \"%s\".", guacenc_log(GUAC_LOG_ERROR, "Failed to locate codec \"%s\".",
codec_name); codec_name);

View File

@ -139,7 +139,7 @@ libguac_la_CFLAGS = \
-Werror -Wall -pedantic -Werror -Wall -pedantic
libguac_la_LDFLAGS = \ libguac_la_LDFLAGS = \
-version-info 20:0:0 \ -version-info 21:0:0 \
-no-undefined \ -no-undefined \
@CAIRO_LIBS@ \ @CAIRO_LIBS@ \
@DL_LIBS@ \ @DL_LIBS@ \

View File

@ -307,6 +307,10 @@ int guac_client_add_user(guac_client* client, guac_user* user, int argc, char**
pthread_rwlock_unlock(&(client->__users_lock)); pthread_rwlock_unlock(&(client->__users_lock));
/* Notify owner of user joining connection. */
if (retval == 0 && !user->owner)
guac_client_owner_notify_join(client, user);
return retval; return retval;
} }
@ -333,6 +337,10 @@ void guac_client_remove_user(guac_client* client, guac_user* user) {
pthread_rwlock_unlock(&(client->__users_lock)); pthread_rwlock_unlock(&(client->__users_lock));
/* Update owner of user having left the connection. */
if (!user->owner)
guac_client_owner_notify_leave(client, user);
/* Call handler, if defined */ /* Call handler, if defined */
if (user->leave_handler) if (user->leave_handler)
user->leave_handler(user); user->leave_handler(user);
@ -675,6 +683,36 @@ static void* __webp_support_callback(guac_user* user, void* data) {
} }
#endif #endif
/**
* A callback function which is invoked by guac_client_owner_supports_msg()
* to determine if the owner of a client supports the "msg" instruction,
* returning zero if the user does not support the instruction or non-zero if
* the user supports it.
*
* @param user
* The guac_user that will be checked for "msg" instruction support.
*
* @param data
* Data provided to the callback. This value is never used within this
* callback.
*
* @return
* A non-zero integer if the provided user who owns the connection supports
* the "msg" instruction, or zero if the user does not. The integer is cast
* as a void*.
*/
static void* guac_owner_supports_msg_callback(guac_user* user, void* data) {
return (void*) ((intptr_t) guac_user_supports_msg(user));
}
int guac_client_owner_supports_msg(guac_client* client) {
return (int) ((intptr_t) guac_client_for_owner(client, guac_owner_supports_msg_callback, NULL));
}
/** /**
* A callback function which is invoked by guac_client_owner_supports_required() * A callback function which is invoked by guac_client_owner_supports_required()
* to determine if the owner of a client supports the "required" instruction, * to determine if the owner of a client supports the "required" instruction,
@ -705,6 +743,124 @@ int guac_client_owner_supports_required(guac_client* client) {
} }
/**
* A callback function that is invokved by guac_client_owner_notify_join() to
* notify the owner of a connection that another user has joined the
* connection, returning zero if the message is sent successfully, or non-zero
* if an error occurs.
*
* @param user
* The user to send the notification to, which will be the owner of the
* connection.
*
* @param data
* The data provided to the callback, which is the user that is joining the
* connection.
*
* @return
* Zero if the message is sent successfully to the owner, otherwise
* non-zero, cast as a void*.
*/
static void* guac_client_owner_notify_join_callback(guac_user* user, void* data) {
const guac_user* joiner = (const guac_user *) data;
if (user == NULL)
return (void*) ((intptr_t) -1);
char* log_owner = "owner";
if (user->info.name != NULL)
log_owner = (char *) user->info.name;
char* log_joiner = "anonymous";
char* send_joiner = "";
if (joiner->info.name != NULL) {
log_joiner = (char *) joiner->info.name;
send_joiner = (char *) joiner->info.name;
}
guac_user_log(user, GUAC_LOG_DEBUG, "Notifying owner \"%s\" of \"%s\" joining.",
log_owner, log_joiner);
/* Send user joined notification to owner. */
const char* args[] = { (const char*)joiner->user_id, (const char*)send_joiner, NULL };
return (void*) ((intptr_t) guac_protocol_send_msg(user->socket, GUAC_MESSAGE_USER_JOINED, args));
}
int guac_client_owner_notify_join(guac_client* client, guac_user* joiner) {
/* Don't send msg instruction if client does not support it. */
if (!guac_client_owner_supports_msg(client)) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Client does not support the \"msg\" instruction and "
"will not be notified of the user joining the connection.");
return -1;
}
return (int) ((intptr_t) guac_client_for_owner(client, guac_client_owner_notify_join_callback, joiner));
}
/**
* A callback function that is invokved by guac_client_owner_notify_leave() to
* notify the owner of a connection that another user has left the connection,
* returning zero if the message is sent successfully, or non-zero
* if an error occurs.
*
* @param user
* The user to send the notification to, which will be the owner of the
* connection.
*
* @param data
* The data provided to the callback, which is the user that is leaving the
* connection.
*
* @return
* Zero if the message is sent successfully to the owner, otherwise
* non-zero, cast as a void*.
*/
static void* guac_client_owner_notify_leave_callback(guac_user* user, void* data) {
const guac_user* quitter = (const guac_user *) data;
if (user == NULL)
return (void*) ((intptr_t) -1);
char* log_owner = "owner";
if (user->info.name != NULL)
log_owner = (char *) user->info.name;
char* log_quitter = "anonymous";
char* send_quitter = "";
if (quitter->info.name != NULL) {
log_quitter = (char *) quitter->info.name;
send_quitter = (char *) quitter->info.name;
}
guac_user_log(user, GUAC_LOG_DEBUG, "Notifying owner \"%s\" of \"%s\" leaving.",
log_owner, log_quitter);
/* Send user left notification to owner. */
const char* args[] = { (const char*)quitter->user_id, (const char*)send_quitter, NULL };
return (void*) ((intptr_t) guac_protocol_send_msg(user->socket, GUAC_MESSAGE_USER_LEFT, args));
}
int guac_client_owner_notify_leave(guac_client* client, guac_user* quitter) {
/* Don't send msg instruction if client does not support it. */
if (!guac_client_owner_supports_msg(client)) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Client does not support the \"msg\" instruction and "
"will not be notified of the user leaving the connection.");
return -1;
}
return (int) ((intptr_t) guac_client_for_owner(client, guac_client_owner_notify_leave_callback, quitter));
}
int guac_client_supports_webp(guac_client* client) { int guac_client_supports_webp(guac_client* client) {
#ifdef ENABLE_WEBP #ifdef ENABLE_WEBP

View File

@ -737,6 +737,21 @@ void guac_client_stream_webp(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, int quality, int lossless); cairo_surface_t* surface, int quality, int lossless);
/**
* Returns whether the owner of the given client supports the "msg"
* instruction, returning non-zero if the client owner does support the
* instruction, or zero if the owner does not.
*
* @param client
* The Guacamole client whose owner should be checked for supporting
* the "msg" instruction.
*
* @return
* Non-zero if the owner of the given client supports the "msg"
* instruction, zero otherwise.
*/
int guac_client_owner_supports_msg(guac_client* client);
/** /**
* Returns whether the owner of the given client supports the "required" * Returns whether the owner of the given client supports the "required"
* instruction, returning non-zero if the client owner does support the * instruction, returning non-zero if the client owner does support the
@ -752,6 +767,42 @@ void guac_client_stream_webp(guac_client* client, guac_socket* socket,
*/ */
int guac_client_owner_supports_required(guac_client* client); int guac_client_owner_supports_required(guac_client* client);
/**
* Notifies the owner of the given client that a user has joined the connection,
* and returns zero if the message was sent successfully, or non-zero if the
* notification failed.
*
* @param client
* The Guacamole Client whose owner should be notified of a user joining
* the connection.
*
* @param joiner
* The Guacamole User who joined the connection.
*
* @return
* Zero if the notification to the owner was sent successfully, or non-zero
* if an error occurred.
*/
int guac_client_owner_notify_join(guac_client* client, guac_user* joiner);
/**
* Notifies the owner of the given client that a user has left the connection,
* and returns zero if the message was sent successfully, or non-zero if the
* notification failed.
*
* @param client
* The Guacamole Client whose owner should be notified of a user leaving
* the connection.
*
* @param quitter
* The Guacamole User who left the connection.
*
* @return
* Zero if the notification to the owner was sent successfully, or non-zero
* if an error occurred.
*/
int guac_client_owner_notify_leave(guac_client* client, guac_user* quitter);
/** /**
* Returns whether all users of the given client support WebP. If any user does * Returns whether all users of the given client support WebP. If any user does
* not support WebP, or the server cannot encode WebP images, zero is returned. * not support WebP, or the server cannot encode WebP images, zero is returned.

View File

@ -38,7 +38,7 @@
* This version is passed by the __guac_protocol_send_args() function from the * This version is passed by the __guac_protocol_send_args() function from the
* server to the client during the client/server handshake. * server to the client during the client/server handshake.
*/ */
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_3_0" #define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_5_0"
/** /**
* The maximum number of bytes that should be sent in any one blob instruction * The maximum number of bytes that should be sent in any one blob instruction

View File

@ -306,9 +306,40 @@ typedef enum guac_protocol_version {
* allowing connections in guacd to request information from the client and * allowing connections in guacd to request information from the client and
* await a response. * await a response.
*/ */
GUAC_PROTOCOL_VERSION_1_3_0 = 0x010300 GUAC_PROTOCOL_VERSION_1_3_0 = 0x010300,
/**
* Protocol version 1.5.0, which supports the "msg" instruction, allowing
* messages to be sent to the client, and adds support for the "name"
* handshake instruction.
*/
GUAC_PROTOCOL_VERSION_1_5_0 = 0x010500
} guac_protocol_version; } guac_protocol_version;
/**
* A type that represents codes for human-readable messages sent by the "msg"
* instruction to the Client, that will be displayed in the client's browser.
* The codes will be interpreted by the client into translatable messages, and
* make take arguments, as noted below.
*/
typedef enum guac_message_type {
/**
* A message that notifies the owner of a connection that another user has
* joined their connection. There should be a single argument provided, the
* name of the user who has joined.
*/
GUAC_MESSAGE_USER_JOINED = 0x0001,
/**
* A message that notifies the owner of a connection that another user has
* left their connection. There should be a single argument provided, the
* name of the user who has left.
*/
GUAC_MESSAGE_USER_LEFT = 0x0002
} guac_message_type;
#endif #endif

View File

@ -171,6 +171,27 @@ int guac_protocol_send_log(guac_socket* socket, const char* format, ...);
int vguac_protocol_send_log(guac_socket* socket, const char* format, int vguac_protocol_send_log(guac_socket* socket, const char* format,
va_list args); va_list args);
/**
* Sends the given string over the socket to be displayed on the client. Returns
* zero if the message was sent successfully or non-zero if an error occurs.
*
* @param socket
* The guac_socket connection to send the message to.
*
* @param msg
* The message code to send to the client.
*
* @param args
* A null-terminated array of strings that will be provided to the client
* as part of the message, that the client may then place in the message,
* or null if the message requires no arguments.
*
* @return
* Zero if the message is sent successfully; otherwise non-zero.
*/
int guac_protocol_send_msg(guac_socket* socket, guac_message_type msg,
const char** args);
/** /**
* Sends a mouse instruction over the given guac_socket connection. * Sends a mouse instruction over the given guac_socket connection.
* *

View File

@ -88,7 +88,7 @@ struct guac_user_info {
* stated resolution of the display size request is recommended. * stated resolution of the display size request is recommended.
*/ */
int optimal_resolution; int optimal_resolution;
/** /**
* The timezone of the remote system. If the client does not provide * The timezone of the remote system. If the client does not provide
* a specific timezone then this will be NULL. The format of the timezone * a specific timezone then this will be NULL. The format of the timezone
@ -102,6 +102,14 @@ struct guac_user_info {
*/ */
guac_protocol_version protocol_version; guac_protocol_version protocol_version;
/**
* The human-readable name of the Guacamole user, supplied by the client
* during the handshake. This is an arbitrary value, with no requirements or
* constraints, including that it need not uniquely identify the user.
* If the client does not provide a name then this will be NULL.
*/
const char* name;
}; };
struct guac_user { struct guac_user {
@ -850,6 +858,17 @@ void guac_user_stream_webp(guac_user* user, 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, int quality, int lossless); cairo_surface_t* surface, int quality, int lossless);
/**
* Returns whether the given user supports the "msg" instruction.
*
* @param user
* The Guacamole user to check for support of the "msg" instruction.
*
* @return
* Non-zero if the user supports the "msg" instruction, otherwise zero.
*/
int guac_user_supports_msg(guac_user* user);
/** /**
* Returns whether the given user supports the "required" instruction. * Returns whether the given user supports the "required" instruction.
* *

View File

@ -65,6 +65,7 @@ guac_protocol_version_mapping guac_protocol_version_table[] = {
{ GUAC_PROTOCOL_VERSION_1_0_0, "VERSION_1_0_0" }, { GUAC_PROTOCOL_VERSION_1_0_0, "VERSION_1_0_0" },
{ GUAC_PROTOCOL_VERSION_1_1_0, "VERSION_1_1_0" }, { GUAC_PROTOCOL_VERSION_1_1_0, "VERSION_1_1_0" },
{ GUAC_PROTOCOL_VERSION_1_3_0, "VERSION_1_3_0" }, { GUAC_PROTOCOL_VERSION_1_3_0, "VERSION_1_3_0" },
{ GUAC_PROTOCOL_VERSION_1_5_0, "VERSION_1_5_0" },
{ GUAC_PROTOCOL_VERSION_UNKNOWN, NULL } { GUAC_PROTOCOL_VERSION_UNKNOWN, NULL }
}; };
@ -658,6 +659,23 @@ int guac_protocol_send_log(guac_socket* socket, const char* format, ...) {
} }
int guac_protocol_send_msg(guac_socket* socket, guac_message_type msg,
const char** args) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "3.msg,")
|| __guac_socket_write_length_int(socket, msg)
|| guac_socket_write_array(socket, args)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream, int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream,
const char* mimetype, const char* name) { const char* mimetype, const char* name) {

View File

@ -27,11 +27,11 @@
*/ */
void test_guac_protocol__version_to_string() { void test_guac_protocol__version_to_string() {
guac_protocol_version version_a = GUAC_PROTOCOL_VERSION_1_3_0; guac_protocol_version version_a = GUAC_PROTOCOL_VERSION_1_5_0;
guac_protocol_version version_b = GUAC_PROTOCOL_VERSION_1_0_0; guac_protocol_version version_b = GUAC_PROTOCOL_VERSION_1_0_0;
guac_protocol_version version_c = GUAC_PROTOCOL_VERSION_UNKNOWN; guac_protocol_version version_c = GUAC_PROTOCOL_VERSION_UNKNOWN;
CU_ASSERT_STRING_EQUAL(guac_protocol_version_to_string(version_a), "VERSION_1_3_0"); CU_ASSERT_STRING_EQUAL(guac_protocol_version_to_string(version_a), "VERSION_1_5_0");
CU_ASSERT_STRING_EQUAL(guac_protocol_version_to_string(version_b), "VERSION_1_0_0"); CU_ASSERT_STRING_EQUAL(guac_protocol_version_to_string(version_b), "VERSION_1_0_0");
CU_ASSERT_PTR_NULL(guac_protocol_version_to_string(version_c)); CU_ASSERT_PTR_NULL(guac_protocol_version_to_string(version_c));

View File

@ -64,6 +64,7 @@ __guac_instruction_handler_mapping __guac_handshake_handler_map[] = {
{"video", __guac_handshake_video_handler}, {"video", __guac_handshake_video_handler},
{"image", __guac_handshake_image_handler}, {"image", __guac_handshake_image_handler},
{"timezone", __guac_handshake_timezone_handler}, {"timezone", __guac_handshake_timezone_handler},
{"name", __guac_handshake_name_handler},
{NULL, NULL} {NULL, NULL}
}; };
@ -684,6 +685,23 @@ int __guac_handshake_image_handler(guac_user* user, int argc, char** argv) {
} }
int __guac_handshake_name_handler(guac_user* user, int argc, char** argv) {
/* Free any past value for the user's name */
free((char *) user->info.name);
/* If a value is provided for the name, copy it into guac_user. */
if (argc > 0 && strcmp(argv[0], ""))
user->info.name = (const char*) strdup(argv[0]);
/* No or empty value was provided, so make sure this is NULLed out. */
else
user->info.name = NULL;
return 0;
}
int __guac_handshake_timezone_handler(guac_user* user, int argc, char** argv) { int __guac_handshake_timezone_handler(guac_user* user, int argc, char** argv) {
/* Free any past value */ /* Free any past value */

View File

@ -218,6 +218,13 @@ __guac_instruction_handler __guac_handshake_video_handler;
*/ */
__guac_instruction_handler __guac_handshake_image_handler; __guac_instruction_handler __guac_handshake_image_handler;
/**
* Internal handler function that is called when the name instruction is
* received during the handshake process, specifying the name of the Guacamole
* user establishing the connection.
*/
__guac_instruction_handler __guac_handshake_name_handler;
/** /**
* Internal handler function that is called when the timezone instruction is * Internal handler function that is called when the timezone instruction is
* received during the handshake process, specifying the timezone of the * received during the handshake process, specifying the timezone of the

View File

@ -296,6 +296,7 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
user->info.audio_mimetypes = NULL; user->info.audio_mimetypes = NULL;
user->info.image_mimetypes = NULL; user->info.image_mimetypes = NULL;
user->info.video_mimetypes = NULL; user->info.video_mimetypes = NULL;
user->info.name = NULL;
user->info.timezone = NULL; user->info.timezone = NULL;
/* Count number of arguments. */ /* Count number of arguments. */
@ -370,7 +371,8 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
guac_free_mimetypes((char **) user->info.image_mimetypes); guac_free_mimetypes((char **) user->info.image_mimetypes);
guac_free_mimetypes((char **) user->info.video_mimetypes); guac_free_mimetypes((char **) user->info.video_mimetypes);
/* Free timezone info. */ /* Free name and timezone info. */
free((char *) user->info.name);
free((char *) user->info.timezone); free((char *) user->info.timezone);
guac_parser_free(parser); guac_parser_free(parser);

View File

@ -316,6 +316,15 @@ void guac_user_stream_webp(guac_user* user, guac_socket* socket,
} }
int guac_user_supports_msg(guac_user* user) {
if (user == NULL)
return 0;
return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_5_0);
}
int guac_user_supports_required(guac_user* user) { int guac_user_supports_required(guac_user* user) {
if (user == NULL) if (user == NULL)

View File

@ -230,6 +230,7 @@ BUILT_SOURCES = \
rdp_keymaps = \ rdp_keymaps = \
$(srcdir)/keymaps/base.keymap \ $(srcdir)/keymaps/base.keymap \
$(srcdir)/keymaps/failsafe.keymap \ $(srcdir)/keymaps/failsafe.keymap \
$(srcdir)/keymaps/cs-cz-qwertz.keymap \
$(srcdir)/keymaps/de_de_qwertz.keymap \ $(srcdir)/keymaps/de_de_qwertz.keymap \
$(srcdir)/keymaps/de_ch_qwertz.keymap \ $(srcdir)/keymaps/de_ch_qwertz.keymap \
$(srcdir)/keymaps/en_gb_qwerty.keymap \ $(srcdir)/keymaps/en_gb_qwerty.keymap \

View File

@ -0,0 +1,79 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
parent "base"
name "cs-cz-qwertz"
freerdp "KBD_CZECH"
#
# Basic keys
#
map -caps -altgr -shift 0x29 0x02..0x0D ~ ";+ěščřžýáíé=´"
map -caps -altgr -shift 0x10..0x1B ~ "qwertzuıopú)"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "asdfghjklů§¨"
map -caps -altgr -shift 0x2C..0x35 ~ "yxcvbnm,.-"
map -caps -altgr +shift 0x29 0x02..0x0D ~ "°1234567890%ˇ"
map -caps -altgr +shift 0x10..0x1B ~ "QWERTZUIOP/("
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "ASDFGHJKL"!'"
map -caps -altgr +shift 0x2C..0x35 ~ "YXCVBNM?:_"
map +caps -altgr -shift 0x29 0x02..0x0D ~ ";+ĚŠČŘŽÝÁÍÉ=´"
map +caps -altgr -shift 0x10..0x1B ~ "QWERTZUIOPÚ)"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "ASDFGHJKL٧¨"
map +caps -altgr -shift 0x2C..0x35 ~ "YXCVBNM,.-"
map +caps -altgr +shift 0x29 0x02..0x0D ~ "°1234567890%ˇ"
map +caps -altgr +shift 0x10..0x1B ~ "qwertzuiop/("
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "asdfghjkl"!'"
map +caps -altgr +shift 0x2C..0x35 ~ "yxcvbnm?:_"
#
# Keys requiring AltGr
#
map +altgr -shift 0x02 ~ "~"
map +altgr -shift 0x10 ~ "\"
map +altgr -shift 0x11 ~ "|"
map +altgr -shift 0x12 ~ "€"
map +altgr -shift 0x1A ~ "÷"
map +altgr -shift 0x1B ~ "×"
map +altgr -shift 0x1F ~ "đ"
map +altgr -shift 0x20 ~ "Đ"
map +altgr -shift 0x21 ~ "["
map +altgr -shift 0x22 ~ "]"
map +altgr -shift 0x25 ~ "ł"
map +altgr -shift 0x26 ~ "Ł"
map +altgr -shift 0x27 ~ "$"
map +altgr -shift 0x28 ~ "ß"
map +altgr -shift 0x2B ~ "¤"
map +altgr -shift 0x2D ~ "#"
map +altgr -shift 0x2E ~ "&"
map +altgr -shift 0x2F ~ "@"
map +altgr -shift 0x30 ~ "{"
map +altgr -shift 0x31 ~ "}"
map +altgr -shift 0x33 ~ "<"
map +altgr -shift 0x34 ~ ">"
map +altgr -shift 0x35 ~ "*"
# END

View File

@ -77,6 +77,8 @@ libguac_terminal_la_LIBADD = \
@LIBGUAC_LTLIB@ @LIBGUAC_LTLIB@
libguac_terminal_la_LDFLAGS = \ libguac_terminal_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined \
@CAIRO_LIBS@ \ @CAIRO_LIBS@ \
@MATH_LIBS@ \ @MATH_LIBS@ \
@PANGO_LIBS@ \ @PANGO_LIBS@ \

View File

@ -20,6 +20,7 @@
#include "common/clipboard.h" #include "common/clipboard.h"
#include "common/cursor.h" #include "common/cursor.h"
#include "common/iconv.h"
#include "terminal/buffer.h" #include "terminal/buffer.h"
#include "terminal/color-scheme.h" #include "terminal/color-scheme.h"
#include "terminal/common.h" #include "terminal/common.h"
@ -936,14 +937,13 @@ void guac_terminal_commit_cursor(guac_terminal* term) {
} }
int guac_terminal_write(guac_terminal* term, const char* c, int size) { int guac_terminal_write(guac_terminal* term, const char* buffer, int length) {
guac_terminal_lock(term); guac_terminal_lock(term);
while (size > 0) { for (int written = 0; written < length; written++) {
/* Read and advance to next character */ /* Read and advance to next character */
char current = *(c++); char current = *(buffer++);
size--;
/* Write character to typescript, if any */ /* Write character to typescript, if any */
if (term->typescript != NULL) if (term->typescript != NULL)
@ -956,7 +956,7 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size) {
guac_terminal_unlock(term); guac_terminal_unlock(term);
guac_terminal_notify(term); guac_terminal_notify(term);
return 0; return length;
} }
@ -2165,11 +2165,20 @@ void guac_terminal_clipboard_reset(guac_terminal* terminal,
void guac_terminal_clipboard_append(guac_terminal* terminal, void guac_terminal_clipboard_append(guac_terminal* terminal,
const char* data, int length) { const char* data, int length) {
guac_common_clipboard_append(terminal->clipboard, data, length);
/* Allocate and clear space for the converted data */
char output_data[GUAC_COMMON_CLIPBOARD_MAX_LENGTH];
char* output = output_data;
/* Convert clipboard contents */
guac_iconv(GUAC_READ_UTF8_NORMALIZED, &data, length,
GUAC_WRITE_UTF8, &output, GUAC_COMMON_CLIPBOARD_MAX_LENGTH);
guac_common_clipboard_append(terminal->clipboard, output_data, output - output_data);
} }
void guac_terminal_remove_user(guac_terminal* terminal, guac_user* user) { void guac_terminal_remove_user(guac_terminal* terminal, guac_user* user) {
/* Remove the user from the terminal cursor */ /* Remove the user from the terminal cursor */
guac_common_cursor_remove_user(terminal->cursor, user); guac_common_cursor_remove_user(terminal->cursor, user);
} }

View File

@ -29,6 +29,22 @@
#include "terminal.h" #include "terminal.h"
#include "typescript.h" #include "typescript.h"
/**
* Handler for characters printed to the terminal. When a character is printed,
* the current char handler for the terminal is called and given that
* character.
*
* @param term
* The terminal receiving the character.
*
* @param c
* The received character.
*
* @return
* Zero if the character was handled successfully, non-zero otherwise.
*/
typedef int guac_terminal_char_handler(guac_terminal* term, unsigned char c);
struct guac_terminal { struct guac_terminal {
/** /**

View File

@ -141,19 +141,35 @@ typedef enum guac_terminal_cursor_type {
} guac_terminal_cursor_type; } guac_terminal_cursor_type;
/** /**
* Handler for characters printed to the terminal. When a character is printed, * Handler that is invoked whenever the necessary terminal codes are sent to
* the current char handler for the terminal is called and given that * to the given terminal to change the path for future file uploads.
* character. *
*/ * @param client
typedef int guac_terminal_char_handler(guac_terminal* term, unsigned char c); * The guac_client associated with the terminal receiving the upload path
* change terminal code.
/** *
* Handler for setting the destination path for file uploads. * @param path
* The requested path.
*/ */
typedef void guac_terminal_upload_path_handler(guac_client* client, char* path); typedef void guac_terminal_upload_path_handler(guac_client* client, char* path);
/** /**
* Handler for creating an outbound file download stream for a specified file. * Handler that is invoked whenever the necessary terminal codes are sent to
* initiate a download of a given remote file.
*
* @param client
* The guac_client associated with the terminal receiving the file download
* terminal code.
*
* @param filename
* The name of the requested file. This may be a relative or absolute path,
* and it is up to the implementation to define how this value is
* interpreted.
*
* @return
* The file stream created for the file download, already configured to
* properly handle "ack" responses, etc. from the client, or NULL if the
* stream could not be created.
*/ */
typedef guac_stream* guac_terminal_file_download_handler(guac_client* client, char* filename); typedef guac_stream* guac_terminal_file_download_handler(guac_client* client, char* filename);
@ -276,11 +292,16 @@ guac_terminal_options* guac_terminal_options_create(
/** /**
* Frees all resources associated with the given terminal. * Frees all resources associated with the given terminal.
*
* @param term
* The terminal to free.
*/ */
void guac_terminal_free(guac_terminal* term); void guac_terminal_free(guac_terminal* term);
/** /**
* Set the upload path handler for the given terminal. * Sets the upload path handler for the given terminal. The upload path handler
* is invoked whenever the terminal codes requesting an upload path change are
* sent.
* *
* @param terminal * @param terminal
* The terminal to set the upload path handler for. * The terminal to set the upload path handler for.
@ -293,12 +314,14 @@ void guac_terminal_set_upload_path_handler(guac_terminal* terminal,
guac_terminal_upload_path_handler* upload_path_handler); guac_terminal_upload_path_handler* upload_path_handler);
/** /**
* Set the file download handler for the given terminal. * Sets the file download handler for the given terminal. The file download
* handler is invoked whenever the terminal codes requesting download of a
* given remote file are sent.
* *
* @param terminal * @param terminal
* The terminal to set the file download handler for. * The terminal to set the file download handler for.
* *
* @param upload_path_handler * @param file_download_handler
* The handler to be called whenever the necessary terminal codes are sent to * The handler to be called whenever the necessary terminal codes are sent to
* the given terminal to initiate a download of a given remote file. * the given terminal to initiate a download of a given remote file.
*/ */
@ -308,6 +331,13 @@ void guac_terminal_set_file_download_handler(guac_terminal* terminal,
/** /**
* Renders a single frame of terminal data. If data is not yet available, * Renders a single frame of terminal data. If data is not yet available,
* this function will block until data is written. * this function will block until data is written.
*
* @param terminal
* The terminal that should be rendered.
*
* @return
* Zero if the frame was rendered successfully, non-zero if an error
* occurred.
*/ */
int guac_terminal_render_frame(guac_terminal* terminal); int guac_terminal_render_frame(guac_terminal* terminal);
@ -316,6 +346,19 @@ int guac_terminal_render_frame(guac_terminal* terminal);
* supplied by calls to guac_terminal_send_key(), * supplied by calls to guac_terminal_send_key(),
* guac_terminal_send_mouse(), and guac_terminal_send_stream(). If input is not * guac_terminal_send_mouse(), and guac_terminal_send_stream(). If input is not
* yet available, this function will block. * yet available, this function will block.
*
* @param terminal
* The terminal to read from.
*
* @param c
* The buffer that should receive the bytes read.
*
* @param size
* The number of bytes available within the buffer.
*
* @return
* The number of bytes read into the buffer, zero if end-of-file is reached
* (STDIN has been closed), or a negative value if an error occurs.
*/ */
int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size); int guac_terminal_read_stdin(guac_terminal* terminal, char* c, int size);
@ -377,8 +420,23 @@ char* guac_terminal_prompt(guac_terminal* terminal, const char* title,
/** /**
* Writes the given format string and arguments to this terminal's STDOUT in * Writes the given format string and arguments to this terminal's STDOUT in
* the same manner as printf(). This function may block until space is * the same manner as printf(). The entire populated format string will always
* be written unless an error occurs. This function may block until space is
* freed in the output buffer by guac_terminal_render_frame(). * freed in the output buffer by guac_terminal_render_frame().
*
* @param terminal
* The terminal to write to.
*
* @param format
* A printf-style format string describing the data to be written to
* STDOUT.
*
* @param ...
* Any arguments to use when filling the format string.
*
* @return
* The number of bytes written to STDOUT, or a negative value if an error
* occurs preventing the data from being written in its entirety.
*/ */
int guac_terminal_printf(guac_terminal* terminal, const char* format, ...); int guac_terminal_printf(guac_terminal* terminal, const char* format, ...);
@ -486,9 +544,24 @@ int guac_terminal_send_data(guac_terminal* term, const char* data, int length);
int guac_terminal_send_string(guac_terminal* term, const char* data); int guac_terminal_send_string(guac_terminal* term, const char* data);
/** /**
* Writes the given string of characters to the terminal. * Writes the given buffer to the given terminal's STDOUT. All requested bytes
* will be written unless an error occurs. This function may block until space
* is freed in the output buffer by guac_terminal_render_frame().
*
* @param term
* The terminal to write to.
*
* @param buffer
* A buffer containing the characters to be written to the terminal.
*
* @param length
* The number of bytes within the given string to write to the terminal.
*
* @return
* The number of bytes written to STDOUT, or a negative value if an error
* occurs preventing the data from being written in its entirety.
*/ */
int guac_terminal_write(guac_terminal* term, const char* c, int size); int guac_terminal_write(guac_terminal* term, const char* buffer, int length);
/** /**
* Initializes the handlers of the given guac_stream such that it serves as the * Initializes the handlers of the given guac_stream such that it serves as the
@ -532,7 +605,7 @@ int guac_terminal_send_stream(guac_terminal* term, guac_user* user,
* STDIN. * STDIN.
* *
* @param ... * @param ...
* Any srguments to use when filling the format string. * Any arguments to use when filling the format string.
* *
* @return * @return
* The number of bytes written to STDIN, or a negative value if an error * The number of bytes written to STDIN, or a negative value if an error
@ -560,7 +633,20 @@ void guac_terminal_dup(guac_terminal* term, guac_user* user,
guac_socket* socket); guac_socket* socket);
/** /**
* Resize the terminal to the given dimensions. * Resize the client display and terminal to the given pixel dimensions.
*
* @param term
* The terminal to resize.
*
* @param width
* The new terminal width, in pixels.
*
* @param height
* The new terminal height, in pixels.
*
* @return
* Zero if the terminal was successfully resized to the given dimensions,
* non-zero otherwise.
*/ */
int guac_terminal_resize(guac_terminal* term, int width, int height); int guac_terminal_resize(guac_terminal* term, int width, int height);