GUACAMOLE-337: Move libguacd functionality into libguac.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/f504b1a5 Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/f504b1a5 Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/f504b1a5 Branch: refs/heads/master Commit: f504b1a5e883ac99a6c3f4058a150fb8c2b59c52 Parents: 5b748a4 Author: Michael Jumper <[email protected]> Authored: Sat Jul 1 15:51:15 2017 -0700 Committer: Michael Jumper <[email protected]> Committed: Sat Jul 1 18:50:03 2017 -0700 ---------------------------------------------------------------------- Makefile.am | 2 - configure.ac | 5 - src/guacd/Makefile.am | 2 - src/guacd/connection.c | 1 - src/guacd/daemon.c | 1 - src/guacd/proc-map.c | 1 - src/guacd/proc-map.h | 1 - src/guacd/proc.c | 3 +- src/libguac/Makefile.am | 3 +- src/libguac/guacamole/user.h | 24 +++ src/libguac/user-handshake.c | 437 ++++++++++++++++++++++++++++++++++++++ src/libguacd/Makefile.am | 47 ---- src/libguacd/libguacd/user.h | 48 ----- src/libguacd/log.c | 71 ------- src/libguacd/log.h | 42 ---- src/libguacd/user.c | 380 --------------------------------- 16 files changed, 464 insertions(+), 604 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/Makefile.am ---------------------------------------------------------------------- diff --git a/Makefile.am b/Makefile.am index d67db2a..3ba1e34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,6 @@ ACLOCAL_AMFLAGS = -I m4 # Subprojects DIST_SUBDIRS = \ src/libguac \ - src/libguacd \ src/common \ src/common-ssh \ src/terminal \ @@ -38,7 +37,6 @@ DIST_SUBDIRS = \ SUBDIRS = \ src/libguac \ src/common \ - src/libguacd \ tests if ENABLE_COMMON_SSH http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/configure.ac ---------------------------------------------------------------------- diff --git a/configure.ac b/configure.ac index b4cc2d3..0800250 100644 --- a/configure.ac +++ b/configure.ac @@ -123,10 +123,6 @@ AC_SUBST([COMMON_INCLUDE], '-I$(top_srcdir)/src/common') AC_SUBST([PULSE_LTLIB], '$(top_builddir)/src/pulse/libguac_pulse.la') AC_SUBST([PULSE_INCLUDE], '-I$(top_srcdir)/src/pulse') -# Common utility library for guacd implementations -AC_SUBST([LIBGUACD_LTLIB], '$(top_builddir)/src/libguacd/libguacd.la') -AC_SUBST([LIBGUACD_INCLUDE], '-I$(top_srcdir)/src/libguacd') - # Common base SSH client AC_SUBST([COMMON_SSH_LTLIB], '$(top_builddir)/src/common-ssh/libguac_common_ssh.la') AC_SUBST([COMMON_SSH_INCLUDE], '-I$(top_srcdir)/src/common-ssh') @@ -1172,7 +1168,6 @@ AC_CONFIG_FILES([Makefile src/common-ssh/Makefile src/terminal/Makefile src/libguac/Makefile - src/libguacd/Makefile src/guacd/Makefile src/guacenc/Makefile src/pulse/Makefile http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/guacd/Makefile.am ---------------------------------------------------------------------- diff --git a/src/guacd/Makefile.am b/src/guacd/Makefile.am index b9433e8..14ab5db 100644 --- a/src/guacd/Makefile.am +++ b/src/guacd/Makefile.am @@ -49,12 +49,10 @@ guacd_SOURCES = \ guacd_CFLAGS = \ -Werror -Wall -pedantic \ @COMMON_INCLUDE@ \ - @LIBGUACD_INCLUDE@ \ @LIBGUAC_INCLUDE@ guacd_LDADD = \ @COMMON_LTLIB@ \ - @LIBGUACD_LTLIB@ \ @LIBGUAC_LTLIB@ guacd_LDFLAGS = \ http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/guacd/connection.c ---------------------------------------------------------------------- diff --git a/src/guacd/connection.c b/src/guacd/connection.c index 6c517bf..ef767ac 100644 --- a/src/guacd/connection.c +++ b/src/guacd/connection.c @@ -22,7 +22,6 @@ #include "connection.h" #include "log.h" #include "move-fd.h" -#include "libguacd/user.h" #include "proc.h" #include "proc-map.h" http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/guacd/daemon.c ---------------------------------------------------------------------- diff --git a/src/guacd/daemon.c b/src/guacd/daemon.c index 3adf433..2e80ef3 100644 --- a/src/guacd/daemon.c +++ b/src/guacd/daemon.c @@ -22,7 +22,6 @@ #include "connection.h" #include "conf-args.h" #include "conf-file.h" -#include "libguacd/user.h" #include "log.h" #include "proc-map.h" http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/guacd/proc-map.c ---------------------------------------------------------------------- diff --git a/src/guacd/proc-map.c b/src/guacd/proc-map.c index bbe50b3..ac87196 100644 --- a/src/guacd/proc-map.c +++ b/src/guacd/proc-map.c @@ -19,7 +19,6 @@ #include "config.h" #include "common/list.h" -#include "libguacd/user.h" #include "proc.h" #include "proc-map.h" http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/guacd/proc-map.h ---------------------------------------------------------------------- diff --git a/src/guacd/proc-map.h b/src/guacd/proc-map.h index 5d8ce97..a242188 100644 --- a/src/guacd/proc-map.h +++ b/src/guacd/proc-map.h @@ -23,7 +23,6 @@ #include "config.h" #include "common/list.h" -#include "libguacd/user.h" #include "proc.h" #include <guacamole/client.h> http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/guacd/proc.c ---------------------------------------------------------------------- diff --git a/src/guacd/proc.c b/src/guacd/proc.c index e5e9586..0a35910 100644 --- a/src/guacd/proc.c +++ b/src/guacd/proc.c @@ -19,7 +19,6 @@ #include "config.h" -#include "libguacd/user.h" #include "log.h" #include "move-fd.h" #include "proc.h" @@ -92,7 +91,7 @@ static void* guacd_user_thread(void* data) { user->owner = params->owner; /* Handle user connection from handshake until disconnect/completion */ - guacd_handle_user(user, GUACD_USEC_TIMEOUT); + guac_user_handle_connection(user, GUACD_USEC_TIMEOUT); /* Stop client and prevent future users if all users are disconnected */ if (client->connected_users == 0) { http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/libguac/Makefile.am ---------------------------------------------------------------------- diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am index 0bfa409..a1f47ed 100644 --- a/src/libguac/Makefile.am +++ b/src/libguac/Makefile.am @@ -91,7 +91,8 @@ libguac_la_SOURCES = \ timestamp.c \ unicode.c \ user.c \ - user-handlers.c + user-handlers.c \ + user-handshake.c # Compile WebP support if available if ENABLE_WEBP http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/libguac/guacamole/user.h ---------------------------------------------------------------------- diff --git a/src/libguac/guacamole/user.h b/src/libguac/guacamole/user.h index c791c2d..b445f62 100644 --- a/src/libguac/guacamole/user.h +++ b/src/libguac/guacamole/user.h @@ -493,6 +493,30 @@ guac_user* guac_user_alloc(); void guac_user_free(guac_user* user); /** + * Handles the portion of a user's Guacamole protocol handshake following the + * "select" instruction all subsequent I/O, automatically populating the + * handshake-related properties of the given guac_user and invoking + * guac_user_handle_instruction() for received instructions after the handshake + * has completed. This function blocks until the connection/user is aborted or + * the user disconnects. + * + * @param user + * The user whose handshake and entire Guacamole protocol exchange should + * be handled. The user must already be associated with a guac_socket and + * guac_client, and the guac_client must already be fully initialized. + * + * @param usec_timeout + * The number of microseconds to wait for instructions from the given + * user before closing the connection with an error. + * + * @return + * Zero if the user's Guacamole connection was successfully handled and + * the user has disconnected, or non-zero if an error prevented the user's + * connection from being handled properly. + */ +int guac_user_handle_connection(guac_user* user, int usec_timeout); + +/** * 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 http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/libguac/user-handshake.c ---------------------------------------------------------------------- diff --git a/src/libguac/user-handshake.c b/src/libguac/user-handshake.c new file mode 100644 index 0000000..b601888 --- /dev/null +++ b/src/libguac/user-handshake.c @@ -0,0 +1,437 @@ +/* + * 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. + */ + +#include "config.h" + +#include "client.h" +#include "error.h" +#include "parser.h" +#include "protocol.h" +#include "socket.h" +#include "user.h" + +#include <pthread.h> +#include <stdlib.h> +#include <string.h> + +/** + * Parameters required by the user input thread. + */ +typedef struct guac_user_input_thread_params { + + /** + * The parser which will be used throughout the user's session. + */ + guac_parser* parser; + + /** + * A reference to the connected user. + */ + guac_user* user; + + /** + * The number of microseconds to wait for instructions from a connected + * user before closing the connection with an error. + */ + int usec_timeout; + +} guac_user_input_thread_params; + +/** + * Prints an error message using the logging facilities of the given user, + * automatically including any information present in guac_error. + * + * @param user + * The guac_user associated with the error that occurred. + * + * @param level + * The level at which to log this message. + * + * @param message + * The message to log. + */ +static void guac_user_log_guac_error(guac_user* user, + guac_client_log_level level, const char* message) { + + if (guac_error != GUAC_STATUS_SUCCESS) { + + /* If error message provided, include in log */ + if (guac_error_message != NULL) + guac_user_log(user, level, "%s: %s", message, + guac_error_message); + + /* Otherwise just log with standard status string */ + else + guac_user_log(user, level, "%s: %s", message, + guac_status_string(guac_error)); + + } + + /* Just log message if no status code */ + else + guac_user_log(user, level, "%s", message); + +} + +/** + * Logs a reasonable explanatory message regarding handshake failure based on + * the current value of guac_error. + * + * @param user + * The guac_user associated with the failed Guacamole protocol handshake. + */ +static void guac_user_log_handshake_failure(guac_user* user) { + + if (guac_error == GUAC_STATUS_CLOSED) + guac_user_log(user, GUAC_LOG_INFO, + "Guacamole connection closed during handshake"); + else if (guac_error == GUAC_STATUS_PROTOCOL_ERROR) + guac_user_log(user, GUAC_LOG_ERROR, + "Guacamole protocol violation. Perhaps the version of " + "guacamole-client is incompatible with this version of " + "libguac?"); + else + guac_user_log(user, GUAC_LOG_WARNING, + "Guacamole handshake failed: %s", + guac_status_string(guac_error)); + +} + +/** + * Copies the given array of mimetypes (strings) into a newly-allocated NULL- + * terminated array of strings. Both the array and the strings within the array + * are newly-allocated and must be later freed via guac_free_mimetypes(). + * + * @param mimetypes + * The array of mimetypes to copy. + * + * @param count + * The number of mimetypes in the given array. + * + * @return + * A newly-allocated, NULL-terminated array containing newly-allocated + * copies of each of the mimetypes provided in the original mimetypes + * array. + */ +static char** guac_copy_mimetypes(char** mimetypes, int count) { + + int i; + + /* Allocate sufficient space for NULL-terminated array of mimetypes */ + char** mimetypes_copy = malloc(sizeof(char*) * (count+1)); + + /* Copy each provided mimetype */ + for (i = 0; i < count; i++) + mimetypes_copy[i] = strdup(mimetypes[i]); + + /* Terminate with NULL */ + mimetypes_copy[count] = NULL; + + return mimetypes_copy; + +} + +/** + * Frees the given array of mimetypes, including the space allocated to each + * mimetype string within the array. The provided array of mimetypes MUST have + * been allocated with guac_copy_mimetypes(). + * + * @param mimetypes + * The NULL-terminated array of mimetypes to free. This array MUST have + * been previously allocated with guac_copy_mimetypes(). + */ +static void guac_free_mimetypes(char** mimetypes) { + + char** current_mimetype = mimetypes; + + /* Free all strings within NULL-terminated mimetype array */ + while (*current_mimetype != NULL) { + free(*current_mimetype); + current_mimetype++; + } + + /* Free the array itself, now that its contents have been freed */ + free(mimetypes); + +} + +/** + * The thread which handles all user input, calling event handlers for received + * instructions. + * + * @param data + * A pointer to a guac_user_input_thread_params structure describing the + * user whose input is being handled and the guac_parser with which to + * handle it. + * + * @return + * Always NULL. + */ +static void* guac_user_input_thread(void* data) { + + guac_user_input_thread_params* params = + (guac_user_input_thread_params*) data; + + int usec_timeout = params->usec_timeout; + guac_user* user = params->user; + guac_parser* parser = params->parser; + guac_client* client = user->client; + guac_socket* socket = user->socket; + + /* Guacamole user input loop */ + while (client->state == GUAC_CLIENT_RUNNING && user->active) { + + /* Read instruction, stop on error */ + if (guac_parser_read(parser, socket, usec_timeout)) { + + if (guac_error == GUAC_STATUS_TIMEOUT) + guac_user_abort(user, GUAC_PROTOCOL_STATUS_CLIENT_TIMEOUT, "User is not responding."); + + else { + if (guac_error != GUAC_STATUS_CLOSED) + guac_user_log_guac_error(user, GUAC_LOG_WARNING, + "Guacamole connection failure"); + guac_user_stop(user); + } + + return NULL; + } + + /* Reset guac_error and guac_error_message (user/client handlers are not + * guaranteed to set these) */ + guac_error = GUAC_STATUS_SUCCESS; + guac_error_message = NULL; + + /* Call handler, stop on error */ + if (guac_user_handle_instruction(user, parser->opcode, parser->argc, parser->argv) < 0) { + + /* Log error */ + guac_user_log_guac_error(user, GUAC_LOG_WARNING, + "User connection aborted"); + + /* Log handler details */ + guac_user_log(user, GUAC_LOG_DEBUG, "Failing instruction handler in user was \"%s\"", parser->opcode); + + guac_user_stop(user); + return NULL; + } + + } + + return NULL; + +} + +/** + * Starts the input/output threads of a new user. This function will block + * until the user disconnects. If an error prevents the input/output threads + * from starting, guac_user_stop() will be invoked on the given user. + * + * @param parser + * The guac_parser to use to handle all input from the given user. + * + * @param user + * The user whose associated I/O transfer threads should be started. + * + * @param usec_timeout + * The number of microseconds to wait for instructions from the given + * user before closing the connection with an error. + * + * @return + * Zero if the I/O threads started successfully and user has disconnected, + * or non-zero if the I/O threads could not be started. + */ +static int guac_user_start(guac_parser* parser, guac_user* user, + int usec_timeout) { + + guac_user_input_thread_params params = { + .parser = parser, + .user = user, + .usec_timeout = usec_timeout + }; + + pthread_t input_thread; + + if (pthread_create(&input_thread, NULL, guac_user_input_thread, (void*) ¶ms)) { + guac_user_log(user, GUAC_LOG_ERROR, "Unable to start input thread"); + guac_user_stop(user); + return -1; + } + + /* Wait for I/O threads */ + pthread_join(input_thread, NULL); + + /* Explicitly signal disconnect */ + guac_protocol_send_disconnect(user->socket); + guac_socket_flush(user->socket); + + /* Done */ + return 0; + +} + +int guac_user_handle_connection(guac_user* user, int usec_timeout) { + + guac_socket* socket = user->socket; + guac_client* client = user->client; + + /* Send args */ + if (guac_protocol_send_args(socket, client->args) + || guac_socket_flush(socket)) { + + /* Log error */ + guac_user_log_handshake_failure(user); + guac_user_log_guac_error(user, GUAC_LOG_DEBUG, + "Error sending \"args\" to new user"); + + return 1; + } + + guac_parser* parser = guac_parser_alloc(); + + /* Get optimal screen size */ + if (guac_parser_expect(parser, socket, usec_timeout, "size")) { + + /* Log error */ + guac_user_log_handshake_failure(user); + guac_user_log_guac_error(user, GUAC_LOG_DEBUG, + "Error reading \"size\""); + + guac_parser_free(parser); + return 1; + } + + /* Validate content of size instruction */ + if (parser->argc < 2) { + guac_user_log(user, GUAC_LOG_ERROR, "Received \"size\" " + "instruction lacked required arguments."); + guac_parser_free(parser); + return 1; + } + + /* Parse optimal screen dimensions from size instruction */ + user->info.optimal_width = atoi(parser->argv[0]); + user->info.optimal_height = atoi(parser->argv[1]); + + /* If DPI given, set the user resolution */ + if (parser->argc >= 3) + user->info.optimal_resolution = atoi(parser->argv[2]); + + /* Otherwise, use a safe default for rough backwards compatibility */ + else + user->info.optimal_resolution = 96; + + /* Get supported audio formats */ + if (guac_parser_expect(parser, socket, usec_timeout, "audio")) { + + /* Log error */ + guac_user_log_handshake_failure(user); + guac_user_log_guac_error(user, GUAC_LOG_DEBUG, + "Error reading \"audio\""); + + guac_parser_free(parser); + return 1; + } + + /* Store audio mimetypes */ + char** audio_mimetypes = guac_copy_mimetypes(parser->argv, parser->argc); + user->info.audio_mimetypes = (const char**) audio_mimetypes; + + /* Get supported video formats */ + if (guac_parser_expect(parser, socket, usec_timeout, "video")) { + + /* Log error */ + guac_user_log_handshake_failure(user); + guac_user_log_guac_error(user, GUAC_LOG_DEBUG, + "Error reading \"video\""); + + guac_parser_free(parser); + return 1; + } + + /* Store video mimetypes */ + char** video_mimetypes = guac_copy_mimetypes(parser->argv, parser->argc); + user->info.video_mimetypes = (const char**) video_mimetypes; + + /* Get supported image formats */ + if (guac_parser_expect(parser, socket, usec_timeout, "image")) { + + /* Log error */ + guac_user_log_handshake_failure(user); + guac_user_log_guac_error(user, GUAC_LOG_DEBUG, + "Error reading \"image\""); + + guac_parser_free(parser); + return 1; + } + + /* Store image mimetypes */ + char** image_mimetypes = guac_copy_mimetypes(parser->argv, parser->argc); + user->info.image_mimetypes = (const char**) image_mimetypes; + + /* Get args from connect instruction */ + if (guac_parser_expect(parser, socket, usec_timeout, "connect")) { + + /* Log error */ + guac_user_log_handshake_failure(user); + guac_user_log_guac_error(user, GUAC_LOG_DEBUG, + "Error reading \"connect\""); + + guac_parser_free(parser); + return 1; + } + + /* Acknowledge connection availability */ + guac_protocol_send_ready(socket, client->connection_id); + guac_socket_flush(socket); + + /* Attempt join */ + if (guac_client_add_user(client, user, parser->argc, parser->argv)) + guac_client_log(client, GUAC_LOG_ERROR, "User \"%s\" could NOT " + "join connection \"%s\"", user->user_id, client->connection_id); + + /* Begin user connection if join successful */ + else { + + guac_client_log(client, GUAC_LOG_INFO, "User \"%s\" joined connection " + "\"%s\" (%i users now present)", user->user_id, + client->connection_id, client->connected_users); + + /* Handle user I/O, wait for connection to terminate */ + guac_user_start(parser, user, usec_timeout); + + /* Remove/free user */ + guac_client_remove_user(client, user); + guac_client_log(client, GUAC_LOG_INFO, "User \"%s\" disconnected (%i " + "users remain)", user->user_id, client->connected_users); + + } + + /* Free mimetype lists */ + guac_free_mimetypes(audio_mimetypes); + guac_free_mimetypes(video_mimetypes); + guac_free_mimetypes(image_mimetypes); + + guac_parser_free(parser); + + /* Successful disconnect */ + return 0; + +} + http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/libguacd/Makefile.am ---------------------------------------------------------------------- diff --git a/src/libguacd/Makefile.am b/src/libguacd/Makefile.am deleted file mode 100644 index c0373f1..0000000 --- a/src/libguacd/Makefile.am +++ /dev/null @@ -1,47 +0,0 @@ -# -# 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. -# - -AUTOMAKE_OPTIONS = foreign - -lib_LTLIBRARIES = libguacd.la - -libguacdincdir = $(includedir)/libguacd - -libguacdinc_HEADERS = \ - libguacd/user.h - -noinst_HEADERS = \ - log.h - -libguacd_la_SOURCES = \ - log.c \ - user.c - -libguacd_la_CFLAGS = \ - -Werror -Wall -pedantic \ - @LIBGUAC_INCLUDE@ - -libguacd_la_LIBADD = \ - @LIBGUAC_LTLIB@ - -libguacd_la_LDFLAGS = \ - -version-info 0:0:0 \ - -no-undefined \ - @PTHREAD_LIBS@ - http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/libguacd/libguacd/user.h ---------------------------------------------------------------------- diff --git a/src/libguacd/libguacd/user.h b/src/libguacd/libguacd/user.h deleted file mode 100644 index 4404a6c..0000000 --- a/src/libguacd/libguacd/user.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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. - */ - - -#ifndef LIBGUACD_USER_H -#define LIBGUACD_USER_H - -#include <guacamole/parser.h> -#include <guacamole/socket.h> -#include <guacamole/user.h> - -/** - * Handles the initial handshake of a user and all subsequent I/O. This - * function blocks until the user disconnects. - * - * @param user - * The user whose handshake and entire Guacamole protocol exchange should - * be handled. - * - * @param usec_timeout - * The number of microseconds to wait for instructions from the given - * user before closing the connection with an error. - * - * @return - * Zero if the user's Guacamole connection was successfully handled and - * the user has disconnected, or non-zero if an error prevented the user's - * connection from being handled properly. - */ -int guacd_handle_user(guac_user* user, int usec_timeout); - -#endif - http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/libguacd/log.c ---------------------------------------------------------------------- diff --git a/src/libguacd/log.c b/src/libguacd/log.c deleted file mode 100644 index 180cc97..0000000 --- a/src/libguacd/log.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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. - */ - -#include "config.h" -#include "log.h" - -#include <guacamole/client.h> -#include <guacamole/error.h> - -#include <stdarg.h> -#include <stdio.h> -#include <unistd.h> - -void guacd_client_log_guac_error(guac_client* client, - guac_client_log_level level, const char* message) { - - if (guac_error != GUAC_STATUS_SUCCESS) { - - /* If error message provided, include in log */ - if (guac_error_message != NULL) - guac_client_log(client, level, "%s: %s", - message, - guac_error_message); - - /* Otherwise just log with standard status string */ - else - guac_client_log(client, level, "%s: %s", - message, - guac_status_string(guac_error)); - - } - - /* Just log message if no status code */ - else - guac_client_log(client, level, "%s", message); - -} - -void guacd_client_log_handshake_failure(guac_client* client) { - - if (guac_error == GUAC_STATUS_CLOSED) - guac_client_log(client, GUAC_LOG_INFO, - "Guacamole connection closed during handshake"); - else if (guac_error == GUAC_STATUS_PROTOCOL_ERROR) - guac_client_log(client, GUAC_LOG_ERROR, - "Guacamole protocol violation. Perhaps the version of " - "guacamole-client is incompatible with this version of " - "guacd?"); - else - guac_client_log(client, GUAC_LOG_WARNING, - "Guacamole handshake failed: %s", - guac_status_string(guac_error)); - -} - http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/libguacd/log.h ---------------------------------------------------------------------- diff --git a/src/libguacd/log.h b/src/libguacd/log.h deleted file mode 100644 index 76509e3..0000000 --- a/src/libguacd/log.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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. - */ - -#ifndef LIBGUACD_LOG_H -#define LIBGUACD_LOG_H - -#include "config.h" - -#include <guacamole/client.h> - -/** - * Prints an error message using the logging facilities of the given client, - * automatically including any information present in guac_error. This function - * accepts parameters identically to printf. - */ -void guacd_client_log_guac_error(guac_client* client, - guac_client_log_level level, const char* message); - -/** - * Logs a reasonable explanatory message regarding handshake failure based on - * the current value of guac_error. - */ -void guacd_client_log_handshake_failure(guac_client* client); - -#endif - http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/f504b1a5/src/libguacd/user.c ---------------------------------------------------------------------- diff --git a/src/libguacd/user.c b/src/libguacd/user.c deleted file mode 100644 index 04f0f49..0000000 --- a/src/libguacd/user.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * 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. - */ - -#include "config.h" - -#include "libguacd/user.h" -#include "log.h" - -#include <guacamole/client.h> -#include <guacamole/error.h> -#include <guacamole/parser.h> -#include <guacamole/protocol.h> -#include <guacamole/socket.h> -#include <guacamole/user.h> - -#include <pthread.h> -#include <stdlib.h> -#include <string.h> - -/** - * Parameters required by the user input thread. - */ -typedef struct guacd_user_input_thread_params { - - /** - * The parser which will be used throughout the user's session. - */ - guac_parser* parser; - - /** - * A reference to the connected user. - */ - guac_user* user; - - /** - * The number of microseconds to wait for instructions from a connected - * user before closing the connection with an error. - */ - int usec_timeout; - -} guacd_user_input_thread_params; - -/** - * Copies the given array of mimetypes (strings) into a newly-allocated NULL- - * terminated array of strings. Both the array and the strings within the array - * are newly-allocated and must be later freed via guacd_free_mimetypes(). - * - * @param mimetypes - * The array of mimetypes to copy. - * - * @param count - * The number of mimetypes in the given array. - * - * @return - * A newly-allocated, NULL-terminated array containing newly-allocated - * copies of each of the mimetypes provided in the original mimetypes - * array. - */ -static char** guacd_copy_mimetypes(char** mimetypes, int count) { - - int i; - - /* Allocate sufficient space for NULL-terminated array of mimetypes */ - char** mimetypes_copy = malloc(sizeof(char*) * (count+1)); - - /* Copy each provided mimetype */ - for (i = 0; i < count; i++) - mimetypes_copy[i] = strdup(mimetypes[i]); - - /* Terminate with NULL */ - mimetypes_copy[count] = NULL; - - return mimetypes_copy; - -} - -/** - * Frees the given array of mimetypes, including the space allocated to each - * mimetype string within the array. The provided array of mimetypes MUST have - * been allocated with guacd_copy_mimetypes(). - * - * @param mimetypes - * The NULL-terminated array of mimetypes to free. This array MUST have - * been previously allocated with guacd_copy_mimetypes(). - */ -static void guacd_free_mimetypes(char** mimetypes) { - - char** current_mimetype = mimetypes; - - /* Free all strings within NULL-terminated mimetype array */ - while (*current_mimetype != NULL) { - free(*current_mimetype); - current_mimetype++; - } - - /* Free the array itself, now that its contents have been freed */ - free(mimetypes); - -} - -/** - * The thread which handles all user input, calling event handlers for received - * instructions. - * - * @param data - * A pointer to a guacd_user_input_thread_params structure describing the - * user whose input is being handled and the guac_parser with which to - * handle it. - * - * @return - * Always NULL. - */ -static void* guacd_user_input_thread(void* data) { - - guacd_user_input_thread_params* params = - (guacd_user_input_thread_params*) data; - - int usec_timeout = params->usec_timeout; - guac_user* user = params->user; - guac_parser* parser = params->parser; - guac_client* client = user->client; - guac_socket* socket = user->socket; - - /* Guacamole user input loop */ - while (client->state == GUAC_CLIENT_RUNNING && user->active) { - - /* Read instruction, stop on error */ - if (guac_parser_read(parser, socket, usec_timeout)) { - - if (guac_error == GUAC_STATUS_TIMEOUT) - guac_user_abort(user, GUAC_PROTOCOL_STATUS_CLIENT_TIMEOUT, "User is not responding."); - - else { - if (guac_error != GUAC_STATUS_CLOSED) - guacd_client_log_guac_error(client, GUAC_LOG_WARNING, - "Guacamole connection failure"); - guac_user_stop(user); - } - - return NULL; - } - - /* Reset guac_error and guac_error_message (user/client handlers are not - * guaranteed to set these) */ - guac_error = GUAC_STATUS_SUCCESS; - guac_error_message = NULL; - - /* Call handler, stop on error */ - if (guac_user_handle_instruction(user, parser->opcode, parser->argc, parser->argv) < 0) { - - /* Log error */ - guacd_client_log_guac_error(client, GUAC_LOG_WARNING, - "User connection aborted"); - - /* Log handler details */ - guac_user_log(user, GUAC_LOG_DEBUG, "Failing instruction handler in user was \"%s\"", parser->opcode); - - guac_user_stop(user); - return NULL; - } - - } - - return NULL; - -} - -/** - * Starts the input/output threads of a new user. This function will block - * until the user disconnects. If an error prevents the input/output threads - * from starting, guac_user_stop() will be invoked on the given user. - * - * @param parser - * The guac_parser to use to handle all input from the given user. - * - * @param user - * The user whose associated I/O transfer threads should be started. - * - * @param usec_timeout - * The number of microseconds to wait for instructions from the given - * user before closing the connection with an error. - * - * @return - * Zero if the I/O threads started successfully and user has disconnected, - * or non-zero if the I/O threads could not be started. - */ -static int guacd_user_start(guac_parser* parser, guac_user* user, - int usec_timeout) { - - guacd_user_input_thread_params params = { - .parser = parser, - .user = user, - .usec_timeout = usec_timeout - }; - - pthread_t input_thread; - - if (pthread_create(&input_thread, NULL, guacd_user_input_thread, (void*) ¶ms)) { - guac_user_log(user, GUAC_LOG_ERROR, "Unable to start input thread"); - guac_user_stop(user); - return -1; - } - - /* Wait for I/O threads */ - pthread_join(input_thread, NULL); - - /* Explicitly signal disconnect */ - guac_protocol_send_disconnect(user->socket); - guac_socket_flush(user->socket); - - /* Done */ - return 0; - -} - -int guacd_handle_user(guac_user* user, int usec_timeout) { - - guac_socket* socket = user->socket; - guac_client* client = user->client; - - /* Send args */ - if (guac_protocol_send_args(socket, client->args) - || guac_socket_flush(socket)) { - - /* Log error */ - guacd_client_log_handshake_failure(client); - guacd_client_log_guac_error(client, GUAC_LOG_DEBUG, - "Error sending \"args\" to new user"); - - return 1; - } - - guac_parser* parser = guac_parser_alloc(); - - /* Get optimal screen size */ - if (guac_parser_expect(parser, socket, usec_timeout, "size")) { - - /* Log error */ - guacd_client_log_handshake_failure(client); - guacd_client_log_guac_error(client, GUAC_LOG_DEBUG, - "Error reading \"size\""); - - guac_parser_free(parser); - return 1; - } - - /* Validate content of size instruction */ - if (parser->argc < 2) { - guac_client_log(client, GUAC_LOG_ERROR, "Received \"size\" " - "instruction lacked required arguments."); - guac_parser_free(parser); - return 1; - } - - /* Parse optimal screen dimensions from size instruction */ - user->info.optimal_width = atoi(parser->argv[0]); - user->info.optimal_height = atoi(parser->argv[1]); - - /* If DPI given, set the client resolution */ - if (parser->argc >= 3) - user->info.optimal_resolution = atoi(parser->argv[2]); - - /* Otherwise, use a safe default for rough backwards compatibility */ - else - user->info.optimal_resolution = 96; - - /* Get supported audio formats */ - if (guac_parser_expect(parser, socket, usec_timeout, "audio")) { - - /* Log error */ - guacd_client_log_handshake_failure(client); - guacd_client_log_guac_error(client, GUAC_LOG_DEBUG, - "Error reading \"audio\""); - - guac_parser_free(parser); - return 1; - } - - /* Store audio mimetypes */ - char** audio_mimetypes = guacd_copy_mimetypes(parser->argv, parser->argc); - user->info.audio_mimetypes = (const char**) audio_mimetypes; - - /* Get supported video formats */ - if (guac_parser_expect(parser, socket, usec_timeout, "video")) { - - /* Log error */ - guacd_client_log_handshake_failure(client); - guacd_client_log_guac_error(client, GUAC_LOG_DEBUG, - "Error reading \"video\""); - - guac_parser_free(parser); - return 1; - } - - /* Store video mimetypes */ - char** video_mimetypes = guacd_copy_mimetypes(parser->argv, parser->argc); - user->info.video_mimetypes = (const char**) video_mimetypes; - - /* Get supported image formats */ - if (guac_parser_expect(parser, socket, usec_timeout, "image")) { - - /* Log error */ - guacd_client_log_handshake_failure(client); - guacd_client_log_guac_error(client, GUAC_LOG_DEBUG, - "Error reading \"image\""); - - guac_parser_free(parser); - return 1; - } - - /* Store image mimetypes */ - char** image_mimetypes = guacd_copy_mimetypes(parser->argv, parser->argc); - user->info.image_mimetypes = (const char**) image_mimetypes; - - /* Get args from connect instruction */ - if (guac_parser_expect(parser, socket, usec_timeout, "connect")) { - - /* Log error */ - guacd_client_log_handshake_failure(client); - guacd_client_log_guac_error(client, GUAC_LOG_DEBUG, - "Error reading \"connect\""); - - guac_parser_free(parser); - return 1; - } - - /* Acknowledge connection availability */ - guac_protocol_send_ready(socket, client->connection_id); - guac_socket_flush(socket); - - /* Attempt join */ - if (guac_client_add_user(client, user, parser->argc, parser->argv)) - guac_client_log(client, GUAC_LOG_ERROR, "User \"%s\" could NOT " - "join connection \"%s\"", user->user_id, client->connection_id); - - /* Begin user connection if join successful */ - else { - - guac_client_log(client, GUAC_LOG_INFO, "User \"%s\" joined connection " - "\"%s\" (%i users now present)", user->user_id, - client->connection_id, client->connected_users); - - /* Handle user I/O, wait for connection to terminate */ - guacd_user_start(parser, user, usec_timeout); - - /* Remove/free user */ - guac_client_remove_user(client, user); - guac_client_log(client, GUAC_LOG_INFO, "User \"%s\" disconnected (%i " - "users remain)", user->user_id, client->connected_users); - - } - - /* Free mimetype lists */ - guacd_free_mimetypes(audio_mimetypes); - guacd_free_mimetypes(video_mimetypes); - guacd_free_mimetypes(image_mimetypes); - - guac_parser_free(parser); - - /* Successful disconnect */ - return 0; - -} -
