You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by vn...@apache.org on 2018/01/30 19:08:29 UTC

[01/11] guacamole-server git commit: GUACAMOLE-313: Add guaclog utility with stubbed interpretation of key events.

Repository: guacamole-server
Updated Branches:
  refs/heads/master 08f854ffe -> 5f5b4ea8e


GUACAMOLE-313: Add guaclog utility with stubbed interpretation of key events.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/ebc731aa
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/ebc731aa
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/ebc731aa

Branch: refs/heads/master
Commit: ebc731aaf390ca06019af58bb6c512faa4605ee6
Parents: db85163
Author: Michael Jumper <mj...@apache.org>
Authored: Sun Nov 26 15:44:03 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 26 16:24:04 2018 -0800

----------------------------------------------------------------------
 Makefile.am                   |   5 ++
 configure.ac                  |  16 ++++
 src/guaclog/.gitignore        |   5 ++
 src/guaclog/Makefile.am       |  51 +++++++++++++
 src/guaclog/guaclog.c         | 119 +++++++++++++++++++++++++++++
 src/guaclog/guaclog.h         |  31 ++++++++
 src/guaclog/instruction-key.c |  43 +++++++++++
 src/guaclog/instructions.c    |  62 +++++++++++++++
 src/guaclog/instructions.h    | 108 ++++++++++++++++++++++++++
 src/guaclog/interpret.c       | 152 +++++++++++++++++++++++++++++++++++++
 src/guaclog/interpret.h       |  51 +++++++++++++
 src/guaclog/log.c             |  85 +++++++++++++++++++++
 src/guaclog/log.h             |  73 ++++++++++++++++++
 src/guaclog/man/guaclog.1.in  |  60 +++++++++++++++
 src/guaclog/state.c           |  97 +++++++++++++++++++++++
 src/guaclog/state.h           |  89 ++++++++++++++++++++++
 16 files changed, 1047 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/Makefile.am
----------------------------------------------------------------------
diff --git a/Makefile.am b/Makefile.am
index 8dfa9d1..e923376 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -27,6 +27,7 @@ DIST_SUBDIRS =           \
     src/terminal         \
     src/guacd            \
     src/guacenc          \
+    src/guaclog          \
     src/pulse            \
     src/protocols/rdp    \
     src/protocols/ssh    \
@@ -75,6 +76,10 @@ if ENABLE_GUACENC
 SUBDIRS += src/guacenc
 endif
 
+if ENABLE_GUACLOG
+SUBDIRS += src/guaclog
+endif
+
 EXTRA_DIST =         \
     .dockerignore    \
     CONTRIBUTING     \

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 1906dc8..8ae0743 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1192,6 +1192,18 @@ AM_CONDITIONAL([ENABLE_GUACENC], [test "x${enable_guacenc}"  = "xyes" \
                                     -a "x${have_libswscale}" = "xyes"])
 
 #
+# guaclog
+#
+
+AC_ARG_ENABLE([guaclog],
+              [AS_HELP_STRING([--disable-guaclog],
+                              [do not build the Guacamole input logging tool])],
+              [],
+              [enable_guaclog=yes])
+
+AM_CONDITIONAL([ENABLE_GUACLOG], [test "x${enable_guaclog}"  = "xyes"])
+
+#
 # Output Makefiles
 #
 
