diff --git a/src/guaclog/Makefile.am b/src/guaclog/Makefile.am index 5007708c..6acced30 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 diff --git a/src/guaclog/key-name.h b/src/guaclog/key-name.h deleted file mode 100644 index a033b36c..00000000 --- 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 - diff --git a/src/guaclog/key-name.c b/src/guaclog/keydef.c similarity index 55% rename from src/guaclog/key-name.c rename to src/guaclog/keydef.c index 5ae73b5d..96165004 100644 --- a/src/guaclog/key-name.c +++ b/src/guaclog/keydef.c @@ -18,39 +18,23 @@ */ #include "config.h" -#include "key-name.h" +#include "keydef.h" #include "log.h" +#include #include #include - -/** - * 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; +#include /** * All known keys. */ -const guaclog_known_key known_keys[] = { - { 0x0020, "Space" }, +const guaclog_keydef known_keys[] = { { 0xFE03, "AltGr" }, { 0xFF08, "Backspace" }, - { 0xFF09, "Tab" }, + { 0xFF09, "Tab", "" }, { 0xFF0B, "Clear" }, - { 0xFF0D, "Return" }, + { 0xFF0D, "Return", "\n" }, { 0xFF13, "Pause" }, { 0xFF1B, "Escape" }, { 0xFF51, "Left" }, @@ -62,9 +46,8 @@ const guaclog_known_key known_keys[] = { { 0xFF63, "Insert" }, { 0xFF65, "Undo" }, { 0xFF6A, "Help" }, - { 0xFF80, "Space" }, - { 0xFF8D, "Enter" }, - { 0xFFBD, "Equals" }, + { 0xFF80, "Space", " " }, + { 0xFF8D, "Enter", "\n" }, { 0xFFBE, "F1" }, { 0xFFBF, "F2" }, { 0xFFC0, "F3" }, @@ -89,8 +72,8 @@ const guaclog_known_key known_keys[] = { { 0xFFD3, "F22" }, { 0xFFD4, "F23" }, { 0xFFD5, "F24" }, - { 0xFFE1, "Shift" }, - { 0xFFE2, "Shift" }, + { 0xFFE1, "Shift", "" }, + { 0xFFE2, "Shift", "" }, { 0xFFE3, "Ctrl" }, { 0xFFE4, "Ctrl" }, { 0xFFE5, "Caps" }, @@ -108,7 +91,7 @@ const guaclog_known_key known_keys[] = { /** * Comparator for the standard bsearch() function which compares an integer - * keysym against the keysym associated with a guaclog_known_key. + * keysym against the keysym associated with a guaclog_keydef. * * @param key * The key value being compared against the member. This MUST be the @@ -125,11 +108,11 @@ const guaclog_known_key known_keys[] = { * 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, +static int guaclog_keydef_bsearch_compare(const void* key, const void* member) { int keysym = (int) ((intptr_t) key); - guaclog_known_key* current = (guaclog_known_key*) member; + guaclog_keydef* current = (guaclog_keydef*) member; /* Compare given keysym to keysym of current member */ return keysym - current->keysym; @@ -138,67 +121,50 @@ static int guaclog_known_key_bsearch_compare(const void* key, /** * 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. + * 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 whose name should be stored in - * key_name. + * The X11 keysym of the key. * * @return - * The length of the name, in bytes, excluding null terminator, or zero if - * the key could not be found. + * A pointer to the static guaclog_keydef associated with the given keysym, + * or NULL if the key could not be found. */ -static int guaclog_locate_key_name(char* key_name, int keysym) { +static guaclog_keydef* guaclog_get_known_key(int keysym) { /* Search through known keys for given keysym */ - guaclog_known_key* found = bsearch((void*) ((intptr_t) keysym), + return 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; + sizeof(known_keys[0]), guaclog_keydef_bsearch_compare); } /** - * 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. + * 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 whose name should be stored in - * key_name. + * The X11 keysym of the key. * * @return - * The length of the name, in bytes, excluding null terminator, or zero if - * a readable name cannot be directly produced via Unicode alone. + * 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 int guaclog_unicode_key_name(char* key_name, int keysym) { +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 0; - - /* Do not translate whitespace - it will be unreadable */ - if (keysym == 0x20) - return 0; + return NULL; int codepoint = keysym & 0xFFFF; @@ -221,13 +187,11 @@ static int guaclog_unicode_key_name(char* key_name, int keysym) { } /* Otherwise, invalid codepoint */ - else { - *(key_name++) = '?'; - return 1; - } + else + return NULL; /* Offset buffer by size */ - key_name += bytes; + char* key_name = unicode_keydef_name + bytes; /* Add null terminator */ *(key_name--) = '\0'; @@ -241,36 +205,72 @@ static int guaclog_unicode_key_name(char* key_name, int keysym) { /* Set initial byte */ *key_name = mask | codepoint; - /* Done */ - return bytes; + /* Return static key definition */ + unicode_keydef.keysym = keysym; + unicode_keydef.name = unicode_keydef.value = unicode_keydef_name; + return &unicode_keydef; } -int guaclog_key_name(char* key_name, int keysym) { +/** + * 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) { - int name_length; + guaclog_keydef* copy = malloc(sizeof(guaclog_keydef)); - /* Attempt to translate straight into a Unicode character */ - name_length = guaclog_unicode_key_name(key_name, keysym); + /* Always copy keysym and name */ + copy->keysym = keydef->keysym; + copy->name = strdup(keydef->name); - /* If not Unicode, search for name within list of known keys */ - if (name_length == 0) - name_length = guaclog_locate_key_name(key_name, keysym); + /* Copy value only if defined */ + if (keydef->value != NULL) + copy->value = strdup(keydef->value); + else + copy->value = NULL; - /* 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; + 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); } diff --git a/src/guaclog/keydef.h b/src/guaclog/keydef.h new file mode 100644 index 00000000..d0b2720a --- /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 + diff --git a/src/guaclog/state.c b/src/guaclog/state.c index 616d0439..4965985d 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, "<"); + + /* 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); - /* 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, " "); - - /* 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); } diff --git a/src/guaclog/state.h b/src/guaclog/state.h index 0dd2f2f9..b476ddeb 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 #include @@ -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).