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) {