@@ -1207,6 +1219,8 @@ AC_CONFIG_FILES([Makefile
                  src/guacd/man/guacd.conf.5
                  src/guacenc/Makefile
                  src/guacenc/man/guacenc.1
+                 src/guaclog/Makefile
+                 src/guaclog/man/guaclog.1
                  src/pulse/Makefile
                  src/protocols/rdp/Makefile
                  src/protocols/ssh/Makefile
@@ -1229,6 +1243,7 @@ AM_COND_IF([ENABLE_VNC],    [build_vnc=yes],    [build_vnc=no])
 
 AM_COND_IF([ENABLE_GUACD],   [build_guacd=yes],   [build_guacd=no])
 AM_COND_IF([ENABLE_GUACENC], [build_guacenc=yes], [build_guacenc=no])
+AM_COND_IF([ENABLE_GUACLOG], [build_guaclog=yes], [build_guaclog=no])
 
 #
 # Init scripts
@@ -1272,6 +1287,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION
 
       guacd ...... ${build_guacd}
       guacenc .... ${build_guacenc}
+      guaclog .... ${build_guaclog}
 
    Init scripts: ${build_init}
 

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/.gitignore
----------------------------------------------------------------------
diff --git a/src/guaclog/.gitignore b/src/guaclog/.gitignore
new file mode 100644
index 0000000..4865f64
--- /dev/null
+++ b/src/guaclog/.gitignore
@@ -0,0 +1,5 @@
+
+# Compiled guaclog
+guaclog
+guaclog.exe
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/Makefile.am
----------------------------------------------------------------------
diff --git a/src/guaclog/Makefile.am b/src/guaclog/Makefile.am
new file mode 100644
index 0000000..f1ad152
--- /dev/null
+++ b/src/guaclog/Makefile.am
@@ -0,0 +1,51 @@
+#
+# 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 
+
+bin_PROGRAMS = guaclog
+
+man_MANS =        \
+    man/guaclog.1
+
+noinst_HEADERS =   \
+    guaclog.h      \
+    instructions.h \
+    interpret.h    \
+    log.h          \
+    state.h
+
+guaclog_SOURCES =     \
+    guaclog.c         \
+    instructions.c    \
+    instruction-key.c \
+    interpret.c       \
+    log.c             \
+    state.c
+
+guaclog_CFLAGS =      \
+    -Werror -Wall     \
+    @LIBGUAC_INCLUDE@
+
+guaclog_LDADD =     \
+    @LIBGUAC_LTLIB@
+
+EXTRA_DIST =         \
+    man/guaclog.1.in
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/guaclog.c
----------------------------------------------------------------------
diff --git a/src/guaclog/guaclog.c b/src/guaclog/guaclog.c
new file mode 100644
index 0000000..4350289
--- /dev/null
+++ b/src/guaclog/guaclog.c
@@ -0,0 +1,119 @@
+/*
+ * 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 "guaclog.h"
+#include "interpret.h"
+#include "log.h"
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+int main(int argc, char* argv[]) {
+
+    int i;
+
+    /* Load defaults */
+    bool force = false;
+
+    /* Parse arguments */
+    int opt;
+    while ((opt = getopt(argc, argv, "s:r:f")) != -1) {
+
+        /* -f: Force */
+        if (opt == 'f')
+            force = true;
+
+        /* Invalid option */
+        else {
+            goto invalid_options;
+        }
+
+    }
+
+    /* Log start */
+    guaclog_log(GUAC_LOG_INFO, "Guacamole input log interpreter (guaclog) "
+            "version " VERSION);
+
+    /* Track number of overall failures */
+    int total_files = argc - optind;
+    int failures = 0;
+
+    /* Abort if no files given */
+    if (total_files <= 0) {
+        guaclog_log(GUAC_LOG_INFO, "No input files specified. Nothing to do.");
+        return 0;
+    }
+
+    guaclog_log(GUAC_LOG_INFO, "%i input file(s) provided.", total_files);
+
+    /* Interpret all input files */
+    for (i = optind; i < argc; i++) {
+
+        /* Get current filename */
+        const char* path = argv[i];
+
+        /* Generate output filename */
+        char out_path[4096];
+        int len = snprintf(out_path, sizeof(out_path), "%s.txt", path);
+
+        /* Do not write if filename exceeds maximum length */
+        if (len >= sizeof(out_path)) {
+            guaclog_log(GUAC_LOG_ERROR, "Cannot write output file for \"%s\": "
+                    "Name too long", path);
+            continue;
+        }
+
+        /* Attempt interpreting, log granular success/failure at debug level */
+        if (guaclog_interpret(path, out_path, force)) {
+            failures++;
+            guaclog_log(GUAC_LOG_DEBUG,
+                    "%s was NOT successfully interpreted.", path);
+        }
+        else
+            guaclog_log(GUAC_LOG_DEBUG, "%s was successfully "
+                    "interpreted.", path);
+
+    }
+
+    /* Warn if at least one file failed */
+    if (failures != 0)
+        guaclog_log(GUAC_LOG_WARNING, "Interpreting failed for %i of %i "
+                "file(s).", failures, total_files);
+
+    /* Notify of success */
+    else
+        guaclog_log(GUAC_LOG_INFO, "All files interpreted successfully.");
+
+    /* Interpreting complete */
+    return 0;
+
+    /* Display usage and exit with error if options are invalid */
+invalid_options:
+
+    fprintf(stderr, "USAGE: %s"
+            " [-f]"
+            " [FILE]...\n", argv[0]);
+
+    return 1;
+
+}
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/guaclog.h
----------------------------------------------------------------------
diff --git a/src/guaclog/guaclog.h b/src/guaclog/guaclog.h
new file mode 100644
index 0000000..7390314
--- /dev/null
+++ b/src/guaclog/guaclog.h
@@ -0,0 +1,31 @@
+/*
+ * 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 GUACLOG_H
+#define GUACLOG_H
+
+#include "config.h"
+
+/**
+ * The default log level below which no messages should be logged.
+ */
+#define GUACLOG_DEFAULT_LOG_LEVEL GUAC_LOG_INFO
+
+#endif
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/instruction-key.c
----------------------------------------------------------------------
diff --git a/src/guaclog/instruction-key.c b/src/guaclog/instruction-key.c
new file mode 100644
index 0000000..0e8501b
--- /dev/null
+++ b/src/guaclog/instruction-key.c
@@ -0,0 +1,43 @@
+/*
+ * 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 "state.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+int guaclog_handle_key(guaclog_state* state, int argc, char** argv) {
+
+    /* Verify argument count */
+    if (argc < 2) {
+        guaclog_log(GUAC_LOG_WARNING, "\"key\" instruction incomplete");
+        return 1;
+    }
+
+    /* Parse arguments */
+    int keysym = atoi(argv[0]);
+    bool pressed = (atoi(argv[1]) != 0);
+
+    /* Update interpreter state accordingly */
+    return guaclog_state_update_key(state, keysym, pressed);
+
+}
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/instructions.c
----------------------------------------------------------------------
diff --git a/src/guaclog/instructions.c b/src/guaclog/instructions.c
new file mode 100644
index 0000000..257e134
--- /dev/null
+++ b/src/guaclog/instructions.c
@@ -0,0 +1,62 @@
+/*
+ * 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 "state.h"
+#include "instructions.h"
+#include "log.h"
+
+#include <string.h>
+
+guaclog_instruction_handler_mapping guaclog_instruction_handler_map[] = {
+    {"key", guaclog_handle_key},
+    {NULL,  NULL}
+};
+
+int guaclog_handle_instruction(guaclog_state* state, const char* opcode,
+        int argc, char** argv) {
+
+    /* Search through mapping for instruction handler having given opcode */
+    guaclog_instruction_handler_mapping* current = guaclog_instruction_handler_map;
+    while (current->opcode != NULL) {
+
+        /* Invoke handler if opcode matches (if defined) */
+        if (strcmp(current->opcode, opcode) == 0) {
+
+            /* Invoke defined handler */
+            guaclog_instruction_handler* handler = current->handler;
+            if (handler != NULL)
+                return handler(state, argc, argv);
+
+            /* Log defined but unimplemented instructions */
+            guaclog_log(GUAC_LOG_DEBUG, "\"%s\" not implemented", opcode);
+            return 0;
+
+        }
+
+        /* Next candidate handler */
+        current++;
+
+    } /* end opcode search */
+
+    /* Ignore any unknown instructions */
+    return 0;
+
+}
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/instructions.h
----------------------------------------------------------------------
diff --git a/src/guaclog/instructions.h b/src/guaclog/instructions.h
new file mode 100644
index 0000000..0c901f5
--- /dev/null
+++ b/src/guaclog/instructions.h
@@ -0,0 +1,108 @@
+/*
+ * 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 GUACLOG_INSTRUCTIONS_H
+#define GUACLOG_INSTRUCTIONS_H
+
+#include "config.h"
+#include "state.h"
+
+/**
+ * A callback function which, when invoked, handles a particular Guacamole
+ * instruction. The opcode of the instruction is implied (as it is expected
+ * that there will be a 1:1 mapping of opcode to callback function), while the
+ * arguments for that instruction are included in the parameters given to the
+ * callback.
+ *
+ * @param state
+ *     The current state of the Guacamole input log interpreter.
+ *
+ * @param argc
+ *     The number of arguments (excluding opcode) passed to the instruction
+ *     being handled by the callback.
+ *
+ * @param argv
+ *     All arguments (excluding opcode) associated with the instruction being
+ *     handled by the callback.
+ *
+ * @return
+ *     Zero if the instruction was handled successfully, non-zero if an error
+ *     occurs.
+ */
+typedef int guaclog_instruction_handler(guaclog_state* state,
+        int argc, char** argv);
+
+/**
+ * Mapping of instruction opcode to corresponding handler function.
+ */
+typedef struct guaclog_instruction_handler_mapping {
+
+    /**
+     * The opcode of the instruction that the associated handler function
+     * should be invoked for.
+     */
+    const char* opcode;
+
+    /**
+     * The handler function to invoke whenever an instruction having the
+     * associated opcode is parsed.
+     */
+    guaclog_instruction_handler* handler;
+
+} guaclog_instruction_handler_mapping;
+
+/**
+ * Array of all opcode/handler mappings for all supported opcodes, terminated
+ * by an entry with a NULL opcode. All opcodes not listed here can be safely
+ * ignored.
+ */
+extern guaclog_instruction_handler_mapping guaclog_instruction_handler_map[];
+
+/**
+ * Handles the instruction having the given opcode and arguments, updating
+ * the state of the interpreter accordingly.
+ *
+ * @param state
+ *     The current state of the Guacamole input log interpreter.
+ *
+ * @param opcode
+ *     The opcode of the instruction being handled.
+ *
+ * @param argc
+ *     The number of arguments (excluding opcode) passed to the instruction
+ *     being handled by the callback.
+ *
+ * @param argv
+ *     All arguments (excluding opcode) associated with the instruction being
+ *     handled by the callback.
+ *
+ * @return
+ *     Zero if the instruction was handled successfully, non-zero if an error
+ *     occurs.
+ */
+int guaclog_handle_instruction(guaclog_state* state,
+        const char* opcode, int argc, char** argv);
+
+/**
+ * Handler for the Guacamole "key" instruction.
+ */
+guaclog_instruction_handler guaclog_handle_key;
+
+#endif
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/interpret.c
----------------------------------------------------------------------
diff --git a/src/guaclog/interpret.c b/src/guaclog/interpret.c
new file mode 100644
index 0000000..d996c24
--- /dev/null
+++ b/src/guaclog/interpret.c
@@ -0,0 +1,152 @@
+/*
+ * 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 "instructions.h"
+#include "log.h"
+#include "state.h"
+
+#include <guacamole/client.h>
+#include <guacamole/error.h>
+#include <guacamole/parser.h>
+#include <guacamole/socket.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+
+/**
+ * Reads and handles all Guacamole instructions from the given guac_socket
+ * until end-of-stream is reached.
+ *
+ * @param state
+ *     The current state of the Guacamole input log interpreter.
+ *
+ * @param path
+ *     The name of the file being parsed (for logging purposes). This file
+ *     must already be open and available through the given socket.
+ *
+ * @param socket
+ *     The guac_socket through which instructions should be read.
+ *
+ * @return
+ *     Zero on success, non-zero if parsing of Guacamole protocol data through
+ *     the given socket fails.
+ */
+static int guaclog_read_instructions(guaclog_state* state,
+        const char* path, guac_socket* socket) {
+
+    /* Obtain Guacamole protocol parser */
+    guac_parser* parser = guac_parser_alloc();
+    if (parser == NULL)
+        return 1;
+
+    /* Continuously read and handle all instructions */
+    while (!guac_parser_read(parser, socket, -1)) {
+        guaclog_handle_instruction(state, parser->opcode,
+                parser->argc, parser->argv);
+    }
+
+    /* Fail on read/parse error */
+    if (guac_error != GUAC_STATUS_CLOSED) {
+        guaclog_log(GUAC_LOG_ERROR, "%s: %s",
+                path, guac_status_string(guac_error));
+        guac_parser_free(parser);
+        return 1;
+    }
+
+    /* Parse complete */
+    guac_parser_free(parser);
+    return 0;
+
+}
+
+int guaclog_interpret(const char* path, const char* out_path, bool force) {
+
+    /* Open input file */
+    int fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        guaclog_log(GUAC_LOG_ERROR, "%s: %s", path, strerror(errno));
+        return 1;
+    }
+
+    /* Lock entire input file for reading by the current process */
+    struct flock file_lock = {
+        .l_type   = F_RDLCK,
+        .l_whence = SEEK_SET,
+        .l_start  = 0,
+        .l_len    = 0,
+        .l_pid    = getpid()
+    };
+
+    /* Abort if file cannot be locked for reading */
+    if (!force && fcntl(fd, F_SETLK, &file_lock) == -1) {
+
+        /* Warn if lock cannot be acquired */
+        if (errno == EACCES || errno == EAGAIN)
+            guaclog_log(GUAC_LOG_WARNING, "Refusing to interpret log of "
+                    "in-progress session \"%s\" (specify the -f option to "
+                    "override this behavior).", path);
+
+        /* Log an error if locking fails in an unexpected way */
+        else
+            guaclog_log(GUAC_LOG_ERROR, "Cannot lock \"%s\" for reading: %s",
+                    path, strerror(errno));
+
+        close(fd);
+        return 1;
+    }
+
+    /* Allocate input state for interpreting process */
+    guaclog_state* state = guaclog_state_alloc(out_path);
+    if (state == NULL) {
+        close(fd);
+        return 1;
+    }
+
+    /* Obtain guac_socket wrapping file descriptor */
+    guac_socket* socket = guac_socket_open(fd);
+    if (socket == NULL) {
+        guaclog_log(GUAC_LOG_ERROR, "%s: %s", path,
+                guac_status_string(guac_error));
+        close(fd);
+        guaclog_state_free(state);
+        return 1;
+    }
+
+    guaclog_log(GUAC_LOG_INFO, "Writing input events from \"%s\" "
+            "to \"%s\" ...", path, out_path);
+
+    /* Attempt to read all instructions in the file */
+    if (guaclog_read_instructions(state, path, socket)) {
+        guac_socket_free(socket);
+        guaclog_state_free(state);
+        return 1;
+    }
+
+    /* Close input and finish interpreting process */
+    guac_socket_free(socket);
+    return guaclog_state_free(state);
+
+}
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/interpret.h
----------------------------------------------------------------------
diff --git a/src/guaclog/interpret.h b/src/guaclog/interpret.h
new file mode 100644
index 0000000..c65764e
--- /dev/null
+++ b/src/guaclog/interpret.h
@@ -0,0 +1,51 @@
+/*
+ * 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 GUACLOG_INTERPRET_H
+#define GUACLOG_INTERPRET_H
+
+#include "config.h"
+
+#include <stdbool.h>
+
+/**
+ * Interprets all input events within the given Guacamole protocol dump,
+ * producing a human-readable log of those input events. A read lock will be
+ * acquired on the input file to ensure that in-progress logs are not
+ * interpreted. This behavior can be overridden by specifying true for the
+ * force parameter.
+ *
+ * @param path
+ *     The path to the file containing the raw Guacamole protocol dump.
+ *
+ * @param out_path
+ *     The full path to the file in which interpreted log should be written.
+ *
+ * @param force
+ *     Interpret even if the input file appears to be an in-progress log (has
+ *     an associated lock).
+ *
+ * @return
+ *     Zero on success, non-zero if an error prevented successful
+ *     interpretation of the log.
+ */
+int guaclog_interpret(const char* path, const char* out_path, bool force);
+
+#endif
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/log.c
----------------------------------------------------------------------
diff --git a/src/guaclog/log.c b/src/guaclog/log.c
new file mode 100644
index 0000000..e99abac
--- /dev/null
+++ b/src/guaclog/log.c
@@ -0,0 +1,85 @@
+/*
+ * 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 "guaclog.h"
+#include "log.h"
+
+#include <guacamole/client.h>
+#include <guacamole/error.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+
+int guaclog_log_level = GUACLOG_DEFAULT_LOG_LEVEL;
+
+void vguaclog_log(guac_client_log_level level, const char* format,
+        va_list args) {
+
+    const char* priority_name;
+    char message[2048];
+
+    /* Don't bother if the log level is too high */
+    if (level > guaclog_log_level)
+        return;
+
+    /* Copy log message into buffer */
+    vsnprintf(message, sizeof(message), format, args);
+
+    /* Convert log level to human-readable name */
+    switch (level) {
+
+        /* Error log level */
+        case GUAC_LOG_ERROR:
+            priority_name = "ERROR";
+            break;
+
+        /* Warning log level */
+        case GUAC_LOG_WARNING:
+            priority_name = "WARNING";
+            break;
+
+        /* Informational log level */
+        case GUAC_LOG_INFO:
+            priority_name = "INFO";
+            break;
+
+        /* Debug log level */
+        case GUAC_LOG_DEBUG:
+            priority_name = "DEBUG";
+            break;
+
+        /* Any unknown/undefined log level */
+        default:
+            priority_name = "UNKNOWN";
+            break;
+    }
+
+    /* Log to STDERR */
+    fprintf(stderr, GUACLOG_LOG_NAME ": %s: %s\n", priority_name, message);
+
+}
+
+void guaclog_log(guac_client_log_level level, const char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    vguaclog_log(level, format, args);
+    va_end(args);
+}
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/log.h
----------------------------------------------------------------------
diff --git a/src/guaclog/log.h b/src/guaclog/log.h
new file mode 100644
index 0000000..bcf90a1
--- /dev/null
+++ b/src/guaclog/log.h
@@ -0,0 +1,73 @@
+/*
+ * 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 GUACLOG_LOG_H
+#define GUACLOG_LOG_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+
+#include <stdarg.h>
+
+/**
+ * The maximum level at which to log messages. All other messages will be
+ * dropped.
+ */
+extern int guaclog_log_level;
+
+/**
+ * The string to prepend to all log messages.
+ */
+#define GUACLOG_LOG_NAME "guaclog"
+
+/**
+ * Writes a message to guaclog's logs. This function takes a format and
+ * va_list, similar to vprintf.
+ *
+ * @param level
+ *     The level at which to log this message.
+ *
+ * @param format
+ *     A printf-style format string to log.
+ *
+ * @param args
+ *     The va_list containing the arguments to be used when filling the format
+ *     string for printing.
+ */
+void vguaclog_log(guac_client_log_level level, const char* format,
+        va_list args);
+
+/**
+ * Writes a message to guaclog's logs. This function accepts parameters
+ * identically to printf.
+ *
+ * @param level
+ *     The level at which to log this message.
+ *
+ * @param format
+ *     A printf-style format string to log.
+ *
+ * @param ...
+ *     Arguments to use when filling the format string for printing.
+ */
+void guaclog_log(guac_client_log_level level, const char* format, ...);
+
+#endif
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/man/guaclog.1.in
----------------------------------------------------------------------
diff --git a/src/guaclog/man/guaclog.1.in b/src/guaclog/man/guaclog.1.in
new file mode 100644
index 0000000..11896d5
--- /dev/null
+++ b/src/guaclog/man/guaclog.1.in
@@ -0,0 +1,60 @@
+.\"
+.\" 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.
+.\"
+.TH guaclog 1 "26 Jan 2018" "version @PACKAGE_VERSION@" "Apache Guacamole"
+.
+.SH NAME
+guaclog \- Guacamole input log interpreter
+.
+.SH SYNOPSIS
+.B guaclog
+[\fB-f\fR]
+[\fIFILE\fR]...
+.
+.SH DESCRIPTION
+.B guaclog
+is an interpreter which accepts Guacamole protocol dumps, such as those saved
+when input logging is enabled on a Guacamole connection, writing human-readable
+text logs as output.
+.B guaclog
+is essentially an implementation of a Guacamole client which accepts
+its input from files instead of a network connection, however unlike
+.B guacenc
+it only handles instructions related to user input.
+.P
+Each \fIFILE\fR specified will be translated into a new human-readable text
+file named \fIFILE\fR.txt. Existing files will not be overwritten; the
+interpreting process for any input file will be aborted if it would result in
+overwriting an existing file.
+.P
+Guacamole acquires a write lock on input logs as they are being written. By
+default,
+.B guaclog
+will check whether the each input file is locked and will refuse to read and
+interpret an input file if it appears to be an in-progress log. This behavior
+can be overridden by specifying the \fB-f\fR option. Interpreting an
+in-progress log will still work; the resulting human-readable text file will
+simply cover the user's session only up to the current point in time.
+.
+.SH OPTIONS
+.TP
+\fB-f\fR
+Overrides the default behavior of
+.B guaclog
+such that input files will be interpreted even if they appear to be logs of
+in-progress Guacamole sessions.

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/state.c
----------------------------------------------------------------------
diff --git a/src/guaclog/state.c b/src/guaclog/state.c
new file mode 100644
index 0000000..6167bd3
--- /dev/null
+++ b/src/guaclog/state.c
@@ -0,0 +1,97 @@
+/*
+ * 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 "state.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+guaclog_state* guaclog_state_alloc(const char* path) {
+
+    /* Open output file */
+    int fd = open(path, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
+    if (fd == -1) {
+        guaclog_log(GUAC_LOG_ERROR, "Failed to open output file \"%s\": %s",
+                path, strerror(errno));
+        goto fail_output_fd;
+    }
+
+    /* Create stream for output file */
+    FILE* output = fdopen(fd, "wb");
+    if (output == NULL) {
+        guaclog_log(GUAC_LOG_ERROR, "Failed to allocate stream for output "
+                "file \"%s\": %s", path, strerror(errno));
+        goto fail_output_file;
+    }
+
+    /* Allocate state */
+    guaclog_state* state = (guaclog_state*) calloc(1, sizeof(guaclog_state));
+    if (state == NULL) {
+        goto fail_state;
+    }
+
+    /* Associate state with output file */
+    state->output = output;
+
+    return state;
+
+    /* Free all allocated data in case of failure */
+fail_state:
+    fclose(output);
+
+fail_output_file:
+    close(fd);
+
+fail_output_fd:
+    return NULL;
+
+}
+
+int guaclog_state_free(guaclog_state* state) {
+
+    /* Ignore NULL state */
+    if (state == NULL)
+        return 0;
+
+    /* Close output file */
+    fclose(state->output);
+
+    free(state);
+    return 0;
+
+}
+
+int guaclog_state_update_key(guaclog_state* state, int keysym, bool pressed) {
+
+    /* STUB */
+    fprintf(state->output, "STUB: keysym=0x%X, pressed=%s\n",
+            keysym, pressed ? "true" : "false");
+
+    return 0;
+
+}
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/ebc731aa/src/guaclog/state.h
----------------------------------------------------------------------
diff --git a/src/guaclog/state.h b/src/guaclog/state.h
new file mode 100644
index 0000000..5891cb5
--- /dev/null
+++ b/src/guaclog/state.h
@@ -0,0 +1,89 @@
+/*
+ * 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 GUACLOG_STATE_H
+#define GUACLOG_STATE_H
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+
+/**
+ * The current state of the Guacamole input log interpreter.
+ */
+typedef struct guaclog_state {
+
+    /**
+     * Output file stream.
+     */
+    FILE* output;
+
+} guaclog_state;
+
+/**
+ * Allocates a new state structure for the Guacamole input log interpreter.
+ * This structure serves as the representation of interpreter state as
+ * input-related instructions are read and handled.
+ *
+ * @param path
+ *     The full path to the file in which interpreted, human-readable should be
+ *     written.
+ *
+ * @return
+ *     The newly-allocated Guacamole input log interpreter state, or NULL if
+ *     the state could not be allocated.
+ */
+guaclog_state* guaclog_state_alloc(const char* path);
+
+/**
+ * Frees all memory associated with the given Guacamole input log interpreter
+ * state, and finishes any remaining interpreting process. If the given state
+ * is NULL, this function has no effect.
+ *
+ * @param state
+ *     The Guacamole input log interpreter state to free, which may be NULL.
+ *
+ * @return
+ *     Zero if the interpreting process completed successfully, non-zero
+ *     otherwise.
+ */
+int guaclog_state_free(guaclog_state* state);
+
+/**
+ * Updates the given Guacamole input log interpreter state, marking the given
+ * key as pressed or released.
+ *
+ * @param state
+ *     The Guacamole input log interpreter state being updated.
+ *
+ * @param keysym
+ *     The X11 keysym of the key being pressed or released.
+ *
+ * @param pressed
+ *     true if the key is being pressed, false if the key is being released.
+ *
+ * @return
+ *     Zero if the interpreter state was updated successfully, non-zero
+ *     otherwise.
+ */
+int guaclog_state_update_key(guaclog_state* state, int keysym, bool pressed);
+
+#endif
+


