1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include "config.h"
21 #include "keydef.h"
22 #include "log.h"
23 
24 #include <assert.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 /**
31  * All known keys.
32  */
33 const guaclog_keydef known_keys[] = {
34     { 0xFE03, "AltGr", "", true },
35     { 0xFF08, "Backspace" },
36     { 0xFF09, "Tab" },
37     { 0xFF0B, "Clear" },
38     { 0xFF0D, "Return", "\n" },
39     { 0xFF13, "Pause" },
40     { 0xFF14, "Scroll" },
41     { 0xFF15, "SysReq" },
42     { 0xFF1B, "Escape" },
43     { 0xFF50, "Home" },
44     { 0xFF51, "Left" },
45     { 0xFF52, "Up" },
46     { 0xFF53, "Right" },
47     { 0xFF54, "Down" },
48     { 0xFF55, "Page Up" },
49     { 0xFF56, "Page Down" },
50     { 0xFF57, "End" },
51     { 0xFF63, "Insert" },
52     { 0xFF65, "Undo" },
53     { 0xFF6A, "Help" },
54     { 0xFF7F, "Num" },
55     { 0xFF80, "Space", " " },
56     { 0xFF8D, "Enter", "\n" },
57     { 0xFF95, "Home" },
58     { 0xFF96, "Left" },
59     { 0xFF97, "Up" },
60     { 0xFF98, "Right" },
61     { 0xFF99, "Down" },
62     { 0xFF9A, "Page Up" },
63     { 0xFF9B, "Page Down" },
64     { 0xFF9C, "End" },
65     { 0xFF9E, "Insert" },
66     { 0xFFAA, "*", "*" },
67     { 0xFFAB, "+", "+" },
68     { 0xFFAD, "-", "-" },
69     { 0xFFAE, ".", "." },
70     { 0xFFAF, "/", "/" },
71     { 0xFFB0, "0", "0" },
72     { 0xFFB1, "1", "1" },
73     { 0xFFB2, "2", "2" },
74     { 0xFFB3, "3", "3" },
75     { 0xFFB4, "4", "4" },
76     { 0xFFB5, "5", "5" },
77     { 0xFFB6, "6", "6" },
78     { 0xFFB7, "7", "7" },
79     { 0xFFB8, "8", "8" },
80     { 0xFFB9, "9", "9" },
81     { 0xFFBE, "F1" },
82     { 0xFFBF, "F2" },
83     { 0xFFC0, "F3" },
84     { 0xFFC1, "F4" },
85     { 0xFFC2, "F5" },
86     { 0xFFC3, "F6" },
87     { 0xFFC4, "F7" },
88     { 0xFFC5, "F8" },
89     { 0xFFC6, "F9" },
90     { 0xFFC7, "F10" },
91     { 0xFFC8, "F11" },
92     { 0xFFC9, "F12" },
93     { 0xFFCA, "F13" },
94     { 0xFFCB, "F14" },
95     { 0xFFCC, "F15" },
96     { 0xFFCD, "F16" },
97     { 0xFFCE, "F17" },
98     { 0xFFCF, "F18" },
99     { 0xFFD0, "F19" },
100     { 0xFFD1, "F20" },
101     { 0xFFD2, "F21" },
102     { 0xFFD3, "F22" },
103     { 0xFFD4, "F23" },
104     { 0xFFD5, "F24" },
105     { 0xFFE1, "Shift", "", true },
106     { 0xFFE2, "Shift", "", true },
107     { 0xFFE3, "Ctrl", NULL, true },
108     { 0xFFE4, "Ctrl", NULL, true },
109     { 0xFFE5, "Caps" },
110     { 0xFFE7, "Meta", NULL, true },
111     { 0xFFE8, "Meta", NULL, true },
112     { 0xFFE9, "Alt", NULL, true },
113     { 0xFFEA, "Alt", NULL, true },
114     { 0xFFEB, "Super", NULL, true },
115     { 0xFFEC, "Super", NULL, true },
116     { 0xFFED, "Hyper", NULL, true },
117     { 0xFFEE, "Hyper", NULL, true },
118     { 0xFFFF, "Delete" }
119 };
120 
121 /**
122  * Comparator for the standard bsearch() function which compares an integer
123  * keysym against the keysym associated with a guaclog_keydef.
124  *
125  * @param key
126  *     The key value being compared against the member. This MUST be the
127  *     keysym value, passed through typecasting to an intptr_t (NOT a pointer
128  *     to the int itself).
129  *
130  * @param member
131  *     The member within the known_keys array being compared against the given
132  *     key.
133  *
134  * @return
135  *     Zero if the given keysym is equal to that of the given member, a
136  *     positive value if the given keysym is greater than that of the given
137  *     member, or a negative value if the given keysym is less than that of the
138  *     given member.
139  */
guaclog_keydef_bsearch_compare(const void * key,const void * member)140 static int guaclog_keydef_bsearch_compare(const void* key,
141         const void* member) {
142 
143     int keysym = (int) ((intptr_t) key);
144     guaclog_keydef* current = (guaclog_keydef*) member;
145 
146     /* Compare given keysym to keysym of current member */
147     return keysym  - current->keysym;
148 
149 }
150 
151 /**
152  * Searches through the known_keys array of known keys for the name of the key
153  * having the given keysym, returning a pointer to the static guaclog_keydef
154  * within the array if found.
155  *
156  * @param keysym
157  *     The X11 keysym of the key.
158  *
159  * @return
160  *     A pointer to the static guaclog_keydef associated with the given keysym,
161  *     or NULL if the key could not be found.
162  */
guaclog_get_known_key(int keysym)163 static guaclog_keydef* guaclog_get_known_key(int keysym) {
164 
165     /* Search through known keys for given keysym */
166     return bsearch((void*) ((intptr_t) keysym),
167             known_keys, sizeof(known_keys) / sizeof(known_keys[0]),
168             sizeof(known_keys[0]), guaclog_keydef_bsearch_compare);
169 
170 }
171 
172 /**
173  * Returns a statically-allocated guaclog_keydef representing an unknown key,
174  * deriving the name of the key from the hexadecimal value of the keysym.
175  *
176  * @param keysym
177  *     The X11 keysym of the key.
178  *
179  * @return
180  *     A statically-allocated guaclog_keydef representing the key associated
181  *     with the given keysym.
182  */
guaclog_get_unknown_key(int keysym)183 static guaclog_keydef* guaclog_get_unknown_key(int keysym) {
184 
185     static char unknown_keydef_name[64];
186     static guaclog_keydef unknown_keydef;
187 
188     /* Write keysym as hex */
189     int size = snprintf(unknown_keydef_name, sizeof(unknown_keydef_name),
190             "0x%X", keysym);
191 
192     /* Hex string is guaranteed to fit within the provided 64 bytes */
193     assert(size < sizeof(unknown_keydef_name));
194 
195     /* Return static key definition */
196     unknown_keydef.keysym = keysym;
197     unknown_keydef.name = unknown_keydef_name;
198     return &unknown_keydef;
199 
200 }
201 
202 /**
203  * Returns a statically-allocated guaclog_keydef representing the key
204  * associated with the given keysym, deriving the name and value of the key
205  * using its corresponding Unicode character.
206  *
207  * @param keysym
208  *     The X11 keysym of the key.
209  *
210  * @return
211  *     A statically-allocated guaclog_keydef representing the key associated
212  *     with the given keysym, or NULL if the given keysym has no corresponding
213  *     Unicode character.
214  */
guaclog_get_unicode_key(int keysym)215 static guaclog_keydef* guaclog_get_unicode_key(int keysym) {
216 
217     static char unicode_keydef_name[8];
218 
219     static guaclog_keydef unicode_keydef;
220 
221     int i;
222     int mask, bytes;
223 
224     /* Translate only if keysym maps to Unicode */
225     if (keysym < 0x00 || (keysym > 0xFF && (keysym | 0xFFFF) != 0x0100FFFF))
226         return NULL;
227 
228     int codepoint = keysym & 0xFFFF;
229 
230     /* Determine size and initial byte mask */
231     if (codepoint <= 0x007F) {
232         mask  = 0x00;
233         bytes = 1;
234     }
235     else if (codepoint <= 0x7FF) {
236         mask  = 0xC0;
237         bytes = 2;
238     }
239     else {
240         assert(codepoint <= 0xFFFF);
241         mask  = 0xE0;
242         bytes = 3;
243     }
244 
245     /* Offset buffer by size */
246     char* key_name = unicode_keydef_name + bytes;
247 
248     /* Add null terminator */
249     *(key_name--) = '\0';
250 
251     /* Add trailing bytes, if any */
252     for (i=1; i<bytes; i++) {
253         *(key_name--) = 0x80 | (codepoint & 0x3F);
254         codepoint >>= 6;
255     }
256 
257     /* Set initial byte */
258     *key_name = mask | codepoint;
259 
260     /* Return static key definition */
261     unicode_keydef.keysym = keysym;
262     unicode_keydef.name = unicode_keydef.value = unicode_keydef_name;
263     unicode_keydef.modifier = false;
264     return &unicode_keydef;
265 
266 }
267 
268 /**
269  * Copies the given guaclog_keydef into a newly-allocated guaclog_keydef
270  * structure. The resulting guaclog_keydef must eventually be freed through a
271  * call to guaclog_keydef_free().
272  *
273  * @param keydef
274  *     The guaclog_keydef to copy.
275  *
276  * @return
277  *     A newly-allocated guaclog_keydef structure copied from the given
278  *     guaclog_keydef.
279  */
guaclog_copy_key(guaclog_keydef * keydef)280 static guaclog_keydef* guaclog_copy_key(guaclog_keydef* keydef) {
281 
282     guaclog_keydef* copy = malloc(sizeof(guaclog_keydef));
283 
284     /* Always copy keysym and name */
285     copy->keysym = keydef->keysym;
286     copy->name = strdup(keydef->name);
287     copy->modifier = keydef->modifier;
288 
289     /* Copy value only if defined */
290     if (keydef->value != NULL)
291         copy->value = strdup(keydef->value);
292     else
293         copy->value = NULL;
294 
295     return copy;
296 
297 }
298 
guaclog_keydef_alloc(int keysym)299 guaclog_keydef* guaclog_keydef_alloc(int keysym) {
300 
301     guaclog_keydef* keydef;
302 
303     /* Check list of known keys first */
304     keydef = guaclog_get_known_key(keysym);
305     if (keydef != NULL)
306         return guaclog_copy_key(keydef);
307 
308     /* Failing that, attempt to translate straight into a Unicode character */
309     keydef = guaclog_get_unicode_key(keysym);
310     if (keydef != NULL)
311         return guaclog_copy_key(keydef);
312 
313     /* Key not known */
314     guaclog_log(GUAC_LOG_DEBUG, "Definition not found for key 0x%X.", keysym);
315     return guaclog_copy_key(guaclog_get_unknown_key(keysym));
316 
317 }
318 
guaclog_keydef_free(guaclog_keydef * keydef)319 void guaclog_keydef_free(guaclog_keydef* keydef) {
320 
321     /* Ignore NULL keydef */
322     if (keydef == NULL)
323         return;
324 
325     free(keydef->name);
326     free(keydef->value);
327     free(keydef);
328 
329 }
330 
331