GUACAMOLE-1293: Merge support for notifying when a user has joined/left a connection.
This commit is contained in:
commit
07acce8a76
@ -302,6 +302,10 @@ int guac_client_add_user(guac_client* client, guac_user* user, int argc, char**
|
||||
/* Update owner pointer if user is owner */
|
||||
if (user->owner)
|
||||
client->__owner = user;
|
||||
|
||||
/* Notify owner of user joining connection. */
|
||||
else
|
||||
guac_client_owner_notify_join(client, user);
|
||||
|
||||
}
|
||||
|
||||
@ -331,6 +335,10 @@ void guac_client_remove_user(guac_client* client, guac_user* user) {
|
||||
if (user->owner)
|
||||
client->__owner = NULL;
|
||||
|
||||
/* Update owner of user having left the connection. */
|
||||
else
|
||||
guac_client_owner_notify_leave(client, user);
|
||||
|
||||
pthread_rwlock_unlock(&(client->__users_lock));
|
||||
|
||||
/* Call handler, if defined */
|
||||
@ -671,6 +679,36 @@ static void* __webp_support_callback(guac_user* user, void* data) {
|
||||
}
|
||||
#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()
|
||||
* to determine if the owner of a client supports the "required" instruction,
|
||||
@ -701,6 +739,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) {
|
||||
|
||||
#ifdef ENABLE_WEBP
|
||||
|
@ -708,6 +708,21 @@ void guac_client_stream_webp(guac_client* client, 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 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"
|
||||
* instruction, returning non-zero if the client owner does support the
|
||||
@ -723,6 +738,42 @@ void guac_client_stream_webp(guac_client* client, guac_socket* socket,
|
||||
*/
|
||||
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
|
||||
* not support WebP, or the server cannot encode WebP images, zero is returned.
|
||||
|
@ -38,7 +38,7 @@
|
||||
* This version is passed by the __guac_protocol_send_args() function from the
|
||||
* server to the client during the client/server handshake.
|
||||
*/
|
||||
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_3_0"
|
||||
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_5_0"
|
||||
|
||||
/**
|
||||
* The maximum number of bytes that should be sent in any one blob instruction
|
||||
|
@ -306,9 +306,40 @@ typedef enum guac_protocol_version {
|
||||
* allowing connections in guacd to request information from the client and
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
|
@ -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,
|
||||
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.
|
||||
*
|
||||
|
@ -88,7 +88,7 @@ struct guac_user_info {
|
||||
* stated resolution of the display size request is recommended.
|
||||
*/
|
||||
int optimal_resolution;
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -102,6 +102,14 @@ struct guac_user_info {
|
||||
*/
|
||||
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 {
|
||||
@ -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,
|
||||
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.
|
||||
*
|
||||
|
@ -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_1_0, "VERSION_1_1_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 }
|
||||
};
|
||||
|
||||
@ -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,
|
||||
const char* mimetype, const char* name) {
|
||||
|
||||
|
@ -27,11 +27,11 @@
|
||||
*/
|
||||
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_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_PTR_NULL(guac_protocol_version_to_string(version_c));
|
||||
|
||||
|
@ -64,6 +64,7 @@ __guac_instruction_handler_mapping __guac_handshake_handler_map[] = {
|
||||
{"video", __guac_handshake_video_handler},
|
||||
{"image", __guac_handshake_image_handler},
|
||||
{"timezone", __guac_handshake_timezone_handler},
|
||||
{"name", __guac_handshake_name_handler},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@ -676,6 +677,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) {
|
||||
|
||||
/* Free any past value */
|
||||
|
@ -218,6 +218,13 @@ __guac_instruction_handler __guac_handshake_video_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
|
||||
* received during the handshake process, specifying the timezone of the
|
||||
|
@ -296,6 +296,7 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
|
||||
user->info.audio_mimetypes = NULL;
|
||||
user->info.image_mimetypes = NULL;
|
||||
user->info.video_mimetypes = NULL;
|
||||
user->info.name = NULL;
|
||||
user->info.timezone = NULL;
|
||||
|
||||
/* 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.video_mimetypes);
|
||||
|
||||
/* Free timezone info. */
|
||||
/* Free name and timezone info. */
|
||||
free((char *) user->info.name);
|
||||
free((char *) user->info.timezone);
|
||||
|
||||
guac_parser_free(parser);
|
||||
|
@ -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) {
|
||||
|
||||
if (user == NULL)
|
||||
|
Loading…
Reference in New Issue
Block a user