GUACAMOLE-313: Merge add "guaclog" utility for producing human-readable interpreatations of keys pressed in session recordings.

This commit is contained in:
Nick Couchman 2018-01-30 14:07:35 -05:00
commit 5f5b4ea8eb
19 changed files with 1713 additions and 1 deletions

View File

@ -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 \

View File

@ -1191,6 +1191,18 @@ AM_CONDITIONAL([ENABLE_GUACENC], [test "x${enable_guacenc}" = "xyes" \
-a "x${have_libavutil}" = "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}

View File

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

5
src/guaclog/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# Compiled guaclog
guaclog
guaclog.exe

53
src/guaclog/Makefile.am Normal file
View File

@ -0,0 +1,53 @@
#
# 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 \
keydef.h \
log.h \
state.h
guaclog_SOURCES = \
guaclog.c \
instructions.c \
instruction-key.c \
interpret.c \
keydef.c \
log.c \
state.c
guaclog_CFLAGS = \
-Werror -Wall \
@LIBGUAC_INCLUDE@
guaclog_LDADD = \
@LIBGUAC_LTLIB@
EXTRA_DIST = \
man/guaclog.1.in

119
src/guaclog/guaclog.c Normal file
View File

@ -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;
}

31
src/guaclog/guaclog.h Normal file
View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

108
src/guaclog/instructions.h Normal file
View File

@ -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

152
src/guaclog/interpret.c Normal file
View File

@ -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);
}

51
src/guaclog/interpret.h Normal file
View File

@ -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

335
src/guaclog/keydef.c Normal file
View File

@ -0,0 +1,335 @@
/*
* 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 <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* All known keys.
*/
const guaclog_keydef known_keys[] = {
{ 0xFE03, "AltGr", "", true },
{ 0xFF08, "Backspace" },
{ 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" },
{ 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", "", true },
{ 0xFFE2, "Shift", "", true },
{ 0xFFE3, "Ctrl", NULL, true },
{ 0xFFE4, "Ctrl", NULL, true },
{ 0xFFE5, "Caps" },
{ 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" }
};
/**
* 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 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.
*
* @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 guaclog_copy_key(guaclog_get_unknown_key(keysym));
}
void guaclog_keydef_free(guaclog_keydef* keydef) {
/* Ignore NULL keydef */
if (keydef == NULL)
return;
free(keydef->name);
free(keydef->value);
free(keydef);
}

81
src/guaclog/keydef.h Normal file
View File

@ -0,0 +1,81 @@
/*
* 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"
#include <stdbool.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;
/**
* 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;
/**
* 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

85
src/guaclog/log.c Normal file
View File

@ -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);
}

73
src/guaclog/log.h Normal file
View File

@ -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

View File

@ -0,0 +1,99 @@
.\"
.\" 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 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
.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 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 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 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>
.
.SH SEE ALSO
.BR guacenc (1)

266
src/guaclog/state.c Normal file
View File

@ -0,0 +1,266 @@
/*
* 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 "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;
/* No keys are initially tracked */
state->active_keys = 0;
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) {
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);
free(state);
return 0;
}
/**
* 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 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.
*
* @return
* Zero if the key state was successfully added, non-zero otherwise.
*/
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->keydef->keysym == keydef->keysym) {
guaclog_keydef_free(key->keydef);
key->keydef = keydef;
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.", keydef->keysym);
return 1;
}
/* Add key to state */
guaclog_key_state* key = &state->key_states[state->active_keys++];
key->keydef = keydef;
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;
}
/* Free all trimmed states */
guaclog_keydef_free(key->keydef);
}
/* No keys are active */
state->active_keys = 0;
}
/**
* 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;
/* Determine nature of key */
guaclog_keydef* keydef = guaclog_keydef_alloc(keysym);
if (keydef == NULL)
return 0;
/* 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 */
else if (pressed) {
if (guaclog_state_is_shortcut(state)) {
fprintf(state->output, "<");
/* Compose log entry by inspecting the state of each tracked key */
for (i = 0; i < state->active_keys; i++) {
/* 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);
}
fprintf(state->output, "%s>", keydef->value);
}
/* Print the key itself */
else {
if (keydef->value != NULL)
fprintf(state->output, "%s", keydef->value);
else
fprintf(state->output, "<%s>", keydef->name);
}
}
return 0;
}

125
src/guaclog/state.h Normal file
View File

@ -0,0 +1,125 @@
/*
* 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 "keydef.h"
#include <stdbool.h>
#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 definition of the key.
*/
guaclog_keydef* keydef;
/**
* 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 {
/**
* Output file stream.
*/
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;
/**
* 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