GUACAMOLE-1204: Merge addition of server-side support for multi-touch events.
diff --git a/Dockerfile b/Dockerfile
index 14f6769..69b4511 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -57,6 +57,7 @@
freerdp2-dev \
gcc \
libcairo2-dev \
+ libgcrypt-dev \
libjpeg62-turbo-dev \
libossp-uuid-dev \
libpango1.0-dev \
diff --git a/configure.ac b/configure.ac
index e74865b..09985bb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -75,21 +75,50 @@
AC_MSG_ERROR("libdl is required on systems which do not otherwise provide dlopen()"),
[#include <dlfcn.h>])])
-# OSSP UUID
-AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid],
- AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid],
- AC_MSG_ERROR("The OSSP UUID library is required")))
+#
+# libuuid
+#
-# Check for and validate OSSP uuid.h header
-AC_CHECK_HEADERS([ossp/uuid.h])
-AC_CHECK_DECL([uuid_make],,
- AC_MSG_ERROR("No OSSP uuid.h found in include path"),
- [#ifdef HAVE_OSSP_UUID_H
- #include <ossp/uuid.h>
- #else
- #include <uuid.h>
- #endif
- ])
+have_libuuid=disabled
+AC_ARG_WITH([libuuid],
+ [AS_HELP_STRING([--with-libuuid],
+ [use libuuid to generate unique identifiers @<:@default=check@:>@])],
+ [],
+ [with_libuuid=check])
+
+if test "x$with_libuuid" != "xno"
+then
+ have_libuuid=yes
+ AC_CHECK_LIB([uuid], [uuid_generate],
+ [UUID_LIBS=-luuid]
+ [AC_DEFINE([HAVE_LIBUUID],, [Whether libuuid is available])],
+ [have_libuuid=no])
+fi
+
+# OSSP UUID (if libuuid is unavilable)
+if test "x${have_libuuid}" != "xyes"
+then
+
+ AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid],
+ AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid],
+ AC_MSG_ERROR([
+ --------------------------------------------
+ Unable to find libuuid or the OSSP UUID library.
+ Either libuuid (from util-linux) or the OSSP UUID library is required for
+ guacamole-server to be built.
+ --------------------------------------------])))
+
+ # Check for and validate OSSP uuid.h header
+ AC_CHECK_HEADERS([ossp/uuid.h])
+ AC_CHECK_DECL([uuid_make],,
+ AC_MSG_ERROR("No OSSP uuid.h found in include path"),
+ [#ifdef HAVE_OSSP_UUID_H
+ #include <ossp/uuid.h>
+ #else
+ #include <uuid.h>
+ #endif
+ ])
+fi
# cunit
AC_CHECK_LIB([cunit], [CU_run_test], [CUNIT_LIBS=-lcunit])
@@ -490,6 +519,27 @@
AC_CHECK_LIB([vncclient], [rfbInitClient], [VNC_LIBS="$VNC_LIBS -lvncclient"], [have_libvncserver=no])
fi
+#
+# Underlying libvncserver usage of gcrypt
+#
+
+if test "x${have_libvncserver}" = "xyes"
+then
+
+ # Whether libvncserver was built against libgcrypt
+ AC_CHECK_DECL([LIBVNCSERVER_WITH_CLIENT_GCRYPT],
+ [AC_CHECK_HEADER(gcrypt.h,,
+ [AC_MSG_WARN([
+ --------------------------------------------
+ libvncserver appears to be built against
+ libgcrypt, but the libgcrypt headers
+ could not be found. VNC will be disabled.
+ --------------------------------------------])
+ have_libvncserver=no])],,
+ [[#include <rfb/rfbconfig.h>]])
+
+fi
+
AM_CONDITIONAL([ENABLE_VNC], [test "x${have_libvncserver}" = "xyes"])
AC_SUBST(VNC_LIBS)
diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c
index c03e984..b45a1f5 100644
--- a/src/common-ssh/ssh.c
+++ b/src/common-ssh/ssh.c
@@ -140,11 +140,21 @@
int guac_common_ssh_init(guac_client* client) {
#ifdef LIBSSH2_USES_GCRYPT
- /* Init threadsafety in libgcrypt */
- gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
- if (!gcry_check_version(GCRYPT_VERSION)) {
- guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch.");
- return 1;
+
+ if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
+
+ /* Init threadsafety in libgcrypt */
+ gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+
+ /* Initialize GCrypt */
+ if (!gcry_check_version(GCRYPT_VERSION)) {
+ guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch.");
+ return 1;
+ }
+
+ /* Mark initialization as completed. */
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+
}
#endif
diff --git a/src/libguac/id.c b/src/libguac/id.c
index 27a714c..e627f89 100644
--- a/src/libguac/id.c
+++ b/src/libguac/id.c
@@ -22,7 +22,9 @@
#include "guacamole/error.h"
#include "id.h"
-#ifdef HAVE_OSSP_UUID_H
+#if defined(HAVE_LIBUUID)
+#include <uuid/uuid.h>
+#elif defined(HAVE_OSSP_UUID_H)
#include <ossp/uuid.h>
#else
#include <uuid.h>
@@ -30,54 +32,73 @@
#include <stdlib.h>
+/**
+ * The length of a UUID in bytes. All UUIDs are guaranteed to be 36 1-byte
+ * characters long.
+ */
+#define GUAC_UUID_LEN 36
+
char* guac_generate_id(char prefix) {
char* buffer;
char* identifier;
- size_t identifier_length;
+ /* Prepare object to receive generated UUID */
+#ifdef HAVE_LIBUUID
+ uuid_t uuid;
+#else
uuid_t* uuid;
-
- /* Attempt to create UUID object */
if (uuid_create(&uuid) != UUID_RC_OK) {
guac_error = GUAC_STATUS_NO_MEMORY;
guac_error_message = "Could not allocate memory for UUID";
return NULL;
}
+#endif
- /* Generate random UUID */
+ /* Generate unique identifier */
+#ifdef HAVE_LIBUUID
+ uuid_generate(uuid);
+#else
if (uuid_make(uuid, UUID_MAKE_V4) != UUID_RC_OK) {
uuid_destroy(uuid);
guac_error = GUAC_STATUS_NO_MEMORY;
guac_error_message = "UUID generation failed";
return NULL;
}
+#endif
/* Allocate buffer for future formatted ID */
- buffer = malloc(UUID_LEN_STR + 2);
+ buffer = malloc(GUAC_UUID_LEN + 2);
if (buffer == NULL) {
+#ifndef HAVE_LIBUUID
uuid_destroy(uuid);
+#endif
guac_error = GUAC_STATUS_NO_MEMORY;
- guac_error_message = "Could not allocate memory for connection ID";
+ guac_error_message = "Could not allocate memory for unique ID";
return NULL;
}
identifier = &(buffer[1]);
- identifier_length = UUID_LEN_STR + 1;
- /* Build connection ID from UUID */
+ /* Convert UUID to string to produce unique identifier */
+#ifdef HAVE_LIBUUID
+ uuid_unparse_lower(uuid, identifier);
+#else
+ size_t identifier_length = GUAC_UUID_LEN + 1;
if (uuid_export(uuid, UUID_FMT_STR, &identifier, &identifier_length) != UUID_RC_OK) {
free(buffer);
uuid_destroy(uuid);
guac_error = GUAC_STATUS_INTERNAL_ERROR;
- guac_error_message = "Conversion of UUID to connection ID failed";
+ guac_error_message = "Conversion of UUID to unique ID failed";
return NULL;
}
+ /* Clean up generated UUID */
uuid_destroy(uuid);
+#endif
buffer[0] = prefix;
- buffer[UUID_LEN_STR + 1] = '\0';
+ buffer[GUAC_UUID_LEN + 1] = '\0';
return buffer;
}
diff --git a/src/libguac/tests/Makefile.am b/src/libguac/tests/Makefile.am
index 7ee5594..d406c4c 100644
--- a/src/libguac/tests/Makefile.am
+++ b/src/libguac/tests/Makefile.am
@@ -36,6 +36,7 @@
test_libguac_SOURCES = \
client/buffer_pool.c \
client/layer_pool.c \
+ id/generate.c \
parser/append.c \
parser/read.c \
pool/next_free.c \
diff --git a/src/libguac/tests/id/generate.c b/src/libguac/tests/id/generate.c
new file mode 100644
index 0000000..3142a6f
--- /dev/null
+++ b/src/libguac/tests/id/generate.c
@@ -0,0 +1,88 @@
+/*
+ * 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 "id.h"
+
+#include <CUnit/CUnit.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * Test which verifies that each call to guac_generate_id() produces a
+ * different string.
+ */
+void test_id__unique() {
+
+ char* id1 = guac_generate_id('x');
+ char* id2 = guac_generate_id('x');
+
+ /* Neither string may be NULL */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(id1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(id2);
+
+ /* Both strings should be different */
+ CU_ASSERT_STRING_NOT_EQUAL(id1, id2);
+
+ free(id1);
+ free(id2);
+
+}
+
+/**
+ * Test which verifies that guac_generate_id() produces strings are in the
+ * correc UUID-based format.
+ */
+void test_id__format() {
+
+ unsigned int ignore;
+
+ char* id = guac_generate_id('x');
+ CU_ASSERT_PTR_NOT_NULL_FATAL(id);
+
+ int items_read = sscanf(id, "x%08x-%04x-%04x-%04x-%08x%04x",
+ &ignore, &ignore, &ignore, &ignore, &ignore, &ignore);
+
+ CU_ASSERT_EQUAL(items_read, 6);
+ CU_ASSERT_EQUAL(strlen(id), 37);
+
+ free(id);
+
+}
+
+/**
+ * Test which verifies that guac_generate_id() takes the specified prefix
+ * character into account when generating the ID string.
+ */
+void test_id__prefix() {
+
+ char* id;
+
+ id = guac_generate_id('a');
+ CU_ASSERT_PTR_NOT_NULL_FATAL(id);
+ CU_ASSERT_EQUAL(id[0], 'a');
+ free(id);
+
+ id = guac_generate_id('b');
+ CU_ASSERT_PTR_NOT_NULL_FATAL(id);
+ CU_ASSERT_EQUAL(id[0], 'b');
+ free(id);
+
+}
+
diff --git a/src/protocols/rdp/keymaps/fr_be_azerty.keymap b/src/protocols/rdp/keymaps/fr_be_azerty.keymap
index c9f9add..35f637e 100644
--- a/src/protocols/rdp/keymaps/fr_be_azerty.keymap
+++ b/src/protocols/rdp/keymaps/fr_be_azerty.keymap
@@ -25,22 +25,22 @@
# Basic keys
#
-map -caps -altgr -shift 0x29 0x02..0x0D ~ "²&é"'(§è!çà)_"
+map -caps -altgr -shift 0x29 0x02..0x0D ~ "²&é"'(§è!çà)-"
map -caps -altgr -shift 0x10..0x19 0x1B ~ "azertyuiop$"
map -caps -altgr -shift 0x1E..0x28 0x2B ~ "qsdfghjklmùµ"
map -caps -altgr -shift 0x56 0x2C..0x35 ~ "<wxcvbn,;:="
-map -caps -altgr +shift 0x29 0x02..0x0D ~ "³1234567890°-"
+map -caps -altgr +shift 0x29 0x02..0x0D ~ "³1234567890°_"
map -caps -altgr +shift 0x10..0x19 0x1B ~ "AZERTYUIOP£"
map -caps -altgr +shift 0x1E..0x28 0x2B ~ "QSDFGHJKLM%£"
map -caps -altgr +shift 0x56 0x2C..0x35 ~ ">WXCVBN?./+"
-map +caps -altgr -shift 0x29 0x02..0x0D ~ "²1234567890°-"
+map +caps -altgr -shift 0x29 0x02..0x0D ~ "²1234567890°_"
map +caps -altgr -shift 0x10..0x19 0x1B ~ "AZERTYUIOP£"
map +caps -altgr -shift 0x1E..0x28 0x2B ~ "QSDFGHJKLM%£"
map +caps -altgr -shift 0x56 0x2C..0x35 ~ "<WXCVBN?./+"
-map +caps -altgr +shift 0x29 0x02..0x0D ~ "³&é"'(§è!çà)_"
+map +caps -altgr +shift 0x29 0x02..0x0D ~ "³&é"'(§è!çà)-"
map +caps -altgr +shift 0x10..0x19 0x1B ~ "azertyuiop$"
map +caps -altgr +shift 0x1E..0x28 0x2B ~ "qsdfghjklmùµ"
map +caps -altgr +shift 0x56 0x2C..0x35 ~ ">wxcvbn,;:="
diff --git a/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c b/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c
index 0a46485..027e773 100644
--- a/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c
+++ b/src/protocols/rdp/plugins/guac-common-svc/guac-common-svc.c
@@ -17,6 +17,7 @@
* under the License.
*/
+#include "config.h"
#include "channels/common-svc.h"
#include <freerdp/svc.h>
diff --git a/src/protocols/rdp/settings.c b/src/protocols/rdp/settings.c
index 38064d1..f834a7f 100644
--- a/src/protocols/rdp/settings.c
+++ b/src/protocols/rdp/settings.c
@@ -865,9 +865,24 @@
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_DISABLE_OFFSCREEN_CACHING, 0);
- settings->disable_glyph_caching =
- guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
- IDX_DISABLE_GLYPH_CACHING, 0);
+ /* FreeRDP does not consider the glyph cache implementation to be stable as
+ * of 2.0.0, and it MUST NOT be used. Usage of the glyph cache results in
+ * unexpected disconnects when using older versions of Windows and recent
+ * versions of FreeRDP. See: https://issues.apache.org/jira/browse/GUACAMOLE-1191 */
+ settings->disable_glyph_caching = 1;
+
+ /* In case the user expects glyph caching to be enabled, either explicitly
+ * or by default, warn that this will not be the case as the glyph cache
+ * is not considered stable. */
+ if (!guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
+ IDX_DISABLE_GLYPH_CACHING, 0)) {
+ guac_user_log(user, GUAC_LOG_DEBUG, "Glyph caching is currently "
+ "universally disabled, regardless of the value of the \"%s\" "
+ "parameter, as glyph caching support is not considered stable "
+ "by FreeRDP as of the FreeRDP 2.0.0 release. See: "
+ "https://issues.apache.org/jira/browse/GUACAMOLE-1191",
+ GUAC_RDP_CLIENT_ARGS[IDX_DISABLE_GLYPH_CACHING]);
+ }
/* Session color depth */
settings->color_depth =
diff --git a/src/protocols/vnc/client.c b/src/protocols/vnc/client.c
index 93371ee..6efe005 100644
--- a/src/protocols/vnc/client.c
+++ b/src/protocols/vnc/client.c
@@ -77,12 +77,27 @@
/* Wait for client thread to finish */
pthread_join(vnc_client->client_thread, NULL);
- /* Free memory not free'd by libvncclient's rfbClientCleanup() */
- if (rfb_client->frameBuffer != NULL) free(rfb_client->frameBuffer);
- if (rfb_client->raw_buffer != NULL) free(rfb_client->raw_buffer);
- if (rfb_client->rcSource != NULL) free(rfb_client->rcSource);
+ /* Free memory that may not be free'd by libvncclient's
+ * rfbClientCleanup() prior to libvncclient 0.9.12 */
- /* Free VNC rfbClientData linked list (not free'd by rfbClientCleanup()) */
+ if (rfb_client->frameBuffer != NULL) {
+ free(rfb_client->frameBuffer);
+ rfb_client->frameBuffer = NULL;
+ }
+
+ if (rfb_client->raw_buffer != NULL) {
+ free(rfb_client->raw_buffer);
+ rfb_client->raw_buffer = NULL;
+ }
+
+ if (rfb_client->rcSource != NULL) {
+ free(rfb_client->rcSource);
+ rfb_client->rcSource = NULL;
+ }
+
+ /* Free VNC rfbClientData linked list (may not be free'd by
+ * rfbClientCleanup(), depending on libvncclient version) */
+
while (rfb_client->clientData != NULL) {
rfbClientData* next = rfb_client->clientData->next;
free(rfb_client->clientData);
diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c
index aed69fb..ade8278 100644
--- a/src/protocols/vnc/vnc.c
+++ b/src/protocols/vnc/vnc.c
@@ -48,12 +48,22 @@
#include <guacamole/timestamp.h>
#include <guacamole/wol.h>
#include <rfb/rfbclient.h>
+#include <rfb/rfbconfig.h>
#include <rfb/rfbproto.h>
+#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
+#include <errno.h>
+#include <gcrypt.h>
+#endif
+
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+
char* GUAC_VNC_CLIENT_KEY = "GUAC_VNC";
#ifdef ENABLE_VNC_TLS_LOCKING
@@ -134,6 +144,27 @@
rfb_client->LockWriteToTLS = guac_vnc_lock_write_to_tls;
rfb_client->UnlockWriteToTLS = guac_vnc_unlock_write_to_tls;
#endif
+
+#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
+
+ /* Check if GCrypt is initialized, do it if not. */
+ if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
+
+ guac_client_log(client, GUAC_LOG_DEBUG, "GCrypt initialization started.");
+
+ /* Initialize thread control. */
+ gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+
+ /* Basic GCrypt library initialization. */
+ gcry_check_version(NULL);
+
+ /* Mark initialization as completed. */
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+ guac_client_log(client, GUAC_LOG_DEBUG, "GCrypt initialization completed.");
+
+ }
+
+#endif
/* Do not handle clipboard and local cursor if read-only */
if (vnc_settings->read_only == 0) {