[02/11] guacamole-server git commit: GUACAMOLE-313: Continuously track key press/release.

Posted by vn...@apache.org.
GUACAMOLE-313: Continuously track key press/release.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/d39757b4
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/d39757b4
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/d39757b4

Branch: refs/heads/master
Commit: d39757b4dc09ff7f255761ca9c2f18e105be626f
Parents: ebc731a
Author: Michael Jumper <mj...@apache.org>
Authored: Sun Nov 26 17:24:49 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 26 16:24:14 2018 -0800

----------------------------------------------------------------------
 src/guaclog/state.c | 106 +++++++++++++++++++++++++++++++++++++++++++++--
 src/guaclog/state.h |  35 ++++++++++++++++
 2 files changed, 138 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/d39757b4/src/guaclog/state.c
----------------------------------------------------------------------
diff --git a/src/guaclog/state.c b/src/guaclog/state.c
index 6167bd3..cc6f763 100644
--- a/src/guaclog/state.c
+++ b/src/guaclog/state.c
@@ -57,6 +57,9 @@ guaclog_state* guaclog_state_alloc(const char* path) {
     /* Associate state with output file */
     state->output = output;
 
+    /* No keys are initially tracked */
+    state->active_keys = 0;
+
     return state;
 
     /* Free all allocated data in case of failure */
@@ -85,11 +88,108 @@ int guaclog_state_free(guaclog_state* state) {
 
 }
 
+/**
+ * Adds the given key state to the array of tracked keys. If the key is already
+ * being tracked, its corresponding entry within the array of tracked keys is
+ * updated, and the number of tracked keys remains the same. If the key is not
+ * already being tracked, it is added to the end of the array of tracked keys
+ * providing there is space available, and the number of tracked keys is
+ * updated. Failures to add keys will be automatically logged.
+ *
+ * @param state
+ *     The Guacamole input log interpreter state being updated.
+ *
+ * @param keysym
+ *     The X11 keysym of the key being pressed or released.
+ *
+ * @param pressed
+ *     true if the key is being pressed, false if the key is being released.
+ *
+ * @return
+ *     Zero if the key state was successfully added, non-zero otherwise.
+ */
+static int guaclog_state_add_key(guaclog_state* state, int keysym, bool pressed) {
+
+    int i;
+
+    /* Update existing key, if already tracked */
+    for (i = 0; i < state->active_keys; i++) {
+        guaclog_key_state* key = &state->key_states[i];
+        if (key->keysym == keysym) {
+            key->pressed = pressed;
+            return 0;
+        }
+    }
+
+    /* If not already tracked, we need space to add it */
+    if (state->active_keys == GUACLOG_MAX_KEYS) {
+        guaclog_log(GUAC_LOG_WARNING, "Unable to log key 0x%X: Too many "
+                "active keys.", keysym);
+        return 1;
+    }
+
+    /* Add key to state */
+    guaclog_key_state* key = &state->key_states[state->active_keys++];
+    key->keysym = keysym;
+    key->pressed = pressed;
+    return 0;
+
+}
+
+/**
+ * Removes released keys from the end of the array of tracked keys, such that
+ * the last key in the array is a pressed key. This function should be invoked
+ * after changes have been made to the interpreter state, to ensure that the
+ * array of tracked keys does not grow longer than necessary.
+ *
+ * @param state
+ *     The Guacamole input log interpreter state to trim.
+ */
+static void guaclog_state_trim_keys(guaclog_state* state) {
+
+    int i;
+
+    /* Reset active_keys to contain only up to the last pressed key */
+    for (i = state->active_keys - 1; i >= 0; i--) {
+        guaclog_key_state* key = &state->key_states[i];
+        if (key->pressed) {
+            state->active_keys = i + 1;
+            return;
+        }
+    }
+
+    /* No keys are active */
+    state->active_keys = 0;
+
+}
+
 int guaclog_state_update_key(guaclog_state* state, int keysym, bool pressed) {
 
-    /* STUB */
-    fprintf(state->output, "STUB: keysym=0x%X, pressed=%s\n",
-            keysym, pressed ? "true" : "false");
+    int i;
+
+    /* Update tracked keysysm state */
+    guaclog_state_add_key(state, keysym, pressed);
+    guaclog_state_trim_keys(state);
+
+    /* Output new log entries only when keys are pressed */
+    if (pressed) {   
+
+        /* STUB: Output raw hex log entry */
+        for (i = 0; i < state->active_keys; i++) {
+
+            if (i != 0)
+                fprintf(state->output, " ");
+
+            guaclog_key_state* key = &state->key_states[i];
+            fprintf(state->output, "0x%X:%s", key->keysym,
+                    key->pressed ? "*" : " ");
+
+        }
+
+        /* Terminate log entry with newline */
+        fprintf(state->output, "\n");
+
+    }
 
     return 0;
 

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/d39757b4/src/guaclog/state.h
----------------------------------------------------------------------
diff --git a/src/guaclog/state.h b/src/guaclog/state.h
index 5891cb5..0dd2f2f 100644
--- a/src/guaclog/state.h
+++ b/src/guaclog/state.h
@@ -26,6 +26,29 @@
 #include <stdio.h>
 
 /**
+ * The maximum number of keys which may be tracked at any one time before
+ * newly-pressed keys are ignored.
+ */
+#define GUACLOG_MAX_KEYS 256
+
+/**
+ * The current state of a single key.
+ */
+typedef struct guaclog_key_state {
+
+    /**
+     * The X11 keysym of the key.
+     */
+    int keysym;
+
+    /**
+     * Whether the key is currently pressed (true) or released (false).
+     */
+    bool pressed;
+
+} guaclog_key_state;
+
+/**
  * The current state of the Guacamole input log interpreter.
  */
 typedef struct guaclog_state {
@@ -35,6 +58,18 @@ typedef struct guaclog_state {
      */
     FILE* output;
 
+    /**
+     * The number of keys currently being tracked within the key_states array.
+     */
+    int active_keys;
+
+    /**
+     * Array of all keys currently being tracked. A key is added to the array
+     * when it is pressed for the first time. Released keys at the end of the
+     * array are automatically removed from tracking.
+     */
+    guaclog_key_state key_states[GUACLOG_MAX_KEYS];
+
 } guaclog_state;
 
 /**


[08/11] guacamole-server git commit: GUACAMOLE-313: Add missing keysyms. Track modifier keys only.

Posted by vn...@apache.org.
GUACAMOLE-313: Add missing keysyms. Track modifier keys only.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/5e5f1fcb
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/5e5f1fcb
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/5e5f1fcb

Branch: refs/heads/master
Commit: 5e5f1fcb3e0d5d2fb2c1c679aacd337142f47393
Parents: 5b612b8
Author: Michael Jumper <mj...@apache.org>
Authored: Fri Dec 8 12:56:27 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 26 16:24:45 2018 -0800

----------------------------------------------------------------------
 src/guaclog/keydef.c | 58 +++++++++++++++++++++++++++++++++++------------
 src/guaclog/keydef.h |  8 +++++++
 src/guaclog/state.c  | 24 ++++++++++++--------
 3 files changed, 66 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/5e5f1fcb/src/guaclog/keydef.c
----------------------------------------------------------------------
diff --git a/src/guaclog/keydef.c b/src/guaclog/keydef.c
index 9616500..427e3ca 100644
--- a/src/guaclog/keydef.c
+++ b/src/guaclog/keydef.c
@@ -30,24 +30,53 @@
  * All known keys.
  */
 const guaclog_keydef known_keys[] = {
-    { 0xFE03, "AltGr" },
+    { 0xFE03, "AltGr", "", true },
     { 0xFF08, "Backspace" },
-    { 0xFF09, "Tab", "<Tab>" },
+    { 0xFF09, "Tab" },
     { 0xFF0B, "Clear" },
     { 0xFF0D, "Return", "\n" },
     { 0xFF13, "Pause" },
+    { 0xFF14, "Scroll" },
+    { 0xFF15, "SysReq" },
     { 0xFF1B, "Escape" },
+    { 0xFF50, "Home" },
     { 0xFF51, "Left" },
     { 0xFF52, "Up" },
     { 0xFF53, "Right" },
     { 0xFF54, "Down" },
     { 0xFF55, "Page Up" },
     { 0xFF56, "Page Down" },
+    { 0xFF57, "End" },
     { 0xFF63, "Insert" },
     { 0xFF65, "Undo" },
     { 0xFF6A, "Help" },
+    { 0xFF7F, "Num" },
     { 0xFF80, "Space", " " },
     { 0xFF8D, "Enter", "\n" },
+    { 0xFF95, "Home" },
+    { 0xFF96, "Left" },
+    { 0xFF97, "Up" },
+    { 0xFF98, "Right" },
+    { 0xFF99, "Down" },
+    { 0xFF9A, "Page Up" },
+    { 0xFF9B, "Page Down" },
+    { 0xFF9C, "End" },
+    { 0xFF9E, "Insert" },
+    { 0xFFAA, "*", "*" },
+    { 0xFFAB, "+", "+" },
+    { 0xFFAD, "-", "-" },
+    { 0xFFAE, ".", "." },
+    { 0xFFAF, "/", "/" },
+    { 0xFFB0, "0", "0" },
+    { 0xFFB1, "1", "1" },
+    { 0xFFB2, "2", "2" },
+    { 0xFFB3, "3", "3" },
+    { 0xFFB4, "4", "4" },
+    { 0xFFB5, "5", "5" },
+    { 0xFFB6, "6", "6" },
+    { 0xFFB7, "7", "7" },
+    { 0xFFB8, "8", "8" },
+    { 0xFFB9, "9", "9" },
     { 0xFFBE, "F1" },
     { 0xFFBF, "F2" },
     { 0xFFC0, "F3" },
@@ -72,20 +101,19 @@ const guaclog_keydef known_keys[] = {
     { 0xFFD3, "F22" },
     { 0xFFD4, "F23" },
     { 0xFFD5, "F24" },
-    { 0xFFE1, "Shift", "" },
-    { 0xFFE2, "Shift", "" },
-    { 0xFFE3, "Ctrl" },
-    { 0xFFE4, "Ctrl" },
+    { 0xFFE1, "Shift", "", true },
+    { 0xFFE2, "Shift", "", true },
+    { 0xFFE3, "Ctrl", NULL, true },
+    { 0xFFE4, "Ctrl", NULL, true },
     { 0xFFE5, "Caps" },
-    { 0xFFE7, "Meta" },
-    { 0xFFE8, "Meta" },
-    { 0xFFE9, "Alt" },
-    { 0xFFEA, "Alt" },
-    { 0xFFEB, "Super" },
-    { 0xFFEB, "Win" },
-    { 0xFFEC, "Super" },
-    { 0xFFED, "Hyper" },
-    { 0xFFEE, "Hyper" },
+    { 0xFFE7, "Meta", NULL, true },
+    { 0xFFE8, "Meta", NULL, true },
+    { 0xFFE9, "Alt", NULL, true },
+    { 0xFFEA, "Alt", NULL, true },
+    { 0xFFEB, "Super", NULL, true },
+    { 0xFFEC, "Super", NULL, true },
+    { 0xFFED, "Hyper", NULL, true },
+    { 0xFFEE, "Hyper", NULL, true },
     { 0xFFFF, "Delete" }
 };
 

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/5e5f1fcb/src/guaclog/keydef.h
----------------------------------------------------------------------
diff --git a/src/guaclog/keydef.h b/src/guaclog/keydef.h
index d0b2720..147fe20 100644
--- a/src/guaclog/keydef.h
+++ b/src/guaclog/keydef.h
@@ -22,6 +22,8 @@
 
 #include "config.h"
 
+#include <stdbool.h>
+
 /**
  * A mapping of X11 keysym to its corresponding human-readable name.
  */
@@ -44,6 +46,12 @@ typedef struct guaclog_keydef {
      */
     char* value;
 
+    /**
+     * Whether this key is a modifier which may affect the interpretation of
+     * other keys, and thus should be tracked as it is held down.
+     */
+    bool modifier;
+
 } guaclog_keydef;
 
 /**

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/5e5f1fcb/src/guaclog/state.c
----------------------------------------------------------------------
diff --git a/src/guaclog/state.c b/src/guaclog/state.c
index 4965985..5e18708 100644
--- a/src/guaclog/state.c
+++ b/src/guaclog/state.c
@@ -217,14 +217,16 @@ int guaclog_state_update_key(guaclog_state* state, int keysym, bool pressed) {
     if (keydef == NULL)
         return 0;
 
-    /* Update tracked key state */
-    if (guaclog_state_add_key(state, keydef, pressed))
-        guaclog_keydef_free(keydef);
-    else
-        guaclog_state_trim_keys(state);
+    /* Update tracked key state for modifiers */
+    if (keydef->modifier) {
+        if (guaclog_state_add_key(state, keydef, pressed))
+            guaclog_keydef_free(keydef);
+        else
+            guaclog_state_trim_keys(state);
+    }
 
     /* Output key states only for printable keys */
-    if (pressed && keydef->value != NULL) {
+    else if (pressed) {
 
         if (guaclog_state_is_shortcut(state)) {
 
@@ -244,13 +246,17 @@ int guaclog_state_update_key(guaclog_state* state, int keysym, bool pressed) {
 
             }
 
-            fprintf(state->output, ">");
+            fprintf(state->output, "%s>", keydef->value);
 
         }
 
         /* Print the key itself */
-        else
-            fprintf(state->output, "%s", keydef->value);
+        else {
+            if (keydef->value != NULL)
+                fprintf(state->output, "%s", keydef->value);
+            else
+                fprintf(state->output, "<%s>", keydef->name);
+        }
 
     }
 


[09/11] guacamole-server git commit: GUACAMOLE-313: Include unknown keys within log.

Posted by vn...@apache.org.
GUACAMOLE-313: Include unknown keys within log.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/b7257d9a
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/b7257d9a
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/b7257d9a

Branch: refs/heads/master
Commit: b7257d9ae45287a8a9e1b80920c677d75863698c
Parents: 5e5f1fc
Author: Michael Jumper <mj...@apache.org>
Authored: Fri Dec 8 13:10:32 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 26 16:24:45 2018 -0800

----------------------------------------------------------------------
 src/guaclog/keydef.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/b7257d9a/src/guaclog/keydef.c
----------------------------------------------------------------------
diff --git a/src/guaclog/keydef.c b/src/guaclog/keydef.c
index 427e3ca..6b979fa 100644
--- a/src/guaclog/keydef.c
+++ b/src/guaclog/keydef.c
@@ -21,6 +21,7 @@
 #include "keydef.h"
 #include "log.h"
 
+#include <assert.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -169,6 +170,36 @@ static guaclog_keydef* guaclog_get_known_key(int keysym) {
 }
 
 /**
+ * Returns a statically-allocated guaclog_keydef representing an unknown key,
+ * deriving the name of the key from the hexadecimal value of the keysym.
+ *
+ * @param keysym
+ *     The X11 keysym of the key.
+ *
+ * @return
+ *     A statically-allocated guaclog_keydef representing the key associated
+ *     with the given keysym.
+ */
+static guaclog_keydef* guaclog_get_unknown_key(int keysym) {
+
+    static char unknown_keydef_name[64];
+    static guaclog_keydef unknown_keydef;
+
+    /* Write keysym as hex */
+    int size = snprintf(unknown_keydef_name, sizeof(unknown_keydef_name),
+            "0x%X", keysym);
+
+    /* Hex string is guaranteed to fit within the provided 64 bytes */
+    assert(size < sizeof(unknown_keydef_name));
+
+    /* Return static key definition */
+    unknown_keydef.keysym = keysym;
+    unknown_keydef.name = unknown_keydef_name;
+    return &unknown_keydef;
+
+}
+
+/**
  * Returns a statically-allocated guaclog_keydef representing the key
  * associated with the given keysym, deriving the name and value of the key
  * using its corresponding Unicode character.
@@ -286,7 +317,7 @@ guaclog_keydef* guaclog_keydef_alloc(int keysym) {
 
     /* Key not known */
     guaclog_log(GUAC_LOG_DEBUG, "Definition not found for key 0x%X.", keysym);
-    return NULL;
+    return guaclog_copy_key(guaclog_get_unknown_key(keysym));
 
 }
 


[06/11] guacamole-server git commit: GUACAMOLE-313: Use binary search to find human-readable names for known keys.

Posted by vn...@apache.org.
GUACAMOLE-313: Use binary search to find human-readable names for known keys.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/3633af5e
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/3633af5e
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/3633af5e

Branch: refs/heads/master
Commit: 3633af5e418876932ab086b910ca5a43df425cbe
Parents: df29735
Author: Michael Jumper <mj...@apache.org>
Authored: Sun Nov 26 18:54:59 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 26 16:24:45 2018 -0800

----------------------------------------------------------------------
 src/guaclog/key-name.c | 99 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 97 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/3633af5e/src/guaclog/key-name.c
----------------------------------------------------------------------
diff --git a/src/guaclog/key-name.c b/src/guaclog/key-name.c
index ffb622d..f3485a2 100644
--- a/src/guaclog/key-name.c
+++ b/src/guaclog/key-name.c
@@ -22,12 +22,107 @@
 #include "log.h"
 
 #include <stdio.h>
+#include <stdlib.h>
+
+/**
+ * A mapping of X11 keysym to its corresponding human-readable name.
+ */
+typedef struct guaclog_known_key {
+
+    /**
+     * The X11 keysym of the key.
+     */
+    const int keysym;
+
+    /**
+     * A human-readable name for the key.
+     */
+    const char* name;
+
+} guaclog_known_key;
+
+/**
+ * All known keys.
+ */
+const guaclog_known_key known_keys[] = {
+    { 0xFFE1, "Shift" }
+};
+
+/**
+ * Comparator for the standard bsearch() function which compares an integer
+ * keysym against the keysym associated with a guaclog_known_key.
+ *
+ * @param key
+ *     The key value being compared against the member. This MUST be the
+ *     keysym value, passed through typecasting to an intptr_t (NOT a pointer
+ *     to the int itself).
+ *
+ * @param member
+ *     The member within the known_keys array being compared against the given
+ *     key.
+ *
+ * @return
+ *     Zero if the given keysym is equal to that of the given member, a
+ *     positive value if the given keysym is greater than that of the given
+ *     member, or a negative value if the given keysym is less than that of the
+ *     given member.
+ */
+static int guaclog_known_key_bsearch_compare(const void* key,
+        const void* member) {
+
+    int keysym = (int) ((intptr_t) key);
+    guaclog_known_key* current = (guaclog_known_key*) member;
+
+    /* Compare given keysym to keysym of current member */
+    return keysym  - current->keysym;
+
+}
+
+/**
+ * Searches through the known_keys array of known keys for the name of the key
+ * having the given keysym. If found, the name of the keysym is copied into the
+ * given buffer, which must be at least GUACLOG_MAX_KEY_NAME_LENGTH bytes long.
+ *
+ * @param key_name
+ *     The buffer to copy the key name into, which must be at least
+ *     GUACLOG_MAX_KEY_NAME_LENGTH.
+ *
+ * @param keysym
+ *     The X11 keysym of the key whose name should be stored in
+ *     key_name.
+ *
+ * @return
+ *     The length of the name, in bytes, excluding null terminator, or zero if
+ *     the key could not be found.
+ */
+static int guaclog_locate_key_name(char* key_name, int keysym) {
+
+    /* Search through known keys for given keysym */
+    guaclog_known_key* found = bsearch((void*) ((intptr_t) keysym),
+            known_keys, sizeof(known_keys) / sizeof(known_keys[0]),
+            sizeof(known_keys[0]), guaclog_known_key_bsearch_compare);
+
+    /* If found, format name and return length of result */
+    if (found != NULL)
+        return snprintf(key_name, GUACLOG_MAX_KEY_NAME_LENGTH,
+                "[ %s ]", found->name);
+
+    /* Key not found */
+    return 0;
+
+}
 
 int guaclog_key_name(char* key_name, int keysym) {
 
+    int name_length;
+
+    /* Search for name within list of known keys */
+    name_length = guaclog_locate_key_name(key_name, keysym);
+
     /* Fallback to using hex keysym as name */
-    int name_length = snprintf(key_name, GUACLOG_MAX_KEY_NAME_LENGTH,
-            "0x%X", keysym);
+    if (name_length == 0)
+        name_length = snprintf(key_name, GUACLOG_MAX_KEY_NAME_LENGTH,
+                "0x%X", keysym);
 
     /* Truncate name if necessary */
     if (name_length >= GUACLOG_MAX_KEY_NAME_LENGTH) {


[10/11] guacamole-server git commit: GUACAMOLE-313: Note that guacenc/guaclog are related.

Posted by vn...@apache.org.
GUACAMOLE-313: Note that guacenc/guaclog are related.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/fdd17e30
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/fdd17e30
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/fdd17e30

Branch: refs/heads/master
Commit: fdd17e30422dd91337d044ecff02db52aab32691
Parents: c0b2871
Author: Michael Jumper <mj...@apache.org>
Authored: Fri Jan 26 16:16:40 2018 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 26 16:24:45 2018 -0800

----------------------------------------------------------------------
 src/guacenc/man/guacenc.1.in | 5 ++++-
 src/guaclog/man/guaclog.1.in | 4 +++-
 2 files changed, 7 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/fdd17e30/src/guacenc/man/guacenc.1.in
----------------------------------------------------------------------
diff --git a/src/guacenc/man/guacenc.1.in b/src/guacenc/man/guacenc.1.in
index edb75f6..4f00787 100644
--- a/src/guacenc/man/guacenc.1.in
+++ b/src/guacenc/man/guacenc.1.in
@@ -16,7 +16,7 @@
 .\" specific language governing permissions and limitations
 .\" under the License.
 .\"
-.TH guacenc 1 "1 Jun 2017" "version @PACKAGE_VERSION@" "Apache Guacamole"
+.TH guacenc 1 "26 Jan 2018" "version @PACKAGE_VERSION@" "Apache Guacamole"
 .
 .SH NAME
 guacenc \- Guacamole video encoder
@@ -75,3 +75,6 @@ Overrides the default behavior of
 .B guacenc
 such that input files will be encoded even if they appear to be recordings of
 in-progress Guacamole sessions.
+.
+.SH SEE ALSO
+.BR guaclog (1)

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/fdd17e30/src/guaclog/man/guaclog.1.in
----------------------------------------------------------------------
diff --git a/src/guaclog/man/guaclog.1.in b/src/guaclog/man/guaclog.1.in
index e5dfa9d..de14559 100644
--- a/src/guaclog/man/guaclog.1.in
+++ b/src/guaclog/man/guaclog.1.in
@@ -94,4 +94,6 @@ would look like:
 .PP
 .RS 0
 Hello WORLD!<Ctrl+a><Ctrl+c><Alt+Shift+Tab><Ctrl+v>
-
+.
+.SH SEE ALSO
+.BR guacenc (1)


[07/11] guacamole-server git commit: GUACAMOLE-313: Refactor guaclog to produce simpler, greppable output.

Posted by vn...@apache.org.
GUACAMOLE-313: Refactor guaclog to produce simpler, greppable output.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/5b612b85
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/5b612b85
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/5b612b85

Branch: refs/heads/master
Commit: 5b612b856afc29073595d7ce450560b0bff65415
Parents: 86b09c8
Author: Michael Jumper <mj...@apache.org>
Authored: Wed Dec 6 21:25:58 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 26 16:24:45 2018 -0800

----------------------------------------------------------------------
 src/guaclog/Makefile.am |   4 +-
 src/guaclog/key-name.c  | 276 -------------------------------------------
 src/guaclog/key-name.h  |  50 --------
 src/guaclog/keydef.c    | 276 +++++++++++++++++++++++++++++++++++++++++++
 src/guaclog/keydef.h    |  73 ++++++++++++
 src/guaclog/state.c     | 111 ++++++++++++-----
 src/guaclog/state.h     |   5 +-
 7 files changed, 434 insertions(+), 361 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/5b612b85/src/guaclog/Makefile.am
----------------------------------------------------------------------
diff --git a/src/guaclog/Makefile.am b/src/guaclog/Makefile.am
index 5007708..6acced3 100644
--- a/src/guaclog/Makefile.am
+++ b/src/guaclog/Makefile.am
@@ -28,7 +28,7 @@ noinst_HEADERS =   \
     guaclog.h      \
     instructions.h \
     interpret.h    \
-    key-name.h     \
+    keydef.h       \
     log.h          \
     state.h
 
@@ -37,7 +37,7 @@ guaclog_SOURCES =     \
     instructions.c    \
     instruction-key.c \
     interpret.c       \
-    key-name.c        \
+    keydef.c          \
     log.c             \
     state.c
 

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/5b612b85/src/guaclog/key-name.c
----------------------------------------------------------------------
diff --git a/src/guaclog/key-name.c b/src/guaclog/key-name.c
deleted file mode 100644
index 5ae73b5..0000000
--- a/src/guaclog/key-name.c
+++ /dev/null
@@ -1,276 +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 "key-name.h"
-#include "log.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-/**
- * A mapping of X11 keysym to its corresponding human-readable name.
- */
-typedef struct guaclog_known_key {
-
-    /**
-     * The X11 keysym of the key.
-     */
-    const int keysym;
-
-    /**
-     * A human-readable name for the key.
-     */
-    const char* name;
-
-} guaclog_known_key;
-
-/**
- * All known keys.
- */
-const guaclog_known_key known_keys[] = {
-    { 0x0020, "Space" },
-    { 0xFE03, "AltGr" },
-    { 0xFF08, "Backspace" },
-    { 0xFF09, "Tab" },
-    { 0xFF0B, "Clear" },
-    { 0xFF0D, "Return" },
-    { 0xFF13, "Pause" },
-    { 0xFF1B, "Escape" },
-    { 0xFF51, "Left" },
-    { 0xFF52, "Up" },
-    { 0xFF53, "Right" },
-    { 0xFF54, "Down" },
-    { 0xFF55, "Page Up" },
-    { 0xFF56, "Page Down" },
-    { 0xFF63, "Insert" },
-    { 0xFF65, "Undo" },
-    { 0xFF6A, "Help" },
-    { 0xFF80, "Space" },
-    { 0xFF8D, "Enter" },
-    { 0xFFBD, "Equals" },
-    { 0xFFBE, "F1" },
-    { 0xFFBF, "F2" },
-    { 0xFFC0, "F3" },
-    { 0xFFC1, "F4" },
-    { 0xFFC2, "F5" },
-    { 0xFFC3, "F6" },
-    { 0xFFC4, "F7" },
-    { 0xFFC5, "F8" },
-    { 0xFFC6, "F9" },
-    { 0xFFC7, "F10" },
-    { 0xFFC8, "F11" },
-    { 0xFFC9, "F12" },
-    { 0xFFCA, "F13" },
-    { 0xFFCB, "F14" },
-    { 0xFFCC, "F15" },
-    { 0xFFCD, "F16" },
-    { 0xFFCE, "F17" },
-    { 0xFFCF, "F18" },
-    { 0xFFD0, "F19" },
-    { 0xFFD1, "F20" },
-    { 0xFFD2, "F21" },
-    { 0xFFD3, "F22" },
-    { 0xFFD4, "F23" },
-    { 0xFFD5, "F24" },
-    { 0xFFE1, "Shift" },
-    { 0xFFE2, "Shift" },
-    { 0xFFE3, "Ctrl" },
-    { 0xFFE4, "Ctrl" },
-    { 0xFFE5, "Caps" },
-    { 0xFFE7, "Meta" },
-    { 0xFFE8, "Meta" },
-    { 0xFFE9, "Alt" },
-    { 0xFFEA, "Alt" },
-    { 0xFFEB, "Super" },
-    { 0xFFEB, "Win" },
-    { 0xFFEC, "Super" },
-    { 0xFFED, "Hyper" },
-    { 0xFFEE, "Hyper" },
-    { 0xFFFF, "Delete" }
-};
-
-/**
- * Comparator for the standard bsearch() function which compares an integer
- * keysym against the keysym associated with a guaclog_known_key.
- *
- * @param key
- *     The key value being compared against the member. This MUST be the
- *     keysym value, passed through typecasting to an intptr_t (NOT a pointer
- *     to the int itself).
- *
- * @param member
- *     The member within the known_keys array being compared against the given
- *     key.
- *
- * @return
- *     Zero if the given keysym is equal to that of the given member, a
- *     positive value if the given keysym is greater than that of the given
- *     member, or a negative value if the given keysym is less than that of the
- *     given member.
- */
-static int guaclog_known_key_bsearch_compare(const void* key,
-        const void* member) {
-
-    int keysym = (int) ((intptr_t) key);
-    guaclog_known_key* current = (guaclog_known_key*) member;
-
-    /* Compare given keysym to keysym of current member */
-    return keysym  - current->keysym;
-
-}
-
-/**
- * Searches through the known_keys array of known keys for the name of the key
- * having the given keysym. If found, the name of the keysym is copied into the
- * given buffer, which must be at least GUACLOG_MAX_KEY_NAME_LENGTH bytes long.
- *
- * @param key_name
- *     The buffer to copy the key name into, which must be at least
- *     GUACLOG_MAX_KEY_NAME_LENGTH.
- *
- * @param keysym
- *     The X11 keysym of the key whose name should be stored in
- *     key_name.
- *
- * @return
- *     The length of the name, in bytes, excluding null terminator, or zero if
- *     the key could not be found.
- */
-static int guaclog_locate_key_name(char* key_name, int keysym) {
-
-    /* Search through known keys for given keysym */
-    guaclog_known_key* found = bsearch((void*) ((intptr_t) keysym),
-            known_keys, sizeof(known_keys) / sizeof(known_keys[0]),
-            sizeof(known_keys[0]), guaclog_known_key_bsearch_compare);
-
-    /* If found, format name and return length of result */
-    if (found != NULL)
-        return snprintf(key_name, GUACLOG_MAX_KEY_NAME_LENGTH,
-                "[ %s ]", found->name);
-
-    /* Key not found */
-    return 0;
-
-}
-
-/**
- * Produces a name for the key having the given keysym using its corresponding
- * Unicode character. If possible, the name of the keysym is copied into the
- * given buffer, which must be at least GUAC_MAX_KEY_NAME_LENGTH bytes long.
- *
- * @param key_name
- *     The buffer to copy the key name into, which must be at least
- *     GUACLOG_MAX_KEY_NAME_LENGTH.
- *
- * @param keysym
- *     The X11 keysym of the key whose name should be stored in
- *     key_name.
- *
- * @return
- *     The length of the name, in bytes, excluding null terminator, or zero if
- *     a readable name cannot be directly produced via Unicode alone.
- */
-static int guaclog_unicode_key_name(char* key_name, int keysym) {
-
-    int i;
-    int mask, bytes;
-
-    /* Translate only if keysym maps to Unicode */
-    if (keysym < 0x00 || (keysym > 0xFF && (keysym & 0xFFFF0000) != 0x01000000))
-        return 0;
-
-    /* Do not translate whitespace - it will be unreadable */
-    if (keysym == 0x20)
-        return 0;
-
-    int codepoint = keysym & 0xFFFF;
-
-    /* Determine size and initial byte mask */
-    if (codepoint <= 0x007F) {
-        mask  = 0x00;
-        bytes = 1;
-    }
-    else if (codepoint <= 0x7FF) {
-        mask  = 0xC0;
-        bytes = 2;
-    }
-    else if (codepoint <= 0xFFFF) {
-        mask  = 0xE0;
-        bytes = 3;
-    }
-    else if (codepoint <= 0x1FFFFF) {
-        mask  = 0xF0;
-        bytes = 4;
-    }
-
-    /* Otherwise, invalid codepoint */
-    else {
-        *(key_name++) = '?';
-        return 1;
-    }
-
-    /* Offset buffer by size */
-    key_name += bytes;
-
-    /* Add null terminator */
-    *(key_name--) = '\0';
-
-    /* Add trailing bytes, if any */
-    for (i=1; i<bytes; i++) {
-        *(key_name--) = 0x80 | (codepoint & 0x3F);
-        codepoint >>= 6;
-    }
-
-    /* Set initial byte */
-    *key_name = mask | codepoint;
-
-    /* Done */
-    return bytes;
-
-}
-
-int guaclog_key_name(char* key_name, int keysym) {
-
-    int name_length;
-
-    /* Attempt to translate straight into a Unicode character */
-    name_length = guaclog_unicode_key_name(key_name, keysym);
-
-    /* If not Unicode, search for name within list of known keys */
-    if (name_length == 0)
-        name_length = guaclog_locate_key_name(key_name, keysym);
-
-    /* Fallback to using hex keysym as name */
-    if (name_length == 0)
-        name_length = snprintf(key_name, GUACLOG_MAX_KEY_NAME_LENGTH,
-                "0x%X", keysym);
-
-    /* Truncate name if necessary */
-    if (name_length >= GUACLOG_MAX_KEY_NAME_LENGTH) {
-        name_length = GUACLOG_MAX_KEY_NAME_LENGTH - 1;
-        key_name[name_length] = '\0';
-        guaclog_log(GUAC_LOG_DEBUG, "Name for key 0x%X was "
-                "truncated.", keysym);
-    }
-
-    return name_length;
-
-}
-

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/5b612b85/src/guaclog/key-name.h
----------------------------------------------------------------------
diff --git a/src/guaclog/key-name.h b/src/guaclog/key-name.h
deleted file mode 100644
index a033b36..0000000
--- a/src/guaclog/key-name.h
+++ /dev/null
@@ -1,50 +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 GUACLOG_KEY_NAME_H
-#define GUACLOG_KEY_NAME_H
-
-#include "config.h"
-
-/**
- * The maximum size of the name of any key, in bytes.
- */
-#define GUACLOG_MAX_KEY_NAME_LENGTH 64
-
-/**
- * Copies the name of the key having the given keysym into the given buffer,
- * which must be at least GUACLOG_MAX_KEY_NAME_LENGTH bytes long. This function
- * always succeeds, ultimately resorting to using the hex value of the keysym
- * as the name if no other human-readable name is known.
- *
- * @param key_name
- *     The buffer to copy the key name into, which must be at least
- *     GUACLOG_MAX_KEY_NAME_LENGTH.
- *
- * @param keysym
- *     The X11 keysym of the key whose name should be stored in
- *     key_name.
- *
- * @return
- *     The length of the name, in bytes, excluding null terminator.
- */
-int guaclog_key_name(char* key_name, int keysym);
-
-#endif
-

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/5b612b85/src/guaclog/keydef.c
----------------------------------------------------------------------
diff --git a/src/guaclog/keydef.c b/src/guaclog/keydef.c
new file mode 100644
index 0000000..9616500
--- /dev/null
+++ b/src/guaclog/keydef.c
@@ -0,0 +1,276 @@
+/*
+ * 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 "keydef.h"
+#include "log.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * All known keys.
+ */
+const guaclog_keydef known_keys[] = {
+    { 0xFE03, "AltGr" },
+    { 0xFF08, "Backspace" },
+    { 0xFF09, "Tab", "<Tab>" },
+    { 0xFF0B, "Clear" },
+    { 0xFF0D, "Return", "\n" },
+    { 0xFF13, "Pause" },
+    { 0xFF1B, "Escape" },
+    { 0xFF51, "Left" },
+    { 0xFF52, "Up" },
+    { 0xFF53, "Right" },
+    { 0xFF54, "Down" },
+    { 0xFF55, "Page Up" },
+    { 0xFF56, "Page Down" },
+    { 0xFF63, "Insert" },
+    { 0xFF65, "Undo" },
+    { 0xFF6A, "Help" },
+    { 0xFF80, "Space", " " },
+    { 0xFF8D, "Enter", "\n" },
+    { 0xFFBE, "F1" },
+    { 0xFFBF, "F2" },
+    { 0xFFC0, "F3" },
+    { 0xFFC1, "F4" },
+    { 0xFFC2, "F5" },
+    { 0xFFC3, "F6" },
+    { 0xFFC4, "F7" },
+    { 0xFFC5, "F8" },
+    { 0xFFC6, "F9" },
+    { 0xFFC7, "F10" },
+    { 0xFFC8, "F11" },
+    { 0xFFC9, "F12" },
+    { 0xFFCA, "F13" },
+    { 0xFFCB, "F14" },
+    { 0xFFCC, "F15" },
+    { 0xFFCD, "F16" },
+    { 0xFFCE, "F17" },
+    { 0xFFCF, "F18" },
+    { 0xFFD0, "F19" },
+    { 0xFFD1, "F20" },
+    { 0xFFD2, "F21" },
+    { 0xFFD3, "F22" },
+    { 0xFFD4, "F23" },
+    { 0xFFD5, "F24" },
+    { 0xFFE1, "Shift", "" },
+    { 0xFFE2, "Shift", "" },
+    { 0xFFE3, "Ctrl" },
+    { 0xFFE4, "Ctrl" },
+    { 0xFFE5, "Caps" },
+    { 0xFFE7, "Meta" },
+    { 0xFFE8, "Meta" },
+    { 0xFFE9, "Alt" },
+    { 0xFFEA, "Alt" },
+    { 0xFFEB, "Super" },
+    { 0xFFEB, "Win" },
+    { 0xFFEC, "Super" },
+    { 0xFFED, "Hyper" },
+    { 0xFFEE, "Hyper" },
+    { 0xFFFF, "Delete" }
+};
+
+/**
+ * Comparator for the standard bsearch() function which compares an integer
+ * keysym against the keysym associated with a guaclog_keydef.
+ *
+ * @param key
+ *     The key value being compared against the member. This MUST be the
+ *     keysym value, passed through typecasting to an intptr_t (NOT a pointer
+ *     to the int itself).
+ *
+ * @param member
+ *     The member within the known_keys array being compared against the given
+ *     key.
+ *
+ * @return
+ *     Zero if the given keysym is equal to that of the given member, a
+ *     positive value if the given keysym is greater than that of the given
+ *     member, or a negative value if the given keysym is less than that of the
+ *     given member.
+ */
+static int guaclog_keydef_bsearch_compare(const void* key,
+        const void* member) {
+
+    int keysym = (int) ((intptr_t) key);
+    guaclog_keydef* current = (guaclog_keydef*) member;
+
+    /* Compare given keysym to keysym of current member */
+    return keysym  - current->keysym;
+
+}
+
+/**
+ * Searches through the known_keys array of known keys for the name of the key
+ * having the given keysym, returning a pointer to the static guaclog_keydef
+ * within the array if found.
+ *
+ * @param keysym
+ *     The X11 keysym of the key.
+ *
+ * @return
+ *     A pointer to the static guaclog_keydef associated with the given keysym,
+ *     or NULL if the key could not be found.
+ */
+static guaclog_keydef* guaclog_get_known_key(int keysym) {
+
+    /* Search through known keys for given keysym */
+    return bsearch((void*) ((intptr_t) keysym),
+            known_keys, sizeof(known_keys) / sizeof(known_keys[0]),
+            sizeof(known_keys[0]), guaclog_keydef_bsearch_compare);
+
+}
+
+/**
+ * Returns a statically-allocated guaclog_keydef representing the key
+ * associated with the given keysym, deriving the name and value of the key
+ * using its corresponding Unicode character.
+ *
+ * @param keysym
+ *     The X11 keysym of the key.
+ *
+ * @return
+ *     A statically-allocated guaclog_keydef representing the key associated
+ *     with the given keysym, or NULL if the given keysym has no corresponding
+ *     Unicode character.
+ */
+static guaclog_keydef* guaclog_get_unicode_key(int keysym) {
+
+    static char unicode_keydef_name[8];
+
+    static guaclog_keydef unicode_keydef;
+
+    int i;
+    int mask, bytes;
+
+    /* Translate only if keysym maps to Unicode */
+    if (keysym < 0x00 || (keysym > 0xFF && (keysym & 0xFFFF0000) != 0x01000000))
+        return NULL;
+
+    int codepoint = keysym & 0xFFFF;
+
+    /* Determine size and initial byte mask */
+    if (codepoint <= 0x007F) {
+        mask  = 0x00;
+        bytes = 1;
+    }
+    else if (codepoint <= 0x7FF) {
+        mask  = 0xC0;
+        bytes = 2;
+    }
+    else if (codepoint <= 0xFFFF) {
+        mask  = 0xE0;
+        bytes = 3;
+    }
+    else if (codepoint <= 0x1FFFFF) {
+        mask  = 0xF0;
+        bytes = 4;
+    }
+
+    /* Otherwise, invalid codepoint */
+    else
+        return NULL;
+
+    /* Offset buffer by size */
+    char* key_name = unicode_keydef_name + bytes;
+
+    /* Add null terminator */
+    *(key_name--) = '\0';
+
+    /* Add trailing bytes, if any */
+    for (i=1; i<bytes; i++) {
+        *(key_name--) = 0x80 | (codepoint & 0x3F);
+        codepoint >>= 6;
+    }
+
+    /* Set initial byte */
+    *key_name = mask | codepoint;
+
+    /* Return static key definition */
+    unicode_keydef.keysym = keysym;
+    unicode_keydef.name = unicode_keydef.value = unicode_keydef_name;
+    return &unicode_keydef;
+
+}
+
+/**
+ * Copies the given guaclog_keydef into a newly-allocated guaclog_keydef
+ * structure. The resulting guaclog_keydef must eventually be freed through a
+ * call to guaclog_keydef_free().
+ *
+ * @param keydef
+ *     The guaclog_keydef to copy.
+ *
+ * @return
+ *     A newly-allocated guaclog_keydef structure copied from the given
+ *     guaclog_keydef.
+ */
+static guaclog_keydef* guaclog_copy_key(guaclog_keydef* keydef) {
+
+    guaclog_keydef* copy = malloc(sizeof(guaclog_keydef));
+
+    /* Always copy keysym and name */
+    copy->keysym = keydef->keysym;
+    copy->name = strdup(keydef->name);
+
+    /* Copy value only if defined */
+    if (keydef->value != NULL)
+        copy->value = strdup(keydef->value);
+    else
+        copy->value = NULL;
+
+    return copy;
+
+}
+
+guaclog_keydef* guaclog_keydef_alloc(int keysym) {
+
+    guaclog_keydef* keydef;
+
+    /* Check list of known keys first */
+    keydef = guaclog_get_known_key(keysym);
+    if (keydef != NULL)
+        return guaclog_copy_key(keydef);
+
+    /* Failing that, attempt to translate straight into a Unicode character */
+    keydef = guaclog_get_unicode_key(keysym);
+    if (keydef != NULL)
+        return guaclog_copy_key(keydef);
+
+    /* Key not known */
+    guaclog_log(GUAC_LOG_DEBUG, "Definition not found for key 0x%X.", keysym);
+    return NULL;
+
+}
+
+void guaclog_keydef_free(guaclog_keydef* keydef) {
+
+    /* Ignore NULL keydef */
+    if (keydef == NULL)
+        return;
+
+    free(keydef->name);
+    free(keydef->value);
+    free(keydef);
+
+}
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/5b612b85/src/guaclog/keydef.h
----------------------------------------------------------------------
diff --git a/src/guaclog/keydef.h b/src/guaclog/keydef.h
new file mode 100644
index 0000000..d0b2720
--- /dev/null
+++ b/src/guaclog/keydef.h
@@ -0,0 +1,73 @@
+/*
+ * 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 GUACLOG_KEYDEF_H
+#define GUACLOG_KEYDEF_H
+
+#include "config.h"
+
+/**
+ * A mapping of X11 keysym to its corresponding human-readable name.
+ */
+typedef struct guaclog_keydef {
+
+    /**
+     * The X11 keysym of the key.
+     */
+    int keysym;
+
+    /**
+     * A human-readable name for the key.
+     */
+    char* name;
+
+    /**
+     * The value which would be typed in a typical text editor, if any. If the
+     * key is not associated with any typable value, or if the typable value is
+     * not generally useful in an auditing context, this will be NULL.
+     */
+    char* value;
+
+} guaclog_keydef;
+
+/**
+ * Creates a new guaclog_keydef which represents the key having the given
+ * keysym. The resulting guaclog_keydef must eventually be freed through a
+ * call to guaclog_keydef_free().
+ *
+ * @param keysym
+ *     The X11 keysym of the key.
+ *
+ * @return
+ *     A new guaclog_keydef which represents the key having the given keysym,
+ *     or NULL if no such key is known.
+ */
+guaclog_keydef* guaclog_keydef_alloc(int keysym);
+
+/**
+ * Frees all resources associated with the given guaclog_keydef. If the given
+ * guaclog_keydef is NULL, this function has no effect.
+ *
+ * @param keydef
+ *     The guaclog_keydef to free, which may be NULL.
+ */
+void guaclog_keydef_free(guaclog_keydef* keydef);
+
+#endif
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/5b612b85/src/guaclog/state.c
----------------------------------------------------------------------
diff --git a/src/guaclog/state.c b/src/guaclog/state.c
index 616d043..4965985 100644
--- a/src/guaclog/state.c
+++ b/src/guaclog/state.c
@@ -18,7 +18,7 @@
  */
 
 #include "config.h"
-#include "key-name.h"
+#include "keydef.h"
 #include "log.h"
 #include "state.h"
 
@@ -77,10 +77,16 @@ fail_output_fd:
 
 int guaclog_state_free(guaclog_state* state) {
 
+    int i;
+
     /* Ignore NULL state */
     if (state == NULL)
         return 0;
 
+    /* Free keydefs of all tracked keys */
+    for (i = 0; i < state->active_keys; i++)
+        guaclog_keydef_free(state->key_states[i].keydef);
+
     /* Close output file */
     fclose(state->output);
 
@@ -100,8 +106,11 @@ int guaclog_state_free(guaclog_state* state) {
  * @param state
  *     The Guacamole input log interpreter state being updated.
  *
- * @param keysym
- *     The X11 keysym of the key being pressed or released.
+ * @param keydef
+ *     The guaclog_keydef of the key being pressed or released. This
+ *     guaclog_keydef will automatically be freed along with the guaclog_state
+ *     if the key state was successfully added, and must be manually freed
+ *     otherwise.
  *
  * @param pressed
  *     true if the key is being pressed, false if the key is being released.
@@ -109,14 +118,17 @@ int guaclog_state_free(guaclog_state* state) {
  * @return
  *     Zero if the key state was successfully added, non-zero otherwise.
  */
-static int guaclog_state_add_key(guaclog_state* state, int keysym, bool pressed) {
+static int guaclog_state_add_key(guaclog_state* state, guaclog_keydef* keydef,
+        bool pressed) {
 
     int i;
 
     /* Update existing key, if already tracked */
     for (i = 0; i < state->active_keys; i++) {
         guaclog_key_state* key = &state->key_states[i];
-        if (key->keysym == keysym) {
+        if (key->keydef->keysym == keydef->keysym) {
+            guaclog_keydef_free(key->keydef);
+            key->keydef = keydef;
             key->pressed = pressed;
             return 0;
         }
@@ -125,13 +137,13 @@ static int guaclog_state_add_key(guaclog_state* state, int keysym, bool pressed)
     /* If not already tracked, we need space to add it */
     if (state->active_keys == GUACLOG_MAX_KEYS) {
         guaclog_log(GUAC_LOG_WARNING, "Unable to log key 0x%X: Too many "
-                "active keys.", keysym);
+                "active keys.", keydef->keysym);
         return 1;
     }
 
     /* Add key to state */
     guaclog_key_state* key = &state->key_states[state->active_keys++];
-    key->keysym = keysym;
+    key->keydef = keydef;
     key->pressed = pressed;
     return 0;
 
@@ -152,11 +164,16 @@ static void guaclog_state_trim_keys(guaclog_state* state) {
 
     /* Reset active_keys to contain only up to the last pressed key */
     for (i = state->active_keys - 1; i >= 0; i--) {
+
         guaclog_key_state* key = &state->key_states[i];
         if (key->pressed) {
             state->active_keys = i + 1;
             return;
         }
+
+        /* Free all trimmed states */
+        guaclog_keydef_free(key->keydef);
+
     }
 
     /* No keys are active */
@@ -164,44 +181,76 @@ static void guaclog_state_trim_keys(guaclog_state* state) {
 
 }
 
+/**
+ * Returns whether the current tracked key state represents an in-progress
+ * keyboard shortcut.
+ *
+ * @param state
+ *     The Guacamole input log interpreter state to test.
+ *
+ * @return
+ *     true if the given state represents an in-progress keyboard shortcut,
+ *     false otherwise.
+ */
+static bool guaclog_state_is_shortcut(guaclog_state* state) {
+
+    int i;
+
+    /* We are in a shortcut if at least one key is non-printable */
+    for (i = 0; i < state->active_keys; i++) {
+        guaclog_key_state* key = &state->key_states[i];
+        if (key->keydef->value == NULL)
+            return true;
+    }
+
+    /* All keys are printable - no shortcut */
+    return false;
+
+}
+
 int guaclog_state_update_key(guaclog_state* state, int keysym, bool pressed) {
 
     int i;
 
-    /* Update tracked keysysm state */
-    guaclog_state_add_key(state, keysym, pressed);
-    guaclog_state_trim_keys(state);
+    /* Determine nature of key */
+    guaclog_keydef* keydef = guaclog_keydef_alloc(keysym);
+    if (keydef == NULL)
+        return 0;
 
-    /* Output new log entries only when keys are pressed */
-    if (pressed) {   
+    /* Update tracked key state */
+    if (guaclog_state_add_key(state, keydef, pressed))
+        guaclog_keydef_free(keydef);
+    else
+        guaclog_state_trim_keys(state);
 
-        /* Compose log entry by inspecting the state of each tracked key */
-        for (i = 0; i < state->active_keys; i++) {
+    /* Output key states only for printable keys */
+    if (pressed && keydef->value != NULL) {
 
-            guaclog_key_state* key = &state->key_states[i];
+        if (guaclog_state_is_shortcut(state)) {
 
-            /* Translate keysym into human-readable name */
-            char key_name[GUACLOG_MAX_KEY_NAME_LENGTH];
-            int name_length = guaclog_key_name(key_name, key->keysym);
+            fprintf(state->output, "<");
 
-            /* If not the final key, omit the name (it was printed earlier) */
-            if (i < state->active_keys - 1) {
-                memset(key_name, ' ', name_length);
-                if (key->pressed)
-                    key_name[name_length / 2] = '*';
-            }
+            /* Compose log entry by inspecting the state of each tracked key */
+            for (i = 0; i < state->active_keys; i++) {
 
-            /* Separate each key by a single space */
-            if (i != 0)
-                fprintf(state->output, " ");
+                /* Translate keysym into human-readable name */
+                guaclog_key_state* key = &state->key_states[i];
+
+                /* Print name of key */
+                if (i == 0)
+                    fprintf(state->output, "%s", key->keydef->name);
+                else
+                    fprintf(state->output, "+%s", key->keydef->name);
+
+            }
 
-            /* Print name of key */
-            fprintf(state->output, "%s", key_name);
+            fprintf(state->output, ">");
 
         }
 
-        /* Terminate log entry with newline */
-        fprintf(state->output, "\n");
+        /* Print the key itself */
+        else
+            fprintf(state->output, "%s", keydef->value);
 
     }
 

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/5b612b85/src/guaclog/state.h
----------------------------------------------------------------------
diff --git a/src/guaclog/state.h b/src/guaclog/state.h
index 0dd2f2f..b476dde 100644
--- a/src/guaclog/state.h
+++ b/src/guaclog/state.h
@@ -21,6 +21,7 @@
 #define GUACLOG_STATE_H
 
 #include "config.h"
+#include "keydef.h"
 
 #include <stdbool.h>
 #include <stdio.h>
@@ -37,9 +38,9 @@
 typedef struct guaclog_key_state {
 
     /**
-     * The X11 keysym of the key.
+     * The definition of the key.
      */
-    int keysym;
+    guaclog_keydef* keydef;
 
     /**
      * Whether the key is currently pressed (true) or released (false).


[05/11] guacamole-server git commit: GUACAMOLE-313: Add remaining key names. Use Unicode where possible.

Posted by vn...@apache.org.
GUACAMOLE-313: Add remaining key names. Use Unicode where possible.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/86b09c8c
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/86b09c8c
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/86b09c8c

Branch: refs/heads/master
Commit: 86b09c8cf7fefd1f2dd7f368f1369f19d430bf7c
Parents: 3633af5
Author: Michael Jumper <mj...@apache.org>
Authored: Sun Nov 26 19:35:47 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 26 16:24:45 2018 -0800

----------------------------------------------------------------------
 src/guaclog/key-name.c | 144 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 141 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/86b09c8c/src/guaclog/key-name.c
----------------------------------------------------------------------
diff --git a/src/guaclog/key-name.c b/src/guaclog/key-name.c
index f3485a2..5ae73b5 100644
--- a/src/guaclog/key-name.c
+++ b/src/guaclog/key-name.c
@@ -45,7 +45,65 @@ typedef struct guaclog_known_key {
  * All known keys.
  */
 const guaclog_known_key known_keys[] = {
-    { 0xFFE1, "Shift" }
+    { 0x0020, "Space" },
+    { 0xFE03, "AltGr" },
+    { 0xFF08, "Backspace" },
+    { 0xFF09, "Tab" },
+    { 0xFF0B, "Clear" },
+    { 0xFF0D, "Return" },
+    { 0xFF13, "Pause" },
+    { 0xFF1B, "Escape" },
+    { 0xFF51, "Left" },
+    { 0xFF52, "Up" },
+    { 0xFF53, "Right" },
+    { 0xFF54, "Down" },
+    { 0xFF55, "Page Up" },
+    { 0xFF56, "Page Down" },
+    { 0xFF63, "Insert" },
+    { 0xFF65, "Undo" },
+    { 0xFF6A, "Help" },
+    { 0xFF80, "Space" },
+    { 0xFF8D, "Enter" },
+    { 0xFFBD, "Equals" },
+    { 0xFFBE, "F1" },
+    { 0xFFBF, "F2" },
+    { 0xFFC0, "F3" },
+    { 0xFFC1, "F4" },
+    { 0xFFC2, "F5" },
+    { 0xFFC3, "F6" },
+    { 0xFFC4, "F7" },
+    { 0xFFC5, "F8" },
+    { 0xFFC6, "F9" },
+    { 0xFFC7, "F10" },
+    { 0xFFC8, "F11" },
+    { 0xFFC9, "F12" },
+    { 0xFFCA, "F13" },
+    { 0xFFCB, "F14" },
+    { 0xFFCC, "F15" },
+    { 0xFFCD, "F16" },
+    { 0xFFCE, "F17" },
+    { 0xFFCF, "F18" },
+    { 0xFFD0, "F19" },
+    { 0xFFD1, "F20" },
+    { 0xFFD2, "F21" },
+    { 0xFFD3, "F22" },
+    { 0xFFD4, "F23" },
+    { 0xFFD5, "F24" },
+    { 0xFFE1, "Shift" },
+    { 0xFFE2, "Shift" },
+    { 0xFFE3, "Ctrl" },
+    { 0xFFE4, "Ctrl" },
+    { 0xFFE5, "Caps" },
+    { 0xFFE7, "Meta" },
+    { 0xFFE8, "Meta" },
+    { 0xFFE9, "Alt" },
+    { 0xFFEA, "Alt" },
+    { 0xFFEB, "Super" },
+    { 0xFFEB, "Win" },
+    { 0xFFEC, "Super" },
+    { 0xFFED, "Hyper" },
+    { 0xFFEE, "Hyper" },
+    { 0xFFFF, "Delete" }
 };
 
 /**
@@ -112,12 +170,92 @@ static int guaclog_locate_key_name(char* key_name, int keysym) {
 
 }
 
+/**
+ * Produces a name for the key having the given keysym using its corresponding
+ * Unicode character. If possible, the name of the keysym is copied into the
+ * given buffer, which must be at least GUAC_MAX_KEY_NAME_LENGTH bytes long.
+ *
+ * @param key_name
+ *     The buffer to copy the key name into, which must be at least
+ *     GUACLOG_MAX_KEY_NAME_LENGTH.
+ *
+ * @param keysym
+ *     The X11 keysym of the key whose name should be stored in
+ *     key_name.
+ *
+ * @return
+ *     The length of the name, in bytes, excluding null terminator, or zero if
+ *     a readable name cannot be directly produced via Unicode alone.
+ */
+static int guaclog_unicode_key_name(char* key_name, int keysym) {
+
+    int i;
+    int mask, bytes;
+
+    /* Translate only if keysym maps to Unicode */
+    if (keysym < 0x00 || (keysym > 0xFF && (keysym & 0xFFFF0000) != 0x01000000))
+        return 0;
+
+    /* Do not translate whitespace - it will be unreadable */
+    if (keysym == 0x20)
+        return 0;
+
+    int codepoint = keysym & 0xFFFF;
+
+    /* Determine size and initial byte mask */
+    if (codepoint <= 0x007F) {
+        mask  = 0x00;
+        bytes = 1;
+    }
+    else if (codepoint <= 0x7FF) {
+        mask  = 0xC0;
+        bytes = 2;
+    }
+    else if (codepoint <= 0xFFFF) {
+        mask  = 0xE0;
+        bytes = 3;
+    }
+    else if (codepoint <= 0x1FFFFF) {
+        mask  = 0xF0;
+        bytes = 4;
+    }
+
+    /* Otherwise, invalid codepoint */
+    else {
+        *(key_name++) = '?';
+        return 1;
+    }
+
+    /* Offset buffer by size */
+    key_name += bytes;
+
+    /* Add null terminator */
+    *(key_name--) = '\0';
+
+    /* Add trailing bytes, if any */
+    for (i=1; i<bytes; i++) {
+        *(key_name--) = 0x80 | (codepoint & 0x3F);
+        codepoint >>= 6;
+    }
+
+    /* Set initial byte */
+    *key_name = mask | codepoint;
+
+    /* Done */
+    return bytes;
+
+}
+
 int guaclog_key_name(char* key_name, int keysym) {
 
     int name_length;
 
-    /* Search for name within list of known keys */
-    name_length = guaclog_locate_key_name(key_name, keysym);
+    /* Attempt to translate straight into a Unicode character */
+    name_length = guaclog_unicode_key_name(key_name, keysym);
+
+    /* If not Unicode, search for name within list of known keys */
+    if (name_length == 0)
+        name_length = guaclog_locate_key_name(key_name, keysym);
 
     /* Fallback to using hex keysym as name */
     if (name_length == 0)


[04/11] guacamole-server git commit: GUACAMOLE-313: Document log format.

Posted by vn...@apache.org.
GUACAMOLE-313: Document log format.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/c0b2871b
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/c0b2871b
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/c0b2871b

Branch: refs/heads/master
Commit: c0b2871b31a6093627247c0fe084ee843f634896
Parents: b7257d9
Author: Michael Jumper <mj...@apache.org>
Authored: Sun Dec 10 12:40:01 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 26 16:24:45 2018 -0800

----------------------------------------------------------------------
 src/guaclog/man/guaclog.1.in | 55 ++++++++++++++++++++++++++++++++-------
 1 file changed, 46 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/c0b2871b/src/guaclog/man/guaclog.1.in
----------------------------------------------------------------------
diff --git a/src/guaclog/man/guaclog.1.in b/src/guaclog/man/guaclog.1.in
index 11896d5..e5dfa9d 100644
--- a/src/guaclog/man/guaclog.1.in
+++ b/src/guaclog/man/guaclog.1.in
@@ -29,8 +29,8 @@ guaclog \- Guacamole input log interpreter
 .SH DESCRIPTION
 .B guaclog
 is an interpreter which accepts Guacamole protocol dumps, such as those saved
-when input logging is enabled on a Guacamole connection, writing human-readable
-text logs as output.
+when input logging is enabled for a Guacamole session recording, writing
+human-readable text logs as output.
 .B guaclog
 is essentially an implementation of a Guacamole client which accepts
 its input from files instead of a network connection, however unlike
@@ -42,19 +42,56 @@ file named \fIFILE\fR.txt. Existing files will not be overwritten; the
 interpreting process for any input file will be aborted if it would result in
 overwriting an existing file.
 .P
-Guacamole acquires a write lock on input logs as they are being written. By
+Guacamole acquires a write lock on recordings as they are being written. By
 default,
 .B guaclog
 will check whether the each input file is locked and will refuse to read and
-interpret an input file if it appears to be an in-progress log. This behavior
-can be overridden by specifying the \fB-f\fR option. Interpreting an
-in-progress log will still work; the resulting human-readable text file will
-simply cover the user's session only up to the current point in time.
+interpret an input file if it appears to be an in-progress recording. This
+behavior can be overridden by specifying the \fB-f\fR option. Interpreting an
+in-progress recording will still work; the resulting human-readable text file
+will simply cover the user's session only up to the current point in time.
 .
 .SH OPTIONS
 .TP
 \fB-f\fR
 Overrides the default behavior of
 .B guaclog
-such that input files will be interpreted even if they appear to be logs of
-in-progress Guacamole sessions.
+such that input files will be interpreted even if they appear to be recordings
+of in-progress Guacamole sessions.
+.
+.SH OUTPUT FORMAT
+The output format of
+.B guaclog
+is meant to match what the user would have typed within a typical text editor
+as closely as possible, while also representing non-printable characters and
+keyboard shortcuts in a human-readable way.
+.P
+All output is on one line, with new lines started only as a result of the user
+pressing enter/return. Keys which produce printable characters are translated
+into their corresponding Unicode codepoints and encoded as UTF-8, while
+non-printable characters are enclosed within angle brackets and represented
+with their human-readable names. Keyboard shortcuts which are made up of more
+than one key are enclosed within angle brackets, with each key within the
+shortcut separated by plus signs.
+.P
+Spaces and newlines are included as their Unicode character, except when
+represented within a keyboard shortcut, in which case their human-readable
+names are used instead. As the output of pressing tab can be easily mistaken
+for spaces, and as pressing tab frequently has special meaning within
+applications, tab is always represented by its human-readable name.
+.P
+Modifiers are output as part of keyboard shortcuts only. Simple pressing and
+releasing of a modifier will be ignored, as are presses of shift or AltGr while
+typing.
+.P
+For example, if the user typed "Hello WORLD!", selected everything by pressing
+Ctrl+a, copied the selected text by pressing Ctrl+c, switched to another
+application by pressing Alt+Shift+Tab, and then pasted the previously-copied
+text by pressing Ctrl+v, the resulting log from
+.B
+guaclog
+would look like:
+.PP
+.RS 0
+Hello WORLD!<Ctrl+a><Ctrl+c><Alt+Shift+Tab><Ctrl+v>
+


[11/11] guacamole-server git commit: GUACAMOLE-313: Merge add "guaclog" utility for producing human-readable interpreatations of keys pressed in session recordings.

Posted by vn...@apache.org.
GUACAMOLE-313: Merge add "guaclog" utility for producing human-readable interpreatations of keys pressed in session recordings.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/5f5b4ea8
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/5f5b4ea8
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/5f5b4ea8

Branch: refs/heads/master
Commit: 5f5b4ea8eb6d7198b6f7456101418ef97e53c5a1
Parents: 08f854f fdd17e3
Author: Nick Couchman <vn...@apache.org>
Authored: Tue Jan 30 14:07:35 2018 -0500
Committer: Nick Couchman <vn...@apache.org>
Committed: Tue Jan 30 14:07:35 2018 -0500

----------------------------------------------------------------------
 Makefile.am                   |   5 +
 configure.ac                  |  16 ++
 src/guacenc/man/guacenc.1.in  |   5 +-
 src/guaclog/.gitignore        |   5 +
 src/guaclog/Makefile.am       |  53 ++++++
 src/guaclog/guaclog.c         | 119 +++++++++++++
 src/guaclog/guaclog.h         |  31 ++++
 src/guaclog/instruction-key.c |  43 +++++
 src/guaclog/instructions.c    |  62 +++++++
 src/guaclog/instructions.h    | 108 ++++++++++++
 src/guaclog/interpret.c       | 152 +++++++++++++++++
 src/guaclog/interpret.h       |  51 ++++++
 src/guaclog/keydef.c          | 335 +++++++++++++++++++++++++++++++++++++
 src/guaclog/keydef.h          |  81 +++++++++
 src/guaclog/log.c             |  85 ++++++++++
 src/guaclog/log.h             |  73 ++++++++
 src/guaclog/man/guaclog.1.in  |  99 +++++++++++
 src/guaclog/state.c           | 266 +++++++++++++++++++++++++++++
 src/guaclog/state.h           | 125 ++++++++++++++
 19 files changed, 1713 insertions(+), 1 deletion(-)
----------------------------------------------------------------------



[03/11] guacamole-server git commit: GUACAMOLE-313: Separate naming logic for keysyms. Align previously-pressed keys.

Posted by vn...@apache.org.
GUACAMOLE-313: Separate naming logic for keysyms. Align previously-pressed keys.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-server/commit/df29735c
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-server/tree/df29735c
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-server/diff/df29735c

Branch: refs/heads/master
Commit: df29735c83c210900ba55d60c1e1a29b3c3ef03e
Parents: d39757b
Author: Michael Jumper <mj...@apache.org>
Authored: Sun Nov 26 18:00:29 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 26 16:24:43 2018 -0800

----------------------------------------------------------------------
 src/guaclog/Makefile.am |  2 ++
 src/guaclog/key-name.c  | 43 +++++++++++++++++++++++++++++++++++++
 src/guaclog/key-name.h  | 50 ++++++++++++++++++++++++++++++++++++++++++++
 src/guaclog/state.c     | 22 +++++++++++++++----
 4 files changed, 113 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/df29735c/src/guaclog/Makefile.am
----------------------------------------------------------------------
diff --git a/src/guaclog/Makefile.am b/src/guaclog/Makefile.am
index f1ad152..5007708 100644
--- a/src/guaclog/Makefile.am
+++ b/src/guaclog/Makefile.am
@@ -28,6 +28,7 @@ noinst_HEADERS =   \
     guaclog.h      \
     instructions.h \
     interpret.h    \
+    key-name.h     \
     log.h          \
     state.h
 
@@ -36,6 +37,7 @@ guaclog_SOURCES =     \
     instructions.c    \
     instruction-key.c \
     interpret.c       \
+    key-name.c        \
     log.c             \
     state.c
 

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/df29735c/src/guaclog/key-name.c
----------------------------------------------------------------------
diff --git a/src/guaclog/key-name.c b/src/guaclog/key-name.c
new file mode 100644
index 0000000..ffb622d
--- /dev/null
+++ b/src/guaclog/key-name.c
@@ -0,0 +1,43 @@
+/*
+ * 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 "key-name.h"
+#include "log.h"
+
+#include <stdio.h>
+
+int guaclog_key_name(char* key_name, int keysym) {
+
+    /* Fallback to using hex keysym as name */
+    int name_length = snprintf(key_name, GUACLOG_MAX_KEY_NAME_LENGTH,
+            "0x%X", keysym);
+
+    /* Truncate name if necessary */
+    if (name_length >= GUACLOG_MAX_KEY_NAME_LENGTH) {
+        name_length = GUACLOG_MAX_KEY_NAME_LENGTH - 1;
+        key_name[name_length] = '\0';
+        guaclog_log(GUAC_LOG_DEBUG, "Name for key 0x%X was "
+                "truncated.", keysym);
+    }
+
+    return name_length;
+
+}
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/df29735c/src/guaclog/key-name.h
----------------------------------------------------------------------
diff --git a/src/guaclog/key-name.h b/src/guaclog/key-name.h
new file mode 100644
index 0000000..a033b36
--- /dev/null
+++ b/src/guaclog/key-name.h
@@ -0,0 +1,50 @@
+/*
+ * 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 GUACLOG_KEY_NAME_H
+#define GUACLOG_KEY_NAME_H
+
+#include "config.h"
+
+/**
+ * The maximum size of the name of any key, in bytes.
+ */
+#define GUACLOG_MAX_KEY_NAME_LENGTH 64
+
+/**
+ * Copies the name of the key having the given keysym into the given buffer,
+ * which must be at least GUACLOG_MAX_KEY_NAME_LENGTH bytes long. This function
+ * always succeeds, ultimately resorting to using the hex value of the keysym
+ * as the name if no other human-readable name is known.
+ *
+ * @param key_name
+ *     The buffer to copy the key name into, which must be at least
+ *     GUACLOG_MAX_KEY_NAME_LENGTH.
+ *
+ * @param keysym
+ *     The X11 keysym of the key whose name should be stored in
+ *     key_name.
+ *
+ * @return
+ *     The length of the name, in bytes, excluding null terminator.
+ */
+int guaclog_key_name(char* key_name, int keysym);
+
+#endif
+

http://git-wip-us.apache.org/repos/asf/guacamole-server/blob/df29735c/src/guaclog/state.c
----------------------------------------------------------------------
diff --git a/src/guaclog/state.c b/src/guaclog/state.c
index cc6f763..616d043 100644
--- a/src/guaclog/state.c
+++ b/src/guaclog/state.c
@@ -18,6 +18,7 @@
  */
 
 #include "config.h"
+#include "key-name.h"
 #include "log.h"
 #include "state.h"
 
@@ -174,15 +175,28 @@ int guaclog_state_update_key(guaclog_state* state, int keysym, bool pressed) {
     /* Output new log entries only when keys are pressed */
     if (pressed) {   
 
-        /* STUB: Output raw hex log entry */
+        /* Compose log entry by inspecting the state of each tracked key */
         for (i = 0; i < state->active_keys; i++) {
 
+            guaclog_key_state* key = &state->key_states[i];
+
+            /* Translate keysym into human-readable name */
+            char key_name[GUACLOG_MAX_KEY_NAME_LENGTH];
+            int name_length = guaclog_key_name(key_name, key->keysym);
+
+            /* If not the final key, omit the name (it was printed earlier) */
+            if (i < state->active_keys - 1) {
+                memset(key_name, ' ', name_length);
+                if (key->pressed)
+                    key_name[name_length / 2] = '*';
+            }
+
+            /* Separate each key by a single space */
             if (i != 0)
                 fprintf(state->output, " ");
 
-            guaclog_key_state* key = &state->key_states[i];
-            fprintf(state->output, "0x%X:%s", key->keysym,
-                    key->pressed ? "*" : " ");
+            /* Print name of key */
+            fprintf(state->output, "%s", key_name);
 
